This is a perfect sample image that includes range of black objects.

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

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
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:

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