政安晨:【Keras机器学习示例演绎】(六)—— 通过 CT 扫描进行 3D 图像分类

news2024/11/13 15:58:55

目录

简介

设置

下载 MosMedData:胸部CT扫描与COVID-19相关发现

加载数据和预处理

建立训练和验证数据集

数据增强

定义 3D 卷积神经网络

训练模型

模型性能可视化

通过一次 CT 扫描进行预测


政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

收录专栏: TensorFlow与Keras机器学习实战

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

本文目标:训练三维卷积神经网络,预测是否存在肺炎。

简介


本示例将展示构建三维卷积神经网络(CNN)所需的步骤,以预测计算机断层扫描(CT)中是否存在病毒性肺炎。二维卷积神经网络通常用于处理 RGB 图像(3 个通道)。

三维卷积神经网络与三维卷积神经网络完全相同:它的输入是三维体积或二维帧序列(如 CT 扫描中的切片),三维卷积神经网络是学习体积数据表示的强大模型。

设置

import os
import zipfile
import numpy as np
import tensorflow as tf  # for data preprocessing

import keras
from keras import layers

下载 MosMedData:胸部CT扫描与COVID-19相关发现

在本示例中,我们使用了 MosMedData.Net 的一个子集:与 COVID-19 相关结果的胸部 CT 扫描。该数据集包括有 COVID-19 相关结果的肺部 CT 扫描,以及没有此类结果的肺部 CT 扫描。

我们将使用 CT 扫描的相关放射学结果作为标签来构建分类器,以预测是否存在病毒性肺炎。

因此,这项任务是一个二元分类问题。

# Download url of normal CT scans.
url = "https://github.com/hasibzunair/3D-image-classification-tutorial/releases/download/v0.2/CT-0.zip"
filename = os.path.join(os.getcwd(), "CT-0.zip")
keras.utils.get_file(filename, url)

# Download url of abnormal CT scans.
url = "https://github.com/hasibzunair/3D-image-classification-tutorial/releases/download/v0.2/CT-23.zip"
filename = os.path.join(os.getcwd(), "CT-23.zip")
keras.utils.get_file(filename, url)

# Make a directory to store the data.
os.makedirs("MosMedData")

# Unzip data in the newly created directory.
with zipfile.ZipFile("CT-0.zip", "r") as z_fp:
    z_fp.extractall("./MosMedData/")

with zipfile.ZipFile("CT-23.zip", "r") as z_fp:
    z_fp.extractall("./MosMedData/")

演绎展示:
 

Downloading data from https://github.com/hasibzunair/3D-image-classification-tutorial/releases/download/v0.2/CT-0.zip

1045162547/1045162547 ━━━━━━━━━━━━━━━━━━━━ 4s 0us/step

加载数据和预处理


文件以 Nifti 格式提供,扩展名为 .nii。为了读取扫描结果,我们使用了 nibabel 软件包。

您可以通过 pip install nibabel 安装该软件包。

CT 扫描以 Hounsfield 单位(HU)存储原始体素强度。在本数据集中,它们的范围从-1024 到 2000 以上。高于 400 的骨骼具有不同的放射性强度,因此将其作为一个较高的界限。通常使用介于 -1000 和 400 之间的阈值对 CT 扫描进行归一化处理。

为了处理数据,我们采取了以下措施:

× 我们首先将体积旋转 90 度,使方向固定下来
× 将 HU 值缩放至 0 和 1 之间。
× 调整宽度、高度和深度。


在此,我们定义了几个辅助函数来处理数据。这些函数将在建立训练和验证数据集时使用。

import nibabel as nib

from scipy import ndimage


def read_nifti_file(filepath):
    """Read and load volume"""
    # Read file
    scan = nib.load(filepath)
    # Get raw data
    scan = scan.get_fdata()
    return scan


def normalize(volume):
    """Normalize the volume"""
    min = -1000
    max = 400
    volume[volume < min] = min
    volume[volume > max] = max
    volume = (volume - min) / (max - min)
    volume = volume.astype("float32")
    return volume


def resize_volume(img):
    """Resize across z-axis"""
    # Set the desired depth
    desired_depth = 64
    desired_width = 128
    desired_height = 128
    # Get current depth
    current_depth = img.shape[-1]
    current_width = img.shape[0]
    current_height = img.shape[1]
    # Compute depth factor
    depth = current_depth / desired_depth
    width = current_width / desired_width
    height = current_height / desired_height
    depth_factor = 1 / depth
    width_factor = 1 / width
    height_factor = 1 / height
    # Rotate
    img = ndimage.rotate(img, 90, reshape=False)
    # Resize across z-axis
    img = ndimage.zoom(img, (width_factor, height_factor, depth_factor), order=1)
    return img


