# 单目摄像机测距（python+opencv）

20 篇文章 2 订阅

我的论文方向目前是使用单目摄像头实现机器人对人的跟随，首先单目摄像头与kinect等深度摄像头最大的区别是无法有效获取深度信息，那就首先从这方面入手，尝试通过图像获取摄像头与人的距离。

在网上看了几天关于摄像头标定和摄像头焦距等原理的文章，然后通过这篇文章真正启发了我：用python和opencv来测量目标到相机的距离  主要的测距的原理是利用相似三角形计算物体到相机的距离。

##### 一　用相似三角形计算物体或者目标到相机的距离

F = (P x D) / W

F = (248px x 24in) / 11in = 543.45

D’ = (W x F) / P

D’ = (11in x 543.45) / 170 = 35 英寸

D’ = (W x F) / P

在原文中，是通过预先拍照，根据第一张照片算出摄像头的焦距，在根据已知的焦距算出接下来的照片中白纸到摄像机的距离，这样不太直观，而且需要预先拍照，我将源程序改为实时测距，简单来说就是将原来的读入照片变为读摄像头，这样的效果看起来比较直观．源程序如下：

#!usr/bin/python
# -*- coding: utf-8 -*-
#定义编码，中文注释

#import the necessary packages
import numpy as np
import cv2

