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

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

一、分水岭算法

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

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

  1. 距离变换

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

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

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

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

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

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

优点

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

缺点

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

示例代码解释:

python 复制代码
# 计算每个二值像素到最近零像素的精确欧几里得距离, 然后找到此距离图中的局部峰值
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. 计算距离变换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 控制显示窗口。

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

完整代码如下:

python 复制代码
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()

分割结果如下:

二、 霍夫变换

霍夫变换(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) \]](https://img-blog.csdnimg.cn/direct/aa07b43e88904dd28b68c0d80898ff28.png)

  4. 检测局部最大值

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

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

示例代码:

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

python 复制代码
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. 读取图像并转换为灰度图

    python 复制代码
    image = cv2.imread('image.jpg')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  2. 应用高斯模糊

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

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

    • 如果检测到圆,将其绘制在原始图像上。
    python 复制代码
    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)
  5. 显示结果图像

    • 显示绘制了圆的结果图像。
    python 复制代码
    cv2.imshow("output", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

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

python 复制代码
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()

检测结果如下:

相关推荐
moonsims10 分钟前
全国产化行业自主无人机智能处理单元-AI飞控+通信一体化模块SkyCore-I
人工智能·无人机
MUTA️15 分钟前
ELMo——Embeddings from Language Models原理速学
人工智能·语言模型·自然语言处理
海豚调度20 分钟前
Linux 基金会报告解读:开源 AI 重塑经济格局,有人失业,有人涨薪!
大数据·人工智能·ai·开源
小龙在山东22 分钟前
Python 包管理工具 uv
windows·python·uv
T__TIII26 分钟前
Dify 插件非正式打包
人工智能
jerwey30 分钟前
大语言模型(LLM)按架构分类
人工智能·语言模型·分类
令狐少侠201141 分钟前
ai之RAG本地知识库--基于OCR和文本解析器的新一代RAG引擎:RAGFlow 认识和源码剖析
人工智能·ai
weixin_3077791343 分钟前
批量OCR的GitHub项目
python·github·ocr
小叮当爱咖啡1 小时前
Seq2seq+Attention 机器翻译
人工智能·自然语言处理·机器翻译
shadowcz0071 小时前
奥特曼论人工智能、OpenAI与创业
人工智能·百度