物品 | 实际(长x宽) | 测量(单位:mm) |
---|---|---|
硬币 | 25.0x25.0 | 25.0x25.0 |
橡皮 | 55.0x18.2 | 59.2x22.0 |
药丸 | 18.0x7.0 | 20.1x7.4 |
卡片 | 87.3x62.7 | 89.0x64.1 |
可以看到,还是比较精准的。这还是在未进行镜头畸变矫正的情况下。
首先,我们需要在图中找一个参照物,并且为了方便找到它,我们将其放到最左边。
我选择了一元硬币作为参照,其是一个直径为25mm的圆。然后可以计算width=25mm在图中占了几个像素,得出pixelPerMetric = pixelNum/width,再计算其他物体所占像素n,便能得出实际长度n * pixelPerMetric。
原文中只计算了横向的pixelPerMetric作为全局pixelPerMetric。
考虑到拍摄角度不正的问题,我稍加改进,分别计算横向的pixelPerMetricX和纵向的pixelPerMetricY,测试下来效果确实比不分纵横好些。
但是,只是单纯的按比例来算大小,肯定会存在误差,理由有二:
想要得到更好效果,需要进行畸变矫正(未实现)。
详细流程如下:
import numpy as np
from scipy.spatial import distance as dist
import cv2
from imutils import contours
from imutils import perspective
import imutils
def show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def midpoint(ptA,ptB):
return ((ptA[0] + ptB[0]) * 0.5 , (ptA[1] + ptB[1]) * 0.5)
img = cv2.imread('dis2.jpg')
width = 25
img = imutils.resize(img,height = 500)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(5,5),0)
edged = cv2.Canny(gray,70,200)
edged = cv2.dilate(edged,None,iterations = 1)
edged = cv2.erode(edged,None,iterations = 1)
cnts,_ = cv2.findContours(edged,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
(cnts,_) = contours.sort_contours(cnts)
pixelPerMetricX = 0
pixelPerMetricY = 0
order = 1
for c in cnts:
if cv2.contourArea(c) < 100:
continue
orig = img.copy()
box = cv2.minAreaRect(c)
box = cv2.boxPoints(box)
box = box.astype('int')
box = perspective.order_points(box)
cv2.drawContours(orig,[box.astype(int)],0,(0,255,0),2)
for x,y in box:
cv2.circle(orig,(int(x),int(y)),5,(0,0,255),3)
(tl,tr,br,bl) = box
(tltrX,tltrY) = midpoint(tl,tr)
(tlblX,tlblY) = midpoint(tl,bl)
(blbrX,blbrY) = midpoint(bl,br)
(trbrX,trbrY) = midpoint(tr,br)
cv2.circle(orig,(int(tltrX),int(tltrY)),5,(183,197,57),-1)
cv2.circle(orig,(int(tlblX),int(tlblY)),5,(183,197,57),-1)
cv2.circle(orig,(int(blbrX),int(blbrY)),5,(183,197,57),-1)
cv2.circle(orig,(int(trbrX),int(trbrY)),5,(183,197,57),-1)
cv2.line(orig,(int(tltrX),int(tltrY)),(int(blbrX),int(blbrY)),(255,0,0),2)
cv2.line(orig,(int(tlblX),int(tlblY)),(int(trbrX),int(trbrY)),(255,0,0),2)
#纵向
dA = dist.euclidean((tltrX,tltrY),(blbrX,blbrY))
#横向
dB = dist.euclidean((tlblX,tlblY),(trbrX,trbrY))
if pixelPerMetricX == 0 or pixelPerMetricY == 0:
pixelPerMetricX = dB / width
pixelPerMetricY = dA / width
dimA = dA / pixelPerMetricY
dimB = dB / pixelPerMetricX
cv2.putText(orig,"{:.1f}mm".format(dimB),(int(tltrX)-10,int(tltrY)),cv2.FONT_HERSHEY_COMPLEX,0.6,(255,255,255),1)
cv2.putText(orig,"{:.1f}mm".format(dimA),(int(trbrX)-10,int(trbrY)),cv2.FONT_HERSHEY_COMPLEX,0.6,(255,255,255),1)
cv2.imwrite('{}.jpg'.format(order),orig)
order += 1