分水岭算法.docx

上传人:b****8 文档编号:9620564 上传时间:2023-02-05 格式:DOCX 页数:21 大小:1.92MB
下载 相关 举报
分水岭算法.docx_第1页
第1页 / 共21页
分水岭算法.docx_第2页
第2页 / 共21页
分水岭算法.docx_第3页
第3页 / 共21页
分水岭算法.docx_第4页
第4页 / 共21页
分水岭算法.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

分水岭算法.docx

《分水岭算法.docx》由会员分享,可在线阅读,更多相关《分水岭算法.docx(21页珍藏版)》请在冰豆网上搜索。

分水岭算法.docx

分水岭算法

图像处理——分水岭算法

分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近(求梯度)的像素点互相连接起来构成一个封闭的轮廓。

分水岭算法常用的操作步骤:

彩色图像灰度化,然后再求梯度图,最后在梯度图的基础上进行分水岭算法,求得分段图像的边缘线。

下面左边的灰度图,可以描述为右边的地形图,地形的高度是由灰度图的灰度值决定,灰度为0对应地形图的地面,灰度值最大的像素对应地形图的最高点。

我们可以自己编程实现灰度图的地形图显示,工程FirstOpenCV6就实现了简单的这个功能,比如上边的灰度图,显示为:

对灰度图的地形学解释,我们我们考虑三类点:

1.局部最小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。

注意:

可能存在一个最小值面,该平面内的都是最小值点。

2.盆地的其它位置点,该位置滴的水滴会汇聚到局部最小点。

3.盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。

假设我们在盆地的最小值点,打一个洞,然后往盆地里面注水,并阻止两个盆地的水汇集,我们会在两个盆地的水汇集的时刻,在交接的边缘线上(也即分水岭线),建一个坝,来阻止两个盆地的水汇集成一片水域。

这样图像就被分成2个像素集,一个是注水盆地像素集,一个是分水岭线像素集。

下面的gif图很好的演示了分水岭算法的效果:

     

在真实图像中,由于噪声点或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在,比如下面的图像,这样的分割效果是毫无用处的。

为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是通过先验知识,来指导分水岭算法,以便获得更好的图像分段效果。

通常的mark图像,都是在某个区域定义了一些灰度层级,在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割。

下面的gif图很好的演示了基于mark的分水岭算法过程:

上面的过度分段图像,我们通过指定mark区域,可以得到很好的分段效果:

 

Opencv中watershed函数原型:

[cpp] viewplain copy

 

1.void watershed( InputArray image, InputOutputArray markers );  

第一个参数image,必须是一个8bit3通道彩色图像矩阵序列,第一个参数没什么要说的。

关键是第二个参数markers,Opencv官方文档的说明如下:

Beforepassingtheimagetothefunction,youhavetoroughlyoutlinethedesiredregionsintheimagemarkerswithpositive(>0)indices.So,everyregionisrepresentedasoneormoreconnectedcomponentswiththepixelvalues1,2,3,andsoon.SuchmarkerscanberetrievedfromabinarymaskusingfindContours()anddrawContours().Themarkersare“seeds”ofthefutureimageregions.Alltheotherpixelsinmarkers,whoserelationtotheoutlinedregionsisnotknownandshouldbedefinedbythealgorithm,shouldbesetto0’s.Inthefunctionoutput,eachpixelinmarkersissettoavalueofthe“seed”componentsorto-1atboundariesbetweentheregions.

就不一句一句翻译了,大意说的是在执行分水岭函数watershed之前,必须对第二个参数markers进行处理,它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过Opencv中findContours方法实现,这个是执行分水岭之前的要求。

接下来执行分水岭会发生什么呢?

算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。

而区域与区域之间的分界处的值被置为“-1”,以做区分。

简单概括一下就是说第二个入参markers必须包含了种子点信息。

Opencv官方例程中使用鼠标划线标记,其实就是在定义种子,只不过需要手动操作,而使用findContours可以自动标记种子点。

而分水岭方法完成之后并不会直接生成分割后的图像,还需要进一步的显示处理,如此看来,只有两个参数的watershed其实并不简单。

下边通过图示来看一下watershed函数的第二个参数markers在算法执行前后发生了什么变化。

对于一个原图:

经过灰度化、滤波、Canny边缘检测、findContours轮廓查找、轮廓绘制等步骤后终于得到了符合Opencv要求的merkers,我们把merkers转换成8bit单通道灰度图看看它里边到底是什么内容:

这个是分水岭运算前的merkers:

这个是findContours检测到的轮廓:

看效果,基本上跟图像的轮廓是一样的,也是简单的勾勒出了物体的外形。

但如果仔细观察就能发现,图像上不同线条的灰度值是不同的,底部略暗,越往上灰度越高。

由于这幅图像边缘比较少,对比不是很明显,再来看一幅轮廓数量较多的图效果:

这个是分水岭运算前的merkers:

这个是findContours检测到的轮廓:

