I try to draw a bounding box on every object in this picture, i wrote this code from documentation
import cv2 as cv2
import os
import numpy as np
img = cv2.imread('1 (2).png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
ret,thresh = cv2.threshold(img,127,255,0)
im2,contours,hierarchy = cv2.findContours(thresh, 1, 2)
for item in range(len(contours)):
cnt = contours[item]
if len(cnt)>20:
print(len(cnt))
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
the result is only one object,
when i change the value 127 in this line to 200 in this line ret,thresh = cv2.threshold(img,127,255,0)
i got different object.
here's the original image
The question is how can i detect all objects once?
The approach is fairly straightforward. We begin by converting to HSV and grabbing only the hue channel.
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h,_,_ = cv2.split(image_hsv)
Next, we find the dominant hues -- first count the occurrences of each hue using numpy.bincount
(we flatten
the hue channel image to make it one-dimensional):
bins = np.bincount(h.flatten())
And then find which ones are common enough using numpy.where
:
MIN_PIXEL_CNT_PCT = (1.0/20.0)
peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]
Now that we've identified all the dominant hues, we can repeatedly process the image to find the areas correspond to each of them:
for i, peak in enumerate(peaks):
We begin by creating a mask which selects all the pixels of this hue (cv2.inRange
, and then extracting the corresponding parts from the input BGR image (cv2.bitwise_and
.
mask = cv2.inRange(h, peak, peak)
blob = cv2.bitwise_and(image, image, mask=mask)
Next, we find the contours (cv2.findContours
of all the continuous areas of this hue, so that we can process each of them individually
_, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Now, for each of the identified continuous area
for j, contour in enumerate(contours):
We determine the bounding box (cv2.boundingRect
, and create a mask corresponding to just this contour by filling the contour polygon with white (numpy.zeros_like
and cv2.drawContours
)
bbox = cv2.boundingRect(contour)
contour_mask = np.zeros_like(mask)
cv2.drawContours(contour_mask, contours, j, 255, -1)
Then we can extra just the ROI corresponding to the bounding box
region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_masked = cv2.bitwise_and(region, region, mask=region_mask)
Or visualize (cv2.rectangle
the bounding box:
result = cv2.bitwise_and(blob, blob, mask=contour_mask)
top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)
Or do any other processing you want.
import cv2
import numpy as np
# Minimum percentage of pixels of same hue to consider dominant colour
MIN_PIXEL_CNT_PCT = (1.0/20.0)
image = cv2.imread('colourblobs.png')
if image is None:
print("Failed to load iamge.")
exit(-1)
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# We're only interested in the hue
h,_,_ = cv2.split(image_hsv)
# Let's count the number of occurrences of each hue
bins = np.bincount(h.flatten())
# And then find the dominant hues
peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]
# Now let's find the shape matching each dominant hue
for i, peak in enumerate(peaks):
# First we create a mask selecting all the pixels of this hue
mask = cv2.inRange(h, peak, peak)
# And use it to extract the corresponding part of the original colour image
blob = cv2.bitwise_and(image, image, mask=mask)
_, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for j, contour in enumerate(contours):
bbox = cv2.boundingRect(contour)
# Create a mask for this contour
contour_mask = np.zeros_like(mask)
cv2.drawContours(contour_mask, contours, j, 255, -1)
print "Found hue %d in region %s." % (peak, bbox)
# Extract and save the area of the contour
region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_masked = cv2.bitwise_and(region, region, mask=region_mask)
file_name_section = "colourblobs-%d-hue_%03d-region_%d-section.png" % (i, peak, j)
cv2.imwrite(file_name_section, region_masked)
print " * wrote '%s'" % file_name_section
# Extract the pixels belonging to this contour
result = cv2.bitwise_and(blob, blob, mask=contour_mask)
# And draw a bounding box
top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)
file_name_bbox = "colourblobs-%d-hue_%03d-region_%d-bbox.png" % (i, peak, j)
cv2.imwrite(file_name_bbox, result)
print " * wrote '%s'" % file_name_bbox
Found hue 32 in region (186, 184, 189, 122).
* wrote 'colourblobs-0-hue_032-region_0-section.png'
* wrote 'colourblobs-0-hue_032-region_0-bbox.png'
Found hue 71 in region (300, 197, 1, 1).
* wrote 'colourblobs-1-hue_071-region_0-section.png'
* wrote 'colourblobs-1-hue_071-region_0-bbox.png'
Found hue 71 in region (301, 195, 1, 1).
* wrote 'colourblobs-1-hue_071-region_1-section.png'
* wrote 'colourblobs-1-hue_071-region_1-bbox.png'
Found hue 71 in region (319, 190, 1, 1).
* wrote 'colourblobs-1-hue_071-region_2-section.png'
* wrote 'colourblobs-1-hue_071-region_2-bbox.png'
Found hue 71 in region (323, 176, 52, 14).
* wrote 'colourblobs-1-hue_071-region_3-section.png'
* wrote 'colourblobs-1-hue_071-region_3-bbox.png'
Found hue 71 in region (45, 10, 330, 381).
* wrote 'colourblobs-1-hue_071-region_4-section.png'
* wrote 'colourblobs-1-hue_071-region_4-bbox.png'
Found hue 109 in region (0, 0, 375, 500).
* wrote 'colourblobs-2-hue_109-region_0-section.png'
* wrote 'colourblobs-2-hue_109-region_0-bbox.png'
Found hue 166 in region (1, 397, 252, 103).
* wrote 'colourblobs-3-hue_166-region_0-section.png'
* wrote 'colourblobs-3-hue_166-region_0-bbox.png'
Yellow bounding box:
Yellow extracted region:
Biggest green bounding box (there are several other small disjoint areas as well):
...and the corresponding extracted region:
First step is to understand what your algorithm is doing...specifically this function:
ret,thresh = cv2.threshold(img,127,255,0)
the value 127
is the greyscale value between 0 and 255. Threshold function changes pixel values below 127 to 0 and above 127 to 255
With reference to your coloured image, the greyscale output for both the green blob and yellow blob is above 127, so both of those are changed to 255, and hence both are captured by the findContours()
method
You can run imshow
on thresh
object to understand exactly what is going on.
Now when you replace 127
with 200
, only the yellow blob has a greyscale value above 200, so only that blob is seen in the thresh
Mat
To detect "all objects" at once, please experiment further with threshold
method and study the thresh
object using imshow
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With