Low Accuracy in Detecting Circles using Hough Circle open cv python

Question:

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

Asked By: reza bagherian

||

Answers:

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
Answered By: fmw42