def process_scan(path):
    """Read and resize volume"""
    # Read scan
    volume = read_nifti_file(path)
    # Normalize
    volume = normalize(volume)
    # Resize width, height and depth
    volume = resize_volume(volume)
    return volume

让我们从类目录中读取 CT 扫描的路径。

# Folder "CT-0" consist of CT scans having normal lung tissue,
# no CT-signs of viral pneumonia.
normal_scan_paths = [
    os.path.join(os.getcwd(), "MosMedData/CT-0", x)
    for x in os.listdir("MosMedData/CT-0")
]
# Folder "CT-23" consist of CT scans having several ground-glass opacifications,
# involvement of lung parenchyma.
abnormal_scan_paths = [
    os.path.join(os.getcwd(), "MosMedData/CT-23", x)
    for x in os.listdir("MosMedData/CT-23")
]

print("CT scans with normal lung tissue: " + str(len(normal_scan_paths)))
print("CT scans with abnormal lung tissue: " + str(len(abnormal_scan_paths)))

演绎展示:

CT scans with normal lung tissue: 100
CT scans with abnormal lung tissue: 100

建立训练和验证数据集


从类目录中读取扫描数据并分配标签。对扫描数据进行下采样,使其大小为 128x128x64。将原始 HU 值的范围调整为 0 至 1。最后,将数据集分成训练子集和验证子集。

# Read and process the scans.
# Each scan is resized across height, width, and depth and rescaled.
abnormal_scans = np.array([process_scan(path) for path in abnormal_scan_paths])
normal_scans = np.array([process_scan(path) for path in normal_scan_paths])

# For the CT scans having presence of viral pneumonia
# assign 1, for the normal ones assign 0.
abnormal_labels = np.array([1 for _ in range(len(abnormal_scans))])
normal_labels = np.array([0 for _ in range(len(normal_scans))])

# Split data in the ratio 70-30 for training and validation.
x_train = np.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0)
y_train = np.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0)
x_val = np.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0)
y_val = np.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0)
print(
    "Number of samples in train and validation are %d and %d."
    % (x_train.shape[0], x_val.shape[0])
)

演绎展示:
 

Number of samples in train and validation are 140 and 60.

数据增强


在训练过程中,我们还通过随机角度旋转来增强 CT 扫描数据。由于数据存储在形状为(样本、高度、宽度、深度)的 3 级张量中,我们在轴 4 上添加了大小为 1 的维度,以便对数据进行三维卷积。因此,新的形状为(样本、高度、宽度、深度、1)。预处理和增强技术种类繁多,本示例展示了几种简单的预处理和增强技术。

import random

from scipy import ndimage


def rotate(volume):
    """Rotate the volume by a few degrees"""

    def scipy_rotate(volume):
        # define some rotation angles
        angles = [-20, -10, -5, 5, 10, 20]
        # pick angles at random
        angle = random.choice(angles)
        # rotate volume
        volume = ndimage.rotate(volume, angle, reshape=False)
        volume[volume < 0] = 0
        volume[volume > 1] = 1
        return volume

    augmented_volume = tf.numpy_function(scipy_rotate, [volume], tf.float32)
    return augmented_volume


def train_preprocessing(volume, label):
    """Process training data by rotating and adding a channel."""
    # Rotate volume
    volume = rotate(volume)
    volume = tf.expand_dims(volume, axis=3)
    return volume, label


def validation_preprocessing(volume, label):
    """Process validation data by only adding a channel."""
    volume = tf.expand_dims(volume, axis=3)
    return volume, label

在定义训练和验证数据加载器时,训练数据会通过增强函数随机旋转不同角度的体积。请注意,训练数据和验证数据都已被重新调整为 0 至 1 之间的值。

# Define data loaders.
train_loader = tf.data.Dataset.from_tensor_slices((x_train, y_train))
validation_loader = tf.data.Dataset.from_tensor_slices((x_val, y_val))

batch_size = 2
# Augment the on the fly during training.
train_dataset = (
    train_loader.shuffle(len(x_train))
    .map(train_preprocessing)
    .batch(batch_size)
    .prefetch(2)
)
# Only rescale.
validation_dataset = (
    validation_loader.shuffle(len(x_val))
    .map(validation_preprocessing)
    .batch(batch_size)
    .prefetch(2)
)

