You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python与OpenCV检测图像中圆形元素的中心点

问题描述

我有如下类型的起始图像:
起始图像
需要检测其中圆形元素的中心点(准确说是圆形元素的中点):
待检测的圆形元素图像

目前我的代码可以检测承载圆形元素的塑料模具,并选择矩形ROI聚焦到相关区域:

import cv2
import imutils
import numpy as np

if __name__ == "__main__":
    image = cv2.imread('Dart - Overview Image - with Film.bmp')
    img = imutils.resize(image, width=700)
    image = img

    output = image.copy()
    roi = image.copy()

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # cv2.imshow("Gray", gray)
    # cv2.waitKey(0)

    # detect circles in the image
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)

    # ensure at least some circles were found
    if circles is not None:
        # convert the (x, y) coordinates and radius of the circles to integers
        circles = np.round(circles[0, :]).astype("int")
        # loop over the (x, y) coordinates and radius of the circles
        for (x, y, r) in circles:
            # draw the circle in the output image, then draw a rectangle
            # corresponding to the center of the circle
            cv2.circle(output, (x, y), r, (0, 255, 0), 2)
            cv2.rectangle(output, (x - 2, y - 2), (x + 2, y + 2), (0, 128, 255), -1)
            roi = roi[y - r: y + r, x - r: x + r]
        cv2.imshow("img", roi)
        cv2.waitKey(0)

这段代码会输出ROI图像:
ROI图像

现在我尝试在该ROI图像中检测圆形元素以获取其中点,但遇到了困难。我尝试了以下方法:

gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray_roi, (3, 3), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]
    cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)

    for c in cnts:
        (x, y), radius = cv2.minEnclosingCircle(c)
        cv2.circle(roi, (int(x), int(y)), int(radius), (35, 255, 12), 3)
        cv2.circle(roi, (int(x), int(y)), 1, (35, 255, 12), 2)
        print(x, y)
        break

    # Find Canny edges
    edged = cv2.Canny(roi, 30, 121, apertureSize=3, L2gradient=True)
    cv2.waitKey(0)

    # Finding Contours
    # Use a copy of the image e.g. edged.copy()
    # since findContours alters the image
    contours, hierarchy = cv2.findContours(edged,
                                           cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

    cv2.imshow('Canny Edges After Contouring', edged)
    cv2.waitKey(0)

得到的输出图像效果很差:
阈值处理后的图像

我也调整过cv2.Canny函数的阈值参数,但仍未得到更好的结果,恳请帮助!


解决方案

针对ROI内的圆形检测问题,可从以下几个方向优化:

1. 优化预处理步骤

ROI图像存在薄膜反光,小核高斯模糊降噪不足,建议:

  • 改用更大的高斯核(如(5,5)或(7,7))增强噪声抑制
  • 优先使用双边滤波,在保留边缘的同时模糊背景,适配反光场景:
gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# 双边滤波替代高斯模糊
blur = cv2.bilateralFilter(gray_roi, 9, 75, 75)

2. 调整阈值策略

OTSU二值化易受反光干扰,可补充形态学操作或改用自适应阈值:

  • 开运算消除小噪点后再做二值化:
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]
# 开运算消除小噪声
kernel = np.ones((3,3), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
  • 自适应阈值适配局部光照不均:
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)

3. 用HoughCircles直接检测ROI内小圆

既然已用HoughCircles定位外层模具,可复用该方法检测内部小圆,调整参数适配尺寸:

gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray_roi, (5,5), 0)
# 调整参数适配小圆尺寸
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, dp=1.2, minDist=50,
                           param1=50, param2=30, minRadius=10, maxRadius=30)
if circles is not None:
    circles = np.round(circles[0, :]).astype("int")
    for (x, y, r) in circles:
        cv2.circle(roi, (x, y), r, (0,255,0), 2)
        cv2.circle(roi, (x, y), 2, (0,0,255), -1)
        print(f"圆形中心点坐标:({x}, {y})")
cv2.imshow("Detected Circle", roi)
cv2.waitKey(0)

关键是根据实际图像微调minRadius/maxRadius(匹配小圆尺寸)、param1/param2(控制边缘检测和圆心判定灵敏度)。

4. 轮廓筛选优化

若继续用轮廓检测,除面积排序外,增加圆形度筛选确保目标是圆形:

for c in cnts:
    perimeter = cv2.arcLength(c, True)
    area = cv2.contourArea(c)
    if perimeter == 0:
        continue
    # 圆形度计算:4π*面积/(周长²),越接近1越圆
    circularity = 4 * np.pi * (area / (perimeter ** 2))
    if circularity > 0.8:  # 设定圆形度阈值
        (x, y), radius = cv2.minEnclosingCircle(c)
        cv2.circle(roi, (int(x), int(y)), int(radius), (35, 255, 12), 3)
        cv2.circle(roi, (int(x), int(y)), 1, (35, 255, 12), 2)
        print(x, y)
        break

内容的提问来源于stack exchange,提问作者Daniel Agam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 00:06:25