双目立体校正 1. 为什么要进行立体校正在2-View Geometry中假设我们有和两个相机、是空间中的三维点投影到不同相机上的像素点。在本文中我们称、为对应的。我们可以通过三角化的方法来得到P在三维空间中的坐标。而求解三角化问题需要已知相机内参、外参还有在像素点之间的对应关系。如何在两张不同的照片中寻找相对应的两个点是有难度的。给定的情况下在另一张图片上遍历所有像素点搜索在计算上是非常昂贵的。幸运的是我们可以用对极约束 (epipolar constraint) 将搜索问题简化到一维上。根据对极几何的知识我们可以知道的对应点一定是在另一张图片的极线上面的。但是因为大部分情况下极线是倾斜的使用图像块去匹配的时候往往效率不高最方便的情况是能将极线与基线平行即保持水平。因此我们需要进行立体校正Stereo Rectification。下图为通常情况下沿极线搜索对应点的示意图。2. 立体校正推导我们可以认为虚构了两个具有以下特性的相机两个相机是完全相同的包括内参。因为焦距相同所以成像平面是共面的。因为内参相同对应点在同一水平线上加速搜索。基线必须平行于相机新的x轴这样可以保证极线平行。对极线是平行的因此极点在无穷远处。我们通过单应变换来将原先的图片变换到新的虚拟相机的平面上。因此重点是求左右两边相机对应的单应变换。我们接下来就来推导这个单应变换。一般我们通过旋转矩阵和平移向量来将点从世界坐标系变换到相机坐标系此时其中是点在相机坐标系中的坐标是点在世界坐标系中的坐标。我们一般会将透视投影写成以下的形式。在之后我们写出左右两个相机的透视投影关系式。我们假设了两个完全相同的虚拟相机他们的成相平面是共面的相同的外参同时他们具有相同的内参。此时我们将世界中的点P投影到我们假设的两个相机平面上可以得到如下的关系式。经过观察我们可以发现新的相机和旧的相机投影关系式有相同的公共部分我们通过提取这个公共部分可以推导出以下的关系式。这就是我们在立体校正过程中将原图像变换到新的虚拟相机上的单应变换。此时仅需要求解虚拟相机的内参以及旋转矩阵3. 立体校正流程先进行图片的去畸变。再计算立体校正需要的单应变换使用双线性插值完成变换。4. OpenCV中的StereoRectify4.1. cv::fisheye::stereoRectifycv::fisheye::StereoRectify()函数主要用于对双目图像做出矫正计算出用于立体矫正的参数具体的使用方法如下void cv::fisheye::stereoRectify (InputArray K1, InputArray D1, InputArray K2, InputArray D2, const Size imageSize, InputArray R, InputArray tvec, OutputArray R1,OutputArray R2, OutputArray P1, OutputArray P2, OutputArray Q, int flags, const Size newImageSize Size(), double balance 0.0, double fov_scale 1.0)其中参数的含义如下K1第一个相机的内参Size为3x3数据类型为 CV_32F 或者 CV_64FD1第一个相机的畸变参数Size必须为4x1数据类型为 CV_32F 或者 CV_64FK2第二个相机的内参Size为3x3数据类型为 CV_32F 或者 CV_64FD2第二个相机的畸变参数Size必须为4x1数据类型为 CV_32F 或者 CV_64FimageSize做双目标定 StereoCalibration() 时用的图片的size如ImageSize cv::Size(640, 480)R两个相机之间的旋转矩阵Rrl如果内参采用Kalibr标定那么这里的R就是Kalibr标定出的T的前3x3tvec两个相机之间的平移向量trl即为左目相机在右目相机坐标系中的坐标所以如果两个相机左右摆放该向量中x值一般为负数R1第一个相机的修正矩阵即从实际去畸变后的左目摆放位姿到经过极线矫正后的左目位姿之间有一个旋转量为R1R2第二个相机的修正矩阵即从实际去畸变后的右目摆放位姿到经过极线矫正后的右目位姿之间有一个旋转量为R2P1修正后第一个相机的投影矩阵P1包含了R1和K1可直接将左目相机坐标系的三维点投影到像素坐标系中要注意会投影到修正后的图像中P2修正后第二个相机的投影矩阵P2包含了R2和K2可直接将左目相机坐标系的三维点投影到像素坐标系中要注意会投影到修正后的图像中Q视差图转换成深度图的矩阵flagsOperation flags that may be zero or fisheye::CALIB_ZERO_DISPARITY. If the flag is set, the function makes the principal points of each camera have the same pixel coordinates in the rectified views. And if the flag is not set, the function may still shift the images in the horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the useful image area.newImageSize修正后图像的新Size。该参数应该与下一步使用initUndistortRectifyMap()时所使用的iMAGE SIZE一致。默认为 (0, 0)表示和 imageSize 一致。当图像的径向畸变较严重时这个值设置的大一点可以更好地保留一个细节(see the stereo_calib.cpp sample in OpenCV samples directory)balance值在[0,1]之间设置这个值可以改变新图像的focal length从最小值到最大值之间变动fov_scale新的focal length original focal length / fov_scale使用示例//设定左目内参和畸变参数: 分别是3x3和4x1 Mat cameraMatrixL (Mat_double(3, 3) 216.0891385028069, 0., 319.19103592168216, 0.,286.915780332698, 237.28788884900933, 0., 0., 1.); Mat distCoeffL (Mat_double(4, 1) 0.16031814840882294, 0.09948097914060017, -0.05647543763319335, 0.02313587059407878); //设定右目内参和畸变参数: 分别是3x3和4x1 Mat cameraMatrixR (Mat_double(3, 3) 216.47145152004367, 0., 319.57751832884156, 0.,287.23866506549973, 240.30796467665027, 0., 0., 1.); Mat distCoeffR (Mat_double(4, 1) 0.15400500709721424, 0.109194432654468, -0.06512886784397008, 0.025788980687450808); //设定两个相机之间的旋转和平移, R为Rrl Mat R (Mat_double(3, 3) 0.9998867545031103, -0.007295111520593036,0.013162808102250304, 0.007472043953567141, 0.9998817186236534,-0.01344311427322331, -0.013063182169384159, 0.013539944981760093,0.9998229959155261); //从平移的数值应该能看出来, t为 左目相机在右目相机的坐标系中的位置 Mat T (Mat_double(3, 1) -0.10139343341319906, -0.0003237508769501881,0.0013986876758678593); Mat Rl, Rr, Pl, Pr, Q; cv::fisheye::stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize,R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY, imageSize); //使用R1,P1输出两个映射矩阵 cv::fisheye::initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize,CV_32FC1, mapLx, mapLy); //使用R2,P2输出两个映射矩阵 cv::fisheye::initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize,V_32FC1, mapRx, mapRy); Mat ImageL, ImageR; ImageL cv::imread(left.png,-1); ImageR cv::imread(right.png, -1); //将双目矫正后的图像放入rectifyImageL2和rectifyImageR2中 Mat rectifyImageL2, rectifyImageR2; cv::remap(ImageL, rectifyImageL2, mapLx, mapLy, cv::INTER_LINEAR); cv::remap(ImageR, rectifyImageR2, mapRx, mapRy, cv::INTER_LINEAR);4.2. cv::stereoRectify#include iostream #include opencv2/core/core.hpp #include opencv2/calib3d/calib3d.hpp int main(int argc, char* argv[]) { cv::Mat K0 (cv::Mat_double(3,3) 1457.572438721727, 0, 1212.945694211622, 0, 1457.522226502963, 1007.32058848921, 0, 0, 1); cv::Mat kk0 cv::Mat_double::zeros(1,5); cv::Mat K1 (cv::Mat_double(3,3) 1460.868570835972, 0, 1215.024068023046, 0, 1460.791367088, 1011.107202932225, 0, 0, 1); cv::Mat kk1 cv::Mat_double::zeros(1,5); cv::Mat R (cv::Mat_double(3,3) 0.9985404059825475, 0.02963547172078553, -0.04515303352041626, -0.03103795276460111, 0.9990471552537432, -0.03068268351343364, 0.04420071389006859, 0.03203935697372317, 0.9985087763742083 ); cv::Mat T (cv::Mat_double(3,1) 0.9995500167379527, 0.0116311595111068, 0.02764923448462666); cv::Size imgsize(2456, 2058); cv::Mat R1; cv::Mat R2; cv::Mat P1; cv::Mat P2; cv::Mat Q; cv::Rect RL; cv::Rect RR; cv::stereoRectify(K0, kk0, K1, kk1, imgsize, R, T, R1, R2, P1, P2, Q, 0, 1.0, imgsize, RL, RR); std::cout Results with OpenCV CV_VERSION_MAJOR . CV_VERSION_MINOR . CV_VERSION_REVISION std::endl; std::cout R1 R1 std::endl; std::cout R2 R1 std::endl; std::cout P1 R1 std::endl; std::cout P2 R1 std::endl; std::cout Q Q std::endl; std::cout RL RL std::endl; std::cout RR RR std::endl; return 0; }双目视觉之立体校正的过程是什么 - 知乎正确使用StereoRectify_三轮车的视觉进阶_的博客-CSDN博客_stereorectifycv::stereoRectify gives different results between version 3.4.0 and 3.4.1 · Issue #11131 · opencv/opencv · GitHub