1、引言
在一年之前小编写了一篇双目测距的博文,引入了大量的童鞋阅读,其博文介绍了详细的相机标定与双目测距过程和代码

https://blog.csdn.net/xiao__run/article/details/78900652

摄像头如前面文章所示,大家可自行购买,小编就是在这家购买

https://shop224405513.taobao.com/search.htm?spm=a1z10.1-c-s.0.0.751b3e49u0Kz6o&search=y

文章评论特别多,由此可见很多读者遇到了很多的问题,有标定不准的,测距距离不准,误差特别大,视差图很差的等各种问题。今天小编再写一篇博文,可能对您有所帮助。

双目测距流程如图

2 标定
首先准备一张标定板,标定板要比较大,不能反光等特性,小博的标定板如图
接下来拍照,大约二十张,就可以了,我重新修改了下拍照程序

#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main()
{/*//双目摄像头,两个usbcv::VideoCapture capl(0);cv::VideoCapture capr(1);int i = 0;cv::Mat cam_left;cv::Mat cam_right;char filename_l[15];char filename_r[15];while(capl.read(cam_left) && capr.read(cam_right)){cv::imshow("cam_left", cam_left);cv::imshow("cam_right", cam_right);char c = cv::waitKey(1);char s[40];if(c==' ') //按空格采集图像{sprintf(filename_l, "/home/lqx/ClionProjects/Calibration/left_img/left%d.jpg",i);imwrite(filename_l, cam_left);sprintf(filename_r, "/home/lqx/ClionProjects/Calibration/right_img/right%d.jpg",i++);imwrite(filename_r, cam_right);//printf(s, "%s%d%s\n", "the ",i++,"th image");cout << "save the "<< i <<"th image\n"<< endl;}if(c=='q' || c=='Q') // 按q退出{break;}}return 0;*///双目摄像头,一个usb//图像大小为640*240,左右各为320*240cv::VideoCapture cap(0);int i = 0;cap.set(CV_CAP_PROP_FRAME_WIDTH,640);//可以cap.set(CV_CAP_PROP_FRAME_HEIGHT,240);cv::Mat cam, cam_left,cam_right;char filename_l[15];char filename_r[15];while(cap.read(cam) ){cam_right=cam(Rect(0,0,320,240));cam_left=cam(Rect(320,0,320,240));cv::imshow("cam_left", cam_left);cv::imshow("cam_right", cam_right);char c = cv::waitKey(1);char s[40];if(c==' ') //按空格采集图像{sprintf(filename_l, "left_img/left%d.jpg",i);imwrite(filename_l, cam_left);sprintf(filename_r, "right_img/right%d.jpg",i++);imwrite(filename_r, cam_right);//printf(s, "%s%d%s\n", "the ",i++,"th image");cout << "save the "<< i <<"th image\n"<< endl;}if(c=='q' || c=='Q') // 按q退出{break;}}return 0;
}

调用上述代码即可得到左右二十多张图像。

接下来我们使用matlab工具箱的标定工具进行标定,也可以使用我上面的博文进行标定。我建议使用matlab标定,结果会更准确点。
标定得到内参和外参如下:

//T 矩阵参数Mat T = Mat::zeros(3,1,CV_64F);T.at<double>(0,0)= 119.5815;T.at<double>(1,0)=-0.5328;T.at<double>(2,0)=-1.7296;//R矩阵// Rodrigues(rec,R);// cout<<R<<endl;Mat R=Mat::eye(3,3,CV_64F);R.at<double>(0,1)= 0.000055498;R.at<double>(0,2)= -0.0184;R.at<double>(1,0)= -0.0001629;R.at<double>(1,2)= 0.0118;R.at<double>(2,0)= 0.0184;R.at<double>(2,1)= -0.0118;//*******************左相机参数**********************************//*******************左相机参数**********************************double fx=202.0386,fy=202.6377;double cx=156.0528,cy=116.9724;Size size(320,240);Rect left(0,0,320,240);Rect right(320,0,320,240);Mat cameraMatrixL=Mat ::eye(3,3,CV_64F);Mat cameraMatrixR=Mat ::eye(3,3,CV_64F);//左边相机内参 cameraMatrixL.at<double>(0,0)=fx;cameraMatrixL.at<double>(0,2)=cx;cameraMatrixL.at<double>(1,1)=fy;cameraMatrixL.at<double>(1,2)=cy;//左边相机畸变Mat distCoffsL=Mat::zeros(5,1,CV_64F);distCoffsL.at<double>(0,0)=-0.0446;distCoffsL.at<double>(1,0)=0.0597;distCoffsL.at<double>(2,0)=0;distCoffsL.at<double>(3,0)=0.;distCoffsL.at<double>(4,0)=0;//*******************右相机参数**********************************cameraMatrixR.at<double>(0,0)=204.1203;cameraMatrixR.at<double>(0,2)=164.9597;cameraMatrixR.at<double>(1,1)=204.5797;cameraMatrixR.at<double>(1,2)=117.9534;Mat distCoffsR=Mat::zeros(5,1,CV_64F);distCoffsR.at<double>(0,0)=-0.0352;distCoffsR.at<double>(1,0)=0.0345;distCoffsR.at<double>(2,0)=0.;distCoffsR.at<double>(3,0)=0;distCoffsR.at<double>(4,0)=0;

