使用matlab进行简单图像分割——分水岭算法

《CV:AMA》一书第九章强调了matlab中分水岭算法对图像的分割

更新:

dalao说一般在进行分水岭前都要灰度化、滤波、边缘检测、轮廓查找然后应用库里的函数,解决过度分割的话要有预先标记的先验知识,一般是mark几个地方,现在程序里一般都有写好的库函数。

参考教程:https://blog.csdn.net/LSGO_MYP/article/details/53611151

è¿éåå¾çæè¿°

这一段讲的很清楚了,

1计算图像梯度幅度图,零值为局部极大强度值(作为边界,如图峰值,连续函数在两个邻近极大值之间必然存在一个极小值)

2将零值作为一个分割区域起点,沿着梯度向后遍历,在一个区域内的邻近像素点都划入该区域

虽然原理相同,但是按照分水岭算法应当是

(将极小值作为起点,比两侧极大值边界小的邻近像素点都划入该区域)

教程里,介绍了内外标记方法不唯一,但要遵从一个标准。

以及改进方法:最小覆盖技术,应当就是imimposemin。除了标记过的位置,都会变亮,对应“像素值上推”。

学习一下官方教程:https://www.mathworks.com/help/images/marker-controlled-watershed-segmentation.html

标记控制的分水岭分割遵循以下基本过程:

1.计算分割函数。这是一张图像,其暗区是要分割的对象。

2.计算前景标记。这些是每个对象中像素的连接斑点。

3.计算背景标记。这些是不属于任何对象的像素。

4.修改分割功能,使其仅在前景和背景标记位置具有最小值。

5.计算修改后的分割函数的分水岭变换。

这个例子突出图像处理工具箱™许多不同的功能,包括imgradientwatershedlabel2rgblabeloverlayimopenimcloseimreconstructimcomplementimregionalmaxbwareaopengraythresh,和imimposemin

步骤1:读取彩色图像并将其转换为灰度

步骤2:使用梯度幅度作为分割函数

>> rgb=imread("d:/lena.jpg");
>> I=rgb2gray(rgb);
>> gmag = imgradient(I);
>> imshow(gmag,[]);

步骤3:标记前景对象

使用“重建开”opening-by-reconstruction和“重建闭”closing-by-reconstruction的形态学技术来“清理”图像。这些操作将在可使用定位的每个对象内创建平坦的最大值imregionalmax

imregionalmax区域最大值是具有恒定强度值的像素的连接分量,周围是具有较低值的像素。

开是腐蚀,然后扩张,而重建开是腐蚀,然后形态重建。首先,使用计算开imopen

se = strel('disk',20);
Io = imopen(I,se);
imshow(Io)

接下来计算“重建开”使用腐蚀imerode和重建imreconstruct

Ie = imerode(I,se);
Iobr = imreconstruct(Ie,I);
imshow(Iobr)

对比一下原始灰度图,重建后的图像可以消除不必要的细节,比如皮肤上的阴影,帽子的羽毛细节等等。

尝试imclose

Ioc = imclose(Io,se);
imshow(Ioc)

现在膨胀imdilate后使用重建imreconstruct。注意,imreconstruct的输入和输出需要求补运算(反转黑白,反色)imcomplement

Iobrd = imdilate(Iobr,se);
Iobrcbr = imreconstruct(imcomplement(Iobrd),imcomplement(Iobr));
Iobrcbr = imcomplement(Iobrcbr);
imshow(Iobrcbr)

通过比较IobrcbrIoc,基于重构的开和闭比标准开和闭,在消除小瑕疵而不影响对象整体形状方面更为有效。

计算的Iobrcbr区域最大值以获得良好的前景标记。

fgm = imregionalmax(Iobrcbr);
imshow(fgm)

将前景标记图像叠加在原始图像上

I2 = labeloverlay(I,fgm);
imshow(I2)

(我感觉好像弄错了前景。。)

某些未完全遮盖阴影的对象没有被标记,这意味着在最终结果中将无法正确分割这些对象。此外,某些对象中的前景标记会一直向上延伸到对象的边缘。应该清洁标记斑点的边缘,然后将其缩小一点。可以先闭再进行侵蚀。

