Unet網絡的Pytorch實現和matlab實現

文章目錄

  • 一、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 nnclass 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)   # 輸入通道 -> 64self.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 的 e4up3 = 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 個 epochmodel.train()running_loss = 0.0for 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:ReLUreluLayer('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.pswp.cn/pingmian/76293.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76293.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76293.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

記錄一次JVM調優過程1

如何通過jmap 診斷,服務運行一段時間后內存使用量飆升的問題 通過 jmap 診斷服務運行一段時間后內存使用量飆升的問題,需結合堆轉儲分析、對象分布統計及工具鏈配合。以下是具體操作步驟和關鍵方法: 一、實時監控與初步分析 獲取進程 PID 使…

接口自動化學習五:mock工具使用

Moco簡介: Mock是一個簡單搭建模擬服務器的框架,可以用來模擬http、https、socket等協議。 原理: Mock會根據一些配置,啟動一個真正的HTTP服務(會監聽本地的某個端口),當發起的請求滿足某個條件時&#xf…

若依 前后端部署

后端:直接把代碼從gitee上拉去到本地目錄 (https://gitee.com/y_project/RuoYi-Vue ) 注意下redis連接時password改auth 后端啟動成功 前端:運行前首先確保安裝了node環境,隨后執行: !!一定要用管理員權限…

Adaptive AUTOSAR 狀態管理和轉換——ActionItemList

在AUTOSAR的狀態轉換管理(STM,State Transition Manager) 框架中,ActionItemList 是連接 狀態機狀態(State Machine State) 與 功能組狀態(Function Group States) 的核心配置元素。 以下是其關系與作用的詳細解釋: 1. 核心概念 狀態機狀態(State Machine State) 表…

一個基于ragflow的工業文檔智能解析和問答系統

工業復雜文檔解析系統 一個基于ragflow的工業文檔智能解析和問答系統,支持多種文檔格式的解析、知識庫管理和智能問答功能。 系統功能 1. 文檔管理 支持多種格式文檔上傳(PDF、Word、Excel、PPT、圖片等)文檔自動解析和分塊處理實時處理進度顯示文檔解析結果預覽批量文檔…

linux系統下如何提交git和調試

我們默認的ubuntu20.04鏡像是沒有Git提交的工具,我們需要配置安裝包。 安裝和更新git的命令 sudo apt update //用于更新軟件包索引sudo apt install git //用于安裝git版本控制工具 git --version //檢查git版本,確認是否安裝成功 隨便進入linux系統下的一…

輕量級爬蟲框架Feapder入門:快速搭建企業級數據管道

一、目標與前置知識 1. 目標概述 本教程的主要目標是: 介紹輕量級爬蟲框架 Feapder 的基本使用方式。快速搭建一個采集豆瓣電影數據的爬蟲,通過電影名稱查找對應的電影詳情頁并提取相關信息(電影名稱、導演、演員、劇情簡介、評分&#xf…

spring mvc的攔截器HandlerInterceptor 接口詳解

HandlerInterceptor 接口詳解 1. 接口方法說明 方法作用執行時機返回值/注意事項preHandle請求處理前攔截在控制器方法執行前調用返回 false 中斷后續流程;返回 true 繼續執行postHandle控制器方法執行后攔截在控制器方法返回結果后,視圖渲染前調用無返…

數據可視化 —— 柱形圖應用(大全)

一、案例一:單柱形圖 1.導入庫 import matplotlib.pyplot as plt import pandas as pd import numpy as np 2.給窗口名稱和畫布大小 plt.figure(num單柱形圖, figsize(6, 4), facecolorw) 3.定義x、y軸的數據 # range(0-4) x np.arange(5) # 創建數組 y1 np.a…

apijson 快速上手

apijson是強大的工具,簡化了CRUD的操作,只要有數據庫表,就能自動生成RESTFUL接口。但初次上手也是摸索了很長時間,尤其是部署與使用上,這里嘗試以初學者角度來說下: 一、好處 1、對于簡單的應用&#xff…

V4L2雜談

V4L2的開發手冊 在做v4l2的開發的時候, 可以使用v4l2-ctl命令協助調試和軟件開發。關于linux多媒體開發可以參考鏈接:https://www.linuxtv.org/wiki/index.php/Main_Page關于v4l2的api接口開發可以參考:https://linuxtv.org/docs.php在linux…

(五)深入了解AVFoundation-播放:多音軌、字幕、倍速播放與橫豎屏切換

引言 在之前的博客中,我們已經實現了一個相對完整的播放器,具備了基本功能,如播放、暫停、播放進度顯示和拖拽快進等。這為我們提供了一個堅實的基礎。接下來,我們將進一步擴展播放器的功能,使其更具靈活性和實用性&a…

3ds Max 2016的版本怎么處理 按鍵輸入被主程序截斷 C#winform窗體接受不到英文輸入

3ds Max 2016的版本怎么處理 按鍵輸入被主程序截斷 C#winform窗體接受不到英文輸入 如果窗體失去焦點應該取消 全局監聽事件 解決方案:在窗體失去焦點時取消全局鍵盤鉤子 為了確保 WinForms 窗體失去焦點時不再攔截鍵盤事件(避免影響 3ds Max 或其他程…

華為手機或平板與電腦實現文件共享

1.手機或平板與電腦在同一個網絡 2.打開手機或平板端,設置---更多連接----快分享或華為分享打開此功能-----開啟共享至電腦 3.打開電腦,網絡中就可看到手機端分享的用戶名稱 4. 登陸就可訪問手機 5.常見問題 5.1 電腦未發現本機 5.2 修改了訪問密碼后再…

elemenPlus中,如何去掉el-input中 文本域 textarea自帶的邊框和角標

1、去掉角標 :deep(.el-textarea__inner) {resize: none !important; // 去除右下角圖標 }2、去除邊框&#xff0c;并自定義背景色 <el-inputref"textareaRef"v-model"tempContent":style"{--el-border-color: rgba(255,255,255,0.0),--el-input-…

xv6-labs-2024 lab2

lab-2 0. 前置 課程記錄 操作系統的隔離性&#xff0c;舉例說明就是&#xff0c;當我們的shell&#xff0c;或者qq掛掉了&#xff0c;我們不希望因為他&#xff0c;去影響其他的進程&#xff0c;所以在不同的應用程序之間&#xff0c;需要有隔離性&#xff0c;并且&#xff0…

MCU控制4G模組(標準AT命令),CatM的最大速率?

根據3GPP標準&#xff0c;Cat M1的上行峰值速率大約是1 Mbps&#xff0c;下行大約是1 Mbps。但實際速率會受到多種因素影響&#xff0c;比如網絡條件、信號強度、模塊配置等。 考慮使用AT命令時的開銷。每次發送數據都需要通過AT命令&#xff0c;比如ATQISEND&#xff0c;會引…

JavaScript(JS進階)

目錄 00閉包 01函數進階 02解構賦值 03通過forEach方法遍歷數組 04深入對象 05內置構造函數 06原型 00閉包 <!-- 閉包 --><html><body><script>// 定義&#xff1a;閉包內層函數&#xff08;匿名函數&#xff09;外層函數的變量&#xff08;s&…

6.1es新特性解構賦值

解構賦值是 ES6&#xff08;ECMAScript 2015&#xff09;引入的語法&#xff0c;通過模式匹配從數組或對象中提取值并賦值給變量。&#xff1a; 功能實現 數組解構&#xff1a;按位置匹配值&#xff0c;如 let [a, b] [1, 2]。對象解構&#xff1a;按屬性名匹配值&#xff0c;…

SpringBoot美容院管理系統設計與實現

基于SpringBoot的美容院管理系統免費源碼&#xff0c;幫助您快速搭建高效、智能的美容院管理平臺。該系統涵蓋了管理員、技師、前臺、普通用戶及會員五大功能模塊&#xff0c;以下是系統的核心功能與部署方式詳細介紹。 ?功能模塊 ?管理員功能 ?美容部位管理&#xff1a;支…