从这两幅图对比可以很明显看到,从图像底部往上,线条的灰度值是越来越高的,并且merkers图像底部部分线条的灰度值由于太低,已经观察不到了。

相互连接在一起的线条灰度值是一样的,这些线条和不同的灰度值又能说明什么呢?

答案是:

每一个线条代表了一个种子,线条的不同灰度值其实代表了对不同注水种子的编号,有多少不同灰度值的线条,就有多少个种子,图像最后分割后就有多少个区域。

再来看一下执行完分水岭方法之后merkers里边的内容发生了什么变化:

可以看到,执行完watershed之后,merkers里边被分割出来的区域已经非常明显了,空间上临近并且灰度值上相近的区域被划分为一个区域,灰度值是一样,不同区域间被划分开,这其实就是分水岭对图像的分割效果了。

总的概括一下watershed图像自动分割的实现步骤:

1.图像灰度化、滤波、Canny边缘检测

2.查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。

3.watershed分水岭运算

4.绘制分割出来的区域,视觉控还可以使用随机颜色填充,或者跟原始图像融合以下,以得到更好的显示效果。

以下是Opencv分水岭算法watershed实现的完整过程:

[cpp] viewplain copy

 

1.#include "opencv2/imgproc/imgproc.hpp"  

2.#include "opencv2/highgui/highgui.hpp"  

3.  

4.#include   

5.  

6.using namespace cv;  

7.using namespace std;  

8.  

9.Vec3b RandomColor(int value);  //生成随机颜色函数  

10.  

11.int main( int argc, char* argv[] )  

12.{  

13.    Mat image=imread(argv[1]);    //载入RGB彩色图像  

14.    imshow("Source Image",image);  

15.  

16.    //灰度化,滤波,Canny边缘检测  

17.    Mat imageGray;  

18.    cvtColor(image,imageGray,CV_RGB2GRAY);//灰度转换  

19.    GaussianBlur(imageGray,imageGray,Size(5,5),2);   //高斯滤波  

20.    imshow("Gray Image",imageGray);   

21.    Canny(imageGray,imageGray,80,150);    

22.    imshow("Canny Image",imageGray);  

23.  

24.    //查找轮廓  

25.    vector> contours;    

26.    vector hierarchy;    

27.    findContours(imageGray,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());    

28.    Mat imageContours=Mat:

:

zeros(image.size(),CV_8UC1);  //轮廓     

29.    Mat marks(image.size(),CV_32S);   //Opencv分水岭第二个矩阵参数  

30.    marks=Scalar:

:

all(0);  

31.    int index = 0;  

32.    int compCount = 0;  

33.    for( ; index >= 0; index = hierarchy[index][0], compCount++ )   

34.    {  

35.        //对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点  

36.        drawContours(marks, contours, index, Scalar:

:

all(compCount+1), 1, 8, hierarchy);  

37.        drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);    

38.    }  

39.  

40.    //我们来看一下传入的矩阵marks里是什么东西  

41.    Mat marksShows;  

42.    convertScaleAbs(marks,marksShows);  

43.    imshow("marksShow",marksShows);  

44.    imshow("轮廓",imageContours);  

45.    watershed(image,marks);  

46.  

47.    //我们再来看一下分水岭算法之后的矩阵marks里是什么东西  

48.    Mat afterWatershed;  

49.    convertScaleAbs(marks,afterWatershed);  

50.    imshow("After Watershed",afterWatershed);  

51.  

52.    //对每一个区域进行颜色填充  

53.    Mat PerspectiveImage=Mat:

:

zeros(image.size(),CV_8UC3);  

54.    for(int i=0;i

55.    {  

56.        for(int j=0;j

57.        {  

58.            int index=marks.at(i,j);  

59.            if(marks.at(i,j)==-1)  

60.            {  

61.                PerspectiveImage.at(i,j)=Vec3b(255,255,255);  

62.            }              

63.            else  

64.            {  

65.                PerspectiveImage.at(i,j) =RandomColor(index);  

66.            }  

67.        }  

68.    }  

69.    imshow("After ColorFill",PerspectiveImage);  

70.  

71.    //分割并填充颜色的结果跟原始图像融合  

72.    Mat wshed;  

73.    addWeighted(image,0.4,PerspectiveImage,0.6,0,wshed);  

74.    imshow("AddWeighted Image",wshed);  

75.  

76.    waitKey();  

77.}  

78.  

79.Vec3b RandomColor(int value)    

 20.8px; font-family:

 sans-serif;">//生成随机颜色函数  

80.{  

81.    value=value%255;  //生成0~255的随机数  

82.    RNG rng;  

83.    int aa=rng.uniform(0,value);  

84.    int bb=rng.uniform(0,value);  

85.    int cc=rng.uniform(0,value);  

86.    return Vec3b(aa,bb,cc);  

87.}  

第一幅图像分割效果:

按比例跟原始图像融合:

第二幅图像原始图:

分割效果:

按比例跟原始图像融合:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1