简单的单目测距原理

单目测距,即用一个摄像头实现测距。一个简单的原理是利用小孔成像,原理图如下:

单目测距python 单目测距是什么_机器学习


其中单目测距python 单目测距是什么_计算机视觉_02是图片上物体的像素宽度,单目测距python 单目测距是什么_深度学习_03是焦距,单目测距python 单目测距是什么_机器学习_04是物体距离摄像头的实际距离,单目测距python 单目测距是什么_相机标定_05是物体实际宽度。

根据相似三角形原理,可得
单目测距python 单目测距是什么_相机标定_06
则物体距摄像头的距离
单目测距python 单目测距是什么_深度学习_07
其中,单目测距python 单目测距是什么_相机标定_05可以测量得到,单目测距python 单目测距是什么_计算机视觉_02通过图片也可以获得,单目测距python 单目测距是什么_深度学习_03焦距是摄像头的参数,这样就可以计算出物体的距离了。

如果焦距未知,可以先用一张图片计算出焦距,即
单目测距python 单目测距是什么_单目测距python_11
需要先实际测量物体的距离单目测距python 单目测距是什么_机器学习_04,代入即可得到焦距单目测距python 单目测距是什么_深度学习_03

基于Yolov5实现简单的单目测距效果展示

一个简单的demo,识别杯子并测距。杯子宽单目测距python 单目测距是什么_相机标定_14,高单目测距python 单目测距是什么_相机标定_15

首先用单目测距python 单目测距是什么_单目测距python_16距离的图片作为输入,计算出摄像头焦距。

单目测距python 单目测距是什么_计算机视觉_17


| 实际测量距离20cm |

|–|–|

下面是预测结果:

单目测距python 单目测距是什么_单目测距python_18


| 实际距离30cm |

|–|–|

单目测距python 单目测距是什么_单目测距python_19


| 实际距离40cm |

|–|–|

单目测距python 单目测距是什么_单目测距python_20


| 实际距离50cm |

|–|–|

可以看到距离越大,误差越大。一个误差是因为拍摄的角度问题,摄像头和物体没有处于同一条水平线上。后期再改进…

代码

输入: 待检测图片+物体的类别和在图片中的位置
物体的类别和在图片中的位置以txt文本给出,如下所示:


单目测距python 单目测距是什么_计算机视觉_21


 
用到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()