Search code examples
pythonopencvimage-processingcomputer-visionhough-transform

Low Accuracy in Detecting Circles using Hough Circle open cv python


I'm new to the Computer Vision field so this question may be fairly easy; yet I'm clueless on how to increase the Precision.

I'm Trying to detect all circles in this picture.

enter image description here

Here is the approach i took

  1. Gray Scaled the image
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  2. Added Median Blur to reduce the noise blur = cv2.medianBlur(gray, 5)
  3. used canny edge detection edges = cv2.Canny(gray, 100, 170, apertureSize = 3) and Here is the result: enter image description here
  4. applied HoughCircles Transform circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 30, param1=150, param2=26, minRadius=1, maxRadius=100) and Here is the result: enter image description here

as you can see a good amount of circles haven't been detected and there are some instances of false positivity how can i increase the accuracy of this detection?

please note that i have fine-tuned the HoughCircles to the best of my abilities and further changes to the function parameters yield little to no improvement at all


Solution

  • Here is a different approach in Python/OpenCV.

    • Read the input
    • Convert to grayscale
    • Threshold
    • Get external contours
    • Filter the contours to keep only those with perimeters with number of sides greater than 4
    • Then draw them on a copy of the input
    • Also fit an ellipse to the circle contours and get the centers, diameters, and compute the radius from the diameters
    • Save the results

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # read the image
    img = cv2.imread('circles_squares.png')
    h, w = img.shape[:2]
    
    # convert to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # threshold
    tval, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    #print(tval)
    
    # get external contours
    cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    # Draw polygons with sides >4
    circle_img = img.copy()
    i = 1
    for c in cnts:
        peri = cv2.arcLength(c, True)
        # get approximate polygon and number of vertices (len)
        approx = cv2.approxPolyDP(c, 0.04 * peri, True)
        if len(approx) > 4:
            #print(len(approx))
            cv2.drawContours(circle_img, [c], -1, (0,0,255), 1)
            (xc,yc),(d1,d2),angle = cv2.fitEllipse(c)
            xc = int(xc)
            yc = int(yc)
            rad = (d1+d2)//2
            d1 = int(d1)
            d2 = int(d2)
            print("#", i, "center:", xc,yc, "radius:", rad, "diameters:", d1,d2)
            i = i + 1
    
    # save results
    cv2.imwrite('circles_squares_circles.png', circle_img)
    
    # show results
    cv2.imshow('thresh', thresh)
    cv2.imshow('circles', circle_img)
    cv2.waitKey(0)
    

    Result:

    enter image description here

    Circles:

    # 1 center: 112 345 radius: 20.0 diameters: 19 20
    # 2 center: 341 336 radius: 54.0 diameters: 54 54
    # 3 center: 160 317 radius: 54.0 diameters: 54 55
    # 4 center: 122 274 radius: 29.0 diameters: 29 29
    # 5 center: 303 244 radius: 20.0 diameters: 18 21
    # 6 center: 88 211 radius: 54.0 diameters: 54 54
    # 7 center: 322 183 radius: 19.0 diameters: 19 19
    # 8 center: 136 178 radius: 20.0 diameters: 20 21
    # 9 center: 98 130 radius: 30.0 diameters: 29 31
    # 10 center: 202 115 radius: 20.0 diameters: 19 20
    # 11 center: 246 101 radius: 29.0 diameters: 28 29
    # 12 center: 341 106 radius: 54.0 diameters: 53 54
    # 13 center: 155 87 radius: 53.0 diameters: 52 54
    # 14 center: 79 63 radius: 54.0 diameters: 53 54
    # 15 center: 308 39 radius: 29.0 diameters: 28 30
    # 16 center: 184 20 radius: 29.0 diameters: 28 29