3

Here is an image which I am trying to get the circles from.

enter image description here

I used difference of gray image and erosion to get the boundaries.

img_path= 'input_data/coins.jpg'
img = cv2.imread(img_path)
rgb,gray=getColorSpaces(img)
a,b=0,255
plt.figure(figsize=(12, 12))

erosion_se=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
erosion = cv2.erode(gray,erosion_se,iterations = 1)
boundary=gray-erosion
image, contours, hierarchy = cv2.findContours(boundary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)    
plt.imshow(boundary,'gray')

I could get the circles for most of the circles whose boundaries are relatively distinct. I want to do two things

  • Get the count of circles that are overlapping.

  • Find the circles that are touching image boundary. I can determine, by comparing the radius of the circle with with image boundary. The issue is 2 specific blobs are not detected as circles.

    circles = cv2.HoughCircles(boundary, cv2.HOUGH_GRADIENT, 1, 20,
                  param1=30,
                  param2=15,
                  minRadius=5,
                  maxRadius=20)

    if circles is not None: 
        circles = np.uint16(np.around(circles))
        for i in circles[0,:]:
            cv2.circle(img,(i[0],i[2]),i[3],(0,255,0),2)
            cv2.circle(img,(i[0],i[2]),2,(0,0,255),3)

        cv2.imshow('circles', img)

        k = cv2.waitKey(0)
        if k == 27:
            cv2.destroyAllWindows()

Below is the output after HoughCircles from the circles boundary image.The big green circle which stands out is undesired.I am not sure why for some of the overlapping regions ,the circles are not detected.

nathancy
  • 42,661
  • 14
  • 115
  • 137
addcolor
  • 455
  • 8
  • 23

1 Answers1

7

Instead of using HoughCircles which requires the circles to be "perfect" circles and is inaccurate on connected blobs, a simple contour filtering approach should work. Here's the main idea:

To count the number of overlapping circles

  • Approximate the contour area of a single circle which is ~375
  • Find contours, iterate through contours and filter using contour area
  • Sum overlapping circles

To find circles touching the image boundary, we limit the detection area to only the outer 10 pixels on the image. We find contours on this new image and then filter using contour area to determine touching circles


Count number of overlapping circles

After converting to grayscale and thresholding to obtain a binary image, we approximate the contour area of a single blob/circle as ~375. Next we find contours on the image and filter using cv2.contourArea(). To determine if there is overlap, we divide the area of each contour by the single circle area then find the ceiling using math.ceil(). If we get a ceiling value greater than 1, it means that the blob was connected and we simply add the ceiling value to our counter

Here's the detected overlapping circles

Overlapping: 213

Find circles touching image boundary

The idea is to create a black box to mask out the inner part of the image that is not on the boundary. We can do this with cv2.fillPoly(). From here we find contours and filter using contour area. The idea is that if the blob is relatively big compared to some threshold area, it means that the blob is most likely touching the edge

Here's the filled in black box and the detected touching circles

Touching: 10

import cv2
import numpy as np
import math

image = cv2.imread('1.jpg')
black_box = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Count overlapping circles
single_area = 375
overlapping = 0

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    blob_area = math.ceil(area/single_area)
    if blob_area > 1:
        overlapping += blob_area
        cv2.drawContours(image, [c], -1, (36,255,12), 2)

# Find circles touching image boundary
h, w, _ = image.shape
boundary = 10
touching = 0
box = np.array(([boundary,boundary], 
                      [w-boundary,boundary], 
                      [w-boundary, h-boundary], 
                      [boundary, h-boundary]))
cv2.fillPoly(black_box, [box], [0,0,0])

copy = black_box.copy()
copy = cv2.cvtColor(copy, cv2.COLOR_BGR2GRAY)
copy = cv2.threshold(copy, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

cnts = cv2.findContours(copy, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area > 100:
        touching += 1
        cv2.drawContours(black_box, [c], -1, (36,255,12), 2)

print('Overlapping:', overlapping)
print('Touching:', touching)
cv2.imshow('image', image)
cv2.imshow('black_box', black_box)
cv2.waitKey()
nathancy
  • 42,661
  • 14
  • 115
  • 137