2

Trying to find a circle in an image that has finite radius. Started off using 'HoughCircles' method from OpenCV as the parameters for it seemed very much related to my situation. But it is failing to find it. Looks like the image may need more pre-processing for it to find reliably. So, started off playing with different thresholds in opencv to no success. Here is an example of an image (note that the overall intensity of the image will vary, but the radius of the circle always remain the same ~45pixels)

Here is what I have tried so far

image = cv2.imread('image1.bmp', 0)
img_in = 255-image
mean_val = int(np.mean(img_in))
ret, img_thresh = cv2.threshold(img_in, thresh=mean_val-30, maxval=255, type=cv2.THRESH_TOZERO)
# detect circle
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.0, 100, minRadius=40, maxRadius=50)

If you look at the image, the circle is obvious, its a thin light gray circle in the center of the blob.

Any suggestions? Edited to show expected result The expected result should be like this, as you can see, the circle is very obvious for naked eye on the original image and is always of the same radius but not at the same location on the image. But there will be only one circle of this kind on any given image.

As of 8/20/2020, here is the code I am using to get the center and radii

from numpy import zeros as np_zeros,\
                full as np_full
from cv2 import calcHist as cv2_calcHist,\
                HoughCircles as cv2_HoughCircles,\
                HOUGH_GRADIENT as cv2_HOUGH_GRADIENT

def getCenter(img_in, saturated, minradius, maxradius):
    img_local = img_in[100:380,100:540,0]
    res = np_full(3, -1)
    # do some contrast enhancement
    img_local = stretchHistogram(img_local, saturated)

    circles = cv2_HoughCircles(img_local, cv2_HOUGH_GRADIENT, 1, 40, param1=70, param2=20,
                               minRadius=minradius,
                                  maxRadius=maxradius)
    if circles is not None: # found some circles
        circles = sorted(circles[0], key=lambda x: x[2])
        res[0] = circles[0][0]+100
        res[1] = circles[0][1]+100
        res[2] = circles[0][2]

    return res #x,y,radii


def stretchHistogram(img_in, saturated=0.35, histMin=0.0, binSize=1.0):
    img_local = img_in.copy()
    img_out = img_in.copy()
    min, max = getMinAndMax(img_local, saturated)
    if max > min:
        min = histMin+min * binSize
        max = histMin+max * binSize

        w, h = img_local.shape[::-1]
        #create a new lut
        lut = np_zeros(256)
        max2 = 255
        for i in range(0, 256):
            if i <= min:
                lut[i] = 0
            elif i >= max:
                lut[i] = max2
            else:
                lut[i] = (round)(((float)(i - min) / (max - min)) * max2)

        #update image with new lut values
        for i in range(0, h):
            for j in range(0, w):
                img_out[i, j] = lut[img_local[i, j]]

    return img_out


def getMinAndMax(img_in, saturated):
    img_local = img_in.copy()
    hist = cv2_calcHist([img_local], [0], None, [256], [0, 256])
    w, h = img_local.shape[::-1]
    pixelCount = w * h
    saturated = 0.5
    threshold = (int)(pixelCount * saturated / 200.0)

    found = False
    count = 0
    i = 0
    while not found and i < 255:
        count += hist[i]
        found = count > threshold
        i = i + 1
    hmin = i

    i = 255
    count = 0
    while not found and i > 0:
        count += hist[i]
        found = count > threshold
        i = i - 1
    hmax = i

    return hmin, hmax

and calling the above function as

getCenter(img, 5.0, 55, 62)

But it is still very unreliable. Not sure why it is so hard to get to an algorithm that works reliably for something that is very obvious to a naked eye. Not sure why there is so much variation in the result from frame to frame even though there is no change between them.

Any suggestions are greatly appreciated. Here are some more samples to play with

8
  • Do you have to apply this on other, maybe similar, images? Try to crop the image around the circle and apply some morphological operation (also here) to make the circle stronger. Commented Jun 14, 2020 at 6:27
  • In your case invert the image (so the circle is white) and try Dilation or Opening. Then try Houghs again. Image processing is lots of try-and-error to see what works. Commented Jun 14, 2020 at 6:28
  • Are the circles always concentric? If so, you can find the centre of any of them and you already know the radius. Commented Jun 14, 2020 at 8:52
  • Thanks Joe and Mark, will try to play with your suggestions (already doing image inverting though), updated original post to reflect expected result and a bit more details Commented Jun 14, 2020 at 15:27
  • @Mark, for any given image of this type, apart from variable overall intensity of the image and variable location of the circle I am trying to find, there will be only one of this type (highlighted in the updated attached image to the original post). But the radius will be about the same in all images Commented Jun 14, 2020 at 16:01

1 Answer 1

2

simple, draw your circles: cv2.HoughCircles returns a list of circles..

take care of maxRadius = 100

for i in circles[0,:]:
    # draw the outer circle
    cv2.circle(image,(i[0],i[1]),i[2],(255,255,0),2)

     # draw the center of the circle
    cv2.circle(image,(i[0],i[1]),2,(255,0,255),3)

a full working code (you have to change your tresholds):

import cv2
import numpy as np

image = cv2.imread('0005.bmp', 0)
height, width = image.shape
print(image.shape)

img_in = 255-image
mean_val = int(np.mean(img_in))

blur = cv2.blur(img_in , (3,3))
ret, img_thresh = cv2.threshold(blur, thresh=100, maxval=255, type=cv2.THRESH_TOZERO)

# detect circle
circles = cv2.HoughCircles(img_thresh, cv2.HOUGH_GRADIENT,1,40,param1=70,param2=20,minRadius=60,maxRadius=0)

print(circles)
for i in circles[0,:]:

    # check if center is in middle of picture
    if(i[0] > width/2-30 and i[0] < width/2+30 \
      and i[1] > height/2-30 and i[1] < height/2+30 ):
        # draw the outer circle
        cv2.circle(image,(i[0],i[1]),i[2],(255,255,0),2)

         # draw the center of the circle
        cv2.circle(image,(i[0],i[1]),2,(255,0,255),3)

cv2.imshow("image", image )

while True:
    keyboard = cv2.waitKey(2320)
    if keyboard == 27:
        break
cv2.destroyAllWindows()

result: enter image description here

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

2 Comments

Thanks Stefan, I already have code for drawing circles (if any found), just didnt show that code here as its not actual algorithm. THanks for suggesting blur filter, this might help to make the edge of the circle I am trying to find easy for edge finding algorithms
All hough transforms are not equal: you should also try scikit-image hough_circle() because IME it is better at finding those pesky circles.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.