当前位置:首页 » 《随便一记》 » 正文

图像分割 - 分水岭算法

4 人参与  2023年05月04日 08:49  分类 : 《随便一记》  评论

点击全文阅读


目录

1. 介绍

2.  分水岭算法的实现

距离变换

连接连通分量

3. 代码


1. 介绍

图像是由x,y表示的,如果将灰度值也考虑进去的话,那么一幅图像需要一个三维的空间去表示。

这样就可以把x,y轴比作大地,将灰度值的z轴比作地面上的坡度。

因为图像的灰度值是不均匀的,那么也意味着这个地面也是坑坑洼洼的。那么试想一下,下雨的时候,由于地面是不平坦的,雨水会顺着高的地面流向地处。必然会导致有的地方堆满了水,有的地方由于地势较陡,没有雨水

分水岭算法就是利用这种“地形学”,或者说灰度值的不均匀对图像进行分割。 

在这种将图像类比成地形的方法里,主要考虑三种点:

属于区域极小值的点水滴所在位置的点,如果把水滴放在任意位置,水滴必然流向某个极小值水等概率的流向不止一个极小值的点

在这里,如果某个区域是极小值,且满足第二个条件的点集称为汇水盆地或者分水岭

满足第三个条件的点形成地形表面的线,称为分界线

分水岭算法实现的思路是:

找到一些初始点,然后对这些点集进行灌水。随着水位的上升,不同区域的水会开始融合。为了阻止这种现象,在这些融合处建立拦水坝,这样拦水坝就把图像进行了分割

初始点的选择需要用到距离变换

2.  分水岭算法的实现

首先尝试对下面的算法进行分水岭算法的分割

 


这里读取的是彩色图像,因为最后的 watershed 函数需要传入彩色图像

这里对图像进行阈值处理的结果为

 


阈值处理发现,这里硬币内部出现了孔洞,在背景上也出现了白色的噪声点

因为开运算可以消除白色的噪声,而闭运算可以消除内部的孔洞,所以这里采用形态学的方法进行处理。具体的可以参考:灰度级形态学 - 灰度开运算和灰度闭运算

 


接下来,通过膨胀,将前景扩大,这样就可以确定哪些是背景,哪些是前景

 

这一步主要为了确定真正的前景区域


距离变换

接下来,通过距离变换可以找到这些硬币近似的中心点。先展示下效果,在做讲解

 

因为数据类型的原因,这种用 matplotlib 展示。这里距离变换会计算前景像素点到周围最近背景像素点的距离,所以这里像素点越亮,距离背景越远。并且输入图像必须是个二值图像

 


然后,获取距离变换中距离背景远处的点,这些点就是我们分水岭算法的初始点

因为现在的前景区域一定是硬币的位置

 


unknown 是不确定区域,或者说,这里是有分线线的地方

 


连接连通分量

它会把将背景标记为 0,其他的对象使用从 1 开始的正整数标记

 

这样,分水岭算法会从每个标记的位置开始注水,除了0未知区域。这样水位升高的时候,不同区域的水就会在未知区域相遇,这样分界线就被找出来了

 

 


分水岭算法会将不同区域之间的边界设置为 -1

watershed的结果中只有-1,0,1,2这样的数

 分水岭算法的结果:

 分割的结果:

 

3. 代码

import cv2import numpy as npimport matplotlib.pyplot as pltsrc = cv2.imread('./img.png')                       # 彩色图像img = src.copy()img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)          # 转为灰度图像ret, img_bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)   # 阈值处理kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))                            # 获取结构元img_close = cv2.morphologyEx(img_bin, cv2.MORPH_CLOSE, kernel, iterations=3)        # 闭运算填充内部的孔洞img_open = cv2.morphologyEx(img_close, cv2.MORPH_OPEN, kernel, iterations=2)        # 开运算消除白色噪声img_bin = cv2.dilate(img_open, kernel, iterations=2)                                # 膨胀确定前景和背景dist_transform = cv2.distanceTransform(img_bin, cv2.DIST_L2, 5)                     # 距离变换ret, img_seed = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, cv2.THRESH_BINARY)   # 获取距离背景最远的点img_seed = np.uint8(img_seed)       # 转为图像的形式unknown = cv2.subtract(img_bin,img_seed)                # 可能是背景,可能是前景ret, markers = cv2.connectedComponents(img_seed)       # 连接连通分量markers = markers + 1                                  # 确保背景是1不是0markers[unknown == 255] = 0                            # 未知区域标记为0markers = cv2.watershed(src, markers)                 # 分水岭算法src[markers == -1] = [255,0,0]                        # 将边界找出plt.imshow(np.abs(markers),cmap = 'jet')                # 分水岭算法结果plt.show()cv2.imshow('img',src)                                   # 分割结果cv2.waitKey()cv2.destroyAllWindows()


点击全文阅读


本文链接:http://zhangshiyu.com/post/60571.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1