Here is an image which I am trying to get the circles from.
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.
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
~375
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()