Unet网络的Pytorch实现和matlab实现

news2025/4/8 5:24:03

文章目录

  • 一、Unet网络简介
    • 1.1 输入图像
    • 1.2 编码器部分(Contracting Path)
    • 1.3 解码器部分(Expanding Path)
    • 1.4 最后一层(输出)
    • 1.5 跳跃连接(Skip Connections)
  • 二、Unet网络的Pytorch实现
    • 2.1 网络构建
    • 2.2 网络训练测试
  • 三、Unet网络的Matlab实现
    • 3.1 网络构建
    • 3.2 网络训练
    • 3.3 网络预测

一、Unet网络简介

UNet 是一种常用于图像分割任务的卷积神经网络(CNN)架构,在论文《U-Net: Convolutional Networks for Biomedical Image Segmentation》中提出。这个网络专为生物医学图像分割设计,但由于其优越的性能,现在已被广泛应用于遥感、自动驾驶、医学图像处理等领域。

UNet 是一种编码器-解码器(Encoder-Decoder)结构的对称网络,其名字中的 “U” 来自于其结构在图像中的形状像一个 “U”。如下图所示

在这里插入图片描述

1.1 输入图像

  • 尺寸:512 x 512,通道数:1(表示单通道灰度图),3(表示RGB图像)

这里 UNet 中使用了 padding=1 的卷积方式,所以卷积输出尺寸保持不变。

1.2 编码器部分(Contracting Path)

编码器类似于传统的 CNN,用于提取图像的特征,逐步降低空间维度、增加特征通道。

每一步都包含:

  • 两次 3x3 卷积 + ReLU
  • 一次 2x2 max pooling
  • 通道数:每次加倍,64 → 128 → 256 → 512 → 1024
  • 特征图尺寸:每次减半

层分析(从上到下):

层级输入大小卷积后大小通道数变化
L1512×512512×5121 → 64 → 64
L2256×256256×256128 → 128
L3128×128128×128256 → 256
L464×6464×64512 → 512
Bottom32×3232×321024

1.3 解码器部分(Expanding Path)

解码器用于将编码器压缩后的特征图逐步恢复到原始图像的空间分辨率,生成与输入大小一致的分割图。

每一步都包含:

  • 2x2 up-convolution
  • 与编码器相同层进行特征图拼接
  • 两次 3x3 卷积 + ReLU
  • 通道数逐渐减半:1024 → 512 → 256 → 128 → 64

解码过程分析:

层级上采样后大小拼接后通道数卷积输出通道数卷积后尺寸
L432×32 → 64×64512 + 51251264×64
L364×64 → 128×128256 + 256256128×128
L2128×128 → 256×256128 + 128128256×256
L1256×256 →512×51264 + 6464388×388

1.4 最后一层(输出)

  • 使用了一个 1x1 卷积,将通道数变成 2

1.5 跳跃连接(Skip Connections)

UNet 的核心创新是将编码器中对应层的特征图直接传递到解码器的对应层进行拼接。这种做法有两个好处:

  • 保留高分辨率的空间信息
  • 有助于训练时的梯度传播

二、Unet网络的Pytorch实现

2.1 网络构建

import torch
import torch.nn as nn

