python图像处理:测量物体大小 Measuring size of objects in an image with OpenCV

python图像处理:测量物体大小 Measuring size of objects in an image with OpenCV

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

物品实际(长x宽)测量(单位:mm)
硬币25.0x25.025.0x25.0
橡皮55.0x18.259.2x22.0
药丸18.0x7.020.1x7.4
卡片87.3x62.789.0x64.1

可以看到,还是比较精准的。这还是在未进行镜头畸变矫正的情况下。

原理

首先,我们需要在图中找一个参照物,并且为了方便找到它,我们将其放到最左边。
我选择了一元硬币作为参照,其是一个直径为25mm的圆。然后可以计算width=25mm在图中占了几个像素,得出pixelPerMetric = pixelNum/width,再计算其他物体所占像素n,便能得出实际长度n * pixelPerMetric

原文中只计算了横向的pixelPerMetric作为全局pixelPerMetric

考虑到拍摄角度不正的问题,我稍加改进,分别计算横向的pixelPerMetricX和纵向的pixelPerMetricY,测试下来效果确实比不分纵横好些。

但是,只是单纯的按比例来算大小,肯定会存在误差,理由有二:

  1. 拍摄角度肯定不是完美的90度俯角
  2. 照片可能会发生径向和切向镜头畸变

想要得到更好效果,需要进行畸变矫正(未实现)。

详细流程如下:

  1. 原图转灰度图,高斯滤波,提取边缘,提取轮廓
  2. 将轮廓从左到右排序,最左边的作为参照物
  3. 计算参照物轮廓大小,pixelPerMetricX,pixelPerMetricY
  4. 计算其他物体轮廓,进行处理

代码

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
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值