可视化增强 CT 扫描。

import matplotlib.pyplot as plt

data = train_dataset.take(1)
images, labels = list(data)[0]
images = images.numpy()
image = images[0]
print("Dimension of the CT scan is:", image.shape)
plt.imshow(np.squeeze(image[:, :, 30]), cmap="gray")

演绎展示:
 

Dimension of the CT scan is: (128, 128, 64, 1)

<matplotlib.image.AxesImage at 0x7fc5b9900d50>

由于 CT 扫描有许多切片,因此我们可以将这些切片做成蒙太奇效果。

def plot_slices(num_rows, num_columns, width, height, data):
    """Plot a montage of 20 CT slices"""
    data = np.rot90(np.array(data))
    data = np.transpose(data)
    data = np.reshape(data, (num_rows, num_columns, width, height))
    rows_data, columns_data = data.shape[0], data.shape[1]
    heights = [slc[0].shape[0] for slc in data]
    widths = [slc.shape[1] for slc in data[0]]
    fig_width = 12.0
    fig_height = fig_width * sum(heights) / sum(widths)
    f, axarr = plt.subplots(
        rows_data,
        columns_data,
        figsize=(fig_width, fig_height),
        gridspec_kw={"height_ratios": heights},
    )
    for i in range(rows_data):
        for j in range(columns_data):
            axarr[i, j].imshow(data[i][j], cmap="gray")
            axarr[i, j].axis("off")
    plt.subplots_adjust(wspace=0, hspace=0, left=0, right=1, bottom=0, top=1)
    plt.show()


# Visualize montage of slices.
# 4 rows and 10 columns for 100 slices of the CT scan.
plot_slices(4, 10, 128, 128, image[:, :, :40])

定义 3D 卷积神经网络

为使模型更易于理解,我们将其结构划分为多个区块。本示例中使用的 3D CNN 架构就是基于这篇论文。

def get_model(width=128, height=128, depth=64):
    """Build a 3D convolutional neural network model."""

    inputs = keras.Input((width, height, depth, 1))

    x = layers.Conv3D(filters=64, kernel_size=3, activation="relu")(inputs)
    x = layers.MaxPool3D(pool_size=2)(x)
    x = layers.BatchNormalization()(x)

    x = layers.Conv3D(filters=64, kernel_size=3, activation="relu")(x)
    x = layers.MaxPool3D(pool_size=2)(x)
    x = layers.BatchNormalization()(x)

    x = layers.Conv3D(filters=128, kernel_size=3, activation="relu")(x)
    x = layers.MaxPool3D(pool_size=2)(x)
    x = layers.BatchNormalization()(x)

    x = layers.Conv3D(filters=256, kernel_size=3, activation="relu")(x)
    x = layers.MaxPool3D(pool_size=2)(x)
    x = layers.BatchNormalization()(x)

    x = layers.GlobalAveragePooling3D()(x)
    x = layers.Dense(units=512, activation="relu")(x)
    x = layers.Dropout(0.3)(x)

    outputs = layers.Dense(units=1, activation="sigmoid")(x)

    # Define the model.
    model = keras.Model(inputs, outputs, name="3dcnn")
    return model


# Build model.
model = get_model(width=128, height=128, depth=64)
model.summary()

训练模型

# Compile model.
initial_learning_rate = 0.0001
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True
)
model.compile(
    loss="binary_crossentropy",
    optimizer=keras.optimizers.Adam(learning_rate=lr_schedule),
    metrics=["acc"],
    run_eagerly=True,
)

# Define callbacks.
checkpoint_cb = keras.callbacks.ModelCheckpoint(
    "3d_image_classification.keras", save_best_only=True
)
early_stopping_cb = keras.callbacks.EarlyStopping(monitor="val_acc", patience=15)

# Train the model, doing validation at the end of each epoch
epochs = 100
model.fit(
    train_dataset,
    validation_data=validation_dataset,
    epochs=epochs,
    shuffle=True,
    verbose=2,
    callbacks=[checkpoint_cb, early_stopping_cb],
)

展示演绎:

Epoch 1/100

70/70 - 40s - 568ms/step - acc: 0.5786 - loss: 0.7128 - val_acc: 0.5000 - val_loss: 0.8744

Epoch 2/100

