A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

内容来自OpenCV-Python Tutorials 自己翻译整理

目标
使用分水岭算法对基于标记的图像进行分割
使用函数cv2.watershed()

原理:
灰度图像可以被看成拓扑平面,灰度值高的区域可以看出山峰,灰度值低的区域可以看成是山谷。向每一个山谷当中灌不同颜色的水。水位升高,不同山谷的水会汇合,为防止不同山谷的水汇合,小在汇合处建立起堤坝。然后继续灌水,然后再建立堤坝,直到山峰都掩模。构建好的堤坝就是图像的分割。

此方法通常会得到过渡分割的结果,因为图像中的噪声以及其他因素。为了减少此影响,opencv使用基于标记的分水岭算法,此算法要设置哪些山谷中的汇合点,哪些不是。这是一种交互式的图像分割算法那。我们要给已知对象打上不同表情。如果某个区域肯定是前景或对象,就使用某个颜色或灰度值标签标记它。如果是背景那么使用其他颜色进行标记,其余不能确定的部分用0标记。然后使用分水岭算法,每次灌水,标签会被更新,当两个不同颜色的标签相遇就会构建堤坝,知道所有山峰掩模,最后得到的边界对象值是-1。

代码:
对挨在一起的对象进行分割。

使用Otsu’s 二值化后的结果为

要出去图像中的白噪声。可以使用形态学运算,使用闭运算去除对象中的空洞。

靠近对象中心的区域是前景,离对象远的区域是背景,不确定的区域是边界。

首先提取硬币区域,使用腐蚀操作去掉边缘,剩下的就是硬币。但硬币没有接触时,此方法有效,但是由于硬币相互接触,就要使用另外一种有效的方法:距离变换加上合适的阈值。

之后,要寻找不确定是否是硬币的区域。这里需要膨胀操作。膨胀操作会将对象边界延伸到背景当中。由于边界区域被去除,现在就能知道哪些区域是前景,哪些是背景。

余下的区域不知道如何区分,那么使用分水岭算法。这些区域通常是前景与背景的交界处。从能否确认是否是背景的区域中减去确定是前景的区域就得到了边界。

(前景和背景)

(上面的图是直接使用作者的代码后生产的结果,提取到了前景,为了演示一下不确定的区域,调了一下计算前景的距离变换的参数,使得中间出现不确定的区域)

这里面使用个cv2.distanceTransform函数
该函数用于计算2值图象中所有像素离其最近的值为0像素的近似距离。
参数为cv2.distanceTransform(src, distanceType, maskSize[, dst]) → dst


src为输入的二值图像。distanceType为计算距离的方式,可以是如下值

DIST_USER    = ⑴,  //!< User defined distance

DIST_L1      = 1,   //!< distance = |x1-x2| + |y1-y2|

DIST_L2      = 2,   //!< the simple euclidean distance

DIST_C       = 3,   //!< distance = max(|x1-x2|,|y1-y2|)

DIST_L12     = 4,   //!< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))

DIST_FAIR    = 5,   //!< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998

DIST_WELSCH  = 6,   //!< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846

DIST_HUBER   = 7    //!< distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345



maskSize是蒙板尺寸,只有0,3,5


DIST_MASK_3       = 3, //!< mask=3

DIST_MASK_5       = 5, //!< mask=5

DIST_MASK_PRECISE = 0  //!< mask=0

import numpy as np

import cv2

from matplotlib import pyplot as plt

img = cv2.imread('21.jpg')

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)


kernel = np.ones((3,3),np.uint8)

opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)

# sure background area

sure_bg = cv2.dilate(opening,kernel,iterations=3)#膨胀

# Finding sure foreground area


dist_transform = cv2.distanceTransform(opening,1,5)

ret, sure_fg = cv2.threshold(dist_transform,0.2*dist_transform.max(),255,0)#参数改小了,出现不确定区域

# Finding unknown region

sure_fg = np.uint8(sure_fg)

unknown = cv2.subtract(sure_bg,sure_fg)#减去前景


cv2.imshow('p',sure_fg)


cv2.waitKey(0)

现在知道了那些背景是硬币,可以创建标签。(与原图像大小相同,数据类型为int32的数组)。

对于已经确定分类的区域,也就是背景和前景,使用整数标记,不确定的区域是用0标记。可以使用cv2.connectedComponents()函数来实现此功能。它会将背景标记为0,其他标记为位从1开始的正整数。

但是,如果背景标记为0,那么分水岭算法会将其当成位置区域,所以使用不同的整数进行标记,对于不确定的区域,函数标记为0.

结果使用JET颜色地图表示。深蓝色未知区域,硬币区域使用不同颜色。其余部分用浅蓝色。

使用分水岭算法

效果不错


5 个回复

倒序浏览
奈斯,优秀
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马