分水岭算法

参考博客:

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

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

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

我们可以自己编程实现灰度图的地形图显示

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

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

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

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

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

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

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

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

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

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

Opencv 中 watershed函数原型:

watershed()
void cv::watershed  (   InputArray  image,
InputOutputArray    markers
)
Python:
markers =  cv.watershed(   image, markers  )

#include <opencv2/imgproc.hpp>

使用分水岭算法进行基于标记的图像分割。

该函数实现了分水岭算法的一个变种,基于非参数标记的分割算法,

在将图像传递给函数之前,必须使用正(>)索引粗略地在图像标记中勾画出所需的区域。因此,每个区域都被表示为一个或多个具有像素值1、2、3等的连接组件。这样的标记可以使用findContours 和 drawContours从二进制掩码中检索(参见watershed.cpp演示)。这些标记是未来图像区域的“种子”。标记中的所有其他像素,其与轮廓区域的关系未知,应由算法定义,应设置为0。在函数输出中,标记中的每个像素被设置为“种子”组件的值,或者在区域之间的边界被设置为-1。

简单概括一下就是说第二个入参markers必须包含了种子点信息。Opencv官方例程中使用鼠标划线标记,其实就是在定义种子,只不过需要手动操作,而使用findContours可以自动标记种子点。而分水岭方法完成之后并不会直接生成分割后的图像,还需要进一步的显示处理,如此看来,只有两个参数的watershed其实并不简单。

Note:任意两个相邻连接的分量不一定被分水岭边界(-1的像素)隔开;例如,它们可以在传递给函数的初始标记图像中相互接触。

Parameters

image 输入8位3通道图像。
markers 输入/输出标记的32位单通道图像(map)。它应该有相同的大小作为图像。

现在我们看看OpenCV中如何使用分水岭算法。

首先我们打开一副图像:

 // 打开另一幅图像cv::Mat    image= cv::imread("../tower.jpg");if (!image.data){cout<<"不能打开图像!"<<endl;return 0;}

接下来,我们要创建mark图像。mark图像格式是有符号整数,其中没有被mark的部分用0表示,其它不同区域的mark标记,我们用非零值表示,通常为1-255,但也可以为其它值,比如大于255的值,不同mark区域甚至可以用同样的值,这个值大小对最后分割可能没有影响(也可能影响),最好不同mark区域还是用不同的值表示,这样能够确保结果正确,之所以用有符号整数,是因为opencv在分水岭算法内部,要用-1,-2等来标记注水区域,最终在mark图像中生成的分水岭线就是用-1表示。

我们通常会创建uchar格式的灰度图,指定mark区域,然后转化为有符号整数的图像格式。

首先对整个背景区域我们创建一个mark域,是下图中白色框框住的部分,其灰度值为255,第二个选择mark域为塔,就是黑色框框住的一块区域,其灰度值为64,最后就是树mark域,蓝色框的部分,其灰度值为128。在分水岭算法时候,会分别对这个3个区域来进行注水操作,如果两个注水盆地被一个mark域覆盖,则它们之间不会有分水岭线产生。

对于mark图像,opencv分水岭算法在初始化时候,会把最外圈的值置为-1,作为整个图像的边界,所以我们第一个mark区域,选择倒数第2外圈,因为设置到最外圈,最后还是会被冲掉。

注意:mark图像是32bit的有符号整数,所以在使用分水岭算法前,我们先对mark图像做一个转化。算法执行完后,再转化为0-255的灰度图。

  imageMask.convertTo(imageMask,CV_32S);// 设置marker和处理图像 cv::watershed(image,imageMask);cv::Mat mark1;imageMask.convertTo(mark1,CV_8U);cv::namedWindow("marker");cv::imshow("marker",mark1);

此时imageMask图像从无符号整数转化为uchar后,如下图所示,第一个mask区域注水,将会使得整个图像为白色,之后分别在第二个,第三个区域的盆地注水,会产生相应的注水图,注水的区域的值即为mark的值,128和64, 分水岭线则为0,注:在转化前分水岭线的值为-1,转化后成为0。

我们使用一个转化函数把分水岭线转化为黑色,其它的部分都白黑色,转化函数的公式为:

最后显示分水岭线,得到下图:(注:在转化前,分水岭线的值为-1)

Opencv 分水岭算法 watershed的图像分割相关推荐

  1. Opencv分水岭算法——watershed自动图像分割用法

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

  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. 分水岭算法c语言,Opencv分水岭算法学习

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

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

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

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

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

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

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

最新文章

  1. vivado----fpga硬件调试 (八)----例化ila核
  2. js条件语句,用if...else if....else方程ax2+bx+c=0一元二次方程。求根
  3. 5G对数据中心的架构产生的影响-vecloud微云
  4. verilog always语法_Verilog初级教程(20)Verilog中的`ifdef 条件编译语句
  5. 肺癌图片识别相关的资料调研
  6. python cookies过期_Python3中关于cookie的创建与保存
  7. 浅说深度学习(4):增强学习
  8. 服务器客户端通信协议,Redis服务端-客户端通信协议
  9. C++ ------------------------- 文件的复制
  10. java.util.stream.IntStream
  11. 利用计算机进行频数分布表制作,实验三 利用Excel软件作频数分布表和统计图表...
  12. 关于淘宝商品信息的爬虫
  13. ASP.NET 安全认证(二)——灵活运用 Form 表单认证中的 deny 与 allow 及保护 .htm 等文件(转)...
  14. 2020牛客寒假算法基础集训营4 G - 音乐鉴赏(概率与期望)
  15. 基于ssm与maven,使用easyui--tree生成类似部门管理树形结构图
  16. 搭建前后端分离主流项目完整步骤——在线教育系统(阿里云服务器部署上线)
  17. filecoin benchmarks v25 GeForce GTX 1080 Ti
  18. 将SkeyeVSS综合安防监控视频流媒体云平台监控画面嵌入微信公众号进行直播
  19. 脱离文档流的方法CSS浮动产生的负面影响及解决办法
  20. 分享5个插件,助你在Python的道路越战越勇

热门文章

  1. 棋牌游戏需要什么服务器
  2. java 农历 节假日_基于Java代码实现判断春节、端午节、中秋节等法定节假日的方法...
  3. 【C++入门】函数重载,引用,内联函数......
  4. BUUCTF SimpleRev(涉及大小端序存储的问题)
  5. Leetcode Best Time to Buy and Sell Stock IV(最大子段和)
  6. linux下Sigal信号值
  7. 服务器里面的文件复制不出来,云服务器 拷贝文件进去弄不了
  8. 高校版MATLAB license checkout failed问题及解决
  9. .NET Core Web API使用HttpClient提交文件的二进制流(multipart/form-data内容类型)
  10. 微机原理及故障的维修(BIOS)