如果使用 OpenCV 的 HoughCircles,是否有推荐的方法来确定 minRadius 以查找图像中的第一个圆?

Is there a recommended methodology to determine minRadius for finding the 1 circle in an image if using OpenCV's HoughCircles?

我需要找出最佳方法来选择用于 OpenCV cv2.HoughCircles function 的 minRadius 值。

我正在努力提高对美国稀有硬币进行分类的 Tensorflow CNN 的准确性。目前,CNN 正在审查 从 300x300 到 1024x1024

所有不同尺寸的 >10k 图像

为了提高模型的准确性,我试图在训练之前将硬币从图像中拉出来,并且只在硬币上而不是在其周围训练模型。

下面的代码在将硬币检测为圆形时工作正常,但我必须尝试几个 minRadius 值才能使 HoughCircles 函数正常工作。

在某些情况下,minRadius=270 适用于 600x600 和 785x1024,而在其他情况下,只有 r=200 适用于 600x600,但不适用于 785x1024。在其他情况下,只有 r=318 有效,但 317 或 319 无效。我没有找到一致的方法。

问题:是否有推荐的方法来确定 minRadius 以找到图像中的第一个圆?假设图像大小不同并且硬币占据图像的 50% 到 90%

以下是典型图像的示例:https://i.ebayimg.com/images/g/r5oAAOSwH8VeHNBf/s-l1600.jpg https://i.ebayimg.com/images/g/~JsAAOSwGtdeyFfU/s-l1600.jpg

image = cv2.imread(r"C:\testimagesa.jpg")
output = image.copy()
height, width = image.shape[:2]

minRadius = 200
maxRadius =0

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(image=gray, 
                           method=cv2.HOUGH_GRADIENT, 
                           dp=1.2, 
                           minDist=200*minRadius, #something large since we are looking for 1
                           param1=200,
                           param2=100,
                           minRadius=minRadius,
                           maxRadius=maxRadius
                          )

#Draw the circles detected
if circles is not None:
    # convert the (x, y) coordinates and radius of the circles to integers
    circlesRound = np.round(circles[0, :]).astype("int")
    # loop over the (x, y) coordinates and radius of the circles
    for (x, y, r) in circlesRound:
        cv2.circle(output, (x, y), r, (0, 255, 0), 4)

    plt.imshow(output)
else:
    print ('No circles found')

这是一种不同的方法,可以将椭圆拟合到从阈值化图像中提取的最大轮廓。如果需要,您可以使用椭圆的长半径和短半径作为霍夫圆的近似值。

输入:

import cv2
import numpy as np

# read input
img = cv2.imread('s-l1600.jpg')

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (21,21))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)

# find largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# fit ellipse to contour and get ellipse center, minor and major diameters and angle in degree 
ellipse = cv2.fitEllipse(big_contour)
(xc,yc),(d1,d2),angle = ellipse
print('center: ',xc,',',yc)
print('diameters: ',d1,',',d2)

# draw ellipse
result = img.copy()
cv2.ellipse(result, ellipse, (0, 0, 255), 2)

# draw circle at center
xc, yc = ellipse[0]
cv2.circle(result, (int(xc),int(yc)), 5, (0, 255, 0), -1)

cv2.imwrite("s-l1600_thresh.jpg", thresh)
cv2.imwrite("s-l1600_morph.jpg", morph)
cv2.imwrite("s-l1600_ellipse.jpg", result)

cv2.imshow("s-l1600_thresh", thresh)
cv2.imshow("s-l1600_morph", morph)
cv2.imshow("s-l1600_ellipse", result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Otsu 阈值图像:

清理阈值图像:

根据显示椭圆轮廓和中心的输入轮廓拟合绘制的椭圆:

椭圆参数:

center:  504.1853332519531 , 524.3350219726562
diameters:  953.078125 , 990.545654296875


这是你的另一张图片。但在这里我使用 inRange().

使用颜色阈值

输入:

import cv2
import numpy as np

# read input
img = cv2.imread('s-l1600b.jpg')

# convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# get color bounds of red circle
lower =(0,0,0) # lower bound for each channel
upper = (150,150,150) # upper bound for each channel

# create the mask and use it to change the colors
thresh = cv2.inRange(hsv, lower, upper)

# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (31,31))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)

# find largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# fit ellipse to contour and get ellipse center, minor and major diameters and angle in degree 
ellipse = cv2.fitEllipse(big_contour)
(xc,yc),(d1,d2),angle = ellipse
print('center: ',xc,',',yc)
print('diameters: ',d1,',',d2)

# draw ellipse
result = img.copy()
cv2.ellipse(result, ellipse, (0, 0, 255), 2)

# draw circle at center
xc, yc = ellipse[0]
cv2.circle(result, (int(xc),int(yc)), 5, (0, 255, 0), -1)

cv2.imwrite("s-l1600_thresh.jpg", thresh)
cv2.imwrite("s-l1600_morph.jpg", morph)
cv2.imwrite("s-l1600_ellipse.jpg", result)

cv2.imshow("s-l1600b_thresh", thresh)
cv2.imshow("s-l1600b_morph", morph)
cv2.imshow("s-l1600b_ellipse", result)
cv2.waitKey(0)
cv2.destroyAllWindows()


阈值图像:

形态学清理图像:

适合椭圆并绘制在输入上的最大外部轮廓:

椭圆参数:

center:  497.53564453125 , 639.7144165039062
diameters:  454.8548583984375 , 458.95843505859375