Search code examples
pythonopencvimage-processinghough-transform

OpenCV Python HoughCircles: Circles detected outside of image boundary


I am using the OpenCV HoughCircles method in Python as follows:

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

This seems to work quite well. However, one thing I noticed is that it detects circles which can extend outside of the image boundaries. Does anyone know how I can filter these results out?


Solution

  • Think of each circle as being bounded inside a square of dimensions 2r x 2r where r is the radius of the circle. Also, the centre of this box is located at (x,y) which also corresponds to where the centre of the circle is located in the image. To see if the circle is within the image boundaries, you simply need to make sure that the box that contains the circle does not go outside of the image. Mathematically speaking, you would need to ensure that:

    r <= x <= cols-1-r
    r <= y <= rows-1-r # Assuming 0-indexing
    

    rows and cols are the rows and columns of your image. All you really have to do now is cycle through every circle in the detected result and filter out those circles that go outside of the image boundaries by checking if the centre of each circle is within the two inequalities specified above. If the circle is within the two inequalities, you would save this circle. Any circles that don't satisfy the inequalities, you don't include this in the final result.

    To put this logic to code, do something like this:

    import cv # Load in relevant packages
    import cv2
    import numpy as np
    
    img = cv2.imread(...,0) # Load in image here - Ensure 8-bit grayscale
    final_circles = [] # Stores the final circles that don't go out of bounds
    circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0) # Your code
    rows = img.shape[0] # Obtain rows and columns
    cols = img.shape[1]
    circles = np.round(circles[0, :]).astype("int") # Convert to integer
    for (x, y, r) in circles: # For each circle we have detected...
        if (r <= x <= cols-1-r) and (r <= y <= rows-1-r): # Check if circle is within boundary
            final_circles.append([x, y, r]) # If it is, add this to our final list
    
    final_circles = np.asarray(final_circles).astype("int") # Convert to numpy array for compatability
    

    The peculiar thing about cv2.HoughCircles is that it returns a 3D matrix where the first dimension is a singleton dimension. To eliminate this singleton dimension, I did circles[0, :] which will result in a 2D matrix. Each row of this new 2D matrix contains a tuple of (x, y, r) and characterizes where a circle is located in your image as well as its radius. I also converted the centres and radii to integers so that if you decide to draw them later on, you will be able to do it with cv2.circle.