Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Accurate result on cv2.findcontours to find objects in BW/black-white image?

This is a perfect sample image that includes range of black objects. silhouette black plants in white background

And this code suppose to find every black plants

import cv2 as cv
import numpy as np
img = cv.imread("12.jpg")
tresh_min= 200
tresh_max=255
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
blurred = cv.GaussianBlur(gray, (5, 5), 0)
_, threshold = cv.threshold(blurred, tresh_min, tresh_max, 0)
(contours, _)= cv.findContours(threshold, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

print(f'Number of countours: {len(contours)}')
mask = np.ones(img.shape[:2], dtype="uint8") * 255


# Draw the contours on the mask
cv.drawContours(mask, contours=contours, contourIdx=-1, color=(0, 255, 255), thickness=2)

But the results is disappointing as this showing contours of the above image's black objects

That includes 118 contours. Note that I need to find and take 14 objects. How to cut each plant when the contour is actually incorrect. Or at least join the very close ones to each bigger contour, to save the object separately? Thanks

like image 862
redParrot Avatar asked Nov 04 '25 05:11

redParrot


1 Answers

We may use dilate instead of GaussianBlur, use RETR_EXTERNAL instead of RETR_TREE, and keep only the large contours.

  • Inverse the threshold:

     _, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
    
  • Dilate with column kernel (assume the plants are tall and narrow):

     dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
    
  • Loop over the contours list and keep only the ones with area above 1000.


Code sample:

import cv2 as cv
import numpy as np

img = cv.imread("12.jpg")
tresh_min= 200
tresh_max=255
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#blurred = cv.GaussianBlur(gray, (5, 5), 0)
_, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
(contours, _)= cv.findContours(dilate_threshold, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)  # Use RETR_EXTERNAL instead of RETR_TREE

print(f'Number of countours: {len(contours)}')
mask = np.ones(img.shape[:2], dtype="uint8") * 255


# Draw the contours on the mask
large_contours = []
for c in contours:
    area_tresh = 1000
    area = cv.contourArea(c)
    if area > area_tresh:
        cv.drawContours(mask, [c], contourIdx=-1, color=(0, 255, 255), thickness=1)
        large_contours.append(c)  # Append to list of "large contours".

print(f'Number of large countours: {len(large_contours)}')

# Show output for testing
cv.imshow('threshold', threshold)
cv.imshow('dilate_threshold', dilate_threshold)
cv.imshow('mask', mask)
cv.waitKey()
cv.destroyAllWindows()

Output mask:
enter image description here

like image 141
Rotem Avatar answered Nov 07 '25 10:11

Rotem



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!