在单目相机中目标检测有着众多的开源算法,但是只是检测到物体在图像中的位置是不够的,实际应用中需要得到的一般是实际物体离相机的距离,这样的话就可以实际应用到项目中了。
实现目标测距思路:
由于单目相机的检测算法一般得到的都是像素坐标,由于缺失一个维度,由二维像素坐标得到三维世界坐标的往往是极为困难的,在这篇博客中我使用了数据分析拟合训练的方法来实现测距,大致分为以下五个阶段:
1. 采集测距参数
2. 选取合适的数据拟合方法
3. 训练模型参数
4. 编写python推理程序
5. 生成测距exe文件
1. 采集测距参数
经过目标检测算法之后我们得到的一般是目标框左上角坐标以及宽高(x1,y1,w,h),当同一目标物纵向距离固定时,其目标框的大小也就固定了,横向距离只会影响左上角的坐标。但是横向距离固定时,纵向距离变大变小,目标框大小和左上角坐标都会变化,但实际上通过对数据分析可知3m到30m内距离相关的主要还是目标框的大小。由于实际需求是要预估行人纵向距离,所以我们这里的参数选为(w,h,dist),如果你的数据均匀充足且想提高准确性参数可以选择为(x,y,w,h,dist)。(需注意此处我训练数据来源1280*720图片!图片不同于我这个最好重新采集数据进行训练,dist为行人纵向距离)。
2. 选取合适的数据拟合方法
由于二维坐标推导三维距离是一个非线性关系且输入输出变量数量较少,这里选取bp神经网络进行参数的拟合。
3. 训练模型参数
这里选用matlab中nftool工具箱训练模型(也可以用python搭建训练网络,但是推荐使用nftool):
1.matlab
打开matlab界面:
在工作区鼠标右键新建X,Y两个变量
将(w,h)复制到变量X中,(dist)复制到变量Y中:
在命令行窗口输入nftool并敲击回车
出现;
点击next,如图选择X,Y变量(注意columns和rows的选择):
然后一路next到如下界面:
点击train,从弹窗中点击第四个按钮查看效果:
四个参数都在0.9以上就是效果不错,如果差距较大点击retrain。
得到满意结果后点击next出现如下界面:
4. 编写推理程序
点击上图第二个按钮,生成如下function
从中找到如下参数(input到output):
复制input到output参数,粘贴到以下编写好的推理程序中并替换同名变量(下面程序中变量是我根据自己数据训练出的权重参数)
from math import exp
import traceback
import time
x1_step1_gain = [0.045455,0.013986]
x1_step1_xoffset = [7.000000,18.000000]
x1_step1_ymin = -1.000000
b1 = [4.385133,-3.340426,2.517180,-1.870918,-0.436608,0.263311,-0.517846,2.531721,2.119660,4.962391];
IW1_1 = [[-2.794530,3.451441],[4.460429,-0.018738],[-3.789230,-2.231532],[0.634038,-4.806527],[-4.192876,0.436725],[2.580258,3.724527],[-5.181921,3.622694],[2.329938,-3.758832],[3.600565,0.679507],[4.569948,-0.008976]]
b2 = 0.726103
LW2_1 = [-0.328219,0.432372,0.498732,-1.453644,1.330706,0.138778,-2.427547,1.007525,-2.388354,-2.285126]
y1_step1_ymin = -1.000000
y1_step1_gain = 0.077821
y1_step1_xoffset = 3.300000
#隐含层神经元数目
HID_LEN = 10
#输入神经元数目
INP_LEN = 2
def BP_CEJU(x):
x1 = [None] * INP_LEN
a = [None] * HID_LEN
a2 = 0
temp = 0
#输入层#
for i in range(0, INP_LEN):
x1[i] = x[i] - x1_step1_xoffset[i]
for i in range(0, INP_LEN):
x1[i] = x1[i] * x1_step1_gain[i] + x1_step1_ymin
#layer
for i in range(0, HID_LEN):
temp = 0
for j in range(0, INP_LEN):
temp = temp + x1[j] * IW1_1[i][j]
a[i] = temp + b1[i]
#传输函数
a[i] = 2.0 / (1.0 + exp(-2.0 * a[i])) - 1.0
#输出层
for i in range(0, HID_LEN):
a2 = a[i] * LW2_1[i] + a2
a2 = a2 + b2
a2 = a2 - y1_step1_ymin
a2 = a2 / y1_step1_gain
a2 = a2 + y1_step1_xoffset
return a2
if __name__ == "__main__":
while True:
try:
while True:
input_w, input_h = int(input('请输入目标框宽度(敲击回车确定):')), int(input('请输入目标框高度:'))
print('纵向距离为:', BP_CEJU([input_w, input_h]))
print('---------------------------------------')
while True:
a_continue = str(input('是否继续?(y/n):'))
if a_continue == 'y':
break
elif a_continue == 'n':
break
else:
print('输入错误,请输入 y 或 n')
continue
if a_continue == 'y':
continue
elif a_continue == 'n':
break
break
except Exception:
print('-------------------------------')
traceback.print_exc()
time.sleep(1)
print('输入有误,请重新输入')
print('-------------------------------')
5. 生成测距exe文件
1.打开终端下载Pyinstaller工具
pip install Pyinstaller
2.生成exe文件
pyinstaller -F 程序名.py
生成exe成功
双击执行,查看效果:
(真值13.8,误差可以接受)
搞定!