如何使用Python OpenCV计算图像中形状的面积?

5
我希望能够计算从红外相机生成的图像中形状的面积。我有一组大型矩阵,这些矩阵是从红外相机中生成的。在每个矩阵/图像中,我大多数都是背景,并带有一个点的图像,该点是红外辐射源。我使用Python OpenCV来隔离源图像,通过将背景置零并计算形状中的像素数来解决问题。问题在于,在每个图像中,部分图像也会变成背景,因此我无法获得完整的图像,就像我想要的那样。
import cv2
import numpy as np
from matplotlib import pyplot as plt

PPmm = 81/55 #Pixel per mm


img = np.genfromtxt('Image 5 Z_plane = 141.0_contour_plot.csv', delimiter= ',')

img_cv = cv2.resize(img,(81,81))
np.savetxt('testing.csv', img_cv, delimiter= ',')

img = (img_cv*255).astype(np.uint8)









edges = cv2.Canny(img,150,250)

se = np.ones((7,7), dtype='uint8')





# Perform morphology

image_close = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, se)

# Your code now applied to the closed image
cnt = cv2.findContours(image_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
mask = np.zeros(img.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)





non_zero_pixel_count = (np.count_nonzero(mask))

Area_in_mm = non_zero_pixel_count*(1/PPmm)**2
print("Area of shape = {0:1f}mm^2".format(Area_in_mm))






plt.subplot(121)
plt.imshow(img,cmap = 'gray')
plt.title('Original Image')
plt.xticks([])
plt.yticks([])
plt.subplot(122)
plt.imshow(mask,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
[enter image description here][1]
plt.show()

形状面积为:58.093278平方毫米。如果我手动计算,大约将得到68平方毫米。在圆形图像中,这甚至更糟糕,我得到的面积是两倍小。

圆形图像

正方形图像

编辑: 使用cv2.THRESH_BINARY


为什么不使用非常低的阈值对原始图像进行二值化,因为您的背景似乎只有黑色? - Clément
这就是问题所在,背景其实并不是真正的黑色。有一些背景噪音,只需一个简单的二进制阈值,我就可以非常清楚地看到它们(附在原帖中)。 - tHeReaver
当前景物和背景之间没有明显的分离,而且背景不是完全恒定的,前景物有变化的灰度级别(即模糊)时,您如何定义“精确”形状? - fmw42
也许提供有关图像生成的更多信息会更好。我不清楚图像中的斑点与辐射源的大小有何关系。如下面的答案所示,简单的阈值可能没有意义。了解这种关系的一个好方法是成像不同大小和形状的源。 - Cris Luengo
正如Cris Luengo所提到的,简单阈值处理不太可能产生有意义的数据。然而,它可能会帮助您识别形状的中心,随后您可以径向向外搜索边缘,并使用边缘点定义形状的外边界。您能否在去除背景之前发布原始图像?背景去除可能已经消除了感兴趣的数据。 - Rethunk
1个回答

3
为了获取形状的确切边缘,您可以执行以下操作:
  • 阈值化
  • 查找轮廓
  • 计算所有非零像素的数量
找到轮廓形状之后,您可以使用cv2.countNonZero()来查找所有白色像素,然后使用校准的像素度量计算面积(由于我不知道精确的原始图像,因此我的面积可能会有所不同)

import cv2

image = cv2.imread('2.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)[1]

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    cv2.drawContours(image,[c], 0, (36,255,12), 2)

area = cv2.countNonZero(thresh)
cv2.putText(image, "Area: {}".format(area), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (156, 188, 24), 1)

cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('image.png', image)
cv2.waitKey()

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