基于深度学习的多光谱图像语义分割 —— 从原理到实践(第二节)

上一节我们建立了语义分割和多光谱图像的基本认知,并明确了本示例的整体流程。
现在进入实战第一步:下载数据、加载到 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)。同样使用辅助函数:

matlab
downloadHamlinBeachMSIData(dataDir);

该函数会将 rit18_data.mat 文件下载到 dataDir 下。下载完成后,我们将其加载到工作区:

matlab
load(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_data       7x12446x7654            1333663576  uint16              
  train_data      7x9393x5642              741934284  uint16              
  val_data        7x8833x6918              855493716  uint16           

可以看到,数据排列为 通道数 × 高度 × 宽度,其中:

第 1 维是通道(7 个通道)

第 2 维是宽度(列数)

第 3 维是高度(行数)

但 MATLAB 中常见的多通道图像布局是 高度 × 宽度 × 通道数。为了后续处理方便,我们必须进行维度转置。

2.3 维度重排:从 CHW 到 HWC

使用 permute 函数将第 2、3 维移到前面,将通道维放到最后:

matlab
train_data = permute(train_data, [2 3 1]);
val_data   = permute(val_data,   [2 3 1]);
test_data  = permute(test_data,  [2 3 1]);

再次用 whos 检查:

matlab
whos train_data val_data test_data

现在尺寸变为:

text
  Name                Size                     Bytes  Class     Attributes

  test_data       12446x7654x7            1333663576  uint16              
  train_data       9393x5642x7             741934284  uint16              
  val_data         8833x6918x7             855493716  uint16    

这样每个变量就是一个 高度 × 宽度 × 通道 的三维数组,符合 MATLAB 图像的惯例。

为什么顺序很重要?

因为后续函数(如 histeq、imshow、blockedImage)都默认前两维是空间维度,第三维是通道。如果顺序不对,这些函数会误解数据,导致错误结果。

2.4 了解光谱波段中心波长

数据集中还提供了一个变量 band_centers,表示每个通道的中心波长(单位:纳米):

matlab
disp(band_centers)

输出:

text
   490   550   680   720   800   900

这里只列出了 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(:,:,[3 2 1]));
rgbVal   = histeq(val_data(:,:,[3 2 1]));
rgbTest  = histeq(test_data(:,:,[3 2 1]));

这里使用了 histeq 进行直方图均衡化,目的是增强对比度,使图像在屏幕上更明亮清晰(原始数据是 16 位无符号整型,直接显示可能偏暗)。

然后我们将三张图像的 RGB 分量并排显示为蒙太奇:

matlab
montage({rgbTrain, rgbVal, rgbTest}, BorderSize=10, BackgroundColor="white");
title("RGB Component of Training, Validation, and Test Image (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 Channels 4, 5, and 6 (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 Test Image (Left to Right)");

掩膜图像为黑白二值图,白色区域代表有效像素。在后续的训练和评估中,我们只关心掩膜内的像素,忽略背景区域,这样可以避免无效数据干扰模型学习。

在这里插入图片描述
在这里插入图片描述

2.8 理解原始标签类别

数据集中除了图像数据,还包含对应的标签图:train_labels、val_labels、test_labels。这些标签图与图像空间尺寸相同,每个像素值是一个整数(0~18),表示该像素的类别 ID。

我们可以查看所有类别的名称:

matlab
disp(classes)

输出为:

text
0. Other Class/Image Border      
1. Road Markings                 
2. Tree                          
3. Building                      
4. Vehicle (Car, Truck, or Bus)  
5. Person                        
6. Lifeguard Chair               
7. Picnic Table                  
8. Black Wood Panel              
9. White Wood Panel              
10. Orange Landing Pad           
11. Water Buoy                   
12. Rocks                        
13. Other Vegetation             
14. Grass                        
15. Sand                         
16. 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 = [2 13 14];
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 文件,以便后续评估时使用:

matlab
imwrite(labelsVal, "gtruth.png");

2.10 可视化训练标签

为了确认标签转换是否正确,我们将训练集的 RGB 图像与新的二值标签叠加显示:

matlab
cmap = [1 0 1; 0 1 0];   % 第一行:品红(非植被),第二行:绿色(植被)
B = labeloverlay(rgbTrain, labelsTrain, Transparency=0.8, Colormap=cmap);
imshow(B, cmap);
title("Training Labels (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 高效管理大图像数据。

敬请期待第三节《分块图像与数据存储——让大图像训练不再困难》。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