OpenCV学习(二十) :分水岭算法:watershed()

参考博客:
OpenCV—分水岭算法
图像处理——分水岭算法
OpenCV学习(7) 分水岭算法(1)
Opencv分水岭算法——watershed自动图像分割用法 -牧野-

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

1、watershed()函数

void watershed(
InputArray image,             // 必须是一个8bit 3通道彩色图像矩阵序列
InputOutputArray markers      // markers必须包含了种子点信息:算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),
//对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,
//直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”,以做区分。==markers==
在将图像传递给函数之前,您必须粗略地勾勒出索引为正(>0)的图像标记中的所需区域。
因此,每个区域都表示为一个或多个具有像素值1,2,3的连接组件,以此类推。
可以使用findcontours()和drawcontours()从二进制掩码中检索这些标记。这些标记是未来图像区域的“种子”。
标记中的所有其他像素,如果它们与轮廓区域的关系未知,应该由算法定义,则应该设置为0。
在函数输出中,标记中的每个像素被设置为“seed”组件的值,或者在区域之间的边界处设置为-1。
);

2、示例一:手动添加 markers

watershed图像自动分割的实现步骤:
1)图像灰度化、滤波、Canny边缘检测
2)查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。
3)watershed分水岭运算
4)绘制分割出来的区域,视觉控还可以使用随机颜色填充,或者跟原始图像融合以下,以得到更好的显示效果。

#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;#define WINDOW_NAME1 "【程序窗口1】"        //为窗口标题定义的宏
#define WINDOW_NAME2 "【分水岭算法效果图】"        //为窗口标题定义的宏Mat g_maskImage, g_srcImage;
Point prevPt(-1, -1);int main()
{//输出一些帮助信息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");//【1】载入原图并显示,初始化掩膜和灰度图g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.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, CV_RETR_CCOMP, CV_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);elsewatershedImage.at<Vec3b>(i,j) = colorTab[index - 1];}//混合灰度图和分水岭效果图并显示最终的窗口watershedImage = watershedImage*0.5 + grayImage*0.5;imshow( WINDOW_NAME2, watershedImage );}}waitKey(0);return 0;
}

鼠标回调函数:

static void on_Mouse( int event, int x, int y, int flags, void* )
{// 1)处理鼠标不在窗口中的情况if( x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows )return;// 2)处理鼠标左键相关消息if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) )prevPt = Point(-1,-1);else if( event == CV_EVENT_LBUTTONDOWN )prevPt = Point(x,y);// 3)鼠标左键按下并移动,绘制出白色线条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(200), 5, 8, 0 );  // 掩模图line( g_srcImage, prevPt, pt, Scalar::all(200), 5, 8, 0 );      //prevPt = pt;imshow(WINDOW_NAME1, g_srcImage);}
}

结果:

3、示例二:自动添加 markers

int main()
{//1、载入原图并显示,初始化掩膜和灰度图Mat image = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);imshow("Source Image",image);// 2、灰度化,滤波,Canny边缘检测Mat imageGray;cvtColor(image,imageGray,CV_RGB2GRAY);//灰度转换GaussianBlur(imageGray,imageGray,Size(5,5),2);   //高斯滤波imshow("Gray Image",imageGray);Canny(imageGray,imageGray,80,150);imshow("Canny Image",imageGray);// 3、查找轮廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(imageGray,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());Mat imageContours=Mat::zeros(image.size(),CV_8UC1);  //轮廓Mat marks(image.size(),CV_32S);   //Opencv分水岭第二个矩阵参数marks=Scalar::all(0);int index = 0;int compCount = 0;for( ; index >= 0; index = hierarchy[index][0], compCount++ ){// 4、对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点drawContours(marks, contours, index, Scalar::all(compCount+1), 1, 8, hierarchy);     // 每个轮廓的标记点不一样drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);}//我们来看一下传入的矩阵marks里是什么东西Mat marksShow;convertScaleAbs(marks,marksShow);  // 转换前的 marker 图imshow("marksBefore",marksShow);imshow("imageContours 轮廓",imageContours);// 5、分水岭算法watershed(image,marks);// 6、我们再来看一下分水岭算法之后的矩阵marks里是什么东西Mat afterWatershed;convertScaleAbs(marks,afterWatershed);   // 转换后的 marker 图imshow("After Watershed",afterWatershed);// 7、对每一个区域进行颜色填充Mat PerspectiveImage=Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<marks.rows;i++){for(int j=0;j<marks.cols;j++){int index=marks.at<int>(i,j);if(marks.at<int>(i,j)==-1){PerspectiveImage.at<Vec3b>(i,j)=Vec3b(255,255,255);}else{PerspectiveImage.at<Vec3b>(i,j) =RandomColor(index);}}}imshow("After ColorFill",PerspectiveImage);//分割并填充颜色的结果跟原始图像融合Mat wshed;addWeighted(image,0.4,PerspectiveImage,0.6,0,wshed);imshow("AddWeighted Image",wshed);waitKey(0);return 0;
}
Vec3b RandomColor(int value) //生成随机颜色函数
{value=value%255;  //生成0~255的随机数RNG rng;int aa=rng.uniform(0,value);int bb=rng.uniform(0,value);int cc=rng.uniform(0,value);return Vec3b(aa,bb,cc);
}




3、基于距离的分水岭算法

使用分水岭算法进行图像分割:

(一)获取灰度图像,二值化图像,进行形态学操作,消除噪点
  (二)在距离变换前加上一步操作:通过对上面形态学去噪点后的图像,进行膨胀操作,可以得到大部分都是背景的区域(原黑色不是我们需要的部分是背景)
  (三)使用距离变换distanceTransform获取确定的前景色
相关知识补充(重点)
  (四)在获取了背景区域和前景区域(其实前景区域是我们的种子,我们将从这里进行灌水,向四周涨水,但是这个需要在markers中表示)后,这两个区域中有未重合部分(注1)怎么办?首先确定这些区域(寻找种子)
   开始获取未知区域unknown(栅栏会创建在这一区域),为下一步获取种子做准备
   (五)获取了这些区域,我们可以获取种子,这是通过connectedComponents实现,获取masker标签,确定的前景区域会在其中显示为以1开始的数据,这就是我们的种子,会从这里开始漫水
  重点:
  (六)根据未知区域unknown在markers中设置栅栏,并将背景区域加入种子区域,一起漫水
  (七)根据种子开始漫水,让水漫起来找到最后的漫出点(栅栏边界),越过这个点后各个山谷中水开始合并。注意watershed会将找到的栅栏在markers中设置为-1

OpenCV学习(二十) :分水岭算法:watershed()相关推荐

  1. OpenCV3学习(7.2)——图像分割之二(分水岭算法watershed)

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

  2. OpenCV学习(二十四 ):角点检测(Corner Detection):cornerHarris(),goodFeatureToTrack()

    OpenCV学习(二十四 ):角点检测(Corner Detection):cornerHarris(),goodFeatureToTrack() 参考博客: Harris角点检测原理详解 Harri ...

  3. OpenCV学习(二十二) :反向投影:calcBackProject(),mixChannels()

    OpenCV学习(二十二) :反向投影:calcHist(),minMaxLoc(),compareHist() 参考博客: 反向投影backproject的直观理解 opencv 反向投影 颜色直方 ...

  4. OpenCV学习三十四:watershed 分水岭算法

    1. watershed void watershed( InputArray image, InputOutputArray markers ); 第一个参数 image,必须是一个8bit 3通道 ...

  5. OpenCV 源码中分水岭算法 watershed 函数源码注解

    为了研究分水岭算法,阅读了OpenCV 2.4.9 中watershed函数的源码实现部分,代码位于 opencv\sources\modules\imgproc\src\segmentation.c ...

  6. OpenCV学习(二十) :直方图匹配、对比:calcHist(),minMaxLoc(),compareHist()

    直方图匹配.对比:calcHist ,minMaxLoc,compareHist 1.calcHist()函数 2.归一化:normalize()函数 3.minMaxLoc()函数 4.compar ...

  7. opencv学习(二十六)之图像金字塔(高斯金字塔、拉普拉斯金字塔)

    在进行数字图像处理时,我们可能会需要将某种尺寸的图像转换为其他尺寸的图像,这样会存在放大图像核缩小图像两种可能.opencv提供了一个真正意义上的图像所方函数resize(),但在本篇中主要学习以下使 ...

  8. opencv学习(二十五)之开运算、闭运算、形态梯度、顶帽、黑帽

    上一篇介绍了形态学的基本操作膨胀和腐蚀,我们这一篇将利用膨胀和腐蚀操作实现对图像更高级的形态学操作,而这些都是建立在膨胀和腐蚀操作基础之上. 首先形态学的主要用途是获取物体拓扑和结果信息,它通过物体和 ...

  9. OpenCV4.5.5学习笔记(十七):分水岭算法watershed(),图像修补inpaint()

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.分水岭算法watershed() 二.图像修补inpaint() 总结 前言 笔者本科时候有幸接触了OpenCV3 ...

最新文章

  1. Elasticsearch7.x 安装及集群的配置
  2. 信息系统项目管理师考试条件/时间/拿证/价值汇总
  3. Java解析HTML之HTMLParser使用与详解
  4. “金融与安全大数据”专题导读
  5. 数论 —— 整数分解
  6. 信息学奥赛C++语言:短信计费
  7. 10 有关业务/事件类型WQ在年2021的号码范围不存在
  8. 2021高校毕业生薪酬Top100出炉!
  9. LeetCode 145 二叉树的后序遍历(非递归)
  10. 初识GDI、GDI+
  11. RESTful API标准设计教程
  12. MyBatis的其它方法
  13. 新场景 + 新应用,Flink 在机器学习领域的生产落地
  14. Linux网络命令之 `Hping3`
  15. 马尔可夫链、隐马尔科夫模型、贝叶斯网络、因子图
  16. 如何制定一个“更好”的点阵字库格式
  17. 网格交易法 | 股票市场低风险稳定盈利的方法
  18. bzoj4199 [Noi2015]品酒大会
  19. python3 pdf下载无加密_如何实现使用python将pdf文档加密?
  20. Android也行5view斜体,Android TextView同时设置粗体和斜体

热门文章

  1. 美团配送系统架构演进实践
  2. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之构建灵活的UI
  3. 知识图谱最新权威综述论文解读:开篇部分
  4. VS2017C++单元测试
  5. JS中的call()和apply()方法(转)
  6. ubuntu 安装yar和使用
  7. 在BingoCC上面解析域名实现智能路由
  8. silverlight 缺少对象错误
  9. 使用BAT批处理执行sql
  10. VS2005 添加 Microsoft.Office.Tools.Word.dll 等引用