70/70 - 26s - 370ms/step - acc: 0.6000 - loss: 0.6760 - val_acc: 0.5000 - val_loss: 1.2741

Epoch 3/100

70/70 - 26s - 373ms/step - acc: 0.5643 - loss: 0.6768 - val_acc: 0.5000 - val_loss: 1.4767

Epoch 4/100

70/70 - 26s - 376ms/step - acc: 0.6643 - loss: 0.6671 - val_acc: 0.5000 - val_loss: 1.2609

Epoch 5/100

70/70 - 26s - 374ms/step - acc: 0.6714 - loss: 0.6274 - val_acc: 0.5667 - val_loss: 0.6470

Epoch 6/100

70/70 - 26s - 372ms/step - acc: 0.5929 - loss: 0.6492 - val_acc: 0.6667 - val_loss: 0.6022

Epoch 7/100

70/70 - 26s - 374ms/step - acc: 0.5929 - loss: 0.6601 - val_acc: 0.5667 - val_loss: 0.6788

Epoch 8/100

70/70 - 26s - 378ms/step - acc: 0.6000 - loss: 0.6559 - val_acc: 0.6667 - val_loss: 0.6090

Epoch 9/100

70/70 - 26s - 373ms/step - acc: 0.6357 - loss: 0.6423 - val_acc: 0.6000 - val_loss: 0.6535

Epoch 10/100

70/70 - 26s - 374ms/step - acc: 0.6500 - loss: 0.6127 - val_acc: 0.6500 - val_loss: 0.6204

Epoch 11/100

70/70 - 26s - 374ms/step - acc: 0.6714 - loss: 0.5994 - val_acc: 0.7000 - val_loss: 0.6218

Epoch 12/100

70/70 - 26s - 374ms/step - acc: 0.6714 - loss: 0.5980 - val_acc: 0.7167 - val_loss: 0.5069

Epoch 13/100

70/70 - 26s - 369ms/step - acc: 0.7214 - loss: 0.6003 - val_acc: 0.7833 - val_loss: 0.5182

Epoch 14/100

70/70 - 26s - 372ms/step - acc: 0.6643 - loss: 0.6076 - val_acc: 0.7167 - val_loss: 0.5613

Epoch 15/100

70/70 - 26s - 373ms/step - acc: 0.6571 - loss: 0.6359 - val_acc: 0.6167 - val_loss: 0.6184

Epoch 16/100

70/70 - 26s - 374ms/step - acc: 0.6429 - loss: 0.6053 - val_acc: 0.7167 - val_loss: 0.5258

Epoch 17/100

70/70 - 26s - 370ms/step - acc: 0.6786 - loss: 0.6119 - val_acc: 0.5667 - val_loss: 0.8481

Epoch 18/100

70/70 - 26s - 372ms/step - acc: 0.6286 - loss: 0.6298 - val_acc: 0.6667 - val_loss: 0.5709

Epoch 19/100

70/70 - 26s - 372ms/step - acc: 0.7214 - loss: 0.5979 - val_acc: 0.5833 - val_loss: 0.6730

Epoch 20/100

70/70 - 26s - 372ms/step - acc: 0.7571 - loss: 0.5224 - val_acc: 0.7167 - val_loss: 0.5710

Epoch 21/100

70/70 - 26s - 372ms/step - acc: 0.7357 - loss: 0.5606 - val_acc: 0.7167 - val_loss: 0.5444

Epoch 22/100

70/70 - 26s - 372ms/step - acc: 0.7357 - loss: 0.5334 - val_acc: 0.5667 - val_loss: 0.7919

Epoch 23/100

70/70 - 26s - 373ms/step - acc: 0.7071 - loss: 0.5337 - val_acc: 0.5167 - val_loss: 0.9527

Epoch 24/100

70/70 - 26s - 371ms/step - acc: 0.7071 - loss: 0.5635 - val_acc: 0.7167 - val_loss: 0.5333

Epoch 25/100

70/70 - 26s - 373ms/step - acc: 0.7643 - loss: 0.4787 - val_acc: 0.6333 - val_loss: 1.0172

Epoch 26/100

70/70 - 26s - 372ms/step - acc: 0.7357 - loss: 0.5535 - val_acc: 0.6500 - val_loss: 0.6926

Epoch 27/100

70/70 - 26s - 370ms/step - acc: 0.7286 - loss: 0.5608 - val_acc: 0.5000 - val_loss: 3.3032

Epoch 28/100

