16

I have a problem with choosing right parameters for HoughCircles function. I try to detect circles from video. This circles are made by me, and has almost the same dimension. Problem is that camera is in move.

When I change maxRadius it still detect bigger circles somehow (see the right picture). I also tried to change param1, param2 but still no success. Left-original picture, Right - after blur and detected circles

 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred = cv2.medianBlur(gray, 25)#cv2.bilateralFilter(gray,10,50,50) minDist = 100 param1 = 500 param2 = 200#smaller value-> more false circles minRadius = 5 maxRadius = 10 circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist, param1, param2, minRadius, maxRadius) if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0,:]: cv2.circle(blurred,(i[0], i[1]), i[2], (0, 255, 0), 2) 

Maybe Im using wrong function?

2
  • Why param1 and param2 is too high? Try these parameters: param1=100, param2 = 30, minradius = 1, maxradius = 100 Commented Mar 11, 2020 at 13:46
  • does minDist=100 make sense in your image? Try decreasing minDist according to the real distance of those circles Commented Mar 11, 2020 at 13:53

2 Answers 2

14

The main problem in your code is 5th argument to HoughCircles function.

According to documentation the argument list is:

cv2.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) → circles

That means the 5th argument applies circles (it gives an option getting the output by reference, instead of using the returned value).

Because you are not passing circles argument, you must pass named arguments for all arguments after the 4th argument (like param1=param1, param2=param2....).

Parameter tuning issues:

  • Reduce the value of param1. param1 is the higher threshold passed to the Canny.
    In your case value should be about 30.
  • Reduce the value of param2 The documentation not so clear, but setting the value around 50 works.
  • Increase maxRadius value - radius 10 is much smaller than the radius of your circles.

Here is the code:

import numpy as np import cv2 img = cv2.imread('circles.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred = cv2.medianBlur(gray, 25) #cv2.bilateralFilter(gray,10,50,50) minDist = 100 param1 = 30 #500 param2 = 50 #200 #smaller value-> more false circles minRadius = 5 maxRadius = 100 #10 # docstring of HoughCircles: HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius) if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0,:]: cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2) # Show result for testing: cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows() 

Result:

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks this is exactly what I needed. You're the best :)
13

Instead of having to fiddle with choosing the right parameters with cv2.HoughCircles, here's an alternative approach using contour filtering. The idea is to obtain a binary image with Otsu's threshold then perform morphological operations to isolate elliptical shaped contours. Finally we find contours and filter using aspect ratio and contour area. Here's the results:

enter image description here

import cv2 import numpy as np # Load image, grayscale, median blur, Otsus threshold image = cv2.imread('1.png') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blur = cv2.medianBlur(gray, 11) thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] # Morph open kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3) # Find contours and filter using contour area and aspect ratio cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] for c in cnts: peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.04 * peri, True) area = cv2.contourArea(c) if len(approx) > 5 and area > 1000 and area < 500000: ((x, y), r) = cv2.minEnclosingCircle(c) cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2) cv2.imshow('thresh', thresh) cv2.imshow('opening', opening) cv2.imshow('image', image) cv2.waitKey() 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.