class UNet(nn.Module):
    def __init__(self, in_channels=1, out_channels=2):
        super(UNet, self).__init__()

        # ========== 编码器部分 ==========
        # 每个 encoder 包括两个卷积(double conv)+ 一个最大池化(下采样)
        self.enc1 = self.double_conv(in_channels, 64)   # 输入通道 -> 64
        self.pool1 = nn.MaxPool2d(2)

        self.enc2 = self.double_conv(64, 128)
        self.pool2 = nn.MaxPool2d(2)

        self.enc3 = self.double_conv(128, 256)
        self.pool3 = nn.MaxPool2d(2)

        self.enc4 = self.double_conv(256, 512)
        self.pool4 = nn.MaxPool2d(2)

        # ========== Bottleneck ==========
        # 最底层特征提取器,包含较多通道(1024),增强感受野
        self.bottom = self.double_conv(512, 1024)
        self.dropout = nn.Dropout(0.5)  # 防止过拟合

        # ========== 解码器部分(上采样 + 拼接 + double conv) ==========
        self.up4 = nn.ConvTranspose2d(1024, 512, kernel_size=2, stride=2)  # 上采样
        self.dec4 = self.double_conv(1024, 512)  # 拼接后再 double conv(512 来自 skip,512 来自 up)

        self.up3 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2)
        self.dec3 = self.double_conv(512, 256)

        self.up2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.dec2 = self.double_conv(256, 128)

        self.up1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.dec1 = self.double_conv(128, 64)

        # ========== 输出层 ==========
        # 使用 1x1 卷积将通道数变为类别数(像素级分类)
        self.out_conv = nn.Conv2d(64, out_channels, kernel_size=1)

    def double_conv(self, in_ch, out_ch):
        """包含两个3x3卷积 + ReLU 的结构"""
        return nn.Sequential(
            nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        # ========== 编码器前向传播 ==========
        e1 = self.enc1(x)     # -> [B, 64, H, W]
        p1 = self.pool1(e1)   # -> [B, 64, H/2, W/2]

        e2 = self.enc2(p1)    # -> [B, 128, H/2, W/2]
        p2 = self.pool2(e2)   # -> [B, 128, H/4, W/4]

        e3 = self.enc3(p2)    # -> [B, 256, H/4, W/4]
        p3 = self.pool3(e3)   # -> [B, 256, H/8, W/8]

        e4 = self.enc4(p3)    # -> [B, 512, H/8, W/8]
        p4 = self.pool4(e4)   # -> [B, 512, H/16, W/16]

        # ========== Bottleneck ==========
        b = self.bottom(p4)   # -> [B, 1024, H/16, W/16]
        b = self.dropout(b)

        # ========== 解码器前向传播 + skip connection 拼接 ==========
        up4 = self.up4(b)                           # 上采样 -> [B, 512, H/8, W/8]
        d4 = self.dec4(torch.cat([up4, e4], dim=1)) # 拼接 encoder 的 e4

        up3 = self.up3(d4)                          
        d3 = self.dec3(torch.cat([up3, e3], dim=1))

        up2 = self.up2(d3)
        d2 = self.dec2(torch.cat([up2, e2], dim=1))

        up1 = self.up1(d2)
        d1 = self.dec1(torch.cat([up1, e1], dim=1))

        # ========== 输出层 ==========
        out = self.out_conv(d1)  # -> [B, num_classes, H, W]
        return out


# ========== 模型测试:构造随机输入看看输出尺寸 ==========
if __name__ == "__main__":
    model = UNet(in_channels=1, out_channels=2)
    x = torch.randn(1, 1, 512, 512)  # 随机生成一个 1 张单通道图像
    y = model(x)
    print("Output shape:", y.shape)  # 应为 [1, 2, 512, 512],2 表示类别数

2.2 网络训练测试

这里用到的数据集是公开数据集(MoNuSeg 2018 Data)

import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import transforms
import matplotlib.pyplot as plt
from model import UNet
from dataset import MonuSegDataset  # 自定义的数据集类
import os

# ========================
# 1️⃣ 数据路径配置
# ========================
train_image_dir = "TrainingData/TissueImages"  # 训练图像路径
train_mask_dir  = "TrainingData/Masks"         # 训练标签路径

test_image_dir  = "TestData/TissueImages"      # 测试图像路径
test_mask_dir   = "TestData/Masks"             # 测试标签路径

# ========================
# 2️⃣ 数据预处理方式定义
# ========================
resize_size = (512, 512)  # 所有图像统一 resize 的尺寸,需与模型输入一致

# 图像预处理:Resize → ToTensor(归一化到 0~1,3通道 RGB)
transform_img = transforms.Compose([
    transforms.Resize(resize_size),
    transforms.ToTensor()
])

# 掩码预处理:Resize → 转张量 → squeeze → 转 long 类型(整数标签)
transform_mask = transforms.Compose([
    transforms.Resize(resize_size, interpolation=transforms.InterpolationMode.NEAREST),  # 保留 mask 标签值
    transforms.PILToTensor(),         # 依旧是整数值,不做归一化
    transforms.Lambda(lambda x: x.squeeze().long())  # [1,H,W] → [H,W],保持 long 类型
])

# ========================
# 3️⃣ 加载数据集(训练 + 测试)
# ========================
train_dataset = MonuSegDataset(train_image_dir, train_mask_dir, transform_img, transform_mask)
train_loader = DataLoader(train_dataset, batch_size=2, shuffle=True)

test_dataset = MonuSegDataset(test_image_dir, test_mask_dir, transform_img, transform_mask)
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False)