se2 = strel(ones(5,5));
fgm2 = imclose(fgm,se2);
fgm3 = imerode(fgm2,se2);

此过程往往会留下一些必须去除的杂散孤立像素。可以使用bwareaopen来执行此操作,该操作会删除所有像素少于一定数量的斑点。

fgm4 = bwareaopen(fgm3,20);
I3 = labeloverlay(I,fgm4);
imshow(I3)

步骤4:计算背景标记

标记背景。

在清理后的图像中,Iobrcbr黑色像素属于背景(果然把lena的头发当成了背景),可以从阈值操作开始。

bw = imbinarize(Iobrcbr);
imshow(bw)

背景像素为黑色,但理想情况下,我们不希望背景标记离我们要分割的对象的边缘太近。我们将通过计算的前景的“受影响区域的骨架”或SKIZ"skeleton by influence zones"来“缩小”背景bw。可以通过计算的距离变换的分水岭变换bw,然后寻找结果的分水岭脊线(DL == 0)来完成。

D = bwdist(bw);
DL = watershed(D);
bgm = DL == 0;
imshow(bgm)

步骤5:计算分割函数的分水岭变换。

imimposemin可用于修改图像,使其仅在某些所需位置具有区域最小值。

imimposemin来修改渐变幅度图像,以使其唯一的区域最小值出现在前景和背景标记像素上。

 

最后,计算基于分水岭法的分割。

L = watershed(gmag2);

步骤6:可视化结果

  • 在原始图像上叠加前景标记,背景标记和分段的对象边界。

根据需要使用膨胀来使某些方面(例如对象边界)更加可见。

对象边界位于L == 0。二进制前景和背景标记缩放到不同的整数值,以便为它们分配不同的标签。

labels = imdilate(L==0,ones(3,3)) + 2*bgm + 3*fgm4;
I4 = labeloverlay(I,labels);
imshow(I4)

该可视化说明了前景标记和背景标记的位置如何影响结果。在几个位置中,部分被遮挡的较暗对象与它们的明亮邻居对象合并,因为被遮挡的对象没有前景标记。

 

  • 将标签矩阵显示为彩色图像。

标签矩阵(例如由watershed和生成的标签矩阵)bwlabel可以使用转换为真彩色图像,以用于可视化目的label2rgb

Lrgb = label2rgb(L,'jet','w','shuffle');
imshow(Lrgb)
title('Colored Watershed Label Matrix')

使用透明度将此伪彩色标签矩阵叠加在原始强度图像的顶部

figure
imshow(I)
hold on
himage = imshow(Lrgb);
himage.AlphaData = 0.3;
title('Colored Labels Superimposed Transparently on Original Image')

这样看来切割效果还是可以的,虽然前景和背景搞反了,

如果不对求补会有什么样的结果呢?即不执行Iobrcbr = imcomplement(Iobrcbr);

得到的lobrcbr得到的bgm

效果还不如前,求补图是很有必要的。

要是能把lena的头完整的分割出来就好了,理想效果(是我画的边界线):

尝试增大了一下对比度imadjust,均衡直方图histeq,没有改变。

尝试将disk大小改为15,分割更精细了一些,但是脸被分开了,不好。

重新看了一遍,重建那里构造的是盘状结构元素disk,毕竟官方给的教程是一堆梨,所以用圆的也是正常,我想了想不如把它改成一个菱形diamond试一试,

se = strel('diamond',20);

还有半个帽子需要努力!

整理一下全部的指令:

