图像处理分水岭算法_图像处理 分水岭算法 - CSDN
  • 图像处理:分水岭算法(图像分割) 分水岭算法 分水岭算法是一种图像区域分割法,分割的过程中将图片转化为灰度图,然后我会将灰度值看作是海拔,然后向较低点注水,这种基于地形学的解释,我们着重考虑三种点: 极...

    图像处理:分水岭算法(图像分割)

    分水岭算法

    分水岭算法是一种图像区域分割法,分割的过程中将图片转化为灰度图,然后我会将灰度值看作是海拔,然后向较低点注水,这种基于地形学的解释,我们着重考虑三种点:

    aZGCp6.png

    • 极小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内的都是极小值点。
    • 盆地的其它位置点,该位置滴的水滴会汇聚到局部最小点。
    • 盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。

    aZ89sg.png

    明白上述三种点之后,我们开始往盆地的极小值点注水,然后随着注水的深入,每一个极小值点慢慢的向外扩展,然后知道两个盆地的水汇合,汇合处就是我们需要的分水岭

    从下图可以直观理解一下,首先这三块区域都含有极小值点

    aZN0i9.png

    然后逐渐填充就能获得分水岭(即分界线)

    amhpdg.png

    得到分界线就能完成图像分割:

    aZUQeO.png

    基于mark的分水岭算法

    然后基于梯度的分水岭算法容易导致图像的过分割(极小值点过多),如以下这张图(图中噪声比较多,因而产生了许多的极小值点)

    aZarDK.png

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

    ae46Wn.gif

    展开全文
  • 图像分割之分水岭算法

    万次阅读 多人点赞 2019-04-23 09:29:31
    分水岭概念是以对图像进行三维可视化处理为基础的:其中两个是坐标,另一个是灰度级。基于“地形学”的这种解释,我们考虑三类点: a.属于局部性最小值的点,也可能存在一个最小值面,该平面内的都是最小值点 b.当...

    使用C++、opencv进行分水岭分割图像

    分水岭概念是以对图像进行三维可视化处理为基础的:其中两个是坐标,另一个是灰度级。基于“地形学”的这种解释,我们考虑三类点:

    a.属于局部性最小值的点,也可能存在一个最小值面,该平面内的都是最小值点

    b.当一滴水放在某点的位置上的时候,水一定会下落到一个单一的最小值点

    c.当水处在某个点的位置上时,水会等概率地流向不止一个这样的最小值点

    对一个特定的区域最小值,满足条件(b)的点的集合称为这个最小值的“汇水盆地”或“分水岭”。满足条件(c)的点的集合组成地形表面的峰线,称做“分割线”或“分水线”。 

    分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,目前较著名且使用较多的有2种算法:

    (1) 自下而上的模拟泛洪的算法 (2) 自上而下的模拟降水的算法 

    这里介绍泛洪算法的过程。

    算法主要思想:

    我们把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,模拟泛洪算法的基本思想是:假设在每个区域最小值的位置上打一个洞并且让水以均匀的上升速率从洞中涌出,从低到高淹没整个地形。当处在不同的汇聚盆地中的水将要聚合在一起时,修建的大坝将阻止聚合。水将达到在水线上只能见到各个水坝的顶部这样一个程度。这些大坝的边界对应于分水岭的分割线。所以,它们是由分水岭算法提取出来的(连续的)边界线。 

     原图像:                                                          地形俯视图:

      

    原图像显示了一个简单的灰度级图像,其中“山峰”的高度与输入图像的灰度级值成比例。为了阻止上升的水从这些结构的边缘溢出,我们想像将整幅地形图的周围用比最高山峰还高的大坝包围起来。最高山峰的值是由输入图像灰度级具有的最大值决定的。

     

        

    图一被水淹没的第一个阶段,这里水用浅灰色表示,覆盖了对应于图中深色背景的区域。在图二和三中,我们看到水分别在第一和第二汇水盆地中上升。由于水持续上升,最终水将从一个汇水盆地中溢出到另一个之中。

     

                                            

    左图中显示了溢出的第一个征兆。这里,水确实从左边的盆地溢出到右边的盆地,并且两者之间有一个短“坝”(由单像素构成)阻止这一水位的水聚合在一起。随着水位不断上升,如右图所显示的那样。这幅图中在两个汇水盆地之间显示了一条更长的坝,另一条水坝在右上角。这条水坝阻止了盆地中的水和对应于背景的水的聚合。

    这个过程不断延续直到到达水位的最大值(对应于图像中灰度级的最大值)。水坝最后剩下的部分对应于分水线,这条线就是要得到的分割结果。

    对于这个例子,分水线在图中显示为叠加到原图上的一个像素宽的深色路径。注意一条重要的性质就是分水线组成一条连通的路径,由此给出了区域之间的连续的边界。 

    动图演示了整个分水岭算法的过程:

    算法实现:

     算法应用:

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

                         

    为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是通过先验知识,来指导分水岭算法,以便获得更好的图像分段效果。通常的mark图像,都是在某个区域定义了一些灰度层级,在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割。下面的动图很好的演示了基于mark的分水岭算法过程:

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

           

    以上参考:冈萨雷斯《数字图象处理(第三版)》和https://www.cnblogs.com/mikewolf2002/p/3304118.html


    相关API:

     void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)   

    winname:窗口的名字
    onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void on_Mouse(int event, int x, int y, int flags, void* param);
    userdate:传给回调函数的参数  

     

    void on_Mouse(int event, int x, int y, int flags, void* param)

    event: CV_EVENT_*变量之一
    x和y:鼠标指针在图像坐标系的坐标(不是窗口坐标系) 
    flags:CV_EVENT_FLAG的组合, param是用户定义的传递到setMouseCallback函数调用的参数。

    附常用的event:CV_EVENT_MOUSEMOVE、CV_EVENT_LBUTTONDOWN 、CV_EVENT_RBUTTONDOWN、   CV_EVENT_LBUTTONUP  、  CV_EVENT_RBUTTONUP   

    和标志位flags有关的:CV_EVENT_FLAG_LBUTTON 
     

    C++: void watershed(InputArray image,InputoutputArray markers)

    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为8位三通道的彩色图像。

    第二个参数,InputOutput Array类型的markers,函数调用后的运算结果存在这里,输入/输出32位单通道图像的标记结果。即这个参数用于存放函数调后的输出结果,需和源图片有一样的尺寸和类型。


    代码实现:

    #include "stdafx.h"
    #include "opencv2/imgproc/imgproc.hpp"  
    #include "opencv2/highgui/highgui.hpp"  
    #include <iostream>  
    #include <fstream>  
    
    using namespace cv;
    using namespace std;
    
    #define WINDOW_NAME1 "【程序窗口1】"        //为窗口标题定义的宏   
    #define WINDOW_NAME2 "【分水岭算法效果图】"        //为窗口标题定义的宏  
    
    //描述:全局变量的声明  
    Mat g_maskImage, g_srcImage;
    Point prevPt(-1, -1);
    //描述:全局函数的声明  
    static void ShowHelpText();
    static void on_Mouse(int event, int x, int y, int flags, void*);
    
    int main()
    {
    	//【0】改变console字体颜色  
    	system("color 02");
    
    	//【1】载入原图并显示,初始化掩膜和灰度图
    	g_srcImage = imread("D:\\pic-sam\\哀.JPG", 1);
    	namedWindow(WINDOW_NAME1, WINDOW_NORMAL);
    	imshow(WINDOW_NAME1, g_srcImage);
    	Mat srcImage, grayImage;
    	g_srcImage.copyTo(srcImage);
    	cvtColor(g_srcImage, g_maskImage, COLOR_BGR2GRAY);
    	cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);
    	g_maskImage = Scalar::all(0);
    	//【2】设置鼠标回调函数
    	setMouseCallback(WINDOW_NAME1, on_Mouse, 0);
    
    	//【3】轮询按键,进行处理
    	while (1)
    	{
    		//获取键值
    		int c = waitKey(0);
    
    		//若按键键值为ESC时,退出
    		if ((char)c == 27)
    			break;
    
    		//按键键值为2时,恢复源图
    		if ((char)c == '2')
    		{
    			g_maskImage = Scalar::all(0);
    			srcImage.copyTo(g_srcImage);
    			imshow("image", g_srcImage);
    		}
    
    		//若检测到按键值为1或者空格,则进行处理
    		if ((char)c == '1' || (char)c == ' ')
    		{
    			//定义一些参数
    			int i, j, compCount = 0;
    			vector<vector<Point> > contours;
    			vector<Vec4i> hierarchy;
    
    			//寻找轮廓
    			findContours(g_maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
    
    			//轮廓为空时的处理
    			if (contours.empty())
    				continue;
    
    			//拷贝掩膜
    			Mat maskImage(g_maskImage.size(), CV_32S);
    			maskImage = Scalar::all(0);
    
    			//循环绘制出轮廓
    			for (int index = 0; index >= 0; index = hierarchy[index][0], compCount++)
    				drawContours(maskImage, contours, index, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);
    
    			//compCount为零时的处理
    			if (compCount == 0)
    				continue;
    
    			//生成随机颜色
    			/*vector<Vec3b> colorTab;
    			for (i = 0; i < compCount; i++)
    			{
    				int b = theRNG().uniform(0, 255);
    				int g = theRNG().uniform(0, 255);
    				int r = theRNG().uniform(0, 255);
    
    				colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    			}*/
    
    			//计算处理时间并输出到窗口中
    			double dTime = (double)getTickCount();
    			watershed(srcImage, maskImage);
    			dTime = (double)getTickCount() - dTime;
    			printf("\t处理时间 = %gms\n", dTime*1000. / getTickFrequency());
    
    			//双层循环,将分水岭图像遍历存入watershedImage中
    			Mat watershedImage(maskImage.size(), CV_8UC3);
    			int index1 = 0;
    			for (i = 0; i < maskImage.rows; i++)
    				for (j = 0; j < maskImage.cols; j++)
    				{
    					if(maskImage.at<int>(i, j)>index1)
    					index1 = maskImage.at<int>(i, j);
    				}
    			for (i = 0; i < maskImage.rows; i++)
    				for (j = 0; j < maskImage.cols; j++)
    				{
    					int index = maskImage.at<int>(i, j);
    					//对watershed函数生成的index的规律不是很清楚,经测试,并不是按照标记顺序给出index的
    					//具体每一块的index是怎么给出的还需要研究源码
    					if (index == -1)
    						watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
    					else if (index <= 0 || index > compCount)
    						watershedImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
    					else if (index ==index1)
    						watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
    					else
    						watershedImage.at<Vec3b>(i, j) = Vec3b(index*10, 0, 0);//这里想给不同的物体标记为不同程度的颜色
    																				//方便后面去除背景,显示目标物体
    				}
    
    			//混合灰度图和分水岭效果图并显示最终的窗口
    			//watershedImage = watershedImage*0.5 + grayImage*0.5;
    			imshow(WINDOW_NAME2, watershedImage);//直接显示分水岭的效果图
    			//这里想直接根据index,将背景显示为黑色,需要分割出来的目标物体直接显示
    			//但对index生成的规律还未搞清楚,结果可能不是很稳定
    			Mat src = imread("D:\\pic-sam\\哀.JPG", 1);
    			for (int i = 0; i < src.rows; i++)
    				for (int j = 0; j < src.cols; j++)
    				{
    					int a = abs(watershedImage.at<Vec3b>(i, j)[0] - 250) / 150;
    					src.at<Vec3b>(i, j)[0] *= a;
    					src.at<Vec3b>(i, j)[1] *= a;
    					src.at<Vec3b>(i, j)[2] *= a;
    				}
    			namedWindow("dst", WINDOW_NORMAL);
    			imshow("dst", src);
    		}
    	}	
    	return 0;
    }
    
    //鼠标消息回调函数  
    static void on_Mouse(int event, int x, int y, int flags, void*)
    {
    	//处理鼠标不在窗口中的情况  
    	if (x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows)
    		return;
    
    	//处理鼠标左键相关消息  
    	if (event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON))
    		prevPt = Point(-1, -1);
    	else if (event == CV_EVENT_LBUTTONDOWN)
    		prevPt = Point(x, y);
    
    	//鼠标左键按下并移动,绘制出线条  
    	else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))
    	{
    		Point pt(x, y);
    		if (prevPt.x < 0)
    			prevPt = pt;
    		line(g_maskImage, prevPt, pt, Scalar::all(255), 4, 8, 0);
    		line(g_srcImage, prevPt, pt, Scalar::all(255), 4, 8, 0);
    		prevPt = pt;
    		imshow(WINDOW_NAME1, g_srcImage);
    	}
    }
    
    //      描述:输出一些帮助信息    
    static void ShowHelpText()
    {
    	printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION);
    	printf("\n\n  ----------------------------------------------------------------------------\n");
    	//输出一些帮助信息    
    	printf("\n\n\n\t欢迎来到【分水岭算法】示例程序~\n\n");
    	printf("\t请先用鼠标在图片窗口中标记出大致的区域,\n\n\t然后再按键【1】或者【SPACE】启动算法。"
    		"\n\n\t按键操作说明: \n\n"
    		"\t\t键盘按键【1】或者【SPACE】- 运行的分水岭分割算法\n"
    		"\t\t键盘按键【2】- 恢复原始图片\n"
    		"\t\t键盘按键【ESC】- 退出程序\n\n\n");
    }

    源图像:

    进行标记的图像:

    分水岭算法得到的图像:

    分割后图像:

    代码的第108-122行是对opencv分水岭算法生成的结果图进行分析,目前对watershed函数生成的index的规律不是很清楚,经测试,并不是按照标记顺序给出index的,具体每一块的index是怎么给出的还需要研究源码

    代码第130-138行,目的是想直接根据分水岭算法生成的图像中的index,将背景显示为黑色,需要分割出来的目标物体直接显示,但对index生成的规律还未搞清楚,结果可能不是很稳定

    以上部分参考: 毛星云 《OpenCV3编程入门》

    -----------------------------------------------------

    2019年4月19日增加:

    查阅到opencv分水岭算法中,在“循环绘制出轮廓”时用到一个参数compCount,这个参数并不是记录轮廓数目的,它的作用是把每个轮廓设为同一像素值,而maskImage中的像素值就是用1-compcount 的像素值标注的,这样问题又转化为不清楚在查找轮廓时,算法是按照什么样的顺序找出轮廓放入vector中的。

     

    展开全文
  • 图像处理分水岭算法 Matlab

    千次阅读 2018-06-29 14:29:17
    I= imread('road.jpg'); imshow(I); h=fspecial('sobel'); %h = fspecial(type) creates a two-dimensional filter h of the specified type. fspecial returns h as %a correlation kerne...
    I= imread('road.jpg');    
    imshow(I);
    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);
    figure;
    imshow(g);
    g2=imclose(imopen(g,ones(3,3)),ones(3,3));
    figure;
    imshow(g2);
    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);
    g4=watershed(g3);
    figure;
    imshow(g4);
    g5=I;
    g5(g4==0)=255;
    figure;
    imshow(g5);


    ####################################################

    另一篇文章

    点击打开链接


    展开全文
  • 图像处理——分水岭算法

    万次阅读 多人点赞 2018-01-20 19:18:35
    首先感谢以下两位的博文帮助我的理解: (1)迈克老狼2012  https://www.cnblogs.com/mikewolf2002/p/3304118.html (2)-牧野-  ...分水岭算法是一种图像区域分割法,在分割的过程中

    首先感谢以下两位的博文帮助我的理解:

    (1)迈克老狼2012   https://www.cnblogs.com/mikewolf2002/p/3304118.html

    (2)-牧野-              http://blog.csdn.net/dcrmg/article/details/52498440


    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近(求梯度)的像素点互相连接起来构成一个封闭的轮廓。分水岭算法常用的操作步骤:彩色图像灰度化,然后再求梯度图,最后在梯度图的基础上进行分水岭算法,求得分段图像的边缘线。

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

    ima1 (2)ima2

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

    image

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

    1. 局部最小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内的都是最小值点。

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

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

    image

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

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

    lpe1 (1)ima3 (1)

         

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

    ima7ima7b

          为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是通过先验知识,来指导分水岭算法,以便获得更好的图像分段效果。通常的mark图像,都是在某个区域定义了一些灰度层级,在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割。

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

    ima4lpe2ima5

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

     

    ima8ima9

    Opencv 中 watershed函数原型:

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

    第一个参数 image,必须是一个8bit 3通道彩色图像矩阵序列,第一个参数没什么要说的。关键是第二个参数 markers,Opencv官方文档的说明如下:

    Before passing the image to the function, you have to roughly outline the desired regions in the image markers with positive (>0) indices. So, every region is represented as one or more connected components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary mask using findContours() and drawContours(). The markers are “seeds” of the future image regions. All the other pixels in markers , whose relation to the outlined regions is not known and should be defined by the algorithm, should be set to 0’s. In the function output, each pixel in markers is set to a value of the “seed” components or to -1 at boundaries between the regions.


    就不一句一句翻译了,大意说的是在执行分水岭函数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实现的完整过程:


    1. #include "opencv2/imgproc/imgproc.hpp"  
    2. #include "opencv2/highgui/highgui.hpp"  
    3.   
    4. #include <iostream>  
    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<vector<Point>> contours;    
    26.     vector<Vec4i> 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<marks.rows;i++)  
    55.     {  
    56.         for(int j=0;j<marks.cols;j++)  
    57.         {  
    58.             int index=marks.at<int>(i,j);  
    59.             if(marks.at<int>(i,j)==-1)  
    60.             {  
    61.                 PerspectiveImage.at<Vec3b>(i,j)=Vec3b(255,255,255);  
    62.             }              
    63.             else  
    64.             {  
    65.                 PerspectiveImage.at<Vec3b>(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)    <span style="line-height: 20.8px; font-family: sans-serif;">//生成随机颜色函数</span>  
    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. }  

    第一幅图像分割效果:



    按比例跟原始图像融合:



    第二幅图像原始图:



    分割效果:



    按比例跟原始图像融合:


    展开全文
  • 图像分割分水岭算法

    2020-07-30 23:30:21
    图像分割的3中matlab算法
  • 分水岭算法原理及实现

    千次阅读 2015-07-02 17:10:01
     参考资料[1]P497对分水岭算法进行了详细的介绍。 2 实现  见参考资料[2]。 参考资料 [1]Rafael C. Gonzalez, Richard E. Woods. 数字图像处理(第三版),电子工业出版社, 2013年10月第6次印刷 [2]分水岭算法...
  • 分水岭算法

    千次阅读 2017-04-14 08:46:45
    OpenCV—图像分割中的分水岭算法原理与应用 图像分割是按照一定的原则,将一幅图像分为若干个互不相交的小局域的过程,它是图像处理中最为基础的研究领域之一。目前有很多图像分割方法,其中分水岭...
  • 分水岭算法的原理及实现

    千次阅读 2017-09-04 22:29:55
    算法步骤: 1.构建图像梯度图像。 2.通过一定规则生成n个最初的注水区域(先验知识或局部梯度...OpenCV学习(7) 分水岭算法 OpenCV—图像分割中的分水岭算法原理与应用 OpenCV—图像分割中的分水岭算法原理与应用
  • matlab基于分水岭算法处理图像分割的源程序,其中有不同的方法,是我收集过来效果比较好的,有利于大家的学习交流
  • matlab实现分水岭算法处理图像分割

    万次阅读 多人点赞 2011-08-08 14:06:29
    此程序为优化后的分水岭算法,避免了图像过分割 I= imread('D:\Images\pic_loc\1870405130305041503.jpg'); imshow(I); h=fspecial('sobel'); %h = fspecial(type) crea
  • 分水岭算法分割图像

    2020-07-30 23:32:24
    分水岭算法进行图像分割,借鉴形态学理论,将一幅图看成拓扑性地图,其中灰度值对应地形高度。
  • 代码实现: f=imread('C:\Users\lenovo\... % 读入图像 figure,imshow(f); % 显示原始图像 title('原始图像'); bw=imbinarize(f,graythresh(f)); % 转换为黑白二值图像 figure; imshow(bw); title('二值图像')...
  • 算法学习笔记

    2020-06-16 19:23:57
    传统的分水岭算法,是基于数学形态学的分割方法。其基本思想是,将2D图像视为3D地形(其中,像素的坐标=地形的位置,像素的灰度=地形的高度),每一个局部极小值及其周围区域称为集水盆地,而集水盆地的边界则称之为...
  • 基于分水岭和区域合并的图像分割算法实现 论文 matlab实现
  • 图像分割分水岭算法MATLAB源代码

    热门讨论 2020-07-30 23:32:53
    数字图像处理 图像分割分水岭算法 源代码 matlab
  • 分水岭算法及其应用

    千次阅读 2013-12-03 18:20:10
    在作物的农业生产中,病害是影响作物产量的重要因素。因此,农作物生长过程中病害的防治就成了一个关键问题。近些年来,计算机图像处理以及模式识别...近年来,分水岭图像分割方法因其在处理图像分割问题时表现出的良好
  • (四)用分水岭算法实现图像分割 分水岭变换,是一种流行的图像处理算法,用于快速将图像分割成多个同质区域。若把图像看成是一个拓扑地貌,同类区域相当于陡峭边缘内相对平坦的盆地;算法通过逐步增高水位,将其...
  • 分水岭算法(watershed),又名模拟浸水法,它是以拓扑学中的数学形态学的图像处理作为基础的图像分割算法。其基本的思想是将图像看作为地理学中的拓扑地形图,这其中由“山谷”和“山峰”,通过模拟自底向上浸水来...
  • 分水岭算法把一张彩色图片分割成了这样,目标是检测出里面的猪,请问当下用什么特征提取比较好,我是新手请多指教~![图片说明](https://img-ask.csdn.net/upload/201603/21/1458559249_475664.jpg)
1 2 3 4 5 ... 20
收藏数 3,407
精华内容 1,362
关键字:

图像处理分水岭算法