本次实验基于分水岭算法对二维平面散点按照距离进行分类。首先生成一个二维散点图 (图BW),然后用bwdist函数计算距离并用分水岭算法对其进行分割。(图RGBclassify_1)可以看到初次分割后的情况。初次分割的目的是间接得到散点图上每个点周围的密度情况。
下图是用bwdist函数计算距离后的三维图像,像山岭和山谷一样。其中的每个凹陷坑就是对应上图中的白点。
要得到点周围的密度情况,需要结合分水岭分割线和bwdist函数计算后的图像,其中分水岭分割线的位置正好对应上图中局部高耸的‘山岭’位置。这些山岭的高低可以代表此处点密度的大小,密度大,则山岭低。所以下一步就是要找到这些山岭的具体值以计算各个点周围的密度情况。这里我只用各个‘山岭’上的最小值作为密度指示值。这部分对应代码中water_line到data那一段。
在得到密度值后就可以按照密度对散点进行分割聚类了。所以本次实验的精髓部分来了,注意。那些密度大的地方,山岭比较低,密度小的地方山岭比较高,而我们就是要用这些高的山岭作为不同聚类点的分界线。那么高度多少合适呢,我在这里用正态分布来确定。对应代码中k就是求得的阈值高度。
确定好阈值后,应该将那些低于阈值的‘障碍’清除掉,就相当于打通了一些山岭使得许多盆地连通起来,这样的话,那些点集,就可以按照连通域进行分类了。下面左图是标记颜色的连通域,右图是分类完成后的图像显示结果。
以下是整个实验的完整代码。
clc
clf
clear
BW=zeros(100,100);%生成1个100x100像素的空白图
%BW=im2uint8(BW);
%给空白图个别点赋值以形成散点图
BW(15,15)=1;BW(10,10)=1;BW(10,15)=1;BW(70,75)=1;BW(60,60)=1;BW(60,65)=1;
D = bwdist(BW);
classify_1=watershed(D);%分水岭分割
RGBclassify_1 = label2rgb(classify_1);%用不同颜色进行标记
%画出3d网状图,直观感受
figure(1)
mesh(D);
water_line=zeros(size(classify_1));
water_line(find(classify_1==0))=1;%将分水岭分割线挑出来
water_line=water_line.*D;
%将water_line中为0的地方补成最大值,以适应imregionalmin寻找每个山岭的最小值
water_line(find(water_line==0))=max(max(water_line));
water_line=im2double(water_line);
Minpoint=imregionalmin(water_line);%得到分水岭分割线上局部极小值的点的标记图
data=water_line(find(Minpoint>0));%得到标记点的值即山岭局部最小值
figure(2)
subplot(121)
imshow(BW)
subplot(122)
imshow(RGBclassify_1)
%计算这些标记点的均值和方差,并以此确定阈值
Meandata=mean(data);%均值
sigma=sqrt(var(data,1));%标准方差
k=Meandata+2*sigma;%设定阈值
%打通部分山岭,实现聚类点的联通
Minpoint_k=Minpoint.*water_line;
Minpoint_k(find(Minpoint_k>k))=0;%根据阈值只留下比较小的值
newclassify=logical(classify_1|Minpoint_k);%利用Minpoint_k中的像素点打通连通域
classify_2=bwlabel(newclassify,8);%对连通域进行标记
RGBclassify_2 = label2rgb(classify_2);%用不同颜色进行标记显示
labelpoint=BW.*classify_2;%将散点按照连通域进行分类
RGBpoint=label2rgb(labelpoint);%将分好类的点用不同颜色标记
figure(3)
subplot(121)
imshow(RGBclassify_2)
subplot(122)
imshow(RGBpoint)