# ========================
# 4️⃣ 初始化模型、损失函数、优化器
# ========================
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model = UNet(in_channels=3, out_channels=2).to(device)  # 3通道输入,2类输出
criterion = nn.CrossEntropyLoss()  # 用于像素级分类
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

# ========================
# 5️⃣ 模型训练主循环
# ========================
for epoch in range(30):  # 总共训练 30 个 epoch
    model.train()
    running_loss = 0.0

    for imgs, masks in train_loader:
        imgs, masks = imgs.to(device), masks.to(device)

        outputs = model(imgs)         # 输出尺寸 [B, 2, H, W]
        loss = criterion(outputs, masks)  # CrossEntropy 会自动处理 one-hot 标签

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    print(f"Epoch {epoch + 1}: Loss = {running_loss / len(train_loader):.4f}")

# ========================
# 6️⃣ 测试 + 可视化预测结果
# ========================
model.eval()  # 切换为评估模式(关闭 Dropout/BN 等)
with torch.no_grad():  # 不计算梯度,加速推理
    for img, mask in test_loader:
        img = img.to(device)
        output = model(img)                # 网络前向推理,输出 shape [1, 2, H, W]
        pred = torch.argmax(output, dim=1)  # 取每个像素的最大概率类别 → shape [1, H, W]

        # 可视化结果
        plt.figure(figsize=(12, 4))
        plt.subplot(1, 3, 1)
        plt.title("Image")
        plt.imshow(img[0].cpu().permute(1, 2, 0))  # [C,H,W] → [H,W,C] 显示 RGB 图

        plt.subplot(1, 3, 2)
        plt.title("Prediction")
        plt.imshow(pred[0].cpu(), cmap="gray")    # 显示预测掩码

        plt.subplot(1, 3, 3)
        plt.title("Ground Truth")
        plt.imshow(mask[0], cmap="gray")          # 显示真值掩码

        plt.tight_layout()
        plt.show()

        break  # 只可视化第一张测试图像

在这里插入图片描述

三、Unet网络的Matlab实现

3.1 网络构建

主函数如下

function lgraph = unetLayers(inputSize, numClasses)
% unetLayers 构建一个标准的 U-Net 分割网络结构
%
% 输入参数:
%   inputSize   - 输入图像的尺寸,例如 [512 512 3]
%   numClasses  - 分割类别数(例如背景+前景=2)
%
% 输出参数:
%   lgraph      - layerGraph 对象,包含整个 U-Net 网络结构

% === 输入层 ===
% 输入图像大小为 inputSize(例如 [512 512 3])
layers = imageInputLayer(inputSize, 'Name', 'input');

% === 编码器部分(Encoder)===
% 每个 encoderBlock 包括 2 个卷积 + ReLU + 最大池化,逐步提取特征 & 降维
% 同时记录每个 block 的输出,用于后续的 skip connection

[enc1, enc1Out] = encoderBlock('enc1', 64);   % 第一层编码器,输出 64 通道特征图
[enc2, enc2Out] = encoderBlock('enc2', 128);  % 第二层编码器
[enc3, enc3Out] = encoderBlock('enc3', 256);  % 第三层编码器
[enc4, enc4Out] = encoderBlock('enc4', 512);  % 第四层编码器

% === Bottleneck(编码器与解码器之间的连接)===
% 特征最抽象、最小尺寸的位置
% 加入 Dropout 层,增强模型泛化能力,避免过拟合
bottleneck = [
    convolution2dLayer(3, 1024, 'Padding','same','Name','bottom_conv1')
    reluLayer('Name','bottom_relu1')
    dropoutLayer(0.5, 'Name','bottom_dropout')  % 50% 随机丢弃通道
    convolution2dLayer(3, 1024, 'Padding','same','Name','bottom_conv2')
    reluLayer('Name','bottom_relu2')
];

