MFC+opencv双目测距
操作流程如下:(文末附上最近用opencv4.5.3重新写过的源码)
1.请配置好vs环境(此版本是vs2010+opencv2.4.3所写,具体配置自己查吧。http://blog.csdn.net/garfielder007/article/details/50197181)
2.运行界面如下图(很简陋):
3.第一步点击采集棋盘图按钮,可以在本地电脑获得两个摄像头的数据。
4.有了图片后,点击立体标定便开始处理,获得标定所需的内外参数
5.点测距按钮便可以进行实时测距(不是每次都需要点击采集棋盘格按钮,如果图片了,直接就可以点击立体标定按钮)
6.代码里要说明的两点
a. opencv双目测距Dlg.cpp 里CvSize board_sz = cvSize( 8, 6);//此处8,6为你的棋盘格大小,我这边是8*6的大小,你需要需要按照自己的设置
b. opencv双目测距Dlg.cpp 里 const float squareSize = 2.5f; //此处为你的棋盘格上一个格子的实际大小,拿个尺子量一下,我这边是2.5cm
7.后续我再详细介绍吧!
源码链接:http://pan.baidu.com/s/1kVLXrzp
- 参考资料:
(1)《学习OpenCV》第11章,12章
(2)opencv官网:OpenCV中文Wiki
(3)邹宇华CSDN博客邹宇华的博客_CSDN博客-生活杂烩,小波变换,学术资源领域博主
- 流程:
- 获取立体标定用图像:
- 程序打开左右摄像头,开始实时采集图像
- 放置好棋盘格图像于俩摄像头之前,注意棋盘图像尽量平行于俩摄像头,并进行左右平移移动,或者前后移动,要保证整个棋盘在两个摄像头的共同视场范围之内。
- 程序会对采集到的一对图像进行有效性判断,若判断结果为有效才保存图像。(当左右两幅图像同时能检测到全部角点视为有效)
- 程序在保存完一定对数的图像后会关闭摄像头,立体标定用图像采集完毕。
(2)立体标定:
a.读入保存好的立体标定图像
b.用立体标定函数cvStereoCalibrate(const cvMat *objectPoints,
const CvMat *imagePoints1,
const CvMat *imagePoints2,
const CVMat *npoints,
CvMat *cameraMatrix1,
CvMat *distCoeffs1,
CvMat *cameraMatrix2,
CvMat *distCoeffs2,
CvMat imageSize,
CvMat R,
CvMat T,
CvMat E,
CvMat F,
CvTermCriteria termcrit
int flags=CV_CALIB_FIX_INTRINSIC
);
来获取立体标定参数:
第一个摄像头的内参数矩阵cameraMatrix1,畸变参数distCoeffs1
第二个摄像头的内参数矩阵cameraMatrix2,畸变系数distCoeffs2
联系左右摄像机的旋转矩阵R,和平移向量T
E和F是本征矩阵E和基础矩阵F,矩阵E包含在物理空间中两个摄像机相关的旋转和平移信息;矩阵F除了包含E的信息外还包含了两个摄像机的内参数。由于F包含了这些内参数,因此它可以在像素坐标系中将两台摄像机关联起来。本征矩阵E是单几何意义上的,与成像仪无关,它将左摄像机观测到的点P的物理坐标与右摄像机观测到的相同点的位置关联起来。基础矩阵F则是将一台摄像机平面上的点在图像坐标(像素)上的坐标和另一台摄像机的像平面上的点关联起来。
一旦有了基础矩阵,我们就能够计算极线。
void cvComputeCorrespondEpilines(
const CvMat* points,
int which_image,
const CvMat* fundamental_matrix,
CvMat* correspondent_lines
);
可根据一幅图像中的点列,计算其在另一幅图像中对应的极线。
当两个像平面是完全的行对准时,计算立体视差是最简单的。不幸的是,如前面所讨论的,由于两台摄像机几乎不可能有准确的共面和行对准的成像平面,完美的对准结构在真实的立体系统中几乎不存在的。我们要保证两个摄像机的图像行在校正后是对准的,使得立体匹配(在不同摄像机视场中发现相同点)更可靠。如下函数使用了Bouguet校正方法。
调用立体校正函数cvSteroRectify(
const CvMat *cameraMatrix1,
const CvMat *cameraMatrix2,
const CvMat *distCoeffs1,
const CvMat *distCoeffs2,
const CvMat *imageSize,
const CvMat *R
const CvMat *T
CvMat *Rl
CvMat *Rr
CvMat *pl
CvMat *pr
CvMat *Q
int flags=CV_CALIB_ZERO_DISPARITY
);
对于这个函数来说,输入的是前面立体标定函数得到的第一个摄像头的内参数矩阵cameraMatrix1,畸变参数distCoeffs1,第二个摄像头的内参数矩阵cameraMatrix2,畸变系数distCoeffs2,棋盘图像大小imageSize,联系左右摄像头的旋转矩阵R和平移矩阵T,返回的是校正后的左右摄像机旋转矩阵Rl和Rr,3×4的左右投影方程Pl和pr,还有一个Q矩阵
调用校正映射函数cvInitUndistortRectifyMap(const CvMat *M,
const CvMat *distCoeffs,
const CvMat *Rrect,
const CvMat *Mrect,
CvArr* mapx,
CvArr* mapy);
如果使用cvStereoRectify()去标定立体摄像机,我们可以从CvStereoRectify()直接读出CvInitUndistortRectifyMap()的输入参数。使用左参数校正左图像,右参数校正右图像。
这个函数被调用两次,左右摄像头各调用一次,具体输入参数为上面立体校正函数所得到的参数
对于左摄像头调用,该函数为:
cvInitUndistortRectifyMap(const CvMat *cameraMatrix1
const CvMat *distCoeffs1,
const CvMat *Rl,
const CvMat *pl,
CvArr* mapx1,
CvArr* mapy1);
输入参数是3×3的左摄像机内参数矩阵cameraMatrix1,左摄像机畸变参数distCoeffs1,校正后的左摄像机矩阵Rl, 3×4的左右投影方程Pl,输出为左摄像头的x,y方向的映射关系矩阵mapx1,mapy1
对于右摄像头调用,该函数为:
cvInitUndistortRectifyMap(const CvMat *cameraMatrix2
const CvMat *distCoeffs2,
const CvMat *Rr,
const CvMat *pr,
CvArr* mapx2,
CvArr* mapy2);
输入参数是3×3的左摄像机内参数矩阵cameraMatrix2,左摄像机畸变参数distCoeffs2,校正后的左摄像机矩阵Rr, 3×4的左右投影方程Pr,输出为左摄像头的x,y方向的映射关系矩阵mapx2,mapy2
保存好Q,mapx1,mapy1,mapx2,mapy2这5个参数矩阵,供下面的匹配测距使用
立体视觉对监控系统、导航、机械人技术这些有实时性能要求的系统来说,变得尤为重要。所以要把立体匹配过程设计成能够快速运行才行。因此我们不能为每次调用cvFindStereoCorrespondenceBM()的匹配过程持续分配内部离散缓存。基于块的匹配参数和内部离散缓存保存在一个叫CvStereoBMState的结构体内。
void cvFindStereoCorrespondenceBM(
const CvArr *leftImage,
const CvArr* rightImage,
CvArr *disparityResult,
Cv StereoBMState *BMState
);
此函数输入校正图像对,能输出视差映射。
立体标定、校正及相应的示例代码可以做立体校正(标定和非标定的方法),同时计算出视差图,并且会显示左右校正后的摄像机图片,并且在上面画几条线,以看校正后效果。
匹配测距
a.读入5个参数Q,mapx1,mapy1,mapx2,mapy2这5个参数矩阵
b.打开左右摄像头
c.在双摄像头前放好标记物体,这里为4×3棋盘格的图像
d.获取一对左右图像
e.对左右图像进行校正,利用cvRemp()函数,这里需要用到读入的mapx1,mapy1对左图像进行校正,用读入到的mapx2,mapy2对右图像进行校正
f.显示校正后的左右图像
g.对左右图像进行角点检测,若能成功检测出所有角点,这里是12个角点,则分别计算左右图像上对应角点所对应的实际空间坐标,获得z值,这里可以用下面公式进行计算:

这里Q为读入的Q矩阵,x,y为左图像上角点坐标,d为左图像上角点坐标x的值与它所对应的右图像上的角点坐标xr值的差,那么该对角点所对应的点的三维坐标就是(X/W,Y/W,Z/W),那么Z/W,就是我们关心的距离
把这些点的距离值做一个平均值来作为标记物体离双摄像头的距离
完成后继续获取一帧图像做重复处理
h.若左右图像未能检测到所有角点,就继续获取下一帧图像,
从三维重投影获得深度映射
博主的测距程序中并没有获取视差图,直接获得校正后两张图像上对应物理世界共同点的两个像素点的x偏差,然后利用公式直接计算获得距离。但在《学习Opencv》书中,提到了2个函数,可以用它们来获得深度信息。
cvPerspectiveTransform(
const CvArr *pointsXYD,
CvArr* result3DPoints,
const CvMat *Q
);
void cvReprojectImageTo3D(
CvArr *disparityImage,
CvArr* result3DImage,
CvArr* *Q
);
输出的是大小与输入相同的3通道浮点型图像。
补充:
我的程序在三个工作空间中,“c做图像提供并判断”,“c做立体标定”,“c做实时测距”,该三部分程序是用VS2010+opencv2.4.4来实现的
在第一个文件夹中“c做图像提供并判断”,程序运行后的标定图像保存在”E:\立体标定图像”文件夹中,图像路径保存在“E:\立体标定图路径.txt“文本中
在第二个文件夹中,“c做立体标定“,记得结合具体棋盘标定物体设置

图中squareSize的值,该值为棋盘图像中一小格的大小,这里用的一小格为2.5cm,所以squareSize的值为2.5f
该程序中的输出参数保存路径为”E:\输出参数文本.txt”
最终的实时测距效果图如下:

补充(2022/12):
对于单摄像机的参数标定,常用到的是cvCalibrateCamera2函数。可以用来计算摄像机的内参数(4个参数,fx, fy,cx,cy),畸变参数(5个参数,3个径向k1,k2,k3和2个切向p1,p2)。内参数直接与棋盘所在空间的3D几何相关(即外参数),而畸变参数则与点集如何畸变的2D集合相关。标定的每个视场图像,需要三个旋转参数和三个平移参数。
cvCalibrateCamera2(
CvMat* object_points, //N*3矩阵,N=k(图像上的点数)*M(图像数)
CvMat* image_points, //N*2
int* point_counts, //每个图像点的个数 M*1
CvSize image_size,
CvMat* intrinsic_maxtrix, //内参数
CvMat* distortion_coeffs, //畸变参数k1,k2,p1,p2,k3
CvMat* rotation_vectors = NULL,//M*3
CvMat* translation_vectors = NULL,//M*3
int flags = 0
);
rotation_vectors和translation_vectors是每个放置棋盘格相对于摄像机的位置。对于rotation_vectors,不是3*3的旋转矩阵,代表棋盘格围绕摄像机坐标系统下三维空间的坐标轴的旋转,其中每个向量的长度表示逆时针旋转的角度。每个旋转向量可以通过调用cvRodrigues2()转换为3*3的旋转矩阵。
cvFindExtrinsicCameraParam2(
const CvMat* object_points,
const CvMat* image_points,
const CvMat* intrinsic_matrix,
const CvMat* distortion_coeffs,
const CvMat* rotation_vector,
const CvMat* translation_vector,
);
该函数的变量与上面函数的相应变量相同,只是内参数矩阵和畸变系数是直接提供而非计算出来的。旋转输出的格式是1*3或者3*1的旋转向量。此可以计算出观察物体相对于相机的位置。
标定摄像机通常是想做两件事情,一个是矫正畸变效应,另个是根据获得的图像重构三维场景。对于单个相机的标定而言,这里只谈校正畸变效应。
这可以通过cvUndistort2()一次性完成所有事项,也可以通过一对cvInitUndistortMap()和cvRemap()来更有效率的处理此事。
void cvInitundistortMap(
const CvMat* intrinsic_matrix,
const CvMat* distortion_coeffs,
cvArr* mapx,
cvArr* mapy
);
void cvUndistort2(
const CvArr* src,
CvArr* dst,
const cvMat* intrinsic_matrix,
const cvMat* distortion_coeffs
);
Opencv摄像机模型与标定最后提供的一次完成标定例子,即是通过获取到指定的棋盘格图像来计算摄像机内参数和畸变参数,以来矫正摄像机图像。
最近又重新用Opencv4.5.5实现了一遍,贴上源码,方便查看:
链接:https://pan.baidu.com/s/1XQUD-gvSi22DBLI2Z_kjGQ
提取码:hnyv
更多推荐



所有评论(0)