2
votes

Is there a manner in which to make circle detection parameter-less? - And why do the parameters in cv2.HoughCircles() not get affected sometimes after changing them? I have a for loop to alter parameters but it will not affect the outcome all of the time.

It seems that Hough Circles requires a lot of parameter optimization by the user, which is an awful problem if the user is wanting to use the system in a very generalized manner. Therefore, I am attempting to create an algorithm to find the best parameters for opencv's houghcirlces function automatically. The houghcirlces function at the moment takes in a maximum and minimum radius for circles that it will find in an image with some thresholds for how good of a match the circles are that are found.

My hope is to split up different ranges for minimum and maximum radii to search within, then to use the found circles across each section to what parameters I should use for the final houghcircles transform. (If anyone has a better approach to this I would love to hear it.)


However I ran into this problem:

It doesn't appear that the minimum and maximum radii parameters, or the Canny and Hough space thresholds are being respected.


For example, we may take this image:

circles

and run the following code I have on it:

import numpy as np
import cv2

file_path = '1.png'
img = cv2.imread(file_path)
gray = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
# Begin using the largest dimesion of the image as the largest possible radius
largest_dimension = np.max([gray.shape[0], gray.shape[1]])
for i in range(1,100):
    # For the first iteration we use the largest possible radius
    if i == 1:
        maxRadius = largest_dimension
    # THe next iterations we use what the minimum radius was last iteration 
    else:
        maxRadius = minRadius
    # Reduce the size by 2 everytime
    minRadius = maxRadius/2
    dp = 1 # Inverse ratio of thresholded image resolution ?? Not sure what to do with this
    minDist = minRadius # Not likely to be much closer than the minimum radius...?
    param1 = 200 # Canny edge threshold - image OTSU threshold dependence ?? 
    param2 = 100 # Hough space threshold - radius dependance?  np.pi*maxRadius**2 ??
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp, minDist, param1, param2, minRadius, maxRadius)

    # Display some inforation about the results
    if circles is not None:
        img_copy = img.copy()
        radii = np.array([c[2] for c in circles[0]])
        num = len(circles[0])
        avg = np.mean(radii)
        print str(num) + ' found: avg radius: ' + str(avg)
        if any(radii < minRadius) or any(radii > maxRadius):
            print 'The minimum and maximum radii given are not being respected' 
        for c in circles[0]:
            cv2.circle(img_copy, (c[0],c[1]), c[2], (255,0,255),2)

        cv2.namedWindow('', cv2.WINDOW_NORMAL)
        cv2.imshow('',img_copy)
        cv2.waitKey(0)

We will get the output shown below and that radii are not respected by the function everytime. Also, if we change param1 and param2 (Canny edge detection and Hough space thresholds) from 3 to 6000 almost nothing changes.

Clearly the image shown below has detected far more circles than any person typically would, and it only gets worse.

cicrlces

I may mention that the images I would like to use this script on in the future will appear as something such as this:

2
the hough transform for circles is a pretty "stupid" algorithm. It brute forces the images based on your parameters. If you don't specify the radius, then it has to brute force for all radii, which will take too long. - Sebastian Wozny
in your first 2 fire images i dont assume any circles after canny edges, tbh - Micka
you can use the canny edge detectiom with upper thres = param you choose in hough and lower thres = param/2 to see in which image circles will be searched. - Micka
I tried running the Canny with my param1 and param1/2 as thresholds and even when I give the value as 2000 (making a black image) I still get the exact same output from houghcirlces. It appears that only the min max radii parameters change anything, but they still don't function properly. - chase
And yes, there are a few processing steps I need for some of these images before Canny works, but that's a bit of the reason I put them there in case anyone had suggestions such as Voroni or Watershed or something I am unaware of to use prior to Canny. - chase

2 Answers

0
votes

I had this same problem with the parameters behaving oddly - you have

circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp, minDist, param1, param2, minRadius, maxRadius)

but the function signature is actually

>>> import cv2
>>> print cv2.HoughCircles.__doc__
HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles

note the circles in the parameter list -

I had copied some code from somewhere which explicitly named the remaining parameters, like

circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)

and at some point I removed the names. Mistake!

0
votes

To more accurately detect circles and get rid of noise, I would actually perform a canny edge detector on the grayscale image before sending it to the HoughCircles() method. By doing this, you get a lot more precision with threshold of the edge detection since you have full control of both parameters rather than just one.

Canny(src_gray, src_gray, 300, 500, 3 );
HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, image.rows/2, 30, 30, 40, 1000 );

Play around with the parameters for canny() method, specifically the 300 and 500 and find a sweet spot for your use case. Hope this is helpful! Good luck