使用 OpenCV 识别空心圆和实心圆

Use OpenCV to identiy hollow and filled circles

我正在使用 OpenCV houghcircles 来识别所有圆圈(空心圆和实心圆)。以下是我的代码:

import numpy as np
import cv2

img = cv2.imread('images/32x32.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

bilateral = cv2.bilateralFilter(gray,10,50,50)

minDist = 30
param1 = 30
param2 = 50
minRadius = 5
maxRadius = 100

circles = cv2.HoughCircles(bilateral, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)

if circles is not None:
    circles = np.uint16(np.around(circles))
    for i in circles[0,:]:
        cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)

# Show result for testing:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

测试输入图片1:

测试输出图像1:

As you can see I'm able identity most of the circles except for few. What am I missing here? I've tried varying the parameters but this is the best i could get.

Also, if I use even more compact circles the script does not identify any circles whatsoever.

另一种想法是使用查找轮廓方法并使用如下所示的近似值检查轮廓是否为圆形。

import cv2

img = cv2.imread('32x32.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

inputImageCopy = img.copy()

# Find the circle blobs on the binary mask:
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Use a list to store the center and radius of the target circles:
detectedCircles = []

# Look for the outer contours:
for i, c in enumerate(contours):

    # Approximate the contour to a circle:
    (x, y), radius = cv2.minEnclosingCircle(c)

    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    
    if len(approx)>5: # check if the contour is circle
        
        # Compute the center and radius:
        center = (int(x), int(y))
        radius = int(radius)

        # Draw the circles:
        cv2.circle(inputImageCopy, center, radius, (0, 0, 255), 2)

        # Store the center and radius:
        detectedCircles.append([center, radius])

cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)
cv2.destroyAllWindows()

我解决了你的问题。使用与您相同的代码。无需修改。我将值从 50 更改为 30。仅此而已。

#!/usr/bin/python39
#OpenCV 4.5.5 Raspberry Pi 3/B/4B-w/4/8GB RAM, Bullseye,v11.
#Date: 19th April, 2022

import numpy as np
import cv2

img = cv2.imread('fill_circles.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
bilateral = cv2.bilateralFilter(gray,10,50,50)

minDist = 30
param1 = 30
param2 = 30
minRadius = 5
maxRadius = 100

circles = cv2.HoughCircles(bilateral, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)

if circles is not None:
    circles = np.uint16(np.around(circles))
    for i in circles[0,:]:
        cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)

cv2.imwrite('lego.png', img)
# Show result for testing:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出:

如果你总能得到(或从真实图像创建)这样“干净”的图像,那么bounding-box的网格(二维圆阵列)区域就可以很容易地得到。

因此,可以知道感兴趣的矩形区域(以及网格的旋转角度,如果可以旋转的话)。

如果沿着矩形的轴向观察像素,就可以很容易地找出一排圆的数量和圆的直径。 因为所有像素都是黑色的线是相邻行(或列)之间的间隙。 (将沿轴方向的像素值求和,是否为0表示这条线是否越过grid-cell。)

如有必要,您可以检查每个 gird-cell 中的轮廓形状是否真的是圆形。