in my project i need to calculate the area of small plant root image.
i have used the following two codes but i still doubt these:
import cv2
image = cv2.imread('D:/R4-15.TIF')
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 180, 255, cv2.THRESH_BINARY)
cv2.imshow('Binary', thresh)
contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
image_copy = image.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=-1, lineType=cv2.LINE_AA)
cv2.imshow('Contour_image', image_copy)
cv2.waitKey(0)
cv2.imwrite('contours_image1.jpg', image_copy)
cv2.destroyAllWindows()
cnt = contours[0]
area= cv2.contourArea(cnt)
print(area)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
img = cv2.imread('D:/R4-16.TIF')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,165,255,cv2.THRESH_BINARY)
cv2.imshow("Mask", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
area2 = cv2.countNonZero(thresh)
print(area2)
CodePudding user response:
If your thresholded image has no noise, then countNonZero
is fine.
But if after threshold you still have some noise, which is usually small black dots scattered here and there, then better to filter them out.
One of possible ways is to apply morphological opening, see Morphological operations. But it definately also alters ground truth root image.
I propose to use connectedComponentsWithStats
Call it against your thresholded root image.
One of returned values is stats
which will also hold area for each connected component. Then you can drop all small components which is a noise, and pickup one or few largest black components and take their area.
More about connected components in wiki
Call example
# ...
# Put it at the end of you second snippet,
# where you get thresholded image
num_components, labels, stats, centroids = \
cv2.connectedComponentsWithStats(thresh)
# Here you can also use something like this:
# total_area = list(itertools.accumulate(
# stats, lambda t, x: t x[cv2.CC_STAT_AREA])
# )[-1]
areas = stats[:, cv2.CC_STAT_AREA]
areas = list(sorted(areas))
assert \
len(areas) >= 2, \
"We expect at least two components: white area" \
" and at least one root fragment."
# We suppose that white area is the biggest one, thus
# we have to count everything except the last item in sorted
# list.
total_area = 0
noise_threshold = 5 # If area is less than 5 pixels we drop it
for i, area in enumerate(areas[:-1]):
print(f"Area of root part #{i} is: {area}")
if area > noise_threshold:
total_area = area
print(f"Total area is: {total_area}")