Detect concentric circles (python)

117 views Asked by At

I want to detect circles in image as below:

enter image description here

So, I tried with cv2.findContours and cv2.Houghcirclesin python, but there were some problems. Both methods were applied after image processing (Canny, threshold, etc.)

First, if I use cv2.findCountours, it cannot detect outer circles since they were overlapped and detect as a single contour. Here's my python code.

contours, hier = cv2.findContours(input, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
    c= (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    
    if area>10000 and area<100000:       # Filtering the target size
        (x, y), radius = cv2.minEnclosingCircle(contour)
        center= (int(x), int(y))
        radius= int(radius)
        cv2.circle(color, center, radius, c,5)
     

enter image description here

Second, if I use cv2.Houghcircles (Split the radius range to detect concentric circles), the results were better, but still the results were poor.

for maxR in range(100,200,10): # minimum 100 to maximum 200 by step 10
    circles = cv2.HoughCircles(input, cv2.HOUGH_GRADIENT, 1, 100, param1=100,param2=22,minRadius=minR,maxRadius=maxR)
    minR+=10
    
    # Check to see if there is any detection
    if circles is not None:
        # If there are some detections, convert radius and x,y(center) coordinates to integer
        circles = np.round(circles[0, :]).astype("int")

        for (x, y, r) in circles:
            c= (random.randint(0,255),random.randint(0,255),random.randint(0,255))
            cv2.circle(view, (x, y), r, c, 3)                                      # draw circumference
            cv2.rectangle(view, (x - 2, y - 2), (x + 2, y + 2), c, 5)              # draw center

enter image description here

Is there any better methods to detect concentric circles in image and measure center coordinates and radius of them?


I compared the encloed area and minimum enclosing circle area. And I got circular contours.

contours, hier = cv2.findContours(aaa, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

for contour in contours:
    c= (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        
    area = cv2.contourArea(contour)
    (cx, cy), radius = cv2.minEnclosingCircle(contour)
    err=abs((area-np.pi*radius*radius)/(np.pi*radius*radius))
    
    if err< 0.1:
        cv2.drawContours(aaa, [contour], -1, (255,255,255) , thickness=cv2.FILLED)
        cv2.drawContours(color, [contour], -1, c , 5)

enter image description here

Then, I filled the area with white color. enter image description here

Now, the problem is very simple. All I need to do is separate the overlapping circles into their own circles. (I can find many references)

Thanks you all!

2

There are 2 answers

1
Mark Setchell On

I haven't done much analysis or testing, but thought the following approach might produce some reasonable results.

  • make a grid of "seed" points that covers the entire image - I am thinking the gridlines should be just less than the radius of the smallest circle you hope to find so that we guarantee a seed point in each circle

  • iterate over the grid points and for each one, flood-fill with red starting from that location

  • remove everything that is not red in the image and then check the area and circularity of the red area and add to a list if sufficiently circular and suitably sized.

You would get things like this depending whether each seed point is within a cell or the background:

enter image description here

enter image description here

I guess you would have to experiment with the flood-filling parameters, and the circularity measure (available from findContours()) and also filter the list of results because you would likely detect some circles twice. You would also need to avoid flood-filling starting on the darker cell-boundaries, so maybe you'd want to reduce to 3 colours first so you only get the light white background and the darker grey cell boundaries and the darkest grey boundaries.

3
toyota Supra On

First, if I use cv2.findCountours, it cannot detect outer circles since they were overlapped and detect as a single contour.

The cv2.findContours() allow you to use cv2.drawContours(). Unfortunately, I have not use random(), yet.

You can try this, then you use random() later on.

Snippet:

import cv2

# read original image
img = cv2.imread('c1.jpg')

# create binary image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (31, 31), 3)
(t, binary) = cv2.threshold(blur, 190, 255, cv2.THRESH_BINARY)

# find contours
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# draw contours over original image
cv2.drawContours(img, contours, -1, (0, 0, 255), 5)

# display original image with contours

cv2.imshow("output", img)
cv2.waitKey(0)

Screenshot for both outer and inner circles:

enter image description here

Second, if I use cv2.Houghcircles (Split the radius range to detect concentric circles), the results were better, but still the results were poor.

Using second script:

Snippet:

import numpy as np
import cv2
import random

img = cv2.imread('c1.jpg')
dst = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 15) 
gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
bilateral = cv2.bilateralFilter(gray,5,50,50)

minDist = 45
param1 = 45
param2 = 80
minRadius = 70
maxRadius = 150
minR = 0

for maxR in range(100,200,10):

    circles = cv2.HoughCircles(bilateral, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)

    if circles is not None:
        # If there are some detections, convert radius and x,y(center) coordinates to integer
        circles = np.round(circles[0, :]).astype("int")

        for (x, y, r) in circles:
            c= (random.randint(0,255),random.randint(0,255),random.randint(0,255))
            cv2.circle(img, (x, y), r, c, 3)                                      # draw circumference
            cv2.rectangle(img, (x - 2, y - 2), (x + 2, y + 2), c, 5)              # draw center

    #cv2.imwrite('circle.jpg', img)
    cv2.imshow('img', img)
    cv2.waitKey(0)
cv2.destroyAllWindows()

Screenshot:

enter image description here