双目立体校正

1. 为什么要进行立体校正

在2-View Geometry中,假设我们有C_1C_2两个相机,p_1p_2是空间中的三维点投影到不同相机上的像素点。在本文中,我们称p_1p_2为对应的。 我们可以通过三角化的方法来得到P在三维空间中的坐标。而求解三角化问题需要已知相机内参、外参还有在像素点之间的对应关系。

如何在两张不同的照片中寻找相对应的两个点是有难度的。给定p_1的情况下,在另一张图片上遍历所有像素点搜索p_2在计算上是非常昂贵的。 幸运的是,我们可以用对极约束(epipolar constraint)将搜索问题简化到一维上。根据对极几何的知识,我们可以知道的p_1对应点p_2一定是在另一张图片的极线上面的。

但是因为大部分情况下极线是倾斜的,使用图像块去匹配的时候,往往效率不高,最方便的情况是能将极线与基线平行,即保持水平。因此我们需要进行立体校正Stereo Rectification。下图为通常情况下沿极线搜索对应点的示意图。

2. 立体校正推导

我们可以认为虚构了两个具有以下特性的相机 两个相机是完全相同的,包括内参。 因为焦距相同,所以成像平面是共面的。 因为内参相同,对应点在同一水平线上,加速搜索。 基线必须平行于相机新的x轴,这样可以保证极线平行。 * 极线是平行的,因此极点在无穷远处。

我们通过单应变换来将原先的图片变换到新的虚拟相机的平面上。因此重点是求左右两边相机对应的单应变换。我们接下来就来推导这个单应变换。 一般我们通过旋转矩阵R=R_{cw}和平移向量

t=t_{cw}来将点从世界坐标系变换到相机坐标系,此时P_c = RP_w +t其中P_c是点在相机坐标系中的坐标,P_w是点在世界坐标系中的坐标。我们一般会将透视投影写成以下的形式。

在之后,我们写出左右两个相机的透视投影关系式。

我们假设了两个完全相同的虚拟相机,他们的成相平面是共面的(相同的外参\hat{R}),同时他们具有相同的内参\hat{K}。此时我们将世界中的点P投影到我们假设的两个相机平面上,可以得到如下的关系式。

经过观察,我们可以发现新的相机和旧的相机投影关系式有相同的公共部分

我们通过提取这个公共部分可以推导出以下的关系式。

这就是我们在立体校正过程中,将原图像变换到新的虚拟相机上的单应变换。

此时仅需要求解虚拟相机的内参\hat{K}以及旋转矩阵\hat{R}

3. 立体校正流程

先进行图片的去畸变。

再计算立体校正需要的单应变换,使用双线性插值完成变换。

4. OpenCV中的StereoRectify

4.1. cv::fisheye::stereoRectify

cv::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_64F
D1 第一个相机的畸变参数, Size必须为4x1, 数据类型为CV_32F 或者 CV_64F
K2 第二个相机的内参,Size为3x3, 数据类型为CV_32F 或者 CV_64F
D2 第二个相机的畸变参数, Size必须为4x1, 数据类型为CV_32F 或者 CV_64F
imageSize 做双目标定StereoCalibration() 时用的图片的size, 如ImageSize = cv::Size(640,480)
R 两个相机之间的旋转矩阵, Rrl, 如果内参采用Kalibr标定, 那么这里的R就是Kalibr标定出的T的前3x3
tvec 两个相机之间的平移向量,trl, 即为左目相机在右目相机坐标系中的坐标, 所以,如果两个相机左右摆放, 该向量中x值一般为负数;
R1 第一个相机的修正矩阵, 即从实际去畸变后的左目摆放位姿到经过极线矫正后的左目位姿之间, 有一个旋转量,为R1
R2 第二个相机的修正矩阵, 即从实际去畸变后的右目摆放位姿到经过极线矫正后的右目位姿之间, 有一个旋转量,为R2
P1 修正后第一个相机的投影矩阵; P1包含了R1和K1, 可直接将左目相机坐标系的三维点,投影到像素坐标系中; 要注意会投影到修正后的图像中
P2 修正后第二个相机的投影矩阵; P2包含了R2和K2, 可直接将左目相机坐标系的三维点,投影到像素坐标系中; 要注意会投影到修正后的图像中
Q 视差图转换成深度图的矩阵; 
flags Operation 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博客_stereorectify

cv::stereoRectify gives different results between version 3.4.0 and 3.4.1 · Issue #11131 · opencv/opencv · GitHub

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340