% === 解码器部分(Decoder)===
% 每个 decoderBlock 先上采样,再与对应 encoder 输出进行 skip connection
% 然后通过卷积融合特征图,逐步恢复空间分辨率

dec4 = decoderBlock('dec4', 512);  % 解码器第4层,对应 encoder 的第4层
dec3 = decoderBlock('dec3', 256);
dec2 = decoderBlock('dec2', 128);
dec1 = decoderBlock('dec1', 64);

% === 输出层 ===
outputLayer = [
    convolution2dLayer(1, numClasses, 'Name', 'final_conv')  % 1x1 卷积,通道映射为类别数
    softmaxLayer('Name', 'softmax')                          % 每个像素的类别概率
    pixelClassificationLayer('Name', 'pixelClassLayer')      % 自动处理标签+计算交叉熵损失
];

% === 组装整个网络 ===
% 把所有层拼成一个大的 LayerGraph(U-Net 主干结构)
lgraph = layerGraph([
    layers;
    enc1; enc2; enc3; enc4;
    bottleneck;
    dec4; dec3; dec2; dec1;
    outputLayer
]);

% === 添加 skip connections(跳跃连接)===
% 将 encoder 的中间输出与对应 decoder 的拼接输入连接起来
% 连接的是 decoderBlock 中的 depthConcatenationLayer 的第 2 个输入端口(in2)

lgraph = connectLayers(lgraph, enc4Out, 'dec4_concat/in2');
lgraph = connectLayers(lgraph, enc3Out, 'dec3_concat/in2');
lgraph = connectLayers(lgraph, enc2Out, 'dec2_concat/in2');
lgraph = connectLayers(lgraph, enc1Out, 'dec1_concat/in2');

end

上面函数里用到的两个辅助函数encoderBlock和decoderBlock。分别如下所示

function [layers, outputName] = encoderBlock(name, outChannels)
% encoderBlock 生成 U-Net 编码器模块的一组层
%
% 输入参数:
%   name        - 当前模块的名字前缀,用于给每一层命名(如 "enc1")
%   outChannels - 输出通道数,即卷积核数量,决定了特征图的深度
%
% 输出参数:
%   layers      - 一组 Layer,用于组成 layerGraph 的一部分
%   outputName  - 最后一层 relu 的名称,用于后续 skip connection 连接

layers = [
    % 第1个卷积层,使用 3x3 卷积核,输出通道为 outChannels,padding=same 保持尺寸不变
    convolution2dLayer(3, outChannels, "Padding", "same", "Name", [name, '_conv1'])

    % ReLU 激活函数,增加非线性表达能力
    reluLayer("Name", [name '_relu1'])

    % 第2个卷积层,继续提取特征(仍是 3x3 卷积)
    convolution2dLayer(3, outChannels, "Padding", "same", "Name", [name, '_conv2'])

    % ReLU 激活函数
    reluLayer("Name", [name '_relu2'])

    % 最大池化层,使用 2x2 核,步长为 2,用于降采样(尺寸缩小一半)
    maxPooling2dLayer(2, 'Stride', 2, 'Name', [name '_pool'])
];
function layers = decoderBlock(name, outChannels)
% decoderBlock 生成 U-Net 解码器模块的一组层
%
% 输入参数:
%   name        - 当前模块的名字前缀(如 "dec1")
%   outChannels - 输出通道数(卷积核数量)
%
% 输出参数:
%   layers      - 一组层,用于构建 U-Net 的解码器部分

layers = [
    % 上采样层:使用 2x2 转置卷积进行上采样,步长为2,使特征图尺寸扩大一倍
    transposedConv2dLayer(2, outChannels, 'Stride', 2, 'Name', [name '_upconv'])

    % 跳跃连接:将 encoder 的输出与上采样结果拼接在深度维度上(channel 维)
    % 注意:输入端需要通过 connectLayers 手动连接 encoder 的输出
    depthConcatenationLayer(2, 'Name', [name '_concat'])

    % 卷积层1:拼接后做一次卷积提取融合后的特征
    convolution2dLayer(3, outChannels, 'Padding','same','Name', [name '_conv1'])

    % 激活层1:ReLU 非线性激活
    reluLayer('Name',[name '_relu1'])

    % 卷积层2:再进一步提取特征
    convolution2dLayer(3, outChannels, 'Padding','same','Name', [name '_conv2'])

    % 激活层2:ReLU
    reluLayer('Name',[name '_relu2'])
];
end

