当前位置:   article > 正文

分水岭算法分割和霍夫变换识别图像中的硬币

分水岭算法分割和霍夫变换识别图像中的硬币

首先解释一下第一种分水岭算法:

一、分水岭算法

分水岭算法是一种基于拓扑学的图像分割技术,广泛应用于图像处理和计算机视觉领域。它将图像视为一个拓扑表面,其中亮度值代表高度。算法的目标是通过模拟雨水从山顶流到山谷的过程,将图像分割成若干独立的区域。

分水岭算法的步骤和原理:

  1. 距离变换

    • 首先对图像进行预处理,将图像转化为灰度图,并进行二值化处理(如Otsu算法)。
    • 对二值图像应用距离变换,计算每个前景像素到最近的背景像素的距离,生成距离图。距离变换后的图像可以看作是一幅"地形图",前景像素的距离值越大,代表的高度越高。
  2. 寻找局部极大值

    • 在距离图中找到局部极大值点。这些点通常位于目标物体的中心位置,将作为初始标记。局部极大值点是那些比其邻域像素值更大的点。
  3. 创建标记图

    • 创建一个与原始图像大小相同的标记图,将局部极大值点的位置赋值为不同的标签(从1开始编号),其余区域标记为0。
  4. 应用分水岭算法

    • 将距离图的负值作为输入图像,标记图作为初始标记,应用分水岭算法。分水岭算法通过模拟水从局部极大值点流向低谷的过程,不断合并像素,形成分割区域。
    • 在这个过程中,水从局部极大值点流向低谷,如果两个不同的标签的水流在某处相遇,该处将被标记为边界。
  5. 生成分割结果

    • 分水岭算法最终会将图像分割成多个区域,每个区域对应一个标签。边界区域通常被标记为0。

分水岭算法的优点和缺点:

优点

  • 分水岭算法可以生成闭合的区域边界,适用于目标物体具有明确边界的图像。
  • 算法可以自动确定分割区域的数量,无需事先设定。

缺点

  • 对噪声和边缘模糊敏感,容易产生过分割,即将一个目标物体分割成多个区域。
  • 需要进行预处理以减少噪声和增强边缘(如均值漂移滤波)。

示例代码解释:

# 计算每个二值像素到最近零像素的精确欧几里得距离, 然后找到此距离图中的局部峰值
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh)

# 根据找到的局部峰值创建标记数组, 标记数组的值对应于每个硬币的序号
markers = np.zeros_like(thresh, dtype=np.int32)
markers[tuple(localMax.T)] = np.arange(1, len(localMax) + 1)

# 应用分水岭算法, 将图像分割为不同的区域
labels = watershed(-D, markers, mask=thresh)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 计算距离变换ndimage.distance_transform_edt(thresh) 计算每个前景像素到最近背景像素的欧几里得距离,生成距离图 D
  2. 寻找局部极大值peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh) 在距离图中寻找局部极大值,这些点将作为初始标记。
  3. 创建标记图markers 初始化为全零矩阵,将局部极大值点的位置赋值为不同的标签。
  4. 应用分水岭算法labels = watershed(-D, markers, mask=thresh) 使用分水岭算法对距离图的负值进行分割,生成标签图 labels

通过以上步骤,分水岭算法将输入图像分割成若干独立区域,每个区域代表一个目标物体。

以检测这张图为例子:
在这里插入图片描述

使用分水岭算法流程如下:

  1. 读取图像并应用均值漂移滤波

    • 使用 cv2.imread 读取输入图像。
    • 使用 cv2.pyrMeanShiftFiltering 对图像进行均值漂移滤波,平滑图像并减少噪点。
  2. 转换为灰度图并二值化

    • 使用 cv2.cvtColor 将平滑后的图像转换为灰度图。
    • 使用 cv2.threshold 结合 Otsu 算法进行自动阈值二值化,将图像转换为二值图像。
  3. 计算欧几里得距离并找到局部峰值

    • 使用 ndimage.distance_transform_edt 计算每个二值像素到最近零像素的欧几里得距离,生成距离变换图。
    • 使用 peak_local_max 找到距离图中的局部峰值,这些峰值将作为分水岭算法的初始标记。
  4. 创建标记数组并应用分水岭算法

    • 创建一个与二值图像大小相同的标记数组 markers,将局部峰值的位置赋值为不同的标签。
    • 使用 watershed 函数进行分水岭算法,将图像分割成不同区域,每个区域对应一个硬币。
  5. 遍历分割出的不同区域,绘制轮廓和标签

    • 遍历分割后的标签,跳过背景标签(标签为0)。
    • 为每个硬币创建一个掩码图像,设置对应标签区域为白色,其余区域为黑色。
    • 使用 cv2.findContours 查找掩码图像中的轮廓,并找到最大的轮廓(即硬币区域)。
    • 使用 cv2.minEnclosingCircle 计算最小外接圆的圆心坐标和半径。
    • 在原始图像上绘制圆形轮廓和标签。
  6. 显示最终结果图像

    • 使用 cv2.imshow 显示处理后的图像。
    • 使用 cv2.waitKeycv2.destroyAllWindows 控制显示窗口。

上述流程通过图像平滑、二值化、距离变换、局部峰值检测和分水岭算法,实现了对硬币图像的分割,并在分割后的图像上绘制了硬币的轮廓和编号标签。
完整代码如下:

import numpy as np
import cv2
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
from scipy import ndimage
import imutils

# 读取图像并应用均值漂移滤波来平滑图像,减少噪点
image = cv2.imread('/coins/1.jpg')
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)

# 将图像转换为灰度图,然后使用Otsu算法自动确定阈值进行二值化
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

