HoughCircles circle detection using opencv and python-
I am trying to use OpenCV's (Hough) Circle definition to detect circles. I created a solid circle on a black background, tried to play with the options, used blur and that's it, but I just can't find anything.
Any ideas, suggestions, etc. would be great, thanks!
my current code looks something like this:
import cv2
import numpy as np
"""
params = dict(dp=1,
minDist=1,
circles=None,
param1=300,
param2=290,
minRadius=1,
maxRadius=100)
"""
img = np.ones((200,250,3), dtype=np.uint8)
for i in range(50, 80, 1):
for j in range(40, 70, 1):
img[i][j]*=200
cv2.circle(img, (120,120), 20, (100,200,80), -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 200, 300)
cv2.imshow('shjkgdh', canny)
gray = cv2.medianBlur(gray, 5)
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1, 20,
param1=100,
param2=30,
minRadius=0,
maxRadius=0)
print circles
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]),2,(0,0,255),3)
cv2.imshow('circles', img)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
source to share
Your code is working fine. The problem is with your thresholds HoughCircles
.
Try to understand the options you are using from the OpenCV docs :
param1 - The first parameter, specific to the method. In the case of CV_HOUGH_GRADIENT, this is the higher threshold of the two that passed to the edge of the Canny () detector (the lower one is half as much).
param2 - The second parameter is specific to the method. In the case of CV_HOUGH_GRADIENT, this is the accumulator threshold for the circle of centers in the detection phase. The smaller it is, the more false circles can be detected. The circles corresponding to the larger accumulator value will be returned first.
So, as you can see, the inner HoughCircles function calls the Canny edge detector, which means that you can use the grayscale in the function instead of your outlines.
Now reduce param1
to 30 and param2
15 and see the results in the following code:
import cv2
import numpy as np
img = np.ones((200,250,3), dtype=np.uint8)
for i in range(50, 80, 1):
for j in range(40, 70, 1):
img[i][j]*=200
cv2.circle(img, (120,120), 20, (100,200,80), -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1, 20,
param1=30,
param2=15,
minRadius=0,
maxRadius=0)
print circles
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]),2,(0,0,255),3)
cv2.imshow('circles', img)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
source to share
If you don't get HoughCircles to bring you pixel perfect solutions for visible circles, you are not using them correctly.
Your mistake is that you are trying to manually tweak your hyperparameters yourself. It won't work. Ask the computer to automatically adjust the settings for you:
import numpy as np
import argparse
import cv2
import signal
from functools import wraps
import errno
import os
import copy
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
orig_image = np.copy(image)
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", gray)
cv2.waitKey(0)
circles = None
minimum_circle_size = 100 #this is the range of possible circle in pixels you want to find
maximum_circle_size = 150 #maximum possible circle size you're willing to find in pixels
guess_dp = 1.0
number_of_circles_expected = 1 #we expect to find just one circle
breakout = False
max_guess_accumulator_array_threshold = 100 #minimum of 1, no maximum, (max 300?) the quantity of votes
#needed to qualify for a circle to be found.
circleLog = []
guess_accumulator_array_threshold = max_guess_accumulator_array_threshold
while guess_accumulator_array_threshold > 1 and breakout == False:
#start out with smallest resolution possible, to find the most precise circle, then creep bigger if none found
guess_dp = 1.0
print("resetting guess_dp:" + str(guess_dp))
while guess_dp < 9 and breakout == False:
guess_radius = maximum_circle_size
print("setting guess_radius: " + str(guess_radius))
print(circles is None)
while True:
#HoughCircles algorithm isn't strong enough to stand on its own if you don't
#know EXACTLY what radius the circle in the image is, (accurate to within 3 pixels)
#If you don't know radius, you need lots of guess and check and lots of post-processing
#verification. Luckily HoughCircles is pretty quick so we can brute force.
print("guessing radius: " + str(guess_radius) +
" and dp: " + str(guess_dp) + " vote threshold: " +
str(guess_accumulator_array_threshold))
circles = cv2.HoughCircles(gray,
cv2.cv.CV_HOUGH_GRADIENT,
dp=guess_dp, #resolution of accumulator array.
minDist=100, #number of pixels center of circles should be from each other, hardcode
param1=50,
param2=guess_accumulator_array_threshold,
minRadius=(guess_radius-3), #HoughCircles will look for circles at minimum this size
maxRadius=(guess_radius+3) #HoughCircles will look for circles at maximum this size
)
if circles is not None:
if len(circles[0]) == number_of_circles_expected:
print("len of circles: " + str(len(circles)))
circleLog.append(copy.copy(circles))
print("k1")
break
circles = None
guess_radius -= 5
if guess_radius < 40:
break;
guess_dp += 1.5
guess_accumulator_array_threshold -= 2
#Return the circleLog with the highest accumulator threshold
# ensure at least some circles were found
for cir in circleLog:
# convert the (x, y) coordinates and radius of the circles to integers
output = np.copy(orig_image)
if (len(cir) > 1):
print("FAIL before")
exit()
print(cir[0, :])
cir = np.round(cir[0, :]).astype("int")
for (x, y, r) in cir:
cv2.circle(output, (x, y), r, (0, 0, 255), 2)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
cv2.imshow("output", np.hstack([orig_image, output]))
cv2.waitKey(0)
The above code will convert this:
To that:
For more information on what this does see https://stackoverflow.com/a/46500223/445131
source to share