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

其他图像分割方法,如阈值,边缘检测等都不会考虑像素在空间关系上的相似性和封闭性这一概念,彼此像素间互相独立,没有统一性。分水岭算法较其他分割方法更具有思想性,更符合人眼对图像的印象。

其他关于分水岭“聚水盆地”、“水坝”、“分水线”等概念不准备赘述,只探讨一下Opencv中分水岭算法的实现方法watershed——这个“简单”到只有两个参数的函数是如何工作的。

Opencv 中 watershed函数原型:

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实现的完整过程:

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"#include <iostream>using namespace cv;
using namespace std;Vec3b RandomColor(int value);  //生成随机颜色函数int main( int argc, char* argv[] )
{Mat image=imread(argv[1]);    //载入RGB彩色图像imshow("Source Image",image);//灰度化,滤波,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);//查找轮廓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++ ) {//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点drawContours(marks, contours, index, Scalar::all(compCount+1), 1, 8, hierarchy);drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);  }//我们来看一下传入的矩阵marks里是什么东西Mat marksShows;convertScaleAbs(marks,marksShows);imshow("marksShow",marksShows);imshow("轮廓",imageContours);watershed(image,marks);//我们再来看一下分水岭算法之后的矩阵marks里是什么东西Mat afterWatershed;convertScaleAbs(marks,afterWatershed);imshow("After Watershed",afterWatershed);//对每一个区域进行颜色填充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();
}Vec3b RandomColor(int value)    <span style="line-height: 20.8px; font-family: sans-serif;">//生成随机颜色函数</span>
{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);
}

第一幅图像分割效果:

按比例跟原始图像融合:

第二幅图像原始图:

分割效果:

按比例跟原始图像融合:

转载于:https://www.cnblogs.com/mtcnn/p/9411969.html

Opencv分水岭算法——watershed自动图像分割用法相关推荐

  1. Opencv 分水岭算法 watershed的图像分割

    分水岭算法 参考博客: (1)迈克老狼2012   https://www.cnblogs.com/mikewolf2002/p/3304118.html (2)-牧野-              h ...

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

    OpenCV学习(二十) :分水岭算法:watershed() 参考博客: OpenCV-分水岭算法 图像处理--分水岭算法 OpenCV学习(7) 分水岭算法(1) Opencv分水岭算法--wat ...

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

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

  4. 使用OpenCV和C++实现的分水岭算法(Watershed)

    分水岭算法(watershed)是一种比较基本的数学形态学分割算法,其基本思想是将灰度图像转换为梯度图像,将梯度值看作高低起伏的山岭,将局部极小值及其邻域看作一个"集水盆".设想一 ...

  5. opencv 分水岭算法详细理论+实践

    分水岭算法看了两天了,基本原理看着挺简单,但是opencv中具体的实现方式看着还挺困难的.今天就说说我理解的地方,有很多不太理解的还得以后深入学习时候再补充. 基本原理:分水岭实则为两个盆地的交界处, ...

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

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

  7. 分水岭算法c语言,Opencv分水岭算法学习

    分水岭算法可以将图像中的边缘转化成"山脉",将均匀区域转化为"山谷",这样有助于分割目标. 分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图 ...

  8. 关于Opencv 分水岭算法种子点(注水口)的选取问题

    Opencv 分水岭算法 void watershed( InputArray image, InputOutputArray markers ); 官方文档中这样表述分水岭算法种子的选取 /* Be ...

  9. OpenCV | 分水岭算法进行图像分割

    分水岭算法进行图像分割 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称 ...

最新文章

  1. win7台式电脑怎么连wifi_win7系统怎么设置WiFi共享
  2. Java事务处理类(源码)
  3. 一起学设计模式 - 命令模式
  4. 含有min()函数的栈,各种操作时间复杂度为O(1)
  5. matlab 提取数列里非零_什么!科研交流免费教Matlab?
  6. Python基础知识点总结
  7. Rust最受喜爱却少有人用,Python仅排第六,2021全球开发者调查报告出炉
  8. “虎力全开”采购季,存储产品已就位
  9. 【备忘录】c++ 整形浮点=字符串 类型转换
  10. 4. tensorflow2实现抽样分布—卡方分布、F分布、t分布、Beta分布、Gamma分布——python实战
  11. 高仿墨迹天气下拉拉伸图片
  12. 移动硬盘提示“需要格式化”
  13. 【渝粤题库】陕西师范大学200101 西方文学 作业(高起专)
  14. 【智能柜领域】智能快递柜终端系统Axure原型作品(另有智能柜整套作品)
  15. 网点分布图如何制作,如何制作网点地图
  16. 面向对象与面向过程的理解
  17. Android通过Canvas手绘一个折线图
  18. php字符串函数处理emoji,PHP中处理内容含有emoji表情的几种方式
  19. win10打开蓝牙,蓝牙开关消失,蓝牙和其他设备设置,蓝牙开关不见了
  20. 1-第1章 第3节-直角坐标与自然坐标转换-1-会用公式就行

热门文章

  1. 七种必看的个股盘口语言
  2. thinkPhp开发流程之一(Windows环境下)
  3. 1、matlab页面和基本操作
  4. 伪类和伪元素的区别?
  5. 运用Javascript实现时间重置、进行时间加减计算
  6. generate()函数
  7. php socket编程实例
  8. oracle不能写入注册表,保存注册表时提示无法创建值,写入注册表时出错怎么办?...
  9. Mysql 出现Error 1118的一种解决方法
  10. [OpenGL]配置GLFW(超详细)