摘要:本文主要讲述在MATLAB2020a环境下利用深度神经网络DeepLabV3+进行语义分割,分割感图像中的云层。讲述了:1.训练数据的获取、训练集制作;2.DeepLabV3+模型的构建;3.DeepLabV3+模型训练和验证
为了方便大家,这里我把我构建完的的数据集放到云盘上(提取码:wtx4):
如果感兴趣数据集是如何建立的,请参考如下部分,否则可以直接跳转至 3)制作神经网络的输入数据
气象卫星遥感图像数据来源与日本的葵花8产品,相应的数据介绍链接:
数据获取可直接跳转本链接:
调整至合适的分辨率(图1)然后右键另存为图像(图2)
图1 网页截图
图2 下载云图
我裁剪的尺寸大概是纬度5个格子(太懒了没换算一个格子是几度)。这样就得到了用于训练的原始数据。
有了原始数据后,还需要相应的标签才能构建完整训练数据集。一般处理图像类的卷积神经网络输入都是图像,而标签(或言之输出结果)是根据任务而定的。以分类任务为例,标签就是一个单一类别,以目标检测为例,输出是4维向量(4个元素分别代表矩形框左上角x坐标,左上角y坐标,矩形框x方向长度,y方向长度)。而语义分割的输出是什么呢?语义分割的本质是对图像中每一个像素进行分类,对图像中每个像素都打上一个标签就是语义分割的标注过程。一般情况下,这个工作需要我们手工完成且工作量极大(在下一篇文章中会讲述用MATLAB的标注工具箱进行完整标注的过程),但本文选取的云图分割由标注好的标签。
如下图(图3)左下角产品类型选中CloudType云类,右上角选项选择Blank Map白板,再右键保存得到(图4)
图3 网页截图(标签)
图4 下载云类别(标签)
至此,图4即为图2的标签数据。在地图上截取不同区域或者换不同时间点下的图像,采集了69组原始图像和原始标签。
结合图3和图4可以看到,标注的云类有9种
云类别 |
数据结果赋值 |
图像结果颜色信息 |
雨层云Ns |
6 |
(204 204 0) |
层云St |
9 |
(255 0 0) |
积云Cu |
7 |
(255 153 153) |
卷云Ci |
1 |
(204 204 255) |
深对流云Dc |
3 |
(0 0 255) |
其他(两种方式二选一) |
卷层云Cs:2 高积云Ac:4 高层云As:5 层积云Sc:8 背景:0 |
(102 102 255) (255 255 204) (255 255 0) (255 102 51) (0 0 0) |
本文不需要对云类分割的这么详细,只需要将九类云都分割为云这一类就行
云类别 |
数据结果赋值 |
图像结果颜色信息 |
云 |
1 |
(204 204 0) (255 0 0) (255 153 153) (204 204 255) (0 0 255) (102 102 255) (255 255 204) (255 255 0) (255 102 51) |
背景 |
0 |
(0 0 0) |
将相应的图像和标签对应上,这里我将第一张原图命名为canvas1.png,第一个标签命名为canvas1-seg.png。由于图像和标签的分辨率都是1464*908,基本上可以划分为6个小块:(在原图和标签图的文件夹下运行如下代码)
%% 裁剪69副图像
for n=1:69
I=imread(['canvas',num2str(n),'.png']);
I1=imcrop(I,[0,0,488,454]);
I2=imcrop(I,[489,0,487,454]);
I3=imcrop(I,[977,0,488,454]);
I4=imcrop(I,[0,455,488,454]);
I5=imcrop(I,[489,455,487,454]);
I6=imcrop(I,[977,455,488,454]);
Img={I1 I2 I3 I4 I5 I6};
for i=1:6
Img2=Img(i);
Img3=Img2{1,1};
% Img3=imresize(Img3,[454 488]);
% imwrite(Img2{1,1},[num2str(n),'seg-',num2str(i),'.jpg']);
imwrite(Img3,[num2str(n),'-',num2str(i),'.jpg']);
end
end
将生成的图像放入Canvas-Img文件夹
%% ImgSegCrop
for n=1:69
I=imread(['canvas',num2str(n),'-seg.png']);
I1=imcrop(I,[0,0,488,454]);
I2=imcrop(I,[488,0,487,454]);
I3=imcrop(I,[977,0,488,454]);
I4=imcrop(I,[0,455,488,454]);
I5=imcrop(I,[489,455,487,454]);
I6=imcrop(I,[977,455,488,454]);
Img={I1 I2 I3 I4 I5 I6};
%% 划分云层和背景
for k=1:6
Ik=Img(k);
Ik=Ik{1,1};
% Ik=imresize(Ik,[454 488]);
Itmp=Ik(:,:,1);
WIDTH=size(Ik);
for i=1:WIDTH(1)
for j=1:WIDTH(2)
if Ik(i,j,1)==0&&Ik(i,j,2)==0&&Ik(i,j,3)==0 %三通道均为0的是背景
Itmp(i,j)=0;
else
Itmp(i,j)=1; %否则是云层
end
end
end
imwrite(Itmp,[num2str(n),'-',num2str(k),'.png'])
end
end
将生成的png标签拷贝到Canvas-Label-2中:
这里的图片全是黑的,是因为图像中只有两个值0和1,在uint8下基本上就都是黑色的了。但是颜色灰度不重要,这里的0,1只是类别标签,分别代表背景和云层。
制备完训练图像和训练标签后,需要将二者作为MATLAB环境下DeepLabV3+可读取的数据类型,
%% Semantic Segmentation for Cloud
dataFolder = fullfile('.\卫星云图0826\卫星云图0826\葵花8\');%这里填写自己数据所在路径
imageFolderTrain = fullfile(dataFolder,'Canvas-Img');%训练图像路径
labelFolderTrain = fullfile(dataFolder,'Canvas-Label-2');%训练标签路径
imdsTrain = imageDatastore(imageFolderTrain);% 加载训练图像
classNames = ["Back" "Cloud"];%设置类别名称
labelIDs = [0 1];%设置类别相应的编号,就是Canvas-Label-2中几乎全黑的png中的值
pxds = pixelLabelDatastore(labelFolderTrain,classNames,labelIDs);%生成相应的类别标签
之后随便挑一张图检验一下:
% Read image and pixel label data.
A = readimage(imdsTrain,5);%选中第5号图片
C = readimage(pxds,5);%选中第5号标签
% Display the label categories in C
% categories(C{1})
cmap = ColorMap;
% Overlay pixel label data on the image and display.
B = labeloverlay(A, C,'ColorMap',cmap,'Transparency',0.6);
figure
imshow(B)
得到下图,对比原图看到,浅蓝色为云层,蓝紫色为背景
这里的函数ColorMap需要自己写一下:
function cmap = ColorMap()
cmap = [
000 000 000 % Back
102 102 255 % Cloud
];
% Normalize between [0 1].
cmap = cmap ./ 255;
end
接下来就开始模型的构建了,直接上代码:
%% 数据分析
%
tbl = countEachLabel(pxds);
frequency = tbl.PixelCount/sum(tbl.PixelCount);
bar(1:numel(classNames),frequency)
xticks(1:numel(classNames))
xticklabels(tbl.Name)
xtickangle(45)
ylabel('Frequency') %这里计算云层像素总个数和背景像素总个数之比,用于更改输出层的权重,如果背景元素数量远多于代分割的目标时,不改权重会大大降低网络性能
%% 制作语义分割网络DeeplabV3+
% 定义网络输入大小,与图像大小相等.
imageSize = [454 488 3];
% 定义类别数量.
numClasses = numel(classNames);
%构建deeplabV3+.
lgraph = deeplabv3plusLayers(imageSize, numClasses, "mobilenetv2");%这里骨干网络(图像特征提取网络)选取mobilenetv2,该网络带有ImageNet训练过的参数,采用迁移学习方法可大大提升训练效率
% 设置类别权重
imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq) ./ imageFreq
% 修改输出层
pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights);
lgraph = replaceLayer(lgraph,"classification",pxLayer);
完成网络搭建后,就可以设置超参数、进行数据增强并开始训练了
%% 设置训练参数
options = trainingOptions('adam', ...%选取adam优化器
'LearnRateSchedule','piecewise',...
'LearnRateDropPeriod',20,...%学习率每20代降低一次
'LearnRateDropFactor',0.4,...%学习率每20代降低0.4
'InitialLearnRate',1e-4, ...%初始学习率
'L2Regularization',0.005, ...
'MaxEpochs',50, ... %最大迭代次数
'MiniBatchSize',8, ...%小批量尺寸,可根据显存更改,这里用的8g显存
'Shuffle','every-epoch', ...
'VerboseFrequency',2,...
'Plots','training-progress');%绘制训练曲线
% 图像增强,
augmenter = imageDataAugmenter('RandXReflection',true,...
'RandXTranslation',[-10 10],'RandYTranslation',[-10 10]);%进行平移和翻转
pximds = pixelLabelImageDatastore(imdsTrain,pxds, ...
'DataAugmentation',augmenter);
% 训练网络
[net, info] = trainNetwork(pximds,lgraph,options);
这里在执行lgraph = deeplabv3plusLayers(imageSize, numClasses, "mobilenetv2");时如果没有安装mobilenetv2,则会运行报错,参考我这篇文章中的解决方式
这样就开始模型的训练了,训练过程如下图
可以看到近4小时训练后最终网络在训练集上的准确率有近85%,不算特别高。先看一下训练集上的训练效果:
%% 动态显示
imagePath=pwd;
imageFiles = dir(imagePath); %%读取目录文件下的所有图片文件
numFiles = length(imageFiles);%%获取图片的数量
figure()
for i = 3:numFiles
j = i-2;
subplot(1,2,1)
imageFile = strcat(imagePath,'\',imageFiles(i).name);
I = imread(imageFile);
imshow(I,[],'parent',gca)
subplot(1,2,2)
C = semanticseg(I, net);
B = labeloverlay(I,C,'Colormap',cmap,'Transparency',0.4);
imshow(B,[],'parent',gca)
% pixelLabelColorbar(cmap, classes);
pause(2)
end
训练时可以将414张图像拆分整训练和验证集,这样验证部分可以放在验证集上进行。由于云层类型较多,有些云层十分稀薄,确实不太好界定,因此能够达到这样的训练效果应该还算可以了。
重新再网上下了几张图,构成测试集:
测试结果如下,效果差不多:
小结:本文主要讲述了再MATLAB环境下使用DeepLabV3+实现云层分割。介绍了数据集构建、模型构建和训练以及效果验证。训练集上实现了85%的准确率。但缺少了完整的数据人工标注过程。后续会继续出一篇包含数据标注的完整语义分割任务Demo。
欢迎交流和指正:企鹅2501896344