简单的单目测距

简单的单目测距原理

单目测距,即用一个摄像头实现测距。一个简单的原理是利用小孔成像,原理图如下:
在这里插入图片描述
其中 P P P是图片上物体的像素宽度, F F F是焦距, D D D是物体距离摄像头的实际距离, W W W是物体实际宽度。

根据相似三角形原理,可得
D W = F P \frac{D}{W} = \frac{F}{P} WD=PF
则物体距摄像头的距离
D = W ∗ F P D = W*\frac{F}{P} D=WPF
其中, W W W可以测量得到, P P P通过图片也可以获得, F F F焦距是摄像头的参数,这样就可以计算出物体的距离了。

如果焦距未知,可以先用一张图片计算出焦距,即
F = P ∗ D W F = P*\frac{D}{W} F=PWD
需要先实际测量物体的距离 D D D,代入即可得到焦距 F F F

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

一个简单的demo,识别杯子并测距。杯子宽 15 c m 15cm 15cm,高 9 c m 9cm 9cm
首先用 20 c m 20cm 20cm距离的图片作为输入,计算出摄像头焦距。
在这里插入图片描述
| 实际测量距离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()

参考资料

https://blog.csdn.net/weixin_41695564/article/details/80454055

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotJunz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值