文章目录
前言
一、YOLO进行道路车辆检测
二、将SGBM算法与目标检测融合
1、原理
2、双目标定
3、生成深度图
4、计算距离
总结
前面两篇文章已经实现了yolo在ZCU104的部署,下面放一段yolo进行路面车辆检测的实例视频,并且本文还融合了SGBM算法进行双目测距。
先将结果放在最前面,第一个是单纯目标检测,第二个视频是sgbm+目标检测
yolo部署zcu104路面检测视频
双目测距和目标检测融合
本次使用的yolo模型检测数量为3,classification:car、person、cycle。
const string classes[3] = {"car", "person", "cycle"};
yolo部署zcu104路面检测视频
别的博客对SGBM算法有很详细的介绍,这里不进行多余的赘述。
SGBM算法主要是在于生成深度图,通过对生成的深度图进行距离的计算。下面是大致的原理图:
这一步主要是用Matlab进行标定,主要原理是根据棋盘图,得到自己相机的畸变参数等。
//可以得到如下的相机标定参数
Mat cameraMatrixL = (Mat_(3, 3) << 434.0335715, -1.030389934, 320.9811719, 0, 432.718959, 252.0799076, 0., 0., 1.);
//获得的畸变参数
Mat distCoeffL = (Mat_(5, 1) << 0.181490620821227, -0.184498033027207, 0.0000134413407872378, 0.00359850299150688, 0);
Mat cameraMatrixR = (Mat_(3, 3) << 435.9373167, -0.850146934, 359.8381699, 0, 434.3082398, 217.6924413, 0., 0., 1);
Mat distCoeffR = (Mat_(5, 1) << 0.180894133084112, -0.167046294188986, -0.000843721505870367, 0.00374096313749366, 0);
Mat T = (Mat_(3, 1) << -58.16115702, -0.253669531, 0.587363906);//T平移向量
Mat rec = (Mat_(3, 3) << 0.999999151, -0.000876073, -0.000964801,
0.000877393, 0.999998679, 0.001368499,
0.000963601, -0.001369345, 0.999998598); //rec旋转向量,对应matlab
Mat R;//R 旋转矩阵
opencv函数中自带SGBM函数的接口,将SGBM算法所需要的参数传入,可以看到代码如下:
void Depth(vart::Runner* runner, const Mat& frame){
m_l_select = Rect(0, 0, 640, 480);
rgbImageL = frame(m_l_select);
cvtColor(rgbImageL, grayImageL, COLOR_BGR2GRAY);
m_r_select = Rect(640, 0, 640, 480);
rgbImageR = frame(m_r_select);
cvtColor(rgbImageR, grayImageR, COLOR_BGR2GRAY);
/*
经过remap之后,左右相机的图像已经共面并且行对准了
*/
remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);
/*
把校正结果显示出来
*/
Mat rgbRectifyImageL, rgbRectifyImageR;
cvtColor(rectifyImageL, rgbRectifyImageL, COLOR_GRAY2BGR); //伪彩色图
cvtColor(rectifyImageR, rgbRectifyImageR, COLOR_GRAY2BGR);
//========================================================================================================================
//=============================================== 添加视差计算=============================================================
int P1 = 8 * img_channels * blockSize * blockSize;
int P2 = 32 * img_channels * blockSize * blockSize;
sgbm->setP1(P1);
sgbm->setP2(P2);
sgbm->setPreFilterCap(1);
sgbm->setUniquenessRatio(10);
sgbm->setSpeckleRange(100);
sgbm->setSpeckleWindowSize(100);
sgbm->setDisp12MaxDiff(-1);
//sgbm->setNumDisparities(1);
sgbm->setMode(cv::StereoSGBM::MODE_HH);
Mat disp, disp8;
sgbm->compute(rectifyImageL, rectifyImageR, disp);//输入图像必须为灰度图,计算视差并输出disp,rectifyImage本身就是灰度图,所以disp也是单通道灰度图
disp8 = Mat(disp.rows, disp.cols, CV_8UC1); //CV_8UC1,8位无符号单通道,单通道代表灰度图
normalize(disp, disp8, 0, 255, NORM_MINMAX, CV_8UC1); //归一化操作,归到0-255,disp8是归一化输出图像
//如果获得视差图像是CV_16S类型的,这样的视差图的每个像素值由一个16bit表示,其中低位的4位存储的是视差值得小数部分,
//所以真实视差值应该是该值除以16。在进行映射后应该乘以16,以获得毫米级真实位置。
//此时xyz是带坐标的图像?
reprojectImageTo3D(disp, xyz, Q, true);
xyz = xyz * 16;
}
可以看到最后生成的深度图“disp8”以及我们所需要的坐标参数,x,y,z,之后在计算目标框距离时将会使用。
我们知道目标检测绘制框图主要是根据左上坐标或右下坐标进行绘制,那么要想得到目标的距离,我们需要得到目标中心点距离,进而从深度图中计算得到该中心点距离。我们先对一张图片进行测试。下面是双目相机拍摄的原图(这是我的大帅哥同学,想要联系方式可以私信我qaq):
之后对两张图进行分割,立体匹配,最后生成深度图。
而目标检测在对输出数据进行画框时会输出坐标信息,我们只需要根据相应坐标找出对应位置的距离即可。
float xmin = (res[i][0] - res[i][2] / 2.0) * w + 1.0;
float ymin = (res[i][1] - res[i][3] / 2.0) * h + 1.0;
float xmax = (res[i][0] + res[i][2] / 2.0) * w + 1.0;
float ymax = (res[i][1] + res[i][3] / 2.0) * h + 1.0;
float x = (xmin + xmax)/2;
float y = (ymin + ymax)/2;
point3 = xyz.at(x, y);
point3[0];
d = point3[0] * point3[0] + point3[1] * point3[1] + point3[2] * point3[2];
d = sqrt(d); //mm
d = d / 10.0; // cm
可以看一下最后处理图片的效果。
那么实现图片之后,对视频的处理也是类似的,我们已经实现了对视频的目标检测,那么只需要在每一帧进行处理的时候,运用每一帧输出的目标的坐标,去深度图计算对应位置距离即可,实现视频效果如下:
双目测距和目标检测融合
这次主要是实现了软硬件融合开发,以及对传统算法的改进,可以看到上板效果很不错。
但是只要人或物体出现在边缘,距离计算就很不准确,应该是标定的参数有些问题,后续继续规范规范标定参数,效果实现会更好。