用matlab可以简单画出这个网络的结构,如下图

inputSize = [512 512 1];  % 输入图像大小
numClasses = 2;           % 前景 / 背景
lgraph = unetLayers(inputSize, numClasses);
plot(lgraph)  % 可视化网络结构

在这里插入图片描述

3.2 网络训练

数据集用到的是网络上公开的数据集(MoNuSeg 2018 Data)。

训练程序如下

% === 路径设置 ===
% 设置训练图像和对应标签(掩膜)的路径
imageDir = 'Training Data/Tissue Images';  % 原始图像路径
maskDir  = 'Training Data/Masks';          % 对应的标签图路径(掩码)

% === 分类标签设置 ===
% 定义语义分割任务中的类别名称和对应的像素值
% 这些 pixel 值应该和 mask 图像中的像素一致
classNames = ["background", "nuclei"];  % 类别名称
labelIDs   = [0, 1];                    % 对应的像素值,0=背景,1=细胞核

% === 创建图像和标签的 Datastore ===
imds = imageDatastore(imageDir);  % 加载图像(支持自动批量读取)
resizeSize = [512 512];           % 所有图像统一 resize 的尺寸

% pixelLabelDatastore 将 mask 图像转为每像素的分类标签
pxds = pixelLabelDatastore(maskDir, classNames, labelIDs);

% === 创建联合数据源:pixelLabelImageDatastore ===
% 用于将图像和标签按顺序配对,并自动 resize
trainingData = pixelLabelImageDatastore(imds, pxds, 'OutputSize', resizeSize);
% 返回的对象可直接输入到 trainNetwork 作为训练集

% === 定义网络结构 ===
inputSize = [512 512 3];       % 图像尺寸是 512x512,RGB 三通道
numClasses = 2;                % 类别数为 2(背景 + 细胞核)

% 创建 U-Net 网络结构(调用你自定义的 unetLayers 函数)
lgraph = unetLayers(inputSize, numClasses);

% === 设置训练参数 ===
options = trainingOptions('adam', ...           % 使用 Adam 优化器
    'InitialLearnRate', 1e-4, ...               % 初始学习率
    'MaxEpochs', 30, ...                        % 最大训练轮数(epoch)
    'MiniBatchSize', 2, ...                     % 每次训练使用 2 张图像
    'Shuffle','every-epoch', ...                % 每轮训练时打乱数据顺序
    'VerboseFrequency', 10, ...                 % 每 10 次迭代输出一次信息
    'Plots','training-progress', ...            % 实时绘制训练损失曲线
    'ExecutionEnvironment','auto');             % 自动选择 CPU / GPU

% === 开始训练 ===
% 使用训练数据、U-Net 结构和训练参数进行模型训练
net = trainNetwork(trainingData, lgraph, options);

% === 保存模型到文件 ===
% 将训练好的模型保存在本地,方便后续使用或预测
save('trained_unet_monuseg.mat', 'net');

在这里插入图片描述

在这里插入图片描述

3.3 网络预测

% === 设置路径 ===
% 读取一张测试图像以及其对应的 ground truth 掩码(标签)
testImage = imread('Test Data/Tissue Images/TCGA-2Z-A9J9-01A-01-TS1.tif');          % 测试原图
gt_mask = imread('Test Data/Masks/TCGA-2Z-A9J9-01A-01-TS1.png');     % 对应的真实标签图

% === Resize 成训练时的网络输入大小 ===
% 如果训练时输入图像是 [512×512],推理时也要保证尺寸一致
testImage = imresize(testImage, [512 512]);

% === 加载训练好的 U-Net 模型 ===
% 模型应包含名为 net 的变量,即训练阶段保存的网络结构和权重
load('trained_unet_monuseg.mat');  % 加载变量 net

