Nit Nit - 21 days ago 11
Python Question

OpenCV get centers of multiple objects

I'm trying to build a simple image analyzing tool that will find items that fit in a color range and then finds the centers of said objects.

As an example, after masking, I'm analyzing an image like this:

Sample image

What I'm doing so far code-wise is rather simple:

import cv2
import numpy

bound = 30
inc = numpy.array([225,225,225])
lower = inc - bound
upper = inc + bound
img = cv2.imread("input.tiff")
cv2.imshow("Original", img)
mask = cv2.inRange(img, lower, upper)
cv2.imshow("Range", mask)
contours = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_TC89_L1)
print contours


This, however, gives me a countless number of contours. I'm somewhat at a loss while reading the corresponding manpage. Can I use moments to reasonably analyze the contours? Are contours even the right tool for this?

I found this question, that vaguely covers finding the center of one object, but how would I modify this approach when there are multiple items?

How do I find the centers of the objects in the image? For example, in the above sample image I'm looking to find three points (the centers of the rectangle and the two circles).

Answer

Try print len(contours). That will give you around the expected answer. The output you see is the full representation of the contours which could be thousands of points.

Try this code:

import cv2
import numpy

img = cv2.imread('inp.png', 0)
_, contours, _ = cv2.findContours(img.copy(), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_TC89_L1)
print len(contours)
centres = []
for i in range(len(contours)):
  moments = cv2.moments(contours[i])
  centres.append((int(moments['m10']/moments['m00']), int(moments['m01']/moments['m00'])))
  cv2.circle(img, centres[-1], 3, (0, 0, 0), -1)

print centres

cv2.imshow('image', img)
cv2.imwrite('output.png',img)
cv2.waitKey(0)

This gives me 4 centres:

[(474, 411), (96, 345), (58, 214), (396, 145)]

The obvious thing to do here is to also check for the area of the contours and if it is too small as a percentage of the image, don't count it as a real contour, it will just be noise. Just add something like this to the top of the for loop:

if cv2.contourArea(contours[i]) < 100:
  continue

For the return values from findContours, I'm not sure what the first value is for as it is not present in the C++ version of OpenCV (which is what I use). The second value is obviously just the contours (an array of arrays) and the third value is a hierarchy holding information on the nesting of contours, which can be very handy.

enter image description here