【OpenCV + Python】分水岭算法图像分割

任何一副灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是
山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水。随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割。这就是分水岭算法的背后哲理。
但是这种方法通常都会得到过度分割的结果,这是由噪声或者图像中其他不规律的因素造成的。为了减少这种影响, OpenCV 采用了基于掩模分水岭算法,在这种算法中我们要设置那些山谷点会汇合,那些不会。这是一种交互式的图像分割。我们要做的就是给我们已知的对象打上不同的标签。如果某个区域肯定是前景或对象,就使用某个颜色(或灰度值)标签标记它。如果某个区域肯定不是对象而是背景就使用另外一个颜色标签标记。而剩下的不能确定是前景还是背景的区域就用 0 标记。这就是我们的标签。然后实施分水岭算法。每一次灌水,我们的标签就会被更新,当两个不同颜色的标签相遇时就构建堤坝,直到将所有山峰淹没,最后我们得到的边界对象(堤坝)的值为 -1。
在这里插入图片描述
在这里插入图片描述

dist_img = cv2.distanceTransform(img_bin, distance_type, mask_size)
# distance_type 计算距离的公式
#     参看 cv2.DIST_* ,常用cv2.DIST_L1
# mask_size 
#     参看 cv2.DIST_MASK_*
import cv2
import numpy as np 
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)  
erosion = cv2.erode(img,kernel,iterations = 1)

腐蚀主要就是调用cv2.erode(img,kernel,iterations),这个函数的参数是
第一个参数:img指需要腐蚀的图
第二个参数:kernel指腐蚀操作的内核,默认是一个简单的3X3矩阵,我们也可以利用getStructuringElement()函数指明它的形状
第三个参数:iterations指的是腐蚀次数,省略是默认为1

现在我们要去除图像中的所有的白噪声。这就需要使用形态学中的开运算。为了去除对象上小的空洞我们需要使用形态学闭运算。所以我们现在知道靠近对象中心的区域肯定是前景,而远离对象中心的区域肯定是背景。而不能确定的区域就是硬币之间的边界。
剩下的区域就是我们不知道该如何区分的了。这就是分水岭算法要做的。这些区域通常是前景与背景的交界处(或者两个前景的交界)。我们称之为边界。从肯定是不是背景的区域中减去肯定是前景的区域就得到了边界区域。

距离变换的基本含义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离。最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全腐蚀。这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心像素点的距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离变换。
现在知道了那些是背景那些是硬币了。那我们就可以创建标签(一个与原图像大小相同,数据类型为in32 的数组),并标记其中的区域了。对我们已经确定分类的区域(无论是前景还是背景)使用不同的正整数标记,对我们不确定的区域使用0 标记。我们可以使用函数cv2.connectedComponents()来做这件事。它会把将背景标记为0,其他的对象使用从1 开始的正整数标记。但是,我们知道如果背景标记为0,那分水岭算法就会把它当成未知区域了。所以我们想使用不同的整数标记它们。而对不确定的区域(函数cv2.connectedComponents 输出的结果中使用unknown 定义未知区域)标记为0。
现在标签准备好了。到最后一步:实施分水岭算法了。标签图像将会被修改,边界区域的标记将变为-1.

实例:

import numpy as np
import cv2
np.set_printoptions(threshold=np.inf)
img = cv2.imread('2.PNG',1)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

cv2.imshow('thresh', thresh)
kernel = np.ones((3,3),np.uint8)
# 开运算
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
cv2.imshow('opening', opening)
# sure background area   膨胀后确保背景
sure_bg = cv2.dilate(opening,kernel,iterations=3)
cv2.imshow('sure_bg', sure_bg)
# 第二个参数0,1,2 分别表示CV_DIST_L1, CV_DIST_L2 , CV_DIST_C
dist_transform = cv2.distanceTransform(opening,1,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
cv2.imshow('sure_fg', sure_fg)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
cv2.imshow('unknown', unknown)
# 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] = [255,255,0]
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

实例2:
先介绍个函数,均值漂移pyrMeanShiftFiltering函数原型:pyrMeanShiftFiltering(src, sp, sr[, dst[, maxLevel[, termcrit]]]) -> dst

src参数表示输入图像,8位,三通道图像。

sp参数表示漂移物理空间半径大小。

sr参数表示漂移色彩空间半径大小。

dst参数表示和源图象相同大小、相同格式的输出图象。

maxLevel参数表示金字塔的最大层数。

import cv2
import numpy as np
def water_demo(img):
    print(img.shape)
    blurred = cv2.pyrMeanShiftFiltering(img,10,100)
    gray = cv2.cvtColor(blurred,cv2.COLOR_BGR2GRAY)
    ret, binnary = cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)
    cv2.imshow("binnary",binnary)
    kerhel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
    mb = cv2.morphologyEx(binnary,cv2.MORPH_OPEN,kerhel,iterations=2)
    sure_bg = cv2.dilate(mb,kerhel,iterations=3)
    cv2.imshow("sure_bg",sure_bg)

    dist = cv2.distanceTransform(mb,cv2.DIST_L2,3)
    dist_optput = cv2.normalize(dist,0,1.0,cv2.NORM_MINMAX)
    cv2.imshow("dist",dist_optput*50)
    ret, surface = cv2.threshold(dist,dist.max()*0.6,255,cv2.THRESH_BINARY)
    cv2.imshow("surface",surface)

    surface_fg = np.uint8(surface)
    unknown = cv2.subtract(sure_bg,surface_fg)
    ret,markers=cv2.connectedComponents(surface_fg)
    print(ret)

    markers = markers+1
    markers[unknown == 255]=0
    markers =cv2.watershed(img,markers = markers)
    img[markers ==-1]=[0,0,255]
    cv2.imshow("return",img)


img = cv2.imread('10.jpg',1)
cv2.namedWindow('img', 0)
cv2.namedWindow('binnary', 0)
cv2.namedWindow('sure_bg', 0)
cv2.namedWindow('dist', 0)
cv2.namedWindow('surface', 0)
cv2.namedWindow('return', 0)
cv2.resizeWindow('img', 400, 320)
cv2.resizeWindow('binnary',400, 320)
cv2.resizeWindow('sure_bg',400, 320)
cv2.resizeWindow('dist',400, 320)
cv2.resizeWindow('surface',400, 320)
cv2.resizeWindow('return',400, 320)
cv2.imshow('img', img)
water_demo(img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

  • 9
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖子工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值