点击上方小白学视觉”,选择加"星标"或“置顶
重磅干货,第一时间送达
本文转载自知乎z.defying
https://zhuanlan.zhihu.com/p/63149294
摄像头测距就是计算照片中的目标物体到相机的距离。可以使用相似三角形(triangle similarity)方法实现,或者使用更复杂但更准确的相机模型的内参来实现这个功能。
使用相似三角形计算物体到相机的距离
假设物体的宽度为 W,将其放到离相机距离为 D 的位置,然后对物体进行拍照。在照片上量出物体的像素宽度 P,于是可以得出计算相机焦距 F 的公式:

比如我在相机前 24 英寸距离(D=24 inches)的位置横着放了一张 8.5 x 11 英寸(W=11 inches)的纸,拍照后通过图像处理得出照片上纸的像素宽度 P=248 pixels。所以焦距 F 等于:
此时移动相机离物体更近或者更远,我们可以应用相似三角形得到计算物体到相机的距离的公式:

原理大概就是这样,接下来使用 OpenCV 来实现。
获取目标轮廓
# import the necessary packagesfromimutils import pathsimportnumpy as npimportimutilsimportcv2deffind_marker(image): # convert the image to grayscale, blur it, and detect edgesgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)gray = cv2.GaussianBlur(gray, (5, 5), 0)edged = cv2.Canny(gray, 35, 125) # find the contours in the edged image and keep the largest one; # we'll assume that this is our piece of paper in the imagecnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key = cv2.contourArea) # compute the bounding box of the of the paper region and return itreturncv2.minAreaRect(c)
定义一个 find_marker 函数,接收一个参数 iamge,用来找到要计算距离的物体。这里我们用一张 8.5 x 11 英寸的纸作为目标物体。第一个任务是在图片中找到目标物体。
下面这三行是先将图片转换为灰度图,并进行轻微模糊处理以去除高频噪声,然后进行边缘检测。
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)gray = cv2.GaussianBlur(gray, (5, 5), 0)edged = cv2.Canny(gray, 35, 125)
做了这几步后图片看起来是这样的:
现在已经可以清晰地看到这张纸的边缘,接下来需要做的是找出这张纸的轮廓。
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key = cv2.contourArea)
用 cv2.findContours 函数找到图片中的众多轮廓,然后获取其中面积最大的轮廓,并假设这是目标物体的轮廓。
这种假设只适用于我们这个场景,在实际使用时,在图片中找出目标物体的方法与应用场景有很大关系。
我们这个场景用简单的边缘检测并找出最大的轮廓就可以了。当然为了使程序更具有鲁棒性,也可以用轮廓近似,并剔除不是四个点的轮廓(纸张是一个有四个点的矩形),然后再找出面积最大,具有四个点的轮廓。
注意: 关于这个方法,详情可以查看这篇文章,用于构建一个移动文本扫描工具。
我们也可以根据颜色特征在图片中找到目标物体,因为目标物体和背景的颜色有着很明显的不同。还可以应用关键点检测(keypoint detection),局部不变性描述子(local invariant descriptors)和关键点匹配(keypoint matching)来寻找目标。但是这些方法不在本文的讨论范围内,而且高度依赖具体场景。
我们现在得到目标物体的轮廓了,find_marker 函数最后返回的是包含轮廓 (x, y) 坐标、像素长度和像素宽度的边框,
计算距离
接下来该使用相似三角形计算目标到相机的距离。
defdistance_to_camera(knownWidth, focalLength, perWidth):# compute and return the distance from the maker to the camerareturn (knownWidth * focalLength) / perWidth
distance_to_camera 函数传入目标的实际宽度,计算得到的焦距和图片上目标的像素宽度,就可以通过相似三角形公式计算目标到相机的距离了。
下面是调用 distance_to_camera 函数之前的准备:
# initialize the known distance from the camera to the object, which# in this case is 24 inchesKNOWN_DISTANCE = 24.0# initialize the known object width, which in this case, the piece of# paper is 12 inches wideKNOWN_WIDTH = 11.0# load the furst image that contains an object that is KNOWN TO BE 2 feet# from our camera, then find the paper marker in the image, and initialize# the focal lengthimage = cv2.imread("images/2ft.jpg")marker = find_marker(image)focalLength = (marker[1][0] * KNOWN_DISTANCE) / KNOWN_WIDTH
首先是测量目标物体的宽度,和目标物体到相机的距离,并根据上面介绍的方法计算相机的焦距。其实这些并不是真正的摄像机标定。真正的摄像机标定包括摄像机的内参,相关知识可以可以查看这里。
使用 cv2.imread 函数从磁盘加载图片,然后通过 find_marker 函数得到图片中目标物体的坐标和长宽信息,最后根据相似三角形计算出相机的焦距。
现在有了相机的焦距,就可以计算目标物体到相机的距离了。
# loop over the imagesfor imagePath in sorted(paths.list_images("images")):# load the image, find the marker in the image, then compute the# distance to the marker from the camera image = cv2.imread(imagePath) marker = find_marker(image) inches = distance_to_camera(KNOWN_WIDTH, focalLength, marker[1][0])# draw a bounding box around the image and display it box = cv2.cv.BoxPoints(marker) if imutils.is_cv2() else cv2.boxPoints(marker) box = np.int0(box) cv2.drawContours(image, [box], -1, (0, 255, 0), 2) cv2.putText(image, "%.2fft" % (inches / 12), (image.shape[1] - 200, image.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 255, 0), 3) cv2.imshow("image", image) cv2.waitKey(0)
使用 for 循环遍历每个图片,计算每张图片中目标对象到相机的距离。在结果中,我们根据得到的轮廓信息将方框画了出来,并显示出了距离。下面是得到的几个结果图:

总结
通过这篇文章,我们学会了使用相似三角形计算图片中一个已知物体到相机的距离。
需要先测量出目标物体的实际宽度和目标物体到相机的距离,然后使用图像处理的方法自动计算图片中目标物体的像素宽度,并使用相似三角形计算出相机的焦距。
根据相机的焦距就可以计算图片中的目标物体到相机的距离。
下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。
下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。
下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
继续阅读
阅读原文