# 计算每个二值像素到最近零像素的精确欧几里得距离,然后找到此距离图中的局部峰值
# 这些峰值将作为分水岭算法的初始标记
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh)

# 根据找到的局部峰值创建标记数组,标记数组的值对应于每个硬币的序号
markers = np.zeros_like(thresh, dtype=np.int32)
markers[tuple(localMax.T)] = np.arange(1, len(localMax) + 1)

# 应用分水岭算法,将图像分割为不同的区域
labels = watershed(-D, markers, mask=thresh)

# 遍历分割出的不同区域,绘制出每个硬币的轮廓和标签
for label in np.unique(labels):
    if label == 0:
        continue

    # 创建一个掩码图像,将当前标签对应的区域设置为白色,其他区域设置为黑色
    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)

# 显示最终的结果图像
cv2.imshow("Output", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

分割结果如下:
在这里插入图片描述

二、 霍夫变换

霍夫变换(Hough Transform)是图像处理中的一种重要技术,用于检测图像中的几何形状。霍夫圆检测(Hough Circle Transform)是霍夫变换的一个具体应用,用于检测图像中的圆形物体。

霍夫圆检测的原理和步骤:

  1. 边缘检测

    • 首先对图像进行边缘检测,常用的方法是Canny边缘检测。边缘检测可以提取出图像中的显著边缘,减少数据量并突出目标物体的轮廓。
  2. 参数空间定义

    • 在检测圆的过程中,需要定义圆的参数空间。一个圆由三个参数定义:圆心坐标 (x, y) 和半径 r。霍夫圆检测将在参数空间中搜索圆的可能位置和大小。
  3. 投票累加

    • 在边缘检测后的二值图像中,每个边缘点 (x, y) 都会在参数空间中投票支持可能的圆心和半径组合。具体而言,对于每个边缘点 (x, y) 和每个可能的半径 r,可以根据圆的方程计算圆心坐标 (a, b):
      [
a = x - r \cdot \cos(\theta)
]
[
b = y - r \cdot \sin(\theta)
]

    • 在参数空间中累加 (a, b) 的投票次数。

  4. 检测局部最大值

    • 在参数空间中,投票次数最多的位置即为最可能的圆心和半径组合。通过检测参数空间中的局部最大值,确定圆的存在和位置。
  5. 绘制检测到的圆

    • 根据检测到的圆心坐标和半径,在原始图像上绘制圆形轮廓。

示例代码:

以下是一个使用OpenCV进行霍夫圆检测的示例代码:

import cv2
import numpy as np

# 读取图像
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 应用高斯模糊,减少噪声
blurred = cv2.GaussianBlur(gray, (9, 9), 2)

# 使用霍夫圆检测
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=20,
                           param1=50, param2=30, minRadius=15, maxRadius=30)

# 如果检测到圆
if circles is not None:
    circles = np.round(circles[0, :]).astype("int")
    
    for (x, y, r) in circles:
        # 绘制圆的轮廓
        cv2.circle(image, (x, y), r, (0, 255, 0), 4)
        # 绘制圆心
        cv2.rectangle(image, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)

# 显示结果图像
cv2.imshow("output", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

代码详解:

  1. 读取图像并转换为灰度图

    image = cv2.imread('image.jpg')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    • 1
    • 2
  2. 应用高斯模糊

    • 使用高斯模糊(Gaussian Blur)来平滑图像,减少噪声。
    blurred = cv2.GaussianBlur(gray, (9, 9), 2)
    
    • 1
  3. 使用霍夫圆检测

    • 调用 cv2.HoughCircles 函数进行霍夫圆检测。参数解释如下:
      • blurred:输入的灰度图像。
      • cv2.HOUGH_GRADIENT:检测方法,使用梯度信息。
      • dp=1.2:累加器分辨率与图像分辨率的反比关系。
      • minDist=20:检测到的圆之间的最小距离。
      • param1=50:Canny边缘检测的高阈值。
      • param2=30:累加器阈值,用于检测圆的阈值,越小越容易检测到不明显的圆。
      • minRadius=15maxRadius=30:检测圆的半径范围。
    circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=20,
                               param1=50, param2=30, minRadius=15, maxRadius=30)
    
    • 1
    • 2
  4. 绘制检测到的圆

    • 如果检测到圆,将其绘制在原始图像上。
    if circles is not None:
        circles = np.round(circles[0, :]).astype("int")
        
        for (x, y, r) in circles:
            cv2.circle(image, (x, y), r, (0, 255, 0), 4)
            cv2.rectangle(image, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  5. 显示结果图像

    • 显示绘制了圆的结果图像。
    cv2.imshow("output", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3

通过上述步骤和代码,霍夫圆检测可以在图像中自动识别和绘制圆形目标。
识别图中硬币的完整代码如下:

import cv2
import numpy as np

# 读取图像并转换为灰度图像
image = cv2.imread('/coins/1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 应用高斯模糊
gray = cv2.GaussianBlur(gray, (15, 15), 0)

# 使用霍夫圆变换检测圆
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1.2, minDist=50, param1=50, param2=30, minRadius=20,
                           maxRadius=60)

# 确保至少检测到一个圆
if circles is not None:
    circles = np.round(circles[0, :]).astype("int")

    for (i, (x, y, r)) in enumerate(circles):
        # 绘制圆圈和中心点
        cv2.circle(image, (x, y), r, (0, 255, 0), 2)
        cv2.putText(image, str(i + 1), (x - 10, y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

# 显示最终结果图像
cv2.imshow("Detected Coins", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

检测结果如下:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/676434
推荐阅读
相关标签
  

闽ICP备14008679号