如图,选择APMCM2019年A题,要求根据该图像中红线圈住的二氧化硅晶体面积估计其体积。
首先对图像进行二值化出理,得到其边缘
可以通过计算其像素点个数获得其面积,但通过面积推断体积时较为复杂。可能普遍的想法是用球体去近似,但精确度不够高,这里我们使用古鲁金定理给出了一种方法
首先求该图形的形心坐标,然后过形心坐标做垂线平分图形为面积相等的两部分。再任选其中一部分求其形心,这样我们就可以使用古鲁金定理了,示意图为
即我们采用其某部分面旋转体体积来近似,计算公式为
V
=
π
p
S
V=\pi pS
V=πpS
其中p为形心到垂线距离,S为图形面积。
代码为
CalVolumeThroughArea.py import cv2 as cv
import numpy as np
datas = []
for i in range(497, 611, 1): m = i
i = '.\\data\\0' + str(i) + '.bmp'
img = cv.imread(i, 0) height = img.shape[0]
ret, thresh = cv.threshold(img, 127, 255, 0)
img2, contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
dataEachImage = 0
for j in range(len(contours)): cnt = contours[j]
# compute the area in the contour[j], 50.2655 is the area of crucible and 847887 is the amount of pixels
# of crucible
mask = np.zeros(img.shape, np.uint8) cv.drawContours(mask, [cnt], 0, 255, -1) pixelPoints = cv.findNonZero(mask)
area = len(pixelPoints) * 50.2655 / 847887
# moments of contours[j] M = cv.moments(cnt)
# compute the centroid coordinate if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
else:
cx = 0
cy = 0
if cx != 0 and cy != 0:
x, y, w, h = cv.boundingRect(cnt) for a in range(y, y + h + 1, 1):
mask[a, cx] = 255
for a in range(0, cx, 1):
for b in range(0, height, 1): mask[b, a] = 0
# process the new contour
newRet, newThresh = cv.threshold(mask, 127, 255, 0)
newImg2, newContours, newHierarchy = cv.findContours(newThresh, cv.RETR_EXTERNAL,
cv.CHAIN_APPROX_SIMPLE)
# moments of newContours
newM = cv.moments(newContours[0])
# compute the centroid coordinate if newM['m00'] != 0:
ncx = int(newM['m10'] / newM['m00'])
ncy = int(newM['m01'] / newM['m00'])
else:
ncx = 0
ncy = 0
radius = abs(ncx - cx) * 8 / 1039 perimeter = 2 * 3.1415926 * radius dataEachImage += perimeter * area / 2
datas.append(dataEachImage) i = m
output = open(".\\results\\Volume.xls", 'w', encoding='gbk') output.write('pictureNum\tVolume\n')
for a in range(len(datas)): output.write(str(a + 497)) output.write('\t')
output.write(str(datas[a])) output.write('\t') output.write('\n')
output.close()
我们使用大量图片进行测试,结果为
看起来效果还算不错。