% === 执行语义分割预测 ===
% 调用 MATLAB 的 semanticseg 函数,自动处理输入并输出预测分类图
% 返回的是 categorical 类型的预测图,每个像素是 "background" 或 "nuclei"
pred = semanticseg(testImage, net);

% === 可视化预测分割结果(与原图叠加)===
% 使用 labeloverlay 将分割掩码叠加在原图上,便于直观观察分割效果
figure;
imshow(labeloverlay(testImage, pred))
title('预测分割结果')

% === 将预测结果转为二值图像 ===
% 把 categorical 类型的预测图转为 0/1 掩码图(uint8)
% nuclei → 1,background → 0
pred_mask = uint8(pred == "nuclei");

% === 显示 原图、预测掩码、真实掩码 的对比 ===
figure;
subplot(1,3,1); imshow(testImage); title('原图');       % 显示输入图像
subplot(1,3,2); imshow(pred_mask,[]); title('预测');     % 显示模型预测的掩码(0/1图)
subplot(1,3,3); imshow(gt_mask,[]);   title('真值');     % 显示人工标注的 ground truth 掩码

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【合新通信】相控阵雷达RFoF方案的应用

一、相控阵雷达为何需要RFoF? 核心需求驱动 分布式部署:相控阵雷达(AESA/PESA)的T/R模块需分散布局(如舰载雷达阵面、卫星载荷),传统同轴电缆导致重量和损耗剧增。高频段挑战:X/Ku/…

原理图输出网表及调入

一、输出网表操作步骤 (1)选中.dsn文件,选者N或进入tools下拉列表选择Creat Netlists (2)导出网表后的文件 二、网表的导入 (1)执行菜单命令“File-Import-Logic/netlist”,将原理…

TDengine JAVA 语言连接器

简介 本节简介 TDengine 最重要且使用最多的连接器, 本节内容是以教科书式方式列出对外提供的接口及功能及使用过程中要注意的技术细节,大家可以收藏起来做为今后开发 TDengine 的参考资料。 taos-jdbcdriver 是 TDengine 的官方 Java 语言连接器,Java…

【NLP 55、实践 ⑬ LoRA完成NER任务】

目录 一、数据文件 二、模型配置文件 config.py 三、数据加载文件 loader.py 1.导入文件和类的定义 2.初始化 3.数据加载方法 代码运行流程 4.文本编码 / 解码方法    ① encode_sentence(): ② decode(): 代码运行流程 ③ padding(): 代码…

【蓝桥杯】Python大学A组第十五届省赛

1.填空题 1.1.拼正方形 问题描述 小蓝正在玩拼图游戏,他有个的方块和个的方块,他需要从中挑出一些来拼出一个正方形。 比如用个和个的方块可以拼出一个的正方形;用个的方块可以拼出一个的正方形。 请问小蓝能拼成的最大的正方形的边长为多少。 import math # 2*2的个数 a =…

小球反弹(蓝桥杯C语言)

有一长方形,长为 343720343720 单位长度,宽为 233333233333 单位长度。在其内部左上角顶点有一小球 (无视其体积),其初速度如图所示且保持运动速率不变,分解到长宽两个方向上的速率之比为 dx:dy15:17dx:dy15:17。小球碰到长方形的…

HarmonyOS-ArkUI Ability进阶系列-UIAbility与各类Context

UIAbility及相关类关系 一个模块编译的时候会出一个HAP包, 每一个HAP包在运行时都对应一个AbilityStage。 AbilityStage持有一个AbilityStageContext一个APP, 有时候会有很多个HAP包, 至少一个。 一个APP运行时,对应的是我们的App…

剑指Offer(数据结构与算法面试题精讲)C++版——day4

剑指Offer(数据结构与算法面试题精讲)C版——day4 题目一:和为k的子数组题目二:0和1个数相同的子数组题目三:左右两边子数组的和相等 题目一:和为k的子数组 结合前面着重阐述的双指针法这一经典的算法技巧&…

WebRTC技术简介及应用场景

写在前面 本文是参考稀土掘金的文章,整理得出,版权归原作者所有!参考链接请点击跳转 WebRTC(Web Real-Time Communication) 是一项开源技术,允许浏览器和移动应用直接进行实时音视频通信和数据传输,无需安装插件或第三方软件。它…

介绍几种创意登录页(含完整源码)