rgb=imread("d:/lena.jpg");
I=rgb2gray(rgb);
gmag = imgradient(I);
%imshow(gmag,[]);
se = strel('disk',20);% 可以调整
Io = imopen(I,se);
Ie = imerode(I,se);
Iobr = imreconstruct(Ie,I);
%imshow(Iobr)
Ioc = imclose(Io,se);
%imshow(Ioc)
Iobrd = imdilate(Iobr,se);
Iobrcbr = imreconstruct(imcomplement(Iobrd),imcomplement(Iobr));
Iobrcbr = imcomplement(Iobrcbr);
%imshow(Iobrcbr)
fgm = imregionalmax(Iobrcbr);
%imshow(fgm)
I2 = labeloverlay(I,fgm);
%imshow(I2)
se2 = strel(ones(5,5));
fgm2 = imclose(fgm,se2);
fgm3 = imerode(fgm2,se2);
fgm4 = bwareaopen(fgm3,20);
I3 = labeloverlay(I,fgm4);
%imshow(I3)
bw = imbinarize(Iobrcbr);
%imshow(bw)
D = bwdist(bw);
DL = watershed(D);
bgm = DL == 0;
%imshow(bgm)
gmag2 = imimposemin(gmag, bgm | fgm4);
L = watershed(gmag2);
labels = imdilate(L==0,ones(3,3)) + 2*bgm + 3*fgm4;
I4 = labeloverlay(I,labels);
%imshow(I4)
Lrgb = label2rgb(L,'jet','w','shuffle');
%imshow(Lrgb)
title('Colored Watershed Label Matrix')
figure
imshow(I)
hold on
himage = imshow(Lrgb);
himage.AlphaData = 0.3;
title('Colored Labels Superimposed Transparently on Original Image')

原练习操作参考教程:

http://blog.sina.com.cn/s/blog_725866260100rz7x.html

https://www.cnblogs.com/GarfieldEr007/p/5374098.html

第1步:读入彩色图像,将其转化成灰度图像

clc; clear all; close all;

>> rgb=imread("e:/lena.jpg");
>> I=rgb2gray(rgb);
>> imshow(I)

第2步:进行滤波和边缘检测

使用Sobel边缘算子对图像进行水平和垂直方向的滤波,然后求取模值,sobel算子滤波后的图像在边界处会显示比较大的值,在没有边界处的值会很小。

h=fspecial('sobel'); 
%h = fspecial(type) creates a two-dimensional filter h of the specified type. fspecial returns h as a correlation kernel, which is the appropriate form to use with imfilter. type is a string having one of these values.   
fd=double(I);
%double使数据变成双精度  
g=sqrt(imfilter(fd,h,'replicate').^2+imfilter(fd,h','replicate').^2);  

显示了一下g(图像梯度幅度图),这是?????

可否直接对梯度幅值图像使用分水岭算法?

>> L = watershed(g);

要不转换成彩色看下?

>> Lrgb = label2rgb(L);

直接使用梯度模值图像进行分水岭算法得到的结果往往会存在过度分割的现象。因此通常需要分别对前景对象和背景对象进行标记,以获得更好的分割效果。

第3步:标记前景对象

有多种方法可以应用在这里来获得前景标记,这些标记必须是前景对象内部的连接斑点像素。

开运算和闭运算:先腐蚀后膨胀称为开;先膨胀后腐蚀称为闭。开和闭这两种运算可以除去比结构元素小的特定图像细节,同时保证不产生全局几何失真。开运算可以把比结构元素小的突刺滤掉,切断细长搭接而起到分离作用;闭运算可以把比结构元素小的缺口或孔填充上,搭接短的间隔而起到连接作用。

开操作后,接着进行闭操作。

g2=imclose(imopen(g,ones(3,3)),ones(3,3));  
imshow(g2);  

imextendedmin计算扩展的最小值变换,它是H-minima变换的区域最小值 。区域最小值是具有恒定强度值的像素的连接分量,并且其外部边界像素都具有较高的值。h是非负标量。

im=imextendedmin(g2,10);  
Lim=watershed(bwdist(im)); %watershed分水岭算法 Lim的值greater than or equal to 0,等于0是分水岭脊像素  
em=Lim==0;  
g3=imimposemin(g2,im|em);  

使用imimposemin函数在输入图像上加上区域最小值。注意,除标记区域外,原始图像的所有暗区变亮。

为了帮助理解这个结果,叠加前景标记到原图上。

g4=watershed(g3);  
imshow(g4);  
g5=I;  
g5(g4==0)=255;  
imshow(g5);  

将改为 im=imextendedmin(g2,5);这个数值越小,划分的区域越小

20

30

50

实现均值漂移分割器

https://blog.csdn.net/qq_34122861/article/details/89556503

 

 

 

发布了46 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36808245/article/details/101562927