基于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图像中检测圆形元素以获取其中点,但遇到了困难。我尝试了以下方法:
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
相关产品推荐
相关产品推荐

