Reputation: 583
I am working on a project where I'm estimating the wheat yield based on the wheat spikes in the image. After detecting spikes using Faster-RCNN and color based segmentation, the following is the resultant image where there are only spikes in the image.
Now my goal is to estimate the yield produced by the spikes in the image using python. For this, we may have to calculate the area covered by the objects of polygon shapes or we may have to work around the pixel values to calculate the area. But I don't know how we can do this. Please Let me know If anyone has the solution. Thanks
Upvotes: 4
Views: 11893
Reputation: 53081
The area in pixels of the image that are not black can be found from creating a binary mask. The area in pixels is equal to the total number of white pixels in the mask. One way to get that is to compute the fraction of white pixels in the image. The number of white pixels will then be the fraction * width * height of the image. The fraction is just the average of the image divided by the maximum possible gray level (255). So
area in pixels of white pixels = (average/255)widthheight
Thus, get the fractional average (average/255) of the binary mask image (by thresholding at 0). The result for the average will be one single value. Then multiply that by the Width of the image and then by the Height of the image. That result will be equal to the total number of white pixels in the mask and thus the total pixels that are not black (i.e. are colored) in your image. The number of white pixels is the pixel area of the non-black pixels in your image.
Input:
import cv2
import numpy as np
img = cv2.imread('img.jpg')
height = img.shape[0]
width = img.shape[1]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)
cv2.imshow("Mask", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
ave = cv2.mean(thresh)[0]/255
print(ave)
0.310184375
area = ave*height*width
print(area)
198518.0
Note that this is the non-black pixel area. Some of your rectangles have black inside them. So this is not the area of the rectangle. You would have ensure that your image had no black pixels before isolating the rectangles to get the area of the rectangles.
ADDITION
A simpler approach, suggested by Mark Setchell, is to simply count the number of nonzero pixels in the thresholded image. It computes the same number as above.
import cv2
import numpy as np
img = cv2.imread('img.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)
cv2.imshow("Mask", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
area2 = cv2.countNonZero(thresh)
print(area2)
198518
ADDITION 2
If you know the ground area or dimensions in meters (0.8 m on aside as per your comment) corresponding to the area covered by the image, then the ground area corresponding to the count of non-zero pixels will be:
area on ground for nonzero pixels = count * 0.8 * 0.8 / (width * height)
where width and height are the pixel dimensions of the image.
import cv2
import numpy as np
img = cv2.imread('img.jpg')
height = img.shape[0]
width = img.shape[1]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)
cv2.imshow("Mask", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
count = cv2.countNonZero(thresh)
area = count*0.8*0.8/(width*height)
print(area)
0.19851800000000003
So the result is 0.198518 square meters
Upvotes: 3
Reputation: 1184
Hope this helps 😉
(0[Black] - 255[White])
60
using cv2.threshold
(3,3)
using cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
Code
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('RIUXF.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
# Area occupied by black region
black_area = np.true_divide(hist[0],np.prod(img.shape))[0]*100
# extract no black parts
thresh = cv2.threshold(img,60,255,cv2.THRESH_BINARY)[1]
kernel = np.ones((3,3),np.uint8)
# fill in the small white spots
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# extract the contours
contours = cv2.findContours(opening, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]
blank_image = np.zeros((img.shape),np.uint8)
image_area = np.prod(img.shape)
# iterate through the contours detected from right top corner
for i,c in enumerate(contours[::-1]):
# turn blank_image black
blank_image *= 0
# draw filled contour
cv2.drawContours(blank_image, [c], 0, (255), thickness=cv2.FILLED)
contour_area = cv2.contourArea(c)
# percentage of area contour
contour_area_pc = np.true_divide(int(contour_area),image_area)*100 if int(contour_area) > 1 else 0
text = ' '.join(['Contour:',str(i),'Area:',str(round(contour_area,2)),'Percentage Area:',str(round(contour_area_pc,2))])
cv2.putText(blank_image,text,(10,60), cv2.FONT_HERSHEY_SIMPLEX, 1,(255),2,cv2.LINE_AA)
plt.imshow(blank_image, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axis
plt.show()
Sample output
PS: I doubt if area cv2 calculates is correct 🤔
Upvotes: 1