前言:
本来没想写这篇博客的,毕竟我也是借鉴前辈的方案(),而且当时也没完全搞明白前辈代码中的D0参数到底指的是什么,后来个人原因没有再继续研究就搁置了。
不过因为我之前在前辈博客下有留言,所以经常收到私信问我研究的结果如何,原博客的博主似乎也没有再维护这篇博客了,很多留言都没有得到回复,我就在这里把自己的一些测试结果记录一下供参考。
我想做的测距是,监控场景,计算任意两点间的距离,所以我是这样去测试的:
首先,提供的代码,在visual studio中创建项目,复现一下, 代码如下,由于有几个参数我不清楚什么意思,就按自己的理解先这样处理了,代码有注释:
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <iostream>
#include <io.h>
#include "windows.h"
#include "fstream"
/*opencv相关,有些头文件可能是原作者别的地方需要,所以包含的头文件比较多。
/但我看了一下本段代码很多其实用不到,例如hog相关的,想删可以删掉,留着也无妨。*/
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
#include <opencv2/objdetect/objdetect.hpp> //hog特征的c文件
using namespace std;
using namespace cv;
RNG rng(12345);
int main()
{
float Alpha;//俯仰角Αα:阿尔法 Alpha
float Theta;//垂直视场角 西塔 Theta
float Beta;//水平视场角Ββ:贝塔 Beta
//测量出的参数,摄像头俯仰角一变,就会变化
/*单位:厘米*/
float H1 = 48.0;//摄像头高度
float Dmin = 10.5;//最短距离
float Dmax = 43.7;//最长距离
/*就是这个D0,我一直没明白指的是什么,所以我就没改动,就用原作者的值。
谁如果明白的话,记得留言分享一下哈*/
float D0 = 15.8;//摄像头坐标与水平面线交点差值
float X0 = 0;//像素坐标值
float Y0 = 0;//像素坐标值
float X1 = 0;//实际距离摄像头水平距离,左负右正
float Y1 = 0;//实际距离摄像头垂直距离
float width = 0;
float height = 0;
//中间变量
float d0 = 0;//步进视场角,用来计算离摄像头垂直距离
float B1 = 0;//每个Y1实际水平距离
//画圈半径,便于显示
int radius = 40;
//参数计算
/*这个参数,原作者计算出来atan(40.5 / 22.5),不过这两个常量值我不知道从哪里获得,
所以我就大概估计了我测试场景的水平视场角:60度,
为了和Alpha、Theta保持一致,所以用3.1415926换算一下*/
Beta = 60 * 3.1415926 / 180.0; //atan(40.5 / 22.5);//水平视场角β
Alpha = atan(Dmin / H1);//俯仰角
//H2 = Dmin*H1 / Dmin1; //摄像头理论高度
Theta = atan(Dmax / H1) - Alpha;//垂直视场角
cout << "水平视场角β为: " << Beta*180.0 / 3.1415926 << " 度" << endl;
cout << "垂直视场角θ为: " << Theta*180.0 / 3.1415926 << " 度" << endl;
cout << "俯仰角α为: " << Alpha*180.0 / 3.1415926 << " 度" << endl;
cout << "摄像头实际高度H1为: " << H1 << " 厘米" << endl;
cout << "最短距离Dmin为: " << Dmin << " 厘米" << endl;
cout << "最长距离Dmax为: " << Dmax << " 厘米" << endl;
long int iiii = 1000;
Mat frame;
//从摄像头读入视频
VideoCapture capture(0);
while (1)
{
iiii++;
//隔一段时间保存一张图片
/*原作者是隔一段时间抓一次图来显示,测试时候可能觉得有点奇怪,
不过不影响最终的结果。
所以也可以理解成最终的坐标换算结果其实只跟前面输入的几个参数有关,与画面中的图像信息无关*/
if (iiii >= 100)//通过改变i后面的值来刷新图片界面
{
iiii = 0;
capture >> frame;// //读取当前帧,capture >> frame与capture.read(frame)功能一样,
if (frame.empty())
{
return 0;
}
width = frame.cols;
height = frame.rows;
cout << "图像宽度" << width << endl;
cout << "图像高度" << height << endl;
//视场中央线
line(frame, Point(width / 2, 0), Point(width / 2, height), Scalar(6, 88, 255), 3, CV_AA);
line(frame, Point(0, height / 2), Point(width, height / 2), Scalar(6, 88, 255), 3, CV_AA);
cout << "请输入像素坐标值X0(以回车结束):" << endl;
cin >> X0;
cout << "请输入像素坐标值Y0(以回车结束):" << endl;
cin >> Y0;
cout << "输入的结果为" << endl;
cout << "当前像素坐标值X0为: " << X0 << endl;
cout << "当前像素坐标值Y0为 " << Y0 << endl;
Point center(X0, Y0);
//绘制圆心
circle(frame, center, 3, Scalar(0, 255, 0), -1, 8, 0);
//绘制圆轮廓
circle(frame, center, radius, Scalar(155, 50, 255), 3, 8, 0);
namedWindow("原图", 0);
imshow("原图", frame);
waitKey(0);
d0 = (height - Y0)*Theta / height; //步进小角度
Y1 = H1*tan(Alpha + d0); //垂直距离
B1 = (Y1 + D0)*tan(Beta / 2.0);
X1 = 2.0*B1*(X0 - width / 2.0) / width;
cout << "距离摄像头水平距离X1为: " << X1 << " 厘米" << endl;
cout << "距离摄像头垂直距离Y1为: " << Y1 << " 厘米" << endl;
}
}
waitKey(0);
}
测试搭建的场景:外接了一个usb摄像头,高度48.0cm,相对于桌面,我是这么理解的,因为我这里主要测桌面的目标,所以以桌面为地平面,如果搭建的测试场景中主要关注地面的目标,那就以地面作为地平面。所以这里也限制了实际监控使用场景最好是开阔的平整的地面。
摄像头抓图: 640x480的图上,一张A4纸(297mmx210mm),在画面中4个点的坐标如下所示,用作者提供的换算公式,计算出来,4个点相对于摄像机的坐标,记录如下。
引用原作者的描述:
该程序实现了输入一个像素坐标点,然后计算出该像素点实际位置距离摄像头水平距离和垂直距离,即实现了单目摄像头测距。
测试过程截图: 每次输入一个点的坐标,然后会计算出该点相对于摄像头的水平距离和垂直距离。
**距离换算:**知道每个点相对于摄像机的坐标后,就能用勾股定理计算两点间的距离了。
例如:P1-P3距离:计算后约20.9cm,这和实际A4纸张的宽度210mm误差不大,可接受。
P3-P4距离:29.9cm和A4纸长度297mm误差不大。
P2-P4:
P1-P2:
说明:
以上就是我的测试,但由于我不知道D0参数是什么,另外视场角和距离都是大概估计的,所以也不能评价这个方案的测距准确度如何,有想继续的研究的可以继续研究并分享。