3、 BM与SGBM匹配算法
SGBM是一种立体匹配算法,准确度和速度适中,工程中比较常用

oid  stereo_SGBM_match(int, void*)
{int mindisparity = 0;int ndisparities = 64;  int SADWindowSize = 11; cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(mindisparity, ndisparities, SADWindowSize);int P1 = 8 * left_camera_calibration.channels() * SADWindowSize* SADWindowSize;int P2 = 32 * left_camera_calibration.channels() * SADWindowSize* SADWindowSize;sgbm->setP1(P1);sgbm->setP2(P2);sgbm->setPreFilterCap(15);sgbm->setUniquenessRatio(6);sgbm->setSpeckleRange(2);sgbm->setSpeckleWindowSize(100);sgbm->setDisp12MaxDiff(1);//sgbm->setNumDisparities(1);sgbm->setMode(cv::StereoSGBM::MODE_HH);Mat disp,disp8U;sgbm->compute(left_camera_calibration, right_camrera_calibration, disp);disp.convertTo(disp, CV_32F, 1.0 / 16);                //除以16得到真实视差值disp8U = Mat(disp.rows, disp.cols, CV_8UC1);       //显示//normalize(disp, disp8U, 0, 255, NORM_MINMAX, CV_8UC1);disp.convertTo(disp8U,CV_8U,255/(numDisparities*16.));reprojectImageTo3D(disp,xyz,Q);xyz=xyz*16;imshow("disparity",disp8U);}

BM算法之前博文已经提过

oid  stereo_match(int, void*)
{bm->setBlockSize(2*blockSize+5);bm->setROI1(validROIL);bm->setROI2(validROIR);bm->setPreFilterCap(31);bm->setMinDisparity(0);
//最小视差,默认值为0, 可以是负值,int型bm->setNumDisparities(numDisparities * 16 + 16);
//视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型bm->setTextureThreshold(10);bm->setUniquenessRatio(uniquenessRation);bm->setSpeckleWindowSize(100);bm->setSpeckleRange(32);bm->setDisp12MaxDiff(-1);Mat disp,disp8;bm->compute(left_camera_calibration,right_camrera_calibration,disp);disp.convertTo(disp8,CV_8U,255/((numDisparities*16+16)*16.));reprojectImageTo3D(disp,xyz,Q);xyz=xyz*16;imshow("disparity",disp8);}

4、视差图测距
我们先看下效果图,效果蛮不错,误差1m以内大约1cm以内;
可以看到本文的测距方法还是挺准,视差图效果也不错。

最后给出部分工程代码吧,此代码未经我优化,仅仅作为学习使用,其实经优化,在树莓派等设备上也能实时生成视差图测距。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;int framewidth=320;
int frameheight=240;
Size imageSize=Size(framewidth,frameheight);
Mat left_camera,right_camera,left_camera_calibration,right_camrera_calibration,xyz;
Point origin;
Rect selection;
bool selectObject= false;
int blockSize=0,uniquenessRation=0,numDisparities=0;
Rect validROIL,validROIR;Mat view,rview,mapL1,map_L2,mapR1,mapR2,RL,PL,RR,PR,Q;
Ptr<StereoBM>bm=StereoBM::create(16,9);
int mindisparity = 0;
int ndisparities = 64;
int SADWindowSize = 11;
cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(mindisparity, ndisparities, SADWindowSize);
//*******************bm**********************************
//*******************bm**********************************/*
void  stereo_match(int, void*)
{bm->setBlockSize(2*blockSize+5);bm->setROI1(validROIL);bm->setROI2(validROIR);bm->setPreFilterCap(31);bm->setMinDisparity(0);
//最小视差,默认值为0, 可以是负值,int型bm->setNumDisparities(numDisparities * 16 + 16);
//视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型bm->setTextureThreshold(10);bm->setUniquenessRatio(uniquenessRation);bm->setSpeckleWindowSize(100);bm->setSpeckleRange(32);bm->setDisp12MaxDiff(-1);Mat disp,disp8;bm->compute(left_camera_calibration,right_camrera_calibration,disp);disp.convertTo(disp8,CV_8U,255/((numDisparities*16+16)*16.));reprojectImageTo3D(disp,xyz,Q);xyz=xyz*16;imshow("disparity",disp8);}*/
//*******************bm**********************************
//*******************bm**********************************void  stereo_SGBM_match(int, void*)
{int P1 = 8 * left_camera_calibration.channels() * SADWindowSize* SADWindowSize;int P2 = 32 * left_camera_calibration.channels() * SADWindowSize* SADWindowSize;sgbm->setP1(P1);sgbm->setP2(P2);sgbm->setPreFilterCap(15);sgbm->setUniquenessRatio(6);sgbm->setSpeckleRange(2);sgbm->setSpeckleWindowSize(100);sgbm->setDisp12MaxDiff(1);//sgbm->setNumDisparities(1);sgbm->setMode(cv::StereoSGBM::MODE_HH);Mat disp,disp8U;sgbm->compute(left_camera_calibration, right_camrera_calibration, disp);disp.convertTo(disp, CV_32F, 1.0 / 16);                //除以16得到真实视差值disp8U = Mat(disp.rows, disp.cols, CV_8UC1);       //显示//normalize(disp, disp8U, 0, 255, NORM_MINMAX, CV_8UC1);disp.convertTo(disp8U,CV_8U,255/(numDisparities*16.));reprojectImageTo3D(disp,xyz,Q);xyz=xyz*16;imshow("disparity",disp8U);}static void onMouse(int envent,int x,int y,int, void*)
{if (selectObject){selection.x=MIN(x,origin.x);selection.y=MIN(y,origin.y);selection.width=abs(x-origin.x);selection.height=abs(y-origin.y);}switch (envent){case EVENT_LBUTTONDOWN:origin=Point(x,y);selection=Rect(x,y,0,0);selectObject= true;cout<<origin<<"in the world coordinate is: "<<xyz.at<Vec3f>(origin)<<endl;break;case EVENT_LBUTTONUP:selectObject= false;if(selection.width>0 && selection.height>0)break;}
}int main()
{//T 矩阵参数Mat T = Mat::zeros(3,1,CV_64F);T.at<double>(0,0)= 119.5815;T.at<double>(1,0)=-0.5328;T.at<double>(2,0)=-1.7296;//Mat rec = Mat::zeros(3,1,CV_64F);//rec.at<double>(0,0)= 0.0191;// rec.at<double>(1,0)=0.03125;//rec.at<double>(2,0)=-0.00960;//Mat R;//R矩阵// Rodrigues(rec,R);// cout<<R<<endl;Mat R=Mat::eye(3,3,CV_64F);R.at<double>(0,1)= 0.000055498;R.at<double>(0,2)= -0.0184;R.at<double>(1,0)= -0.0001629;R.at<double>(1,2)= 0.0118;R.at<double>(2,0)= 0.0184;R.at<double>(2,1)= -0.0118;//*******************左相机参数**********************************//*******************左相机参数**********************************double fx=202.0386,fy=202.6377;double cx=156.0528,cy=116.9724;Size size(320,240);Rect left(0,0,320,240);Rect right(320,0,320,240);Mat cameraMatrixL=Mat ::eye(3,3,CV_64F);Mat cameraMatrixR=Mat ::eye(3,3,CV_64F);//左边相机内参 cameraMatrixL.at<double>(0,0)=fx;cameraMatrixL.at<double>(0,2)=cx;cameraMatrixL.at<double>(1,1)=fy;cameraMatrixL.at<double>(1,2)=cy;//左边相机畸变Mat distCoffsL=Mat::zeros(5,1,CV_64F);distCoffsL.at<double>(0,0)=-0.0446;distCoffsL.at<double>(1,0)=0.0597;distCoffsL.at<double>(2,0)=0;distCoffsL.at<double>(3,0)=0.;distCoffsL.at<double>(4,0)=0;//*******************做相机参数**********************************//*******************右相机参数**********************************cameraMatrixR.at<double>(0,0)=204.1203;cameraMatrixR.at<double>(0,2)=164.9597;cameraMatrixR.at<double>(1,1)=204.5797;cameraMatrixR.at<double>(1,2)=117.9534;Mat distCoffsR=Mat::zeros(5,1,CV_64F);distCoffsR.at<double>(0,0)=-0.0352;distCoffsR.at<double>(1,0)=0.0345;distCoffsR.at<double>(2,0)=0.;distCoffsR.at<double>(3,0)=0;distCoffsR.at<double>(4,0)=0;//*******************有相机参数**********************************//*******************有相机参数**********************************cout<<"calibration ......."<<endl;//Mat new_cameraMatrix_L=getOptimalNewCameraMatrix(cameraMatrix1, distCoeffs1, image_size, 1, image_size, 0);stereoRectify(cameraMatrixL,distCoffsL,cameraMatrixR,distCoffsR,size,R,T,RL,RR,PL,PR,Q,CALIB_ZERO_DISPARITY,0,size,&validROIL,&validROIR);initUndistortRectifyMap(cameraMatrixL,distCoffsL,RL,PL ,size,CV_16SC2,mapL1,map_L2);initUndistortRectifyMap(cameraMatrixR,distCoffsR,RR,PR ,size,CV_16SC2,mapR1,mapR2);VideoCapture cap (0);Mat rectifyL,rectifyR;cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);cap.set(CV_CAP_PROP_FRAME_HEIGHT, 320);while (cap.isOpened()){Mat frame,grayL,grayR;cap>>frame;left_camera=frame(left);right_camera=frame(right);imshow("left_org",left_camera);imshow("right_org",right_camera);imshow("org",frame);cvtColor(left_camera,grayL,CV_RGB2GRAY);cvtColor(right_camera,grayR,CV_RGB2GRAY);remap(grayL,left_camera_calibration,mapL1,map_L2,INTER_LINEAR);remap(grayR,right_camrera_calibration,mapR1,mapR2,INTER_LINEAR);
/*Mat canvas;double sf;int w ,h;sf=240. /320;w=cvRound(240*sf);h=cvRound(320*sf);canvas.create(h,w*2,CV_8UC3);Mat canvasPart=canvas(Rect(0,0,w,h));resize(left_camera_calibration,canvasPart,canvasPart.size(),0,0,INTER_AREA);Rect vroiL(cvRound(validROIL.x*sf),cvRound(validROIL.y*sf),cvRound(validROIL.width*sf),cvRound(validROIL.height*sf));canvasPart=canvas(Rect(w,0,w,h));resize(right_camrera_calibration,canvasPart,canvasPart.size(),0,0,INTER_AREA);Rect vroiR(cvRound(validROIR.x*sf),cvRound(validROIR.y*sf),cvRound(validROIR.width*sf),cvRound(validROIR.height*sf));for(int i=0;i<canvas.rows;i+=16)line(canvas,Point(0,i),Point(canvas.cols,i),Scalar(0,255,0),1,8);imshow("rectify",canvas);
*/namedWindow("disparity",CV_WINDOW_AUTOSIZE);//        createTrackbar("blocksize:\n","disparity",&blockSize,16,stereo_match);//        createTrackbar("UniquenessRatio:\n","disparity",&uniquenessRation,50,stereo_match);createTrackbar("NumDisparities:\n","disparity",&numDisparities,16,stereo_SGBM_match);setMouseCallback("disparity",onMouse,0);//        stereo_match(0,0);stereo_SGBM_match(0, 0);// Mat output;//sgbm->compute(left_camera_calibration,right_camrera_calibration,output);//imshow("SGBM",output);//cvtColor(left_camera_calibration,left_camera_calibration,CV_GRAY2BGR);// cvtColor(right_camrera_calibration,right_camrera_calibration,CV_GRAY2BGR);imshow("cali_right",right_camrera_calibration);imshow("cali_left",left_camera_calibration);int key=waitKey(1);if (key==27){break;}}return 0;
}

若测得距离为负数,可将T向量的三个值变换个符号即可,OK ,先讲这么多,觉得不错的点个赞哦。

opencv双目测距(BM 与SGBM匹配)相关推荐

  1. 学习OpenCV双目测距原理及常见问题解答

    转自博客:https://blog.csdn.net/angle_cal/article/details/50800775 一. 整体思路和问题转化.   图1. 双摄像头模型俯视图  图1解释了双摄 ...

  2. opencv双目测距资料整理

    2010年: http://download.csdn.net/detail/s620888/2428778 使用OpenCV开发的双目标定与景深测距,包括从两个摄像头中直接提取图片,然后直接进行标定 ...

  3. opencv双目测距实现

    虽然最近注意力已经不可遏制地被神经科学.大脑记忆机制和各种毕业活动吸引过去了,但是还是觉得有必要把这段时间双目视觉方面的进展总结一下.毕竟从上一篇博文发表之后,很多同仁发E-mail来与我讨论,很多原 ...

  4. OpenCV双目标定校正及三维重建的一些经验之谈

    本文主要是对之前做的关于双目的一些工作的经验总结,主要是关键流程及函数的理解,并不全面,但可以给大家提供一些参考.文中一些解释都是基于自己的理解,没有去看函数源码,有错误的地方还请大家指正. 图像标定 ...

  5. OpenCV双目稠密匹配BM算法源代码详细解析

    点击上方"视学算法",选择"星标" 干货第一时间送达 本文由知乎作者David LEE授权转载,不得擅自二次转载.原文链接:https://zhuanlan.z ...

  6. 研电赛项目之双目测距,涉及matlab相机标定,opencv多线程编程,摄像头读取,行人检测、BM立体匹配等等

    1 前言 今年参加了十五届研电赛,前天刚提交了作品,还有几天就答辩了,趁这几天总结一下这一个多月的收获. 本次研电赛作品为汽车行驶防碰撞系统,主要面向大型汽车在低速行驶场景下的防碰撞问题,通过双目相机 ...

  7. 使用opencv做双目测距(相机标定+立体匹配+测距)

    转载自:     http://www.cnblogs.com/daihengchen/p/5492729.html 最近在做双目测距,觉得有必要记录点东西,所以我的第一篇博客就这么诞生啦~ 双目测距 ...

  8. 学习笔记:使用opencv做双目测距(相机标定+立体匹配+测距).

    装载:https://www.cnblogs.com/daihengchen/p/5492729.html 最近在做双目测距,觉得有必要记录点东西,所以我的第一篇博客就这么诞生啦~ 双目测距属于立体视 ...

  9. 【OpenCV】双目测距(双目标定、双目校正和立体匹配)

    本文采用MATLAB标定工具箱和OpenCV3.10来实现双目测距,设备为两个CMOS工业相机和相应的双目云台. 首先感谢CSDN上两位大神前辈邹宇华和scyscyao,虽然是六年前的博客,OpenC ...

最新文章

  1. 漫画:最长公共子序列
  2. 搞容器,必须考虑这五大安全要素
  3. python垃圾回收价格表_深度解析Python垃圾回收机制(超级详细)
  4. 一加6怎么刷android p6,一加6秒速跟进安卓P 教你尝鲜速成开发者
  5. python压缩文件夹为zip_python打包压缩文件夹zip+组装文件夹
  6. Apache 简单设置虚拟主机
  7. error restore 01
  8. 【C】malloc(0)问题
  9. 剑指Offer_16_合并两个排序的链表
  10. C语言实现成语接龙完整版
  11. 智能小家居——智能插座TOP10方案合集
  12. android xml 设置半透明
  13. 美团店铺评价语言处理以及文本分类(logistic regression)
  14. HTML——表格、表格嵌套、表格布局
  15. 五个经典故事讲述人脉经营全攻略
  16. 利用Canvas绘制雷达图
  17. BottomNavigationBar(Materia-Design风格)
  18. idea出现Can not set int field xxx to java.lang.Long 错误
  19. C语言处理excel
  20. 高级篇——数据库调优步骤(性能分析工具)

热门文章

  1. c语言程序0xc0000005解决方案,应用程序无法正常启动0xc0000005如何解决
  2. 2019 年终回顾:不忘初心,负重前行
  3. Atlas:手淘Native容器化框架和思考
  4. python实现视频播放器_对目前的视频播放器不满意?教你用Python做一个视频播放器...
  5. JAVA计算机毕业设计桌游店会员管理系统Mybatis+系统+数据库+调试部署
  6. 地方两会前瞻布局 谋定·经信研究-李刚:各地元宇宙虚实融合
  7. RVIZ 的菜单背景变成黑色
  8. 32位程序和64位程序这些区别你知道吗?
  9. 小微企业如何通过运营公众号提升业绩?
  10. linux查看riak版本,Riak学习(一):Linux Centos 下安装 Riak 服务