Search code examples
pythonopencvhough-transform

Correct tuning of parameters for Hough circles transform


I'm trying to locate the circles in this image using the Hough transform, however I can't get good results, in fact they are quite bad, however I don't understand what's wrong, the image looks quite clean to me

here is the code:

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(drilling_gray_image, cv2.MORPH_CLOSE, kernel, iterations=2)

cv2_imshow(morph)
plt.figure(figsize=(20, 4))
plt.imshow(morph, cmap='gray', vmin=0, vmax=255)

_, bin_image = cv2.threshold(morph, 128, 255, cv2.THRESH_BINARY)
plt.figure(figsize=(20, 4))
plt.imshow(bin_image, cmap='gray', vmin=0, vmax=255)

rows = bin_image.shape[0]
circles = cv2.HoughCircles(bin_image, cv2.HOUGH_GRADIENT, minDist=10, dp=rows / 16, param1=100, param2=200, minRadius=0, maxRadius=0)
print(circles)

result = drilling_gray_image.copy()
if circles is not None:
  circles = np.uint16(np.around(circles))
  for i in circles[0, :]:
    center = (i[0], i[1])
    # circle center
    cv2.circle(result, center, 1, (0, 100, 100), 3)
    # circle outline
    radius = i[2]
    cv2.circle(result, center, radius, (255, 0, 255), 3)

plt.figure(figsize=(20, 4))
plt.imshow(result, cmap='gray', vmin=0, vmax=255)

here you can find the starting image: link

I try to follow this example that is similar to my case: stackoverflow

can anybody help me in detecting the circle in the image after the binarization?


Solution

  • i am not the expert on HoughCircle Algorithm but let me show you how i would do it:

    • First your image has circles with varying radius, so divide them into categories.
    • Calibrate the algorithm for each category with narrower limits and more precision. (which is also easier)

    Also about the parameters of the HoughCircle Algorithm:

    • minDist : minimum distance between any to circle centers.
    • dp : just use recommended value 1.5 except for very smal circles.
    • param1 : high threshold for Canny Edge Detection, also low threshold is set to half of param1. Therefore first try canny on your image and find the best value for this.
    • param2 : i am not exactly sure about this one but smaller means more circles. In the documentation it is explained as it is the accumulator threshold for the circle centers at the detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned first.
    • minRadius : name is the explanation
    • maxRadius : name is the explanation

    For tuning the HoughCircles transform, first give fixed values to minDist, minRadius and maxRadius according to your circle's radius. Choose the best canny high threshold value by applying cv2.Canny() and set it to param1 and use the recomended dp = 1.5. Finally, play with the last remaining variable which is param2.

    Now, for your example i divided the circles into 3 categories according to their radius, 2 big ones on the left (green colored), 3 mid sized ones at the bottom (blue colored), and the remaining small ones (red colored). After running the algorithm for each category this is the final result:

    enter image description here

    As you see for the mid sized circles there is a false one inside a green circle. You can eliminate theese type false cirles with various methods for example after finding each circle draw them filled on the source image. I hope this is helpful for you.

    This is the final code:

    import cv2
    import numpy
    
    #Read the image and get blurred version
    img = cv2.imread('image.png')
    result = img.copy()
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    blur = cv2.medianBlur(gray,9)
    
    #Run the hough algorithm three times 
    small_circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, minDist=20, dp=1, param1=100, param2=30, minRadius=7, maxRadius=40)
    big_circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, minDist=100, dp=1, param1=100, param2=20, minRadius=90, maxRadius=120)
    mid_circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, minDist=60, dp=1, param1=100, param2=12, minRadius=50, maxRadius=65)
    
    
    #Draw detected small circles
    if small_circles is not None:
        small_circles = numpy.uint16(numpy.around(small_circles))
        for i in small_circles[0, :]:
            center = (i[0], i[1])
            radius = i[2]
            cv2.circle(result, center, radius, (0, 0, 255), 3)
    
    
    #Draw detected big circles
    if big_circles is not None:
        big_circles = numpy.uint16(numpy.around(big_circles))
        for i in big_circles[0, :]:
            center = (i[0], i[1])
            radius = i[2]
            cv2.circle(result, center, radius, (0, 255, 0), 3)
    
    
    #Draw detected mid circles
    if mid_circles is not None:
        mid_circles = numpy.uint16(numpy.around(mid_circles))
        for i in mid_circles[0, :]:
            center = (i[0], i[1])
            radius = i[2]
            cv2.circle(result, center, radius, (255, 0, 0), 3)
    
    #Show the result image
    cv2.imshow('img',cv2.resize(result,(0,0),fx=0.5,fy=0.5))
    cv2.waitKey()