简单的单目测距原理
单目测距,即用一个摄像头实现测距。一个简单的原理是利用小孔成像,原理图如下:
其中是图片上物体的像素宽度,是焦距,是物体距离摄像头的实际距离,是物体实际宽度。
根据相似三角形原理,可得
则物体距摄像头的距离
其中,可以测量得到,通过图片也可以获得,焦距是摄像头的参数,这样就可以计算出物体的距离了。
如果焦距未知,可以先用一张图片计算出焦距,即
需要先实际测量物体的距离,代入即可得到焦距。
基于Yolov5实现简单的单目测距效果展示
一个简单的demo,识别杯子并测距。杯子宽,高。
首先用距离的图片作为输入,计算出摄像头焦距。
| 实际测量距离20cm |
|–|–|
下面是预测结果:
| 实际距离30cm |
|–|–|
| 实际距离40cm |
|–|–|
| 实际距离50cm |
|–|–|
可以看到距离越大,误差越大。一个误差是因为拍摄的角度问题,摄像头和物体没有处于同一条水平线上。后期再改进…
代码
输入: 待检测图片+物体的类别和在图片中的位置
物体的类别和在图片中的位置以txt文本给出,如下所示:
用到yolo里的utils.plots画框的函数,需要在yolo工程文件下运行。
danMu.py
# -*- coding: utf-8 -*-
# @Time : 2022/1/18 15:43
# @Author : Zhang Jun
# @File : danMu.py
# @Software: PyCharm
# 利用三角形相似原理进行简单单目测距
import numpy as np
import cv2
from utils.plots import plot_one_box
from name import names, colors
# 初始物体距离摄像头的距离(单位:厘米)
KNOWN_DISTANCE = 20.0
# 红色杯子属性,宽:15cm,高:9cm
KNOWN_WIDTH = 15
KNOWN_HEIGHT = 9
# 需要预测的图像的路径
IMAGE_PATHS = "data/images2/"
def distance_to_camera(knownWidth, focalLength, perWidth):
return (knownWidth * focalLength) / perWidth
def calculate_focalDistance(img_path):
first_image = cv2.imread(img_path)
txt_path = 'runs/detect/exp/labels/cup_20cm.txt'
with open(txt_path, 'r') as f:
tmp = f.readlines()
tmp = tmp[0].split()
cls = tmp[0]
xyxy = list(map(int, tmp[1:]))
perWidth = xyxy[2] - xyxy[0]
focalLength = (perWidth * KNOWN_DISTANCE) / KNOWN_WIDTH
print('焦距(focalLength )= ', focalLength)
return focalLength
def calculate_Distance(image_path, focalLength_value, cls, xyxy, perWidth):
image = cv2.imread(image_path)
cv2.imshow("image", image)
cv2.waitKey(300)
# 计算得到目标物体到摄像头的距离,单位为cm,
distance = distance_to_camera(KNOWN_WIDTH, focalLength_value, perWidth)
label = f"{names[int(cls)]} {str(round(distance,1)) + 'cm'}"
plot_one_box(xyxy, image, label=label, color=colors[int(cls)], line_thickness=5)
cv2.imshow("image", image)
new_path = "result/" + image_path.split('/')[-1]
cv2.imwrite(new_path, image)
if __name__ == "__main__":
# 相机标定,获得摄像头焦距
img_path = "data/images2/cup_20cm.jpg"
focalLength= calculate_focalDistance(img_path)
# 进行单目测距
for i in range(7):
image_path = IMAGE_PATHS + 'cup_' + str(i+3) + '0cm.jpg'
txt_path = 'runs/detect/exp/labels/cup_'+ str(i+3) +'0cm.txt'
with open(txt_path, 'r') as f:
tmp = f.readlines()
tmp = tmp[0].split()
cls = tmp[0]
xyxy = list(map(int, tmp[1:]))
perWidth = xyxy[2] - xyxy[0]
calculate_Distance(image_path, focalLength, cls, xyxy, perWidth)
cv2.waitKey(1000)
cv2.destroyAllWindows()