本篇文章为单目相机测距的姊妹篇,上一篇文章只考虑了摄像头平行于地面的情况,而本篇文章讨论了摄像头存在角度即悬挂的情况。
其实本来不打算更新这篇文章,因为里面有些地方在我看来存在争议,但是刚巧我做报告要讲这一部分,因此顺便更新一下。存在争议的地方我会在文章中指出。
本篇文章内容参考自:
https://blog.csdn.net/mao_hui_fei/article/details/80822339
正文:
目标:输入一个像素坐标点,然后计算出该像素点实际位置距离摄像头水平距离和垂直距离,即实现了单目摄像头测距
限制:摄像头固定角度,倾斜照射到地面,不考虑镜头畸变(选用无畸变相机)
需要提前测得的量:
H:摄像头高度
Dmin:图像底边距摄像头实际距离
Dmax:图像顶边距摄像头实际距离
β:水平视场角
height:图像像素高度
width:图像像素宽度
输入:
X0:要测量的点的像素X坐标
Y0:要测量的点的像素Y坐标
输出:
X1:该点距摄像头的水平距离
Y1:该点距摄像头的垂直距离
下面是具体计算过程,我是从ppt中直接截的图,如果给读者带来不便,在此表示歉意。
在图中我标明了坐标轴,对于一些距离我也是采用的箭头标注,因为qq截图好像没有其他工具,可能有些混乱。
这里就是有争议的地方,在原文中,作者用了一个D1变量,但是并没有说明D1究竟代表什么,他最终推导出来的结果为B1=(Y1+D1)*tan(β/2)。这也是该篇博客评论区讨论最多的地方,由于联系不上作者,我就采用了评论区中大多数人的看法:D1=Dmax-Y1,但是,问题来了,如果D1=Dmax-Y1,那么Y1+D1=Dmax,那么D1就是一个无用变量 ,这本身就不合理了。而且,该作者还提供了c++代码,在他的源码中(我后面会附上),指定了D1的值,而且我将Dmax-Y1的值输出,发现Dmax-Y1根本不等于D1。
所以我就混乱了,为此我还特意咨询了一位评论区大神,他很坚定的告诉我D1就是Dmax-Y1,引入D1只是起了误导作用,完全没用,但是当我问他关于源码的问题,他说不要看源码。。。
这也是为什么我本来不打算更新这篇文章的原因,因为D1究竟是什么,怎么确定的,我现在仍然不知道。
以下为C++源码:
#include #include #include #include #include #include "windows.h" #include "fstream" //opencv相关#include #include #include #include #include #include #include #include #include //hog特征的c文件//自己编写的文件//#include "UART.h"//#include "findline.h"//#include "DrawImage.h"//绘制图形using namespace std;using namespace cv;using namespace cv::ml;RNG rng(12345);int main(){ float Alpha;//俯仰角Αα:阿尔法 Alpha float Theta;//垂直视场角 西塔 Theta float Beta;//水平视场角Ββ:贝塔 Beta,可以求 //测量出的参数,摄像头俯仰角一变,就会变化 float H1 = 44.0;//摄像头高度 float Dmin = 19.2;//最短距离 float Dmax = 187.1;//最长距离 //float D1 = 29.8;//摄像头坐标与水平面线交点差值 float X0 = 0;//像素坐标值 float Y0 = 0;//像素坐标值 float X1 = 0;//实际距离摄像头水平距离,左负右正 float Y1 = 0;//实际距离摄像头垂直距离 float width = 0; float height = 0; //中间变量 float d0 = 0;//步进视场角,用来计算离摄像头垂直距离,d(theta) float B1 = 0;//每个Y1实际水平距离 //画圈半径,便于显示 int radius = 40; //参数计算 Beta = 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; /*Mat srcImage = imread("1.jpg");*/ long int iiii = 1000; Mat frame; //【1】从摄像头读入视频 VideoCapture capture(0); //该参数为0,则打开计算机自带摄像头,如果为1则打开外置USB摄像头 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, LINE_AA); line(frame, Point(0, height / 2), Point(width, height / 2), Scalar(6, 88, 255), 3, LINE_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(2); waitKey(200); waitKey(200); waitKey(200); d0 = (height - Y0) * Theta / height; //步进小角度 Y1 = H1 * tan(Alpha + d0); //垂直距离 //B1 = (Y1 + D1) * tan(Beta / 2.0); B1 = (Dmax) * tan(Beta / 2.0); X1 = 2.0 * B1 * (X0 - width / 2.0) / width; cout << "距离摄像头水平距离X1为:" << X1 << " 厘米" << endl; cout << "距离摄像头垂直距离Y1为:" << Y1 << " 厘米" << endl; } } waitKey(0);}
第138行被我改为了第139行,输出结果是不一样的,很恼火。
由于测试不是很容易操作,因此后续看情况再决定增不增加测试部分,先这样吧