# 找到目标函数
def find_marker(image):
# convert the image to grayscale, blur it, and detect edges
gray = 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 image
(cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 求最大面积
c = max(cnts, key = cv2.contourArea)

# compute the bounding box of the of the paper region and return it
# cv2.minAreaRect() c代表点集，返回rect[0]是最小外接矩形中心点坐标，
# rect[1][0]是width，rect[1][1]是height，rect[2]是角度
return cv2.minAreaRect(c)

# 距离计算函数
def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth

# initialize the known distance from the camera to the object, which
# in this case is 24 inches
KNOWN_DISTANCE = 24.0

# initialize the known object width, which in this case, the piece of
# paper is 11 inches wide
# A4纸的长和宽(单位:inches)
KNOWN_WIDTH = 11.69
KNOWN_HEIGHT = 8.27

# initialize the list of images that we'll be using
IMAGE_PATHS = ["Picture1.jpg", "Picture2.jpg", "Picture3.jpg"]

# 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 length
#读入第一张图，通过已知距离计算相机焦距
marker = find_marker(image)
focalLength = (marker[1][0] * KNOWN_DISTANCE) / KNOWN_WIDTH

#通过摄像头标定获取的像素焦距
#focalLength = 811.82
print('focalLength = ',focalLength)

#打开摄像头
camera = cv2.VideoCapture(0)

while camera.isOpened():
# get a frame
marker = find_marker(frame)
if marker == 0:
print(marker)
continue
inches = distance_to_camera(KNOWN_WIDTH, focalLength, marker[1][0])

# draw a bounding box around the image and display it
box = np.int0(cv2.cv.BoxPoints(marker))
cv2.drawContours(frame, [box], -1, (0, 255, 0), 2)

# inches 转换为 cm
cv2.putText(frame, "%.2fcm" % (inches *30.48/ 12),
(frame.shape[1] - 200, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX,
2.0, (0, 255, 0), 3)

# show a frame
cv2.imshow("capture", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
camera.release()
cv2.destroyAllWindows()



1. 需要将find_marker函数中

(cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

(_, cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

• image
• contours
• hierarchy

2. 需要将:

box = np.int0(cv2.cv.BoxPoints(marker))

box = cv2.boxPoints(marker)
box = np.int0(box)

1. 程序在运行时在未检测到A4纸时有时候会报错：

Traceback (most recent call last):
File "video_paper_distance.py", line 86, in <module>
marker = find_marker(frame)
File "video_paper_distance.py", line 18, in find_marker
c = max(cnts, key = cv2.contourArea)

ValueError: max() arg is an empty sequence

2. 程序是通过第一张图已知目标到相机的距离来计算摄像头焦距，然后再通过焦距计算接下来目标到摄像头的距离，在这里焦距是一个关键的参数，所以我准备尝试通过对摄像头的标定直接获取相机的像素焦距，我是通过ros的一个包实现了对相机的标定，不过通过相机标定得出的像素焦距计算出来的距离并没有通过第一张图片计算出的焦距计算出来的距离准确，这个具体原因也没有搞明白，可能是我标定的结果不够准确？

3. 在通过摄像头测距时, 得出的距离也是准确且随着摄像头距离桌面远近而线性变化的,但距离偶尔会出现突变,目前也没找到是什么原因造成的.

ros相机标定主要参考的是这篇博客，博主是白巧克力亦唯心，ROS大神：

##### 二　使用相机计算人到相机的距离

在第一部分中我们已经计算出了A4纸距离相机的距离，在具体应用中，我需要计算的是人距离相机的距离，来实现机器人对目标人距离的判断，应用与对目标人的跟随。在这里主要的思路是先通过opencv中的HOG方法检测到人，再根据人的预估身高和摄像头焦距计算人到摄像机的距离。在这里选择身高的原因在于人的身高在不同方向上变化较小，而且我们的摄像头高度是固定的，所以选择身高。

１．首先要使用opencv进行行人检测：

#!usr/bin/python
# -*- coding: utf-8 -*-

# import the necessary packages
from __future__ import print_function
from imutils.object_detection import non_max_suppression
from imutils import paths
import numpy as np
import argparse
import imutils
import cv2

cap = cv2.VideoCapture(0)

# initialize the HOG descriptor/person detector
hog = cv2.HOGDescriptor()
#　使用opencv默认的SVM分类器
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

while(1):
# get a frame

frame = imutils.resize(frame, width=min(400, frame.shape[1]))

# detect people in the image
(rects, weights) = hog.detectMultiScale(frame, winStride=(4, 4),

rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
#　非极大抑制 消除多余的框 找到最佳人体
pick = non_max_suppression(rects, probs=None, overlapThresh=0.65)

# 画出边框
for (xA, yA, xB, yB) in pick:
cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2)

# show a frame
cv2.imshow("capture", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows() 

２．将行人检测与测距代码结合：

while camera.isOpened():
# get a frame

# 如果不能抓取到一帧，说明我们到了视频的结尾
if not grabbed:
break

frame = imutils.resize(frame, width=min(400, frame.shape[1]))

#marker = find_marker(frame)
marker = find_person(frame)

#inches = distance_to_camera(KNOWN_WIDTH, focalLength, marker[1][0])
for (xA, yA, xB, yB) in marker:
cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2)
ya_max = yA
yb_max = yB

pix_person_height = yb_max - ya_max
if pix_person_height == 0:
#pix_person_height = 1
continue
print (pix_person_height)
#print (pix_person_height)
inches = distance_to_camera(KNOW_PERSON_HEIGHT, focalLength, pix_person_height)
print("%.2fcm" % (inches *30.48/ 12))
# draw a bounding box around the image and display it
#box = np.int0(cv2.cv.BoxPoints(marker))
#cv2.drawContours(frame, [box], -1, (0, 255, 0), 2)
cv2.putText(frame, "%.2fcm" % (inches *30.48/ 12),
(frame.shape[1] - 200, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX,
2.0, (0, 255, 0), 3)

# show a frame
cv2.imshow("capture", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break

３．存在的问题：

目前使用HOG检测行人的效果不是很好，会把类似人体形状的物体都框出来，比如实验室的三脚架等物体，受背景干扰较大。程序中存在一个bug就是在没有检测到人时，pix_person_height会为０，这样分母为０时无法计算，在接下来我也要通过３个方面改进，首先要想办法进一步改进人体检测，使用YOLO的方法目前是比较好的，但在CPU下速度较慢。然后要改进的是精度，这里需要主要的是选择摄像头要选择固定焦距的摄像头，自动变焦摄像头焦距会变化，测量的距离也会变。最后就是尽可能完善程序，减少bug。

4 . 将要进行的工作

通过程序可以看到使用单目摄像头检测人到摄像头的距离，其中一个影响较大的因素是对人体的准确检测，如果想要使测量的距离准确（完全准确是不可能的，但要达到可以用于机器人跟随人的功能的程度），那就要尽可能的准确的检测出人，通过我的测试，在准确知道目标人的身高前提下，在离摄像头固定距离上对人拍照，然后手动对人进行画框，标定出目标人的在画面中的高度，通过计算，得到的距离比较准确，其精度完全是可以接受的，所以接下来的工作主要是如何通过程序来准确的框出目标人来获取其在图像中的高度。

05-22
03-13 4110

03-22 946
05-25 4万+
04-20 2512
05-21 4254
01-15 2万+
10-25 3万+
05-23 3万+
01-25 1万+
08-30 2万+
08-08 3205
11-27 547

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