分水岭算法-python-opencv

分水岭算法简单原理:

对于一个图像的灰度值,将图像放平,可以看成是类似与山谷与山顶的图像,灰度值小的就是山底。先找到若干个山底,同时加水,当加到一定程度时候,某些山顶会被淹没,此时在山顶修建大坝,避免两处水汇集在一起。此时这个大坝就是分水岭,即边缘。

原图像:

二值化:

 利用OTSU方法首先进行二值化操作:

详情请看:图像阈值处理-OpenCV_独憩的博客-CSDN博客

import cv2.cv2
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread(r'XXXXX\water_coins.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)

形态学得到边界区域:

对于分水岭算法,我们首先要得到边界可能存在的区域,你们对于上述图像来说,我们要得到的图像为”圆环“

开始直接我们采用开运算去除噪声;

故我们先需要得到一些确定的”背景“,和一些确定的”内部“,利用两者相减得到边界可能存在的区域,即圆环;

确定的”背景“可以使原二值图像”膨胀“得到,确定的”内部“可以使原二值图像”腐蚀“得到,具体原理见:图像膨胀、腐蚀、开运算、闭运算---python.opencv_独憩的博客-CSDN博客

kernel = np.ones((3,3),np.uint8)
#去除噪声
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 3)
#膨胀得到背景
sure_bg = cv.dilate(opening,kernel,iterations=3)
#腐蚀得到前景
sure_fg = cv2.erode(opening, kernel, iterations=3)
sure_fg = np.uint8(sure_fg)
#两者相减得到边缘可能存在区域
unknown = cv.subtract(sure_bg,sure_fg)

plt.figure()
plt.subplot(1,3,1)
plt.imshow(sure_bg,'gray')
plt.title('sure background area')
plt.xticks([]),plt.yticks([])
plt.subplot(1,3,2)
plt.imshow(sure_fg,'gray')
plt.title('sure foreground area')
plt.xticks([]),plt.yticks([])
plt.subplot(1,3,3)
plt.imshow(unknown,'gray')
plt.title('unknown')
plt.xticks([]),plt.yticks([])
plt.show()


但是从图像看出,unknown图像中由于硬币重叠,有部分边界被抵消,故我们采用另外一种方法:

首先介绍一个函数:

dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)

我们只需要知道,这个函数的作用是得到图像(灰度)中所有非零像素与最近的零像素的距离

 我们可以通过对dist_transform图像进行阈值处理得到确定的”内部“,这样的好处是可以控制圆环的粗细,阈值处理 cv.threshold具体见:图像阈值处理-OpenCV_独憩的博客-CSDN博客

kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 3)

sure_bg = cv.dilate(opening,kernel,iterations=3)

dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.5*dist_transform.max(),255,0)
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)

plt.figure()
plt.subplot(1,3,1)
plt.imshow(sure_bg,'gray')
plt.title('sure background area')
plt.xticks([]),plt.yticks([])
plt.subplot(1,3,2)
plt.imshow(sure_fg,'gray')
plt.title('sure foreground area')
plt.xticks([]),plt.yticks([])
plt.subplot(1,3,3)
plt.imshow(unknown,'gray')
plt.title('unknown')
plt.xticks([]),plt.yticks([])
plt.show()

背景,前景,边界区域分类:

 然后我们需要对背景,前景(内部),边界区域进行分类:

ret, markers = cv2.connectedComponents(sure_fg)

markers = markers+1 #使得背景为1

markers[unknown==255] = 0  #使得边界区域为0
markers_copy = markers.copy()

markers_copy[markers==0] = 150  # 灰色表示背景
markers_copy[markers==1] = 0    # 黑色表示背景
markers_copy[markers>1] = 255   # 白色表示前景

markers_copy = np.uint8(markers_copy)
plt.figure()
plt.imshow(markers_copy,'gray')
plt.title('markers')
plt.xticks([]),plt.yticks([])
plt.show()

 cv2.watershed():

最后直接调用分水岭算法函数:

markers = cv2.watershed(img, markers)
img=cv.cvtColor(img,cv.COLOR_BGR2RGB)
img[markers==-1] = [255,0,0]
plt.figure()
plt.imshow(img,'gray')

plt.xticks([]),plt.yticks([])
plt.show()

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值