简介:实时测量物体尺寸是使用OpenCV进行计算机视觉应用的一个常见案例。下面我来简单实现一下如何在以一张A4纸为背景的前提下进行物体测量。主要功能是从视频或图像中识别出特定的对象(如A4纸),并进行固定的流程:边缘检测、轮廓提取、透视变换和物体尺寸测量。
一、新建My_utils.py文件,编写以下功能:图像预处理、查找A4纸轮廓、进而查找A4纸里的物体轮廓、透视变换、填充、测量物体边长:
import cv2 as cv
import numpy as np
def getContours(img,thur = [100,100],showCanny = False,minArea = 1000,filter =0,draw=False): #图像预处理,模糊、边缘检测、腐蚀膨胀等
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
img_blur = cv.GaussianBlur(img_gray,(5,5),1)
img_Canny = cv.Canny(img_blur,thur[0],thur[1])
kernel = np.ones((5,5))
img_dail = cv.dilate(img_Canny,kernel,iterations=3)
img_erode = cv.erode(img_dail,kernel,iterations=2)
if showCanny:cv.imshow('ShowCanny',img_erode)
contours, hiearchy = cv.findContours(img_erode,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) #查找轮廓
finalContors = []
for i in contours:
area = cv.contourArea(i) # 计算轮廓面积
if area > minArea:
peri = cv.arcLength(i,True)
approx = cv.approxPolyDP(i,0.02*peri,True)
bbox = cv.boundingRect(approx) # 获取轮廓的边界框
if filter>0:
if len(approx) == filter: # 根据点数过滤轮廓 ,这里我规定查找四边形的物体,可以修改filter满足需求
finalContors.append((len(approx), area, approx, bbox, i))
else:
finalContors.append((len(approx), area, approx, bbox, i))
# 根据面积大小降序排序轮廓
finalContors = sorted(finalContors,key=lambda x:x[1],reverse=True)
# 如果设置绘制,则在图像上绘制轮廓
if draw:
for con in finalContors:
cv.drawContours(img,con[4],-1,(0,0,255),3)
return img,finalContors
def reorder(myPoints):
print(myPoints.shape)
myPointsNew = np.zeros_like(myPoints)
myPoints = myPoints.reshape((4,2))
add = myPoints.sum(1)
# 重新排序点,确保它们是正确的顺序(左上,右上,左下,右下)
myPointsNew[0] = myPoints[np.argmin(add)]
myPointsNew[3] = myPoints[np.argmax(add)]
diff = np.diff(myPoints,axis=1)
myPointsNew[1] = myPoints[np.argmin(diff)]
myPointsNew[2] = myPoints[np.argmax(diff)]
return myPointsNew
def wrapImg(img,points,w,h,pad=20):
#透视变换
# print(points)
# print(reorder(points))
points = reorder(points)
pst1 = np.float32(points)
pst2 = np.float32([[0,0],[w,0],[0,h],[w,h]])
# 获取透视变换矩阵
martrix = cv.getPerspectiveTransform(pst1,pst2)
imgWrap = cv.warpPerspective(img,martrix,(w,h))
imgWrap = imgWrap[pad:imgWrap.shape[0]-pad,pad:imgWrap.shape[1]-pad]
return imgWrap
def findDis(pts1,pts2):
# 计算两点之间的欧氏距离,用于测量边长
return ((pts2[0]-pts1[0])**2 + (pts2[1]-pts1[1])**2)**0.5
主函数:
import cv2 as cv
import my_utils
# 视频设置
# 使用mp4v编解码器
fourcc = cv.VideoWriter_fourcc(*'mp4v')
# 创建VideoWriter对象,保存为.mp4格式
out = cv.VideoWriter('output.mp4', fourcc, 20.0, (int(380), int(554))) #这里是视频尺寸,要和imgContours2.shape一样
wibcam = True
img_path = 'test.jpg'
camera = cv.VideoCapture('test1.mp4')
camera.set(10, 160)
camera.set(3, 1920)
camera.set(4, 1080)
scale = 2
wP = 210 * scale
hP = 297 * scale
while True:
if wibcam:
success, img = camera.read()
else:
img = cv.imread(img_path)
imgContours, conts = my_utils.getContours(img, minArea=50000, filter=4, draw=True)
if len(conts) != 0:
biggest = conts[0][2]
imgWarp = my_utils.wrapImg(img, biggest, wP, hP)
imgContours2, conts2 = my_utils.getContours(imgWarp, minArea=50, filter=4, thur=[50, 50], draw=False)
if len(conts2) != 0:
for obj in conts2:
cv.polylines(imgContours2, [obj[2]], True, (0, 255, 0), 2)
nPoints = my_utils.reorder(obj[2])
nW = round((my_utils.findDis(nPoints[0][0]//scale, nPoints[1][0]//scale)/10), 1)
nH = round((my_utils.findDis(nPoints[0][0]//scale, nPoints[2][0]//scale)/10), 1)
cv.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[1][0][0], nPoints[1][0][1]), (255, 0, 255), 3, 8, 0, 0.05)
cv.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[2][0][0], nPoints[2][0][1]), (255, 0, 255), 3, 8, 0, 0.05)
x, y, w, h = obj[3]
cv.putText(imgContours2, '{}cm'.format(nW), (x + 30, y - 10), cv.FONT_HERSHEY_COMPLEX_SMALL, 0.5, (255, 0, 255), 2)
cv.putText(imgContours2, '{}cm'.format(nH), (x - 70, y + h // 2), cv.FONT_HERSHEY_COMPLEX_SMALL, 0.5, (255, 0, 255), 2)
cv.imshow('A4', imgContours2)
out.write(imgContours2) # 将图像写入视频文件
print(imgContours2.shape) #写入尺寸要和这里一样
img = cv.resize(img, (0, 0), None, 0.3, 0.3)
cv.imshow('Original', img)
if cv.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环
break
# 释放资源
camera.release()
out.release() # 关闭视频文件
cv.destroyAllWindows()
来看看效果:
output
以上代码实现了一个实时测量物体尺寸的系统。通过摄像头或视频文件输入,检测物体并测量其尺寸,并在画面上显示结果
以上即为全部内容!