基于深度学习的多光谱图像语义分割 —— 从原理到实践(第二节)
基于深度学习的多光谱图像语义分割 —— 从原理到实践(第二节)
上一节我们建立了语义分割和多光谱图像的基本认知,并明确了本示例的整体流程。
现在进入实战第一步:下载数据、加载到 MATLAB、理解数据结构,并进行初步可视化。
2. 数据获取与预处理
2.1 下载预训练的 U‑Net 模型(可选)
如果你只是想快速体验分割效果,而不想花费数小时训练网络,可以直接使用官方提供的预训练模型。该模型已经在 Hamlin Beach 数据集上训练了 70 个 epoch,并取得了很好的精度。
我们首先指定一个存放数据的文件夹(这里使用系统临时目录下的rit18_data文件夹):
dataDir=fullfile(tempdir,"rit18_data");然后下载预训练网络的 ZIP 压缩包,并解压加载其中的 net 变量: ```matlab matlab trainedNet_url="https://ssd.mathworks.com/supportfiles/"+..."vision/data/trainedMultispectralUnetModel_v2.zip";downloadTrainedNetwork(trainedNet_url,dataDir);load(fullfile(dataDir,"trainedMultispectralUnetModel",..."trainedMultispectralUnetModel_v2.mat"));` ````注意:downloadTrainedNetwork 是本示例自带的辅助函数,它会自动处理下载和解压。如果你的网络环境无法直接访问 MathWorks 服务器,可以手动下载该 ZIP 文件并解压到对应目录,然后执行 load 命令。
加载成功后,工作区中会出现 net 变量(一个 dlnetwork 对象),这就是训练好的 U‑Net。
2.2 下载原始数据集
接下来下载 Hamlin Beach 多光谱数据集(MAT 版本,约 3.0 GB)。同样使用辅助函数:
matlabdownloadHamlinBeachMSIData(dataDir);该函数会将 rit18_data.mat 文件下载到 dataDir 下。下载完成后,我们将其加载到工作区:
matlabload(fullfile(dataDir,"rit18_data.mat"));此时工作区中会新增多个变量,最重要的三个是:
train_data —— 训练集图像
val_data —— 验证集图像
test_data —— 测试集图像
用 whos 查看它们的原始尺寸:
matlab whos train_data val_data test_data输出如下(具体数值可能因版本略有差异):
text Name Size Bytes Class Attributes test_data7x12446x76541333663576uint16 train_data7x9393x5642741934284uint16 val_data7x8833x6918855493716uint16可以看到,数据排列为 通道数 × 高度 × 宽度,其中:
第 1 维是通道(7 个通道)
第 2 维是宽度(列数)
第 3 维是高度(行数)
但 MATLAB 中常见的多通道图像布局是 高度 × 宽度 × 通道数。为了后续处理方便,我们必须进行维度转置。
2.3 维度重排:从 CHW 到 HWC
使用 permute 函数将第 2、3 维移到前面,将通道维放到最后:
matlab train_data=permute(train_data,[231]);val_data=permute(val_data,[231]);test_data=permute(test_data,[231]);再次用 whos 检查:
matlab whos train_data val_data test_data现在尺寸变为:
text Name Size Bytes Class Attributes test_data12446x7654x71333663576uint16 train_data9393x5642x7741934284uint16 val_data8833x6918x7855493716uint16这样每个变量就是一个 高度 × 宽度 × 通道 的三维数组,符合 MATLAB 图像的惯例。
为什么顺序很重要?
因为后续函数(如 histeq、imshow、blockedImage)都默认前两维是空间维度,第三维是通道。如果顺序不对,这些函数会误解数据,导致错误结果。
2.4 了解光谱波段中心波长
数据集中还提供了一个变量 band_centers,表示每个通道的中心波长(单位:纳米):
matlabdisp(band_centers)输出:
text490550680720800900这里只列出了 6 个值(因为第 7 通道是掩膜,没有波长信息)。对应关系为:
通道 1:490 nm(蓝色)
通道 2:550 nm(绿色)
通道 3:680 nm(红色)
通道 4:720 nm(近红外)
通道 5:800 nm(近红外)
通道 6:900 nm(近红外)
可见本数据集涵盖了可见光到近红外的广泛范围,为植被、水体等判别提供了丰富信息。
2.5 可视化多光谱数据
为了直观感受不同通道的含义,我们先显示 RGB 组合图像。已知 RGB 通道对应的是第 3(红)、2(绿)、1(蓝)通道,因此提取这三个通道并组合:
matlab rgbTrain=histeq(train_data(:,:,[321]));rgbVal=histeq(val_data(:,:,[321]));rgbTest=histeq(test_data(:,:,[321]));这里使用了 histeq 进行直方图均衡化,目的是增强对比度,使图像在屏幕上更明亮清晰(原始数据是 16 位无符号整型,直接显示可能偏暗)。
然后我们将三张图像的 RGB 分量并排显示为蒙太奇:
matlabmontage({rgbTrain,rgbVal,rgbTest},BorderSize=10,BackgroundColor="white");title("RGB Component of Training,Validation,and TestImage(Left to Right)");运行后会得到类似下面的效果(由于是文字描述,无法显示实际图像,你可以在 MATLAB 中亲自运行查看):
训练图像(左)覆盖了公园的主要区域,包含树木、草地、道路、建筑等。
验证图像(中)和测试图像(右)是不同航拍区域,场景略有差异。
2.6 显示近红外通道
接下来单独显示第 4、5、6 通道(均为近红外),同样做直方图均衡化以便观察:
matlab ir4Train=histeq(train_data(:,:,4));ir5Train=histeq(train_data(:,:,5));ir6Train=histeq(train_data(:,:,6));montage({ir4Train,ir5Train,ir6Train},BorderSize=10,BackgroundColor="white");title("Infrared Channels4,5,and6(Left to Right)of Training Image");你会看到:
第 4 通道(720 nm)中树木显得较暗,而草地相对亮一些。
第 5、6 通道(800、900 nm)中树木的亮度逐渐变化,这反映了不同植被在近红外不同子波段的反射特性差异。
这些额外的光谱信息正是多光谱图像相对于普通 RGB 的优势所在——它们能够“看到”人眼不可见的细节,从而让深度学习模型更好地分离不同地物。
2.7 查看掩膜通道
第 7 通道是一个二值掩膜(Mask),值为 1 表示该像素属于有效分析区域(即图像内部有意义的部分),值为 0 表示图像边界或无效区域(例如飞行器拍摄时边缘的暗角)。我们提取三个数据集的掩膜:
matlab maskTrain=train_data(:,:,7);maskVal=val_data(:,:,7);maskTest=test_data(:,:,7);montage({maskTrain,maskVal,maskTest},BorderSize=10,BackgroundColor="white");title("Mask of Training,Validation,and TestImage(Left to Right)");掩膜图像为黑白二值图,白色区域代表有效像素。在后续的训练和评估中,我们只关心掩膜内的像素,忽略背景区域,这样可以避免无效数据干扰模型学习。
2.8 理解原始标签类别
数据集中除了图像数据,还包含对应的标签图:train_labels、val_labels、test_labels。这些标签图与图像空间尺寸相同,每个像素值是一个整数(0~18),表示该像素的类别 ID。
我们可以查看所有类别的名称:
matlabdisp(classes)输出为:
text0.Other Class/Image Border1.Road Markings2.Tree3.Building4.Vehicle(Car,Truck,or Bus)5.Person6.Lifeguard Chair7.Picnic Table8.Black Wood Panel9.White Wood Panel10.Orange Landing Pad11.Water Buoy12.Rocks13.Other Vegetation14.Grass15.Sand16.Water(Lake)17.Water(Pond)18.Asphalt(Parking Lot/Walkway)共有 18 个类别,其中 ID 0 是背景/图像边界,不计入分析。本例为了简化问题,只区分 植被(Vegetation) 和 非植被(NotVegetation),因此需要将原始标签合并为二类。
2.9 将标签转换为二分类
我们定义目标类别集合:
matlab classNames=["NotVegetation","Vegetation"];根据语义,植被包括类别 ID 为 2 (Tree)、13 (Other Vegetation) 和 14 (Grass)。其余所有非背景类(以及背景)都属于非植被。我们创建新的标签数组,其中:
值为 1 表示非植被
值为 2 表示植被
具体代码如下:
matlab vegetationClassIDs=[21314];nonvegetationClassIDs=setdiff(1:length(classes),vegetationClassIDs);labelsTrain=zeros(size(train_labels),"uint8");labelsTrain(ismember(train_labels,nonvegetationClassIDs))=1;labelsTrain(ismember(train_labels,vegetationClassIDs))=2;labelsVal=zeros(size(val_labels),"uint8");labelsVal(ismember(val_labels,nonvegetationClassIDs))=1;labelsVal(ismember(val_labels,vegetationClassIDs))=2;这里使用 ismember 快速判断每个像素的原始类别是否属于目标集合,然后赋值。注意我们将背景(ID 0)也归为非植被,因为 nonvegetationClassIDs 包含所有非植被类别(包括 ID 0)。
接着,我们将验证集的真实标签保存为 PNG 文件,以便后续评估时使用:
matlabimwrite(labelsVal,"gtruth.png");2.10 可视化训练标签
为了确认标签转换是否正确,我们将训练集的 RGB 图像与新的二值标签叠加显示:
matlab cmap=[101;010];% 第一行:品红(非植被),第二行:绿色(植被)B=labeloverlay(rgbTrain,labelsTrain,Transparency=0.8,Colormap=cmap);imshow(B,cmap);title("TrainingLabels(Magenta=NotVegetation,Green=Vegetation)");numClasses=numel(classNames);ticks=1/(numClasses*2):1/numClasses:1;colorbar(TickLabels=cellstr(classNames),Ticks=ticks,TickLength=0,TickLabelInterpreter="none");运行后你会看到一幅彩色叠加图,其中品红色区域代表非植被(道路、建筑、水体等),绿色区域代表植被(树木、草地、灌木等)。这样可以直观地判断标签质量以及分类的合理性。
2.11 本节小结
至此,我们已经完成了:
下载预训练模型和原始数据集;
将数据从 CHW 格式转为 HWC 格式;
可视化 RGB、红外通道和掩膜,理解多光谱数据的组成;
将原始 18 类标签合并为二分类;
保存验证集真实标签并可视化训练标签。
下一节,我们将面对一个现实挑战:图像尺寸巨大,无法一次性送入 GPU。届时我们将引入 分块处理(Patch‑based Processing) 技术,利用 MATLAB 的 blockedImage 和 blockedImageDatastore 高效管理大图像数据。
敬请期待第三节《分块图像与数据存储——让大图像训练不再困难》。