Python OpenCV分水岭算法分割和提取重叠或有衔接的图像中的对象

本文将介绍如何使用分水岭算法对触摸和重叠的图像中的对象进行分割和提取。
参考:https://www.pyimagesearch.com/2015/11/02/watershed-opencv/
在这里插入图片描述在这里插入图片描述分水岭算法是一种分割的经典算法,在提取图像中连接或重叠的图像中的对象(例如上图中的硬币)时特别有用。 使用传统的图像处理方法(例如阈值检测和轮廓检测),我们将无法从图像中提取每个硬币–但是,利用分水岭算法,我们能够准确地检测和提取每个硬币。

简单阀值和边缘轮廓检测很强大,但对于图片中有重叠和互相接触的对象,简单阀值和轮廓检测将无能为力。

简单阀值和边缘轮廓检测结果如下所示:
在这里插入图片描述硬币数量对不上,也不准确。

要应用分水岭算法,我们需要定义与图像中的对象相对应的标记。这些标记可以是用户定义的,也可以应用图像处理技术(例如阈值)为我们找到标记。
在应用分水岭算法时,获取准确的标记至关重要。
给定标记,我们可以计算欧几里得距离变换并将距离图传递给分水岭函数本身,该分水岭函数从初始标记开始并向外移动,从而“淹没”距离图中的山谷。在分割过程中,水的“池”相遇的地方可以视为边界线。 分水岭算法的输出是一组标签,其中每个标签对应于图像中的唯一对象。从那里开始,我们要做的就是分别遍历每个标签并提取每个对象。

# import the necessary packages
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2

# 我们已成功检测到图像中的所有九个硬币。此外,我们还能够清晰地绘制每个硬币周围的边界。这与使用简单阈值检测和轮廓检测的先前示例形成了鲜明对比,在先前示例中,仅(错误地)检测到两个对象。

image = cv2.imread('D:/pyimagesearch/images/coin4.jpg')
# 应用金字塔均值漂移滤波 以提高阈值设置步骤的准确性  【金字塔均值偏移滤波可以看做是对彩色图像平滑颜色的一种操作】
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Input", image)

# 将经过金字塔均值偏移滤波处理的图像 读取为灰度图像
# 应用Otsu的阈值将背景从前景中分割出来:
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,
                       cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Thresh", thresh)

# 分割的第一步:通过distance_transform_edt计算欧几里德距离变换(EDT Euclidean distance)功能 此函数为每个前景像素计算最接近零的欧几里得距离(即背景像素)。
D = ndimage.distance_transform_edt(thresh)
# 在距离图中找到峰值(即局部最大值)。我们将确保每个峰之间的距离至少为20像素。采用peak_local_max的输出功能,并使用8连接性应用连接组件分析。
localMax = peak_local_max(D, indices=False, min_distance=20,
                          labels=thresh)

# 该函数的输出为我们提供了标记 然后我们将其馈入分水岭函数
# 分水岭算法  假设我们的标记代表我们的距离图中的局部最小值(即山谷),因此我们采用D的负值。
# 分水岭函数返回标签矩阵,一个NumPy数组,其宽度和高度与我们的输入图像相同。每个像素值作为唯一的标签值。具有相同标签值的像素属于同一对象。
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))

# 最后一步是简单地循环唯一标签值并提取每个唯一对象
# loop over the unique labels returned by the Watershed
# algorithm
for label in np.unique(labels):
    # label为0 默认为背景,忽略
    if label == 0:
        continue
    # 为我们的遮罩分配内存 并将属于当前标签的像素设置为255(白色)。
    mask = np.zeros(gray.shape, dtype="uint8")
    mask[labels == label] = 255
    # 检测到遮罩中的轮廓   并提取最大的轮廓-该轮廓将代表图像中给定对象的轮廓/边界。
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    c = max(cnts, key=cv2.contourArea)
    # 绘制围绕对象的包围圆边界。我们还可以计算对象的边界框,应用按位运算,并提取每个单独的对象。
    ((x, y), r) = cv2.minEnclosingCircle(c)
    cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)
    cv2.putText(image, "#{}".format(label), (int(x) - 10, int(y)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)

猜你喜欢

转载自blog.csdn.net/qq_40985985/article/details/105579672