分水岭算法分割图像的原理概述及OpenCV代码实现

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

前面博文中提到的图像阈值化,图像边缘检测,图像轮廓检测实际上都是一种图像分割法,图像分割在图像处理识别是非常重要的,这点没有做过图像识别或图像处理的人都很容易想到,就不多解释了。所以图像分割法就根据不同的应用需要出现了很多方法。

本篇博文介绍利用分水岭进行图像分割的方法。它是一种区域分割法,区域分割法利用图像的空间性质,以像素点之间的相似性为依据,根据不同的分割准则进行图像分割。这样能弥补阈值、边缘检测、轮廓检测中忽略像素点空间关系的缺点。

分水岭算法应用于图像分割领域,不仅能够保留了一些传统分割方法的普遍优点(PS:从下面给出的代码中可以看出,在应用分山岭算法前,要用阈值化,边缘检测等对图像作处理得到分水岭标记图,所以,分水岭算法保留了一些传统分割方法的普遍优点),还可以有效克服传统图像分割算法中存在的缺点和弊端,该算法应用于图像分割已经越来越得到研究领域的重视,同时有更加广阔的应用。

分水岭分割是基于自然的启发算法来模拟水流通过地形起伏的现象从而研究总结出来的一种分割方法,其基本原理是将图像特征看作地理上的地貌特征,利用像素的灰度值分布特征,对每个符合特征的区域进行划分,形成边界以构成分水岭。

下面是分水岭算法的物理模型

在上面的水岭算法示意图中局部极小值、积水盆地,分水岭线以及水坝的概念可以描述为:
(1)区域极小值:导数为0的点,局部范围内的最小值点;
(2)集水盆(汇水盆地):当“水”落到汇水盆地时,“水”会自然而然地流到汇水盆地中的区域极小值点处。每一个汇水盆地中有且仅有一个区域极小值点;
(3)分水岭:当“水”处于分水岭的位置时,会等概率地流向多个与它相邻的汇水盆地中;

(4)水坝:人为修建的分水岭,防止相邻汇水盆地之间的“水”互相交汇影响。

实现分水岭算法有多种方法,最通用最广泛的实现方法的是Vincent和Soille在1991年提出的模拟浸没算法以及Meyer在1994年提出的基于距离函数的算法,OpenCV提供了函数watershed()来实现分水岭算法,它采用的是Meyer在1994年提出的基于距离函数的算法,具体的论文大家可以搜索“Meyer, F. Color Image Segmentation, ICIP92, 1992”

watershed()函数的原型如下:

void watershed(InputArray image, InputOutputArray markers)

可见,非常简单,就两个参数,但实际上不是那么简单的,因为markers可不是给一个Mat就行的,官方文档对这个参数的说明如下:

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() (see the watershed.cpp demo). 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.

翻译如下

markers中存储了图像的大致轮廓,并且以值1,2,3...分别表示各个components.markers通常由函数findContours() 和 drawContours()结合使用来获得。markers相当于watershed()运行时的种子参数。markers中,不属于轮廓(outlined regions)的点的值应置为0.函数运行后,图像中的像素如果是在由某个轮廓种子生成的区域中,那么其值就置为这个种子的编号,如果像素不在轮廓种子生成的区域中,则置为-1。

PS:上面这段话我想尽可能为大家描述清楚,但实在是文字水平有限,感觉还是说得不清楚,大家见谅。不过不要紧,如果看不懂,下面给出的代码看了之后你就全懂了。

代码如下

代码请访问博文0040-使用函数watershed实现分水岭图片分割_清溪算法-CSDN博客获取

代码请访问博文0040-使用函数watershed实现分水岭图片分割_清溪算法-CSDN博客获取

运行结果如下图所示

几点说明:

Canny边缘检测的阈值由thresh变量定,大家可以根据不同的图像去调整,如果想动态调整,那么可以用createTrackbar创建滑动条的方法在窗口中调整阈值,详情见我写的博文利用OpenCV的函数createTrackbar创建滑动条查看二值化的最优阈值的源码及讲解_昊虹图像算法-CSDN博客

从运行结果来看,出现了分水岭水割算法常见的问题,即过度分割的问题。解决过度分割一般采用的方法是合并的方法。具体来说一般是先计算分割部分的像素归属,利用直方直方图信息统计相关特征,对每个分割部分统计直方图对比的相似性,然后根据相似性判断分割的两个部分是否需要合并成一个区域。我根据这个思路写了一个合并函数,然而效果不好,应该说是失败的,所以就不把源码帖出来让大家见笑了,工程名是“watershedSegment_08”,以后如果确实需要这个功能,可以再详细研究。失败的运行结果如下图所示:

我在某博客上看到过一种获取轮廓之前对图像的操作的思路:首先,对源图像进行灰度化,并使用OTSU进行二值化操作;然后,对二值化图像进行形态学开操作,再利用distanceTransform完成图像距离变换操作;最后,归一化距离变换的图像,再用这个图像求轮廓。我用这个思路写了源码,效果也不理想,所以也不贴代码了,工程编号是“watermeshed_07”,运行结果如下图所示:

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值