今天为大家收集了几种不同风格的登录页,搭配动态渐变背景,效果绝对惊艳! CSS3实现动态渐变玻璃拟态登录页 一、开篇语 纯CSS实现当下最火的玻璃拟态(Morphism)风格登录页,搭配动态渐变背景,效果绝对惊艳! …

Uni-app入门到精通:uni-app的基础组件

1、view view是容器组件&#xff0c;类似于HTML中的<div></div>标签&#xff0c;用于包裹各种元素内容&#xff0c;是页面布局常用的组件。view组件的属性如下 属性类型默认值说明hover-classStringnone指定按下去的样式类。当hover-class"none"时&…

大文件上传源码,支持单个大文件与多个大文件

大文件上传源码&#xff0c;支持单个大文件与多个大文件 Ⅰ 思路Ⅱ 具体代码前端--单个大文件前端--多个大文件前端接口后端 Ⅰ 思路 具体思路请参考我之前的文章&#xff0c;这里分享的是上传流程与源码 https://blog.csdn.net/sugerfle/article/details/130829022 Ⅱ 具体代码…

C语言--插入排序

插入排序&#xff1a;简单而高效的排序算法 在计算机科学中&#xff0c;排序是一种常见的操作&#xff0c;用于将一组数据按照特定的顺序排列。插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的工作原理类似于我们整理扑克牌的过程。…

L2-024 部落 #GPLT,并查集 C++

文章目录 题目解读输入格式输出格式 思路Ac Code参考 题目解读 我们认为朋友的朋友都算在一个部落里&#xff0c;于是要请你统计一下&#xff0c;在一个给定社区中&#xff0c;到底有多少个互不相交的部落&#xff1f;并且检查任意两个人是否属于同一个部落。 输入格式 第一…

在线记事本——支持Markdown

项目地址 https://github.com/Anyuersuper/CloudNotebook 百度网盘 通过网盘分享的文件&#xff1a;CloudNotebook-master.zip 链接: https://pan.baidu.com/s/1_Y--aBzNkKiFRIMHYmwPdA?pwdyuer 提取码: yuer &#x1f4dd; 云笔记 (Cloud Notebook) 云笔记是一个简洁、安全…

Day2:前端项目uniapp壁纸实战

先来做一个轮番图。 效果如下&#xff1a; common-style.css view,swiper,swiper-item{box-sizing: border-box; } index.vue <template><view class"homeLayout"><view class"banner"><swiper circular indicator-dots autoplay…

LeetCode Hot100 刷题笔记(3)—— 链表

目录 前言 1. 相交链表 2. 反转链表 3. 回文链表 4. 环形链表 5. 环形链表 II 6. 合并两个有序链表 7. 两数相加 8. 删除链表的倒数第 N 个结点 9. 两两交换链表中的节点 10. K 个一组翻转链表 11. 随机链表的复制 12. 排序链表 13. 合并 K 个升序链表 14. LRU 缓存 前言 一、…

状态机思想编程

1. LED流水灯的FPGA代码 在这个任务中&#xff0c;首先我们会使用状态机的思想来设计一个LED流水灯的控制逻辑。LED流水灯一般需要依次点亮不同的LED&#xff0c;并且循环播放。我们将其分为几个状态&#xff0c;每个状态控制一个或一组LED灯。 状态机设计 假设我们有8个LED…

第二十八章:Python可视化图表扩展-和弦图、旭日图、六边形箱图、桑基图和主题流图

一、引言 在数据可视化领域&#xff0c;除了常见的折线图、柱状图和散点图&#xff0c;还有一些高级图表类型可以帮助我们更直观地展示复杂数据关系。本文将介绍五种扩展图表&#xff1a;和弦图、旭日图、六边形箱图、桑基图和主题流图。这些图表在展示数据关系、层次结构和流量…

基于vue框架的重庆美食网站的设计与实现kt945(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,美食分类,美食菜品 开题报告内容 基于Vue框架的重庆美食网站的设计与实现开题报告 一、选题背景与意义 &#xff08;一&#xff09;选题背景 重庆&#xff0c;作为中国西南地区的璀璨明珠&#xff0c;以其独特的地理位置和丰富…