OpenCV和图像处理(五)

本文以下OpenCV都简写成"cv2"的形式,所有img都默认为一张图片
关联文章:
OpenCV和图像处理(一)

OpenCV和图像处理(二)

OpenCV和图像处理(三)

OpenCV和图像处理(四)

OpenCV和图像处理(六)

九、分水岭算法

分水岭算法是一种经典且实用的 切割算法 。分水岭算法中有一个核心思想:距离变换。

1、距离变换

距离变换是指把某点到某个特定区域,一般是二值化图的黑色部分,因为黑色=0,可以代表背景。然后我们定义某一个图像,或者某一个不为零的像素,其到背景0的最短距离的数值替换成像素值,那么整个图片中,越远离背景的地方就越"亮",越靠近背景的地方就越"黑"。再配合一个阈值操作,就可以找到每一个需要切割的图像的中心点了,因为中心点肯定是最亮的。

但是如果要计算最短距离,就得算出所有距离然后取min,这样计算量十分庞大。科学家想出了用腐蚀的方法来计算距离:从图形边缘开始腐蚀,每腐蚀一次,被腐蚀的点的距离就是"+1",直到把整个图形腐蚀掉,这样一遍操作,里面所有的点的距离都得到了。

我个人观点:距离变换的思想和上面图像直方图中直方图反向投影是类似的,反向投影是把该像素在整体像素分布中所占的"权重"代替原像素值;而距离变换是把该像素在黑色背景中的"海拔高度"来代替原像素值。用不同的角度去寻找图像的特征,这应该是我们可以学习的一种全新思路。

2、分水岭算法原理

分水岭算法,顾名思义,就是用"水"来分割"山岭"。这里的山就是距离变换后的二值化图,水就是用来切割并标记的工具。我简单说一下我的理解:首先背景是黑色的,高度为0,不同的边缘有不同的高度。然后开始灌水,一开始水都是在最底层,互相独立不连通,随着水高度越来越高,有些低洼的地方就被漫过,水就汇合了,每汇合一次,就在那个地方标一个"-1",这样直到淹没整个图,所有的轮廓都被标注出来了。由于一开始背景黑色就是0,所以只要找出小于0的就是轮廓了。个人理解,不一定对,下面分享一个链接:OpenCV—分水岭算法,我觉得它讲的很好,里面有很多发散的知识点,值得细读!

下面的代码是用来分割一堆硬币的:

img = cv2.imread('coins.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
'''
上面3步是得到二值化后的图片thresh
'''
# noise removal
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
'''
上面2步是对二值化图片进行开操作,为了去除周边背景的噪点
'''
# sure background area
sure_bg = cv2.dilate(opening, kernel, iterations=3)  # 膨胀操作,突出背景

dist_transform = cv2.distanceTransform(opening, cv2.DIST_L1, cv2.DIST_MASK_5)
r'''
这一步叫距离变换,配合下面阈值操作,可以得到每个切割图像的大致中心点位置,
相当于进行了先验处理。
参数:
-----------------------------------------------------------------------
src=opening   :传入的二值图
distance_type :计算距离的公式,参看"轮廓"中=>3-4:凸包和凸性检测中cv2.fitLine()下的解释
mask_size     :尺寸。就是cv2.erode(img, kernel)中kernel的大小
     cv2.DIST_MASK_3 = 3
     cv2.DIST_MASK_5 = 5
     cv2.DIST_MASK_PRECISE = 0
-----------------------------------------------------------------------
'''
# 由于我也不是很清楚,下面代码我就不一一注释了,上面分水岭算法原理解析网页有很详细说明
ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)

# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)

# Marker labelling
ret, markers1 = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers1 + 1
# Now, mark the region of unknown with zero
markers[unknown == 255] = 0
markers3 = cv2.watershed(img, markers) # 这就是分水岭算法
img[markers3 == -1] = [0, 0, 255]
cv2.imshow("img", img) # 展示结果
cv2.waitKey(0)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值