70/70 - 26s - 370ms/step - acc: 0.7429 - loss: 0.5436 - val_acc: 0.6500 - val_loss: 0.6438

<keras.src.callbacks.history.History at 0x7fc5b923e810>

值得注意的是,样本数量非常少(只有 200 个),而且我们没有指定随机种子。因此,结果可能会有很大差异。完整数据集包括 1000 多张 CT 扫描图像,可在此处找到。使用完整数据集,准确率达到 83%。在这两种情况下,分类结果都有 6% 到 7% 的差异。

模型性能可视化


这里绘制了训练集和验证集的模型准确率和损失。由于验证集是类平衡的,因此准确率能无偏见地反映模型的性能。

fig, ax = plt.subplots(1, 2, figsize=(20, 3))
ax = ax.ravel()

for i, metric in enumerate(["acc", "loss"]):
    ax[i].plot(model.history.history[metric])
    ax[i].plot(model.history.history["val_" + metric])
    ax[i].set_title("Model {}".format(metric))
    ax[i].set_xlabel("epochs")
    ax[i].set_ylabel(metric)
    ax[i].legend(["train", "val"])

通过一次 CT 扫描进行预测

# Load best weights.
model.load_weights("3d_image_classification.keras")
prediction = model.predict(np.expand_dims(x_val[0], axis=0))[0]
scores = [1 - prediction[0], prediction[0]]

class_names = ["normal", "abnormal"]
for score, name in zip(scores, class_names):
    print(
        "This model is %.2f percent confident that CT scan is %s"
        % ((100 * score), name)
    )

演绎展示:


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1611803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国人为什么不说自信,而说信天

中国人从来不说自信&#xff0c;中国人信天&#xff0c;老天爷是最公平的。做好自己&#xff0c;天命注定&#xff0c;我都这么努力了&#xff0c;老天爷不帮我帮谁&#xff1f; 中国人信天是有逻辑关系的&#xff0c;很简单&#xff1a;做错事情了或者结果不好了&#xff0c;…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之二 简单人脸检测添加戴眼镜效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之二 简单人脸检测添加戴眼镜效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之二 简单人脸检测添加戴眼镜效果 一、简单介绍 二、简单人脸检测添加戴眼镜效…

就业班 第三阶段(ansible) 2401--4.16 day2 ansible2 剧本+角色

六、Ansible playbook 简介 playbook 是 ansible 用于配置&#xff0c;部署&#xff0c;和管理被控节点的剧本。   通过 playbook 的详细描述&#xff0c;执行其中的一系列 tasks &#xff0c;可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的…

设计了一个最佳分词自回归模型训练方案

概述 使用Token和二值Token进行分词&#xff1a; 文本被划分为一系列的Token&#xff0c;并引入二值Token来指示分词的边界。随机分配二值Token并训练模型&#xff1a; 训练数据的Token被随机分配二值Token&#xff0c;表示可能的分词位置&#xff0c;然后这些Token序列被输入…

Python基础学习之**kwargs

在Python编程中&#xff0c;**kwargs 是一个强大的工具&#xff0c;它允许我们在函数定义中接受任意数量的关键字参数。kwargs 是 "keyword arguments" 的缩写&#xff0c;实际上是一个字典&#xff0c;其中包含了传递给函数的所有关键字参数。本文将详细介绍 **kwar…

Zynq 7000 系列中的JTAG和DAP子系统

Zynq 7000系列SoC器件通过标准JTAG调试接口提供调试访问。在内部&#xff0c;SoC设备器件在处理系统&#xff08;PS&#xff09;内部实现了一个Arm调试访问端口&#xff08;DAP&#xff09;&#xff0c;同时在可编程逻辑&#xff08;PL&#xff09;内部实现了一个标准的JTAG测试…

机器视觉系统:PVC片材表面缺陷检测的锐利“眼睛”

PVC片材作为一种广泛应用于建筑、包装、医疗等领域的塑料材料&#xff0c;其表面质量对于产品的性能和使用寿命至关重要。然而&#xff0c;在生产过程中&#xff0c;PVC片材可能会出现多种表面缺陷&#xff0c;如划痕、污渍、气泡、压痕等。为了确保产品质量&#xff0c;机器视…

亚信安慧AntDB:数据库性能新高度

亚信安慧AntDB秉持着为客户提供最佳数据库解决方案的理念&#xff0c;不断探索并创新&#xff0c;最近取得了重大的突破。他们成功地研发出一种先进的数据库割接方案&#xff0c;实现了不停服、零故障的数据库割接操作&#xff0c;有效地将替换所带来的业务影响降至最低。 这一…

