标签: 机器视觉
在机器视觉学习笔记(7)——基于OpenCV的双目摄像机标定中,我们已经计算出描述两个{camera}坐标系关系的矩阵R和T,立体校正主要就是这两个参数在发挥作用。双目摄像机系统主要的任务就是测距,而视差求距离公式是在双目系统处于理想情况下推导的,所以就要将实际的双目系统校正为理想的双目系统。
理想双目系统:两摄像机图像平面平行,光轴和图像平面垂直,极点处于无线远处,此时点 (x0,y0) 对应的级线就是 y=y0
重投影矩阵Q实现了世界坐标系{world}和像素坐标系{pixel}之间的转换。具体如下:
其实三维坐标根据图形相似的原理可以直接写出来,如果能做到此步骤,证明是真的理解了双目测距的原理。
一共有3个过程,分别是计算旋转矩阵和投影矩阵,计算校正查找映射表,校正两幅图像检验效果。
使用OpenCV中stereoRectify
函数即可
void stereoRectify(
InputArray cameraMatrix1,
InputArray distCoeffs1,
InputArray cameraMatrix2,
InputArray distCoeffs2,
Size imageSize,
InputArray R,
InputArray T,
OutputArray R1,//左摄像机旋转矩阵
OutputArray R2,//右摄像机旋转矩阵
OutputArray P1,//左摄像机投影矩阵
OutputArray P2,//右摄像机投影矩阵
OutputArray Q,//重投影矩阵
int flags=CALIB_ZERO_DISPARITY,//主点坐标相同
double alpha,//裁减系数
Size newImageSize=Size(),
Rect* roi1=0, Rect* roi2=0
)
//应用
//Mat R1, R2, P1, P2, Q;
//Rect roi1, roi2;
//stereoRectify(M1, D1, M2, D2, imageSize, R, T, R1, R2, P1, P2, Q,
// CALIB_ZERO_DISPARITY, 0, imageSize, &roi1, &roi2);
校正查找映射表可以将原始图像和校正后的图像上的点一一对应起来
使用OpenCV中initUndistortRectifyMap
函数即可,需要注意的是要针对左右相机分别计算校正查找映射表。
void initUndistortRectifyMap(
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2
)
//应用
//Mat rmap[2][2];
//initUndistortRectifyMap(M1, D1, R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
//initUndistortRectifyMap(M2, D2, R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);
载入两张图片,校正后合并到一张大图上,每隔32个像素画一条横线,可以明显的看到实现了行对准。
Mat img1 = imread("left01.jpg"), img1r;
Mat img2 = imread("right01.jpg"), img2r;
Mat img(imageSize.height, imageSize.width * 2, CV_8UC3);//高度一样,宽度双倍
imshow("rectified", img);
remap(img1, img1r, rmap[0][0], rmap[0][1], CV_INTER_AREA);//左校正
remap(img2, img2r, rmap[1][0], rmap[1][1], CV_INTER_AREA);//右校正
Mat imgPart1 = img( Rect(0, 0, imageSize.width, imageSize.height) );//浅拷贝
Mat imgPart2 = img( Rect(imageSize.width, 0, imageSize.width, imageSize.height) );//浅拷贝
resize(img1r, imgPart1, imgPart1.size(), 0, 0, CV_INTER_AREA);
resize(img2r, imgPart2, imgPart2.size(), 0, 0, CV_INTER_AREA);
//画横线
for( int i = 0; i < img.rows; i += 32 )
line(img, Point(0, i), Point(img.cols, i), Scalar(0, 255, 0), 1, 8);
//显示行对准的图形
Mat smallImg;//由于我的分辨率1:1显示太大,所以缩小显示
resize(img, smallImg, Size(), 0.5, 0.5, CV_INTER_AREA);
imshow("rectified", smallImg);