基于深度学习的多光谱图像语义分割 —— 从原理到实践(第四节)
基于深度学习的多光谱图像语义分割 —— 从原理到实践第四节经过前三节的数据准备与分块处理我们现在手握预训练的 U‑Net 模型或自己训练的模型终于可以对测试图像进行真正的语义分割了。本节将完整演示测试图像推理、后处理去噪、结果可视化以及植被覆盖率计算带你走完实际应用的全链路。4. 测试图像推理、后处理与植被覆盖率计算4.1 测试图像同样需要分块推理测试图像test_data的尺寸为12446×7654×7宽×高×通道整张图像有近 1 亿像素加上 6 个通道数据量超过 1.3 GB。直接加载会耗尽内存更别提送入 GPU。因此推理时也必须采用分块策略。不过推理时我们可以使用更大的补片例如1024×1024因为推理不涉及反向传播显存占用比训练小得多大补片能减少块数加快速度。我们只取前 6 个光谱通道作为输入第 7 通道是掩膜不用于预测并创建分块图像对象patchSize[10241024];bimTestblockedImage(test_data(:,:,1:6),BlockSizepatchSize);matlab bimSegapply(bimTest,(bs)semanticseg(bs.Data,net,Outputtypeuint8),...PadPartialBlockstrue,PadMethod0);4.2 使用 semanticseg 对每个块进行分割semanticseg 是 Computer Vision Toolbox 提供的深度学习分割函数它接受图像块和训练好的网络输出每个像素的类别索引1 或 2。我们利用 apply 函数将这个操作应用到分块图像的每一个块上参数解释(bs)semanticseg(bs.Data, net, Outputtype“uint8”)匿名函数对每个块数据 bs.Data 调用 semanticseg输出类型设为 uint8节省内存。PadPartialBlockstrue当图像边缘不足一个完整块时自动填充填充值由 PadMethod 指定。PadMethod0填充值为 0对类别索引无影响因为我们的类别是 1 和 2。apply 返回一个新的 blockedImage 对象 bimSeg它记录了每个块的分割结果但数据仍分散在磁盘或内存中。4.3 将所有块拼接成完整图像使用 gather 函数从分块图像中读取所有块并拼接成一整张图像matlab segmentedImagegather(bimSeg);现在 segmentedImage 是一个 12446×7654 的二维矩阵单通道标签图每个像素值为 1非植被或 2植被。4.4 应用掩膜——只保留有效区域测试图像的第 7 通道是掩膜标记了哪些像素属于有效分析区域值为 1哪些是图像边界或无效区域值为 0。我们需要将无效区域的预测结果屏蔽掉以免干扰后续统计。matlab maskTesttest_data(:,:,7);% 提取掩膜segmentedImagesegmentedImage.*uint8(maskTest~0);这样掩膜为 0 的位置在 segmentedImage 中变为 0后续统计时这些像素将被忽略nnz 不会计数 0。4.5 观察原始分割结果含噪声先显示一下未去噪的分割图看看效果matlabimshow(segmentedImage,[]);title(SegmentedImage(Before Denoising));你会看到分割图中散布着许多孤立的小点、细碎区域这就是典型的椒盐噪声。噪声产生的原因通常是模型在局部上下文不一致时做出了随机性误判。4.6 中值滤波去除椒盐噪声中值滤波是一种非线性滤波它将每个像素的值替换为其邻域窗口内所有像素的中值。这种操作对孤立噪声点非常有效同时能较好地保留边缘。我们采用 7×7 的窗口窗口大小应大于噪声颗粒尺寸但不宜过大以免模糊真实边界matlab segmentedImagemedfilt2(segmentedImage,[77]);imshow(segmentedImage,[]);title(Segmented Image with Noise Removed);滤波后分割图变得平滑干净区域边界更加清晰。4.7 将分割结果叠加到 RGB 图像上可视化为了更直观地评估分割质量我们将去噪后的标签图叠加在测试图像的彩色RGB版本上。rgbTest 我们在第二节已经生成经过直方图均衡化。matlab cmap[101;010];% 第一行品红非植被第二行绿色植被Blabeloverlay(rgbTest,segmentedImage,Transparency0.8,Colormapcmap);imshow(B,cmap);title(Labeled SegmentedImage(Overlay on RGB));numClasses2;ticks1/(numClasses*2):1/numClasses:1;colorbar(TickLabelscellstr(classNames),Ticksticks,TickLength0,TickLabelInterpreternone);现在你可以看到植被区域被绿色覆盖非植被区域被品红覆盖叠加在原图上便于肉眼验证模型的识别能力。4.8 计算植被覆盖率——解决生态学实际问题语义分割不只是为了好看其最终目的是提取有价值的信息。例如环保机构常需要知道某区域的植被覆盖百分比用于生态评估或变化监测。我们统计分割结果中所有属于植被的像素数再除以有效区域的总像素数即 ROI 大小matlab vegetationPixelsismember(segmentedImage(:),vegetationClassIDs);numVegetationPixelssum(vegetationPixels(:));numROIPixelsnnz(segmentedImage);% 有效区域内被预测为 1 或 2 的像素总数percentVegetationCover(numVegetationPixels/numROIPixels)*100;disp(The percentage of vegetation cover is percentVegetationCover%);输出示例text The percentage of vegetation cover is60.7028%这说明该测试区域约有 60.7% 的面积被植被覆盖与 Hamlin Beach 州立公园的真实情况吻合。4.9 本节完整代码汇总以下是本节所有代码的完整顺序从推理到计算覆盖率matlab% 1. 分块推理patchSize[10241024];bimTestblockedImage(test_data(:,:,1:6),BlockSizepatchSize);bimSegapply(bimTest,(bs)semanticseg(bs.Data,net,Outputtypeuint8),...PadPartialBlockstrue,PadMethod0);segmentedImagegather(bimSeg);% 2. 应用掩膜maskTesttest_data(:,:,7);segmentedImagesegmentedImage.*uint8(maskTest~0);% 3. 显示原始分割figure;imshow(segmentedImage,[]);title(SegmentedImage(Before Denoising));% 4. 中值滤波去噪segmentedImagemedfilt2(segmentedImage,[77]);figure;imshow(segmentedImage,[]);title(Segmented Image with Noise Removed);% 5. 叠加到RGB可视化rgbTesthisteq(test_data(:,:,[321]));% 若尚未生成cmap[101;010];Blabeloverlay(rgbTest,segmentedImage,Transparency0.8,Colormapcmap);figure;imshow(B,cmap);title(Labeled SegmentedImage(Overlay on RGB));numClasses2;ticks1/(numClasses*2):1/numClasses:1;colorbar(TickLabelscellstr(classNames),Ticksticks,TickLength0,TickLabelInterpreternone);% 6. 计算植被覆盖率vegetationPixelsismember(segmentedImage(:),vegetationClassIDs);numVegetationPixelssum(vegetationPixels(:));numROIPixelsnnz(segmentedImage);percentVegetationCover(numVegetationPixels/numROIPixels)*100;disp(The percentage of vegetation cover is percentVegetationCover%);4.10 本节小结在本节中我们完成了以下关键任务对测试图像进行分块推理利用预训练的 U‑Net 生成标签图拼接所有块得到完整的预测结果使用掩膜屏蔽无效区域应用中值滤波去除椒盐噪声提升分割质量将分割结果叠加在 RGB 图像上实现可视化验证基于分割图计算植被覆盖率展示深度学习在生态监测中的实际价值。4.11 下一步训练自己的 U‑Net到目前为止我们一直使用预训练模型。但实际项目中你可能需要针对自己的数据集重新训练模型或者调整类别数量。从下一节开始我们将进入训练环节详细讲解如何利用我们构建的数据存储 dsTrain结合自定义损失函数和训练选项从零开始训练 U‑Net。敬请期待第五节《U‑Net 构建、训练与损失函数设计》。