Consider the images below:
As can be seen, many circles are not detected. I have already played with
param1 and param2 (cv2.HoughCircles)
in the code below:
import cv2
import numpy as np
from PIL import Image
# Load the image and downscale it
img = Image.open('/tmp/F01-02.jpg')
img = img.resize((img.size[0] // 2, img.size[1] // 2)) # downscale by a factor of 2
open_cv_image = np.array(img)
# Convert to grayscale
gray = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2GRAY)
# Apply Hough transform
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 40, param1=5, param2=30, minRadius=7, maxRadius=10)
# Ensure at least some circles were found
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
cv2.circle(open_cv_image, (x, y), r, (0, 255, 0), 2)
pil_image = Image.fromarray(open_cv_image)
pil_image.save('/tmp/result.jpg')
Any ideas? Thanks!


HoughCircles
I managed to find every circle of interest by avoiding the downscale step at line 7 and using the following arguments for the HoughCircles function
result:
I don't know how robust this approach is, it would be necessary to test with a wider range of inputs. Maybe other methods not using HoughCircles might be more robust (e.g. detecting the grid itself and inferring the circle position).
Template matching
This appears to be more robust, it uses template matching with a template I produced by hand, i would recommend something like this over the HoughCircles. It seems to work fine even with the downscaling:
Template:
Warning: I filled it with gray as a dirty way to compensate for it being filled or not (kind of detect both).It is a better idea to create a template for each case for using the opencv template matching, something like this:
You can adjust the sensitivity with the variable threshold:
Example:
Template matching result:
Result: