标签:
分水岭是区域分割三个方法的最后一个,对于前景背景的分割有不错的效果。
分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭算法一般和区域生长法或聚类分析法相结合。
分水岭算法一般用于分割感兴趣的图像区域,应用如细胞边界的分割,分割出相片中的头像等等
分水岭算法主要用于图像分段,通常是把一副彩色图像灰度化,然后再求梯度图,最后在梯度图的基础上进行分水岭算法,求得分段图像的边缘线。
opencv中的算法是先把输入图像转化成梯度图(标量)
如果把梯度图看成是一个地形的话,就会发现,梯度高的地方就成了山脉,梯度低的地方就是山谷
我们经过标记为不同的区域后,就从各个标记的地方注水进去,注入的水越来越多的时候,就会出现把流过低些的山脉,从而流到别的山谷中,那么他们就连一了一片区域。
区域分割的要求是把不同的标记分割成不同的地方。所以如果一直注水,可能就会覆盖别的区域了。这时算法就采取某种方法,修大坝使标记的不同区域不会因为注水而相连
他们会互不相干的扩张领地,直到把整个领地都扩张完为止。
再看看下图,是一个图像的地形拓扑
对灰度图的地形学解释,我们我们考虑三类点:
1. 局部最小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内的都是最小值点。
2. 盆地的其它位置点,该位置滴的水滴会汇聚到局部最小点。
3. 盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。
<span style="font-size:18px;">函数声明:CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers ); InputArray image 要分割的原始图片 InputOutputArray markers 标记数组,非零的32位有符号的int型数组,用于标记出要分割的关键 点,进而区域生长,扩展出感兴趣的区域。</span>
<span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> 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( int argc, char** argv ) { //【1】载入原图并显示,初始化掩膜和灰度图 g_srcImage = imread("lena.jpg", 1); 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); for( i = 0; i < maskImage.rows; i++ ) for( j = 0; j < maskImage.cols; j++ ) { int index = maskImage.at<int>(i,j); 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 watershedImage.at<Vec3b>(i,j) = colorTab[index - 1]; } //混合灰度图和分水岭效果图并显示最终的窗口 watershedImage = watershedImage*0.5 + grayImage*0.5; imshow( WINDOW_NAME2, watershedImage ); } } 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 == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON) ) prevPt = Point(-1,-1); else if( event == EVENT_LBUTTONDOWN ) prevPt = Point(x,y); //鼠标左键按下并移动,绘制出白色线条 else if( event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON) ) { Point pt(x, y); if( prevPt.x < 0 ) prevPt = pt; line( g_maskImage, prevPt, pt, Scalar::all(255), 5, 8, 0 ); line( g_srcImage, prevPt, pt, Scalar::all(255), 5, 8, 0 ); prevPt = pt; imshow(WINDOW_NAME1, g_srcImage); } } </span>
这里给出一个最简单,不过有过度切割的现象,还有很多的好的标记分割方法,想学习的可以再深入,这里给出的是入门,效果不是太好
<span style="font-size:18px;">clear,clc%三种方法进行分水岭分割 %读入图像 filename='pears.png'; f=imread(filename); Info=imfinfo(filename); if Info.BitDepth>8 f=rgb2gray(f); end b=im2bw(f,graythresh(f));%二值化,注意应保证集水盆地的值较低(为0),否则就要对b取反 d=bwdist(b); %求零值到最近非零值的距离,即集水盆地到分水岭的距离 l=watershed(-d); %matlab自带分水岭算法,l中的零值即为风水岭 w=l==0; %取出边缘 g=b&~w; %用w作为mask从二值图像中取值 figure subplot(2,3,1), imshow(f); subplot(2,3,2), imshow(b); subplot(2,3,3), imshow(d); subplot(2,3,4), imshow(l); subplot(2,3,5), imshow(w); subplot(2,3,6), imshow(g);</span>
图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流
标签:
原文地址:http://blog.csdn.net/qq_20823641/article/details/52201735