同旺科技 USB TO SPI / I2C适配器读写24LC128--读写

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、24LC128芯片&#xff1b; 适应于同旺科技 USB TO SPI / I2C适配器专业版&#xff1b; 专业版配套软件更新&#xff1b; 直接读取HEX文件&#xff0c;自动完成文件解析&#xff1b; 支持芯片&#xf…

书生·浦语大模型实战训练营--第二期第六节课--Lagent AgentLego 智能体应用搭建--notebook

一、 大模型的局限性 大模型本身存在下面的几个问题&#xff1a;幻觉&#xff08;虚假信息&#xff0c;不符合实际&#xff09;、时效性&#xff08;训练数据过时&#xff0c;不能实时更新&#xff09;、可靠性&#xff08;对于复杂任务&#xff0c;可能错误输出&#xff09; …

K-means和DBSCAN

目录 一、K-means和DBSCAN之间的主要区别 二、DBSCAN聚类算法 2.1DBSCAN聚类算法实现点集数据的聚类 2.2DBSCAN聚类算法实现鸢尾花数据集的聚类 三、K-means聚类算法 3.1K-means聚类算法实现随机数据的聚类 3.2K-means聚类算法实现鸢尾花数据集的聚类 一、K-means和DBSC…

【点云语义分割】弱监督点云语义分割-双教师指导的对比学习

Weakly Supervised Learning for Point Cloud Semantic Segmentation With Dual Teacher 摘要&#xff1a; 为了增强特征学习能力&#xff0c;我们在这项工作中引入了双教师指导的对比学习框架&#xff0c;用于弱监督点云语义分割。双教师框架可以减少子网络耦合&#xff0c;促…

LeetCode - 283.移动零

题目链接&#xff1a; LeetCode - 283.移动零 题目分析&#xff1a; ​​​​​ 题解代码&#xff1a; #include<iostream> #include<vector> using namespace std;class Solution { public:void moveZeroes(vector<int>& nums) {for (int cur 0, des…

4步生成高质量图像,Stable Diffusion WebUI 1.9.0来了!

上周Stable Diffusion WebUI正式发布了1.9.0版本&#xff0c;我也第一时间把AutoDL镜像升级到了最新版本&#xff0c;有几个比较重要的更新再和大家同步下。 1、为SDXL-Lightning模型使用SGM统一调度器 SDXL-Lightning由字节跳动开源&#xff0c;是一款闪电般的快速文生图模型…

插入排序的可视化实现(Python)

插入排序的Python代码 import tkinter as tk import random import timeclass InsertionSortVisualizer:def __init__(self, root, canvas_width800, canvas_height400, num_bars10):self.root rootself.canvas_width canvas_widthself.canvas_height canvas_heightself.nu…

【从浅学到熟知Linux】基础IO第三弹=>文件系统介绍、软链接与硬链接(含磁盘结构、文件系统存储原理、软硬链接的创建、原理及应用详解)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 理解文件系统物理角度认识磁盘逻辑角度认识磁盘磁盘寻址磁盘中的寄存器 磁盘存储管理 软链接与硬链接软链接…

60.网络游戏逆向分析与漏洞攻防-利用数据包构建角色信息-根据数据包内容判断数据包作用

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 现在的代码都是依据数据包来写的&#xff0c;如果看不懂代码&#xff0c;就说明没看懂数据包…

docker (CentOS,ubuntu)安装及常用命令

Docker和虚拟机一样&#xff0c;都拥有环境隔离的能力&#xff0c;但它比虚拟机更加轻量级&#xff0c;可以使资源更大化地得到应用 Client&#xff08;Docker客户端&#xff09;&#xff1a;是Docker的用户界面&#xff0c;可以接受用户命令&#xff08;docker build&#xff…

【JavaSE】异常

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 认识异常 异常分类 举例 栈溢出错误 空指针异常&#xff08;运行时异常&#xff09; 编译时异常 处理异常 抛出 异常 程序本身触发异常 手动抛出异常 举例 利用try ca…

C++修炼之路之多态--多态的条件与例外,重载+重写+重定义

目录 前言 一&#xff1a;构成多态的条件及一些特殊情况&#xff08;前提是构成父子类&#xff09; 1.多态是在不同的继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的结果 2.两个条件 3.三同的两个例外 1.协变---返回值类型可以不同&#xff0c;但必…