Assuming the input image named as "circles.jpg".
First of all, import required libraries and load image in gray format. Use Numpy.where() function to purify the foreground and background of the image. Then find all the contours on this image:
import cv2
import numpy as np
image_gray = cv2.imread("circles.jpg", 0)
image_gray = np.where(image_gray > 30, 255, image_gray)
image_gray = np.where(image_gray <= 30, 0, image_gray)
image_gray = cv2.adaptiveThreshold(image_gray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 115, 1)
_, contours, _ = cv2.findContours(image_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Secondly, create an empty image with same width and height. Then, draw the contours on the newly created image, with a filtering condition - the contour must have a circle-like shape:
image_copy = np.zeros_like(image_gray) # create a new emtpy image
for cnt in contours:
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
(x, y, w, h) = cv2.boundingRect(cnt)
ar = w / float(h)
if len(approx) > 5 and 0.9 < ar < 1.1: # filtering condition
cv2.drawContours(image_copy, [cnt], 0, 255, -1)
Thirdly, you need to store the area of each contour into a list and remove any contours that are too small:
cnt_areas = []
cnt_circles = []
for cnt in contours:
(x, y, w, h) = cv2.boundingRect(cnt)
cnt_areas.append(w*h)
cnt_circles.append(cnt)
import statistics
median_size_limit = statistics.median(cnt_areas) * 0.3
cnt_circles = [cnt for i, cnt in enumerate(cnt_circles)
if cnt_areas[i] > median_size_limit]
And finally, display and check the result:
# Draw the result and display
image = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2RGB)
cv2.drawContours(image, cnt_circles, -1, (0, 0,255), 2)
cv2.imshow("Result Preview", image)
cv2.waitKey()
Preview of final result can be seen here:
