温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Opencv如何实现分水岭算法

发布时间:2021-06-11 14:28:32 来源:亿速云 阅读:226 作者:小新 栏目:编程语言

这篇文章将为大家详细讲解有关Opencv如何实现分水岭算法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

分水岭算法可以将图像中的边缘转化成“山脉”,将均匀区域转化为“山谷”,这样有助于分割目标。

分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中的每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响区域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。

分水岭的计算过程是一个迭代标注过程。分水岭计算分成两个步骤:一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高的排序,然后在从低到高实现淹没的过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点即为分水岭。显然,分水岭表示的是输入图像的极大值点。

简而言之,分水岭算法首先计算灰度图的梯度,这对图像中的“山谷”或没有纹理的“盆地”(亮度值低的点)的形成是很有效的,也对“山头”或图像中有主导线段的“山脉”(山脊对应的边缘)的形成有效。然后开始从用户指定点(或者算法得到点)开始持续“灌注”盆地直到这些区域连成一片。基于这样产生的标记就可以把区域合并到0一起,合并后的区域又通过聚集的方式进行分割,好像图像被“填充”起来一样。

实现分水岭算法–watershed函数

函数watershed实现的分水岭算法是基于标记的分割算法中的一种。在把图像传给函数之前,需要大致勾画标记出图像中的期望进行分割的区域,它们被标记为正指数,所以,每一个区域都会被标记为像素值1、2、3等,表示成为一个或者多个连接组件,这些标记的值可以使用findContours函数和drawContours函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的“种子”,而没有标记清楚的区域,被置为0,在函数的输出中,每一个标记中的像素被设置为“种子”的值,而区域间的值被设置为-1。
void watershed(inputArray,intputOutputArray markers)
*第一个参数,输入图像,需为8位三通道的彩色图像。
*第二个参数,函数调用后的运算结果存在这里,输入/输入32位单通道图像的标记结果。

#include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> using namespace cv; using namespace std; //宏定义 #define WINDOW_NAME "image[procedure window]" //全局变量声明 Mat g_srcImage,g_maskImage; Point prevPt(-1,-1); //全局函数声明 static void on_Mouse(int event,int x,int y,int flags,void*); //主函数 int main() {   //载入源图像   g_srcImage=imread("/Users/new/Desktop/1.jpg");   if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}   //显示源图像   imshow(WINDOW_NAME,g_srcImage);   Mat srcImage,grayImage;   g_srcImage.copyTo(srcImage);   //灰度化   cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY);   //imshow("image[mask]",g_maskImage);   cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);   //imshow("image[gray]",grayImage);   //掩膜图像初始化为0   g_maskImage=Scalar::all(0);   //设置鼠标回调函数   setMouseCallback(WINDOW_NAME, on_Mouse,0);   //轮询按键处理   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')     {       //定义一些参数       int i,j,compCount=0;       vector<vector<Point>>contours;       vector<Vec4i> hierarchy;       //寻找轮廓       findContours(g_maskImage, contours, hierarchy, CV_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(int 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("image[watershed]",watershedImage);     }   }     return 0; } //回调函数定义 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),2,8,0);//画白线     line(g_srcImage,prevPt,pt,Scalar::all(255),2,8,0);//画白线     prevPt=pt;     imshow(WINDOW_NAME, g_srcImage);   } }

Opencv如何实现分水岭算法
Opencv如何实现分水岭算法Opencv如何实现分水岭算法

Opencv技巧

(1)计算算法运行时间:

//计算处理时间并输出到窗口中         double dTime=(double)getTickCount();         //进行分水岭算法         watershed(srcImage, maskImage);         dTime=(double)getTickCount()-dTime;         printf("\t 处理时间=%gms\n",dTime*1000./getTickFrequency());

(2)改变图像某点像素值:Mat类中的at方法对于获取图像矩阵某点的RGB值或者改变某点的值很方便,

对于单通道的图像:image.at<uchar>(i, j) 对于RGB通道的图像:image.at<Vec3b>(i, j)[0]           image.at<Vec3b>(i, j)[1]           image.at<Vec3b>(i, j)[2]

(3)Point(-1,-1)解析:由于卷积过程,图像矩阵要进行填充,Point(-1,-1)即代表卷积开始的位置,这决定了不填充时的结果A处于填充后结果B的位置的那个部分,从(-1,-1)开始卷积的结果是A处于B的正中间那块位置。

关于“Opencv如何实现分水岭算法”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI