在图像中计算不规则形状对象覆盖的区域

4

我正在进行一个项目,根据图像中的小麦穗估算小麦产量。在使用 Faster-RCNN 和基于颜色的分割检测到小麦穗之后,以下是只有小麦穗的图像结果。

Image having Wheat spikes only

现在我的目标是使用Python估算图像中针的产量。为此,我们可能需要计算多边形形状对象覆盖的区域,或者我们可能需要围绕像素值进行计算面积。但我不知道如何做到这一点。如果有人有解决方案,请告诉我。谢谢。


获取二进制掩模图像的平均值,这将是一个单一的值。然后将其乘以图像的宽度,再乘以图像的高度。四舍五入结果。该结果将等于掩模中白色像素的总数,因此也就是您图像中未为黑色(即有颜色)的总像素数。 - fmw42
那将返回实际白色像素的数量。那么,我们如何利用它来估计面积或估算产量呢? - Faizan Khan
白色像素的数量是由白色像素转换的区域(以像素为单位)。 - fmw42
1
白色像素的面积(平方英尺)=掩模平均值*图像面积(平方英尺) - fmw42
1
@FaizanKhan,请看一下fmw42的好解决方案。我的答案与他计算二值化图像中非零像素数量的方法类似。该解决方案的结果告诉我们针尖覆盖的图像面积和比率,为了估算产量,您需要自己确定校准重量比。不幸的是,图像本身无法告诉我们针尖的重量或提供任何其他测量属性。 - nathancy
显示剩余8条评论
2个回答

3
通过创建二进制掩模可以找到图像中非黑色的像素区域。像素面积等于掩模中白色像素的总数。一种获取此值的方法是计算图像中白色像素的分数。白色像素的数量将是分数 * 图像的宽度 * 高度。分数只是图像平均值除以最大可能灰度级(255)。因此,白色像素的像素面积为 (average/255) * width * height。
因此,获取二进制掩模图像的分数平均值(通过阈值设置为0)。结果为一个单一值。然后将其乘以图像的宽度和高度。该结果将等于掩模中白色像素的总数,因此也等于您的图像中非黑色(即有颜色)的像素总数。白色像素的数量是图像中非黑色像素的像素面积。
输入:

enter image description here

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

请注意,这是非黑像素区域。你的一些矩形内部有黑色。所以这不是矩形的面积。在隔离矩形以获取矩形的面积之前,你需要确保你的图像没有黑色像素。 补充 一个更简单的方法,由Mark Setchell建议,就是简单地计算二值化图像中非零像素的数量。它计算出与上述相同的数字。
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

附加2

如果您知道以米为单位的地面面积或尺寸(根据您的评论,0.8米在一侧),对应于图像覆盖的面积,则与非零像素计数相对应的地面面积将是:

area on ground for nonzero pixels = count * 0.8 * 0.8 / (width * height)


其中width和height是图像的像素尺寸。

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

所以结果是0.198518平方米。

请看我在问题下面的注释。只需将掩模平均值乘以完整图像的平方英尺面积即可。请告诉我们图像的面积是多少平方英尺、平方英寸或类似单位。 - fmw42
2
Mark Setchell(私人电子邮件)建议使用cv2.countNonZero(),这当然比我的方法简单得多(这是我在ImageMagick中处理它的方式)。我已经将其添加到我的上面的帖子中。 - fmw42
2
请看我的回答中的“ADDITION 2”部分,其中area = count * 0.8 * 0.8 / (width * height) - fmw42
2
你需要将面积乘以kg/area。你需要通过实际叶子的物理测量来计算它。取一些植物叶子并测量表面积和重量,然后得出重量与面积的比率。这超出了本论坛的范围。 - fmw42
1
不行!不知道每平方米的重量是多少。图像无法知道您的植被重量。 - fmw42
显示剩余8条评论

1
我希望这可以帮到您
  1. 以灰度形式加载图像(0[黑色] - 255[白色])
  2. 提取高于某个水平的像素。 我使用cv2.threshold来获取大于60的像素
  3. 使用最小内核(3,3)cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)填充小的白色斑点
  4. 从阈值图像中提取轮廓
  5. 倒序迭代每个轮廓(从右上到右下角)
  6. 在遍历每个轮廓时计算其属性

代码

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()

样例输出

enter image description here

PS:我怀疑cv2计算的面积是否正确


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接