目录
基于单目相机的2D测量技术在许多领域中具有重要的背景和意义。
工业制造:在工业制造过程中,精确测量是确保产品质量和一致性的关键。基于单目相机的2D测量技术可以用于检测和测量零件尺寸、位置、形状等参数,进而实现自动化生产和质量控制。通过实时监测并反馈测量结果,可以快速发现和纠正生产中的偏差,提高产品的一致性和合格率。
计算机视觉:单目相机作为计算机视觉的传感器之一,能够捕捉并记录场景中的图像信息。基于单目相机的2D测量技术可以通过对图像进行处理和分析来提取目标物体的特征和参数。这种技术在目标检测、物体跟踪、姿态估计等计算机视觉任务中起着至关重要的作用。
地理测绘和导航:基于单目相机的2D测量技术可以应用于地理测绘和导航领域。通过获取地面或航空图像,并利用图像处理和计算机视觉算法,可以实现地表特征的提取、地形建模、数字地图的生成等工作。这对于城市规划、农业管理、导航系统等方面具有重要的应用价值。
医学影像:在医学领域,基于单目相机的2D测量技术可以用于医学影像的分析和测量。通过对医学图像进行处理和分析,可以提取器官、病灶的形状、大小、位置等信息,辅助医生进行诊断和治疗决策。这种技术在影像学、放射学、眼科等医学专业中得到广泛应用。
综上所述,基于单目相机的2D测量技术在工业制造、计算机视觉、地理测绘和导航、医学影像等领域都有着重要的背景和意义。它可以提高生产效率、产品质量,推动科学研究和医学进步,为各个领域带来更多的机遇和挑战。
因为是静态测量,所以核心测量算法使用仿射变换。
首先拿到参照物区域,进行真实距离和像素距离的尺寸换算
在参照物区域找到物体轮廓,进行多边形拟合,对拟合到的物体进行测量
代码思路的简要描述:
导入所需的库,包括cv2
和自定义的utlis
。
初始化一些变量,如是否使用网络摄像机 (webcam
)、图像路径 (path
)、捕获对象 (cap
)、图像缩放比例 (scale
)、纸张宽度和高度 (wP
和 hP
) 以及上一帧时间 (pTime
)。
进入主循环。在每次循环中,如果使用网络摄像机,则读取图像帧;否则,从指定路径读取图像。
对图像进行轮廓检测,获取所有检测到的轮廓 (conts
) 和最大轮廓 (biggest
)。
使用最大轮廓对图像进行透视变换 (warpImg
),得到纸张的鸟瞰图 (imgWarp
)。
对纸张鸟瞰图进行轮廓检测,获取所有检测到的轮廓 (conts2
)。
遍历每个轮廓对象,绘制轮廓线和箭头,并计算测量结果 (纸张宽度 nW
和高度 nH
)。
在图像上绘制测量结果和帧率信息。
显示原始图像和处理后的图像。
等待按键,继续下一次循环。
以上是代码的简要思路,具体实现和功能可以参考代码中的注释。
关于复现,你可以准备一张A4纸,一张小卡片,或者边缘信息明显的其他物件,按照图片中方式摆放即可。
像素与真实距离换算为
3像素=1mm
检测帧率可达30帧,误差在2%(误差取决相机分辨率),缺点是无法检测轮廓不清晰的物件以及复杂物体。
下一篇介绍如何测量复杂形状的物体
- import cv2
- import utlis
- import time
-
- ###################################
- webcam = True # 网络摄像机一开始为FALSE
- path = '1.jpg'
- cap = cv2.VideoCapture(0) # 使用捕获和cv定义相机-点点视频捕获,,我们将定义id,因此在这种情况下为0
- cap.set(10, 160) # 设置参数,宽度,高度,亮度,为他们中的每一根写入间隙点集,具有不同的亮度id,有十个,所以将其保持为160,然后在宽度和高度上,宽度为3,1920
- cap.set(3, 1920)
- cap.set(4, 1080)
- scale = 3
- wP = 270 * scale
- hP = 370 * scale
- ###################################
- pTime = 0
- while True:
- if webcam:
- success, img = cap.read()
- else:
- img = cv2.imread(path)
-
- imgContours, conts = utlis.getContours(img, minArea=50000, filter=4)
- if len(conts) != 0:
- biggest = conts[0][2]
- # print(biggest)
- imgWarp = utlis.warpImg(img, biggest, wP, hP)
- imgContours2, conts2 = utlis.getContours(imgWarp,
- minArea=2000, filter=4,
- cThr=[50, 50], draw=False)
- if len(conts) != 0:
- for obj in conts2:
- cv2.polylines(imgContours2, [obj[2]], True, (0, 255, 0), 2)
- nPoints = utlis.reorder(obj[2])
- nW = round((utlis.findDis(nPoints[0][0] // scale, nPoints[1][0] // scale) / 10), 1)
- nH = round((utlis.findDis(nPoints[0][0] // scale, nPoints[2][0] // scale) / 10), 1)
- cv2.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)
- cv2.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]
- cv2.putText(imgContours2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
- (255, 0, 255), 2)
- cv2.putText(imgContours2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
- (255, 0, 255), 2)
- cTime = time.time()
-
- fps = 1 / (cTime - pTime)
- pTime = cTime
- cv2.putText(imgContours2, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,
- (255, 0, 255), 3)
- # 图像预处理及边缘检测
- cv2.imshow('A4', imgContours2)
-
- img = cv2.resize(img, (0, 0), None, 0.5, 0.5)
- cv2.imshow('Original', img)
- cv2.waitKey(1)
utlis模块代码
- import cv2
- import numpy as np
- import math
- import time
- def getContours(img, cThr=[100, 100], showCanny=False, minArea=5000, filter=0, draw=False):
- # 将输入图像转换为灰度图像
- imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- # 对灰度图像进行高斯模糊
- imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
- # 使用Canny边缘检测算法得到边缘图像
- imgCanny = cv2.Canny(imgBlur, cThr[0], cThr[1])
- # 对Canny边缘图像进行膨胀操作
- kernel = np.ones((5, 5))
- imgDial = cv2.dilate(imgCanny, kernel, iterations=3)
- # 对膨胀后的图像进行腐蚀操作
- imgThre = cv2.erode(imgDial, kernel, iterations=1)
- # gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- # blurred = cv2.GaussianBlur(gray, (5, 5), 0)
- # kernal = np.ones((5, 5), np.uint8)
- # blurred = cv2.erode(blurred, kernal) # 腐蚀
- # blurred = cv2.erode(blurred, kernal)
- # edges = cv2.Canny(blurred, 50, 150)
-
- # 寻找图像中的轮廓
- contours, hierarchy = cv2.findContours(imgThre, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- finalContours = []
- for i in contours:
- area = cv2.contourArea(i)
- # 仅保留面积大于minArea的轮廓
- if area > minArea:
- peri = cv2.arcLength(i, True)
- approx = cv2.approxPolyDP(i, 0.02 * peri, True)
- bbox = cv2.boundingRect(approx)
- if filter > 0:
- # 如果指定了过滤条件,仅保留满足条件的轮廓
- if len(approx) == filter:
- finalContours.append([len(approx), area, approx, bbox, i])
- else:
- finalContours.append([len(approx), area, approx, bbox, i])
- # 根据面积对轮廓进行排序
- finalContours = sorted(finalContours, key=lambda x: x[1], reverse=True)
- if draw:
- # 将保留的轮廓在原始图像上绘制出来
- for con in finalContours:
- cv2.drawContours(img, con[4], -1, (0, 0, 255), 3)
- return img, finalContours
-
-
- def dectshow(org_img, boxs):
- img = org_img.copy()
- for box in boxs:
- cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 2)
-
- x1 = box[0]
- y1 = box[1]
- x2 = box[2]
- y2 = box[3]
-
- cv2.circle(img, (x1, y1), 5, (0, 0, 255), -1) # 红色圆点
- cv2.circle(img, (x2, y1), 5, (0, 255, 0), -1) # 绿色圆点
- cv2.circle(img, (x2, y2), 5, (255, 0, 0), -1) # 蓝色圆点
-
- point1 = [x1, y1]
- point2 = [x2, y1]
- point3 = [x2, y2]
-
- width = math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
- height = math.sqrt((point3[0] - point2[0]) ** 2 + (point3[1] - point2[1]) ** 2)
- width = width/3
- height = height/3
-
- # cv2.imshow('1', img)
- # 计算两点之间的距离
-
- print("宽 is:", width, "m")
- print("长 is:", height, "m")
-
-
- cv2.putText(img, f'W: {str(width)[:4] }mm H: {str(height)[:4]}mm', (int(box[0]), int(box[1]) - 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
- (0, 255, 0), 2)
- cv2.imshow('dec_img', img)
-
- return img
-
- # 对角点排序函数
- def reorder(myPoints):
- 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 warpImg(img, points, w, h, pad=20):
- points = reorder(points)
- pts1 = np.float32(points)
- pts2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]])
- matrix = cv2.getPerspectiveTransform(pts1, pts2)
- imgWarp = cv2.warpPerspective(img, matrix, (w, h))
- imgWarp = imgWarp[pad:imgWarp.shape[0] - pad, pad:imgWarp.shape[1] - pad]
- return imgWarp
-
-
- # 计算两点之间的距离
- def findDis(pts1, pts2):
- return ((pts2[0] - pts1[0]) ** 2 + (pts2[1] - pts1[1]) ** 2) ** 0.5