1

I want to detect and count the objects inside an image that touch while ignoring what could be considered as a single object. I have the basic image, on which i tried applying a cv2.HoughCircles() method to try and identify some circles. I then parsed the returned array and tried using cv2.circle() to draw them on the image.

However, I seem to always get too many circles returned by cv2.HoughCircles() and couldn't figure out how to only count the objects that are touching.

This is the image i was working on

My code so far:

import numpy
import matplotlib.pyplot as pyp
import cv2

segmentet = cv2.imread('photo')
houghCircles = cv2.HoughCircles(segmented, cv2.HOUGH_GRADIENT, 1, 80, param1=450, param2=10, minRadius=30, maxRadius=200)
houghArray = numpy.uint16(houghCircles)[0,:]

for circle in houghArray:
    cv2.circle(segmented, (circle[0], circle[1]), circle[2], (0, 250, 0), 3)

And this is the image i get, which is quite a far shot from want i really want.

How can i properly identify and count said objects?

beginner420
  • 39
  • 1
  • 7
  • 1
    [This](https://docs.opencv.org/master/d3/db4/tutorial_py_watershed.html) might help. – beaker Dec 06 '20 at 16:21
  • @beaker if my understanding is correct, that example will find objects that are touching and separate them into individual circles. What i want is to find the objects that are touching and label/count them while ignoring those that are separate. – beginner420 Dec 06 '20 at 17:21
  • Find all the contours for all regions. Then test the ratio of the area of the contour to the area of the convex hull. If ratio is near 1, then it is a single object. If too much lower, it is multiple touching objects. See https://docs.opencv.org/4.1.1/d3/dc0/group__imgproc__shape.html#ga014b28e56cb8854c0de4a211cb2be656 – fmw42 Dec 06 '20 at 19:31

2 Answers2

1

Here is one way in Python OpenCV by getting contour areas and the convex hull area of the contours. The take the ratio (area/convex_hull_area). If small enough, then it is a cluster of blobs. Otherwise it is an isolated blob.

Input:

enter image description here

import cv2
import numpy as np

# read input image
img = cv2.imread('blobs_connected.jpg')

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold to binary
thresh = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)[1]

# find contours
#label_img = img.copy()
contour_img = img.copy()
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
index = 1
isolated_count = 0
cluster_count = 0
for cntr in contours:
    area = cv2.contourArea(cntr)
    convex_hull = cv2.convexHull(cntr)
    convex_hull_area = cv2.contourArea(convex_hull)
    ratio = area / convex_hull_area
    #print(index, area, convex_hull_area, ratio)
    #x,y,w,h = cv2.boundingRect(cntr)
    #cv2.putText(label_img, str(index), (x,y), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0,0,255), 2)
    if ratio < 0.91:
        # cluster contours in red
        cv2.drawContours(contour_img, [cntr], 0, (0,0,255), 2)
        cluster_count = cluster_count + 1
    else:
        # isolated contours in green
        cv2.drawContours(contour_img, [cntr], 0, (0,255,0), 2)
        isolated_count = isolated_count + 1
    index = index + 1
    
print('number_clusters:',cluster_count)
print('number_isolated:',isolated_count)

# save result
cv2.imwrite("blobs_connected_result.jpg", contour_img)

# show images
cv2.imshow("thresh", thresh)
#cv2.imshow("label_img", label_img)
cv2.imshow("contour_img", contour_img)
cv2.waitKey(0)

Clusters in Red, Isolated blobs in Green:

enter image description here

Textual Information:

number_clusters: 4
number_isolated: 81

fmw42
  • 46,825
  • 10
  • 62
  • 80
0

approach it in steps.

label connected components. two connected blobs get the same label because they're connected. so far so good.

now separate your blobs. use watershed (first comment) or whatever other method gives you results. I can't fully predict the watershed approach. it might deal with touching blobs of dissimilar size or it might do something silly. the sample/tutorial also assumes a minimum size (0.7 * max peak); plug in something absolute in pixels maybe.

then, for each separated blob, check which label it sits on (take coordinates of centroid to be safe), and note down a +1 for that label (a histogram).

any label that has more than one separated blob sitting on it, would be what you are looking for.

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36