The input image is here : the input image
I try to use cv2.HoughCircles in opencv-python to find the expected circle, but the result is noise as in this picture : result in param2=0.2
the code is:
import cv2
import numpy as np
img = cv2.imread('image.png')
# apply GaussianBlur
kernel_size = (15, 15)
sigma = 0
blurred_image = cv2.GaussianBlur(img, kernel_size, sigma)
gray = cv2.cvtColor(blurred_image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT_ALT, dp=1.5, minDist=20, param1=50, param2=0.2, minRadius=100, maxRadius=400)
# draw the result
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, 255, 0), 2)
cv2.circle(img, (i[0], i[1]), 1, (0, 0, 255), 3)
# show the result
cv2.imshow('used image', gray)
cv2.imshow('detected circles', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
If i set param2=0.8, then no any circle can be found!
What should I do to get a better result that fit the further perfect circle in the example picture above?
The "perfect circle" can be defined as follows: Given an angular resolution of 0.1 degrees, the area will be divided into 360/0.1 = 3600 segments.
A perfect circle includes as many sectors as possible, where these sectors contain pixels with grayscale values below a certain threshold (e.g., 15).
In other words, the goal is to have the proportion of sectors that meet the criteria as close to 1 as possible.
If there are multiple resulted circles with same proportion, any one of them can be considered a perfect circle.
An example "perfect circle" : perfect circle
This is sort of a static solution for now, but if you always have such nice contrast and you can accuretly get the coordinates, then fitting a circle with least square might not be such a bad idea:
def fit_circle(x, y):
A = np.c_[x, y, np.ones(len(x))] # design matrix A with columns for x, y, and a constant term
f = x**2 + y**2 # get the function as x²+y²
C, _, _, _ = np.linalg.lstsq(A, f, rcond=None) # optimize
cx = C[0] / 2 # get the centre x coordinate
cy = C[1] / 2 # get the centre y coordinate
radius = np.sqrt(C[2] + cx**2 + cy**2) # calculate the radius
return round(cx), round(cy), round(radius) # return everythin as int
To use this function, I did the following:
im = cv2.imread("circle.png") # read as BGR
imGray = cv2.imread("circle.png", 0) # read as gray
y, x = np.where(imGray==0) # get x,y coords
cx, cy, r = fit_circle(x,y) # get the circle properties, center and radius
im = cv2.circle(im, (cx,cy), r, (255, 0, 0), 5)
im = cv2.line(im, (cx-r,cy), (cx+r,cy), (0, 255,0), 2)
im = cv2.line(im, (cx,cy-r), (cx,cy+r), (0, 255,0), 2)
im = cv2.circle(im, (cx,cy), 10, (0, 0, 255), -1)
cv2.imwrite("WithCircle.png", im) # save im for stack
The result:
As I said, very important that you can get those nice pixels that define your circle, this method can work with some added noise as well I presume, but definitely not good if you have any one-sided deviations.