Opencvの両眼補正機能stereoRectifyの詳細解説

公式の機能説明

関数プロトタイプ

void cv::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 = -1,
                            Size        newImageSize = Size(),
                            Rect *      validPixROI1 = 0,
                            Rect *      validPixROI2 = 0 
	) 	

この関数の機能は、修正された両眼画像に必要な変換行列と射影行列を取得し、それを initUndistortRectifyMap 関数に渡して修正された画像から元の画像のピクセル座標へのマッピングを生成し、最後にremap 関数を使用して、修正された両眼画像を取得します。
上記の補正された両眼画像の特徴は、 1. 双眼カメラの撮像面が同一平面であること、 2. 左右の画像が同一のエピポーラ線に平行であり、エピポーラ線上のすべての点の y 座標が一致していることです。は同じ。

パラメータの説明:

入力パラメータ:

cameraMatrix1:左目カメラ固有パラメータ行列
distCoeffs1:左目カメラ歪みパラメータ
cameraMatrix2:右目カメラ固有パラメータ行列
distCoeffs2:右目カメラ歪みパラメータ
imageSize:画像サイズ:
R左目カメラ座標系から右目カメラ座標系への回転変換、つまりR rl R_{rl}Rrl _
T: 左目カメラの座標系から右目カメラの座標系への並進変換、つまりtrl t_{rl}trl _

flags: に設定するとCALIB_ZERO_DISPARITY、この機能は 2 つのカメラの主点を同じに設定します。それ以外の場合、画像は有効な画像領域を最大化するために変換されます。
alpha: フリースケーリングパラメータ。-1 に設定するか、設定しない場合、関数はデフォルトのスケーリングを実行します。それ以外の場合、引数は 0 ~ 1 でなければなりません。0: 最終イメージに有効なピクセルのみが含まれるように、修正されたイメージがズームインおよび変換されます; 1: 元のイメージのすべてのピクセルが表示されるように、イメージがズームアウトおよび変換されます。
newImageSize: 画像解像度を修正しました。デフォルト (0, 0)、元の画像サイズに設定されます。高解像度に設定すると、特に歪みが大きい場合に、元の画像の詳細をより多く保存できます。
validPixROI1: 最大でも有効なピクセルを含む四角形。(左目の画像)
validPixROI2: 最大でも有効なピクセルを含む四角形。(右目用画像)

出力パラメータ:

R1:補正回転行列。第 1 のカメラ座標系の未補正点を第 1 のカメラ補正座標系、つまり R_{左補正座標系}{左未補正座標系}: 補正回転行列 に変換します
R2第 2 のカメラ座標系の未補正点を第 2 のカメラ補正座標系、つまり R_{右補正座標系}{右未補正座標系}: 3x4 左カメラ射影行列 に変換します
P1左修正座標系の点を左修正座標系の画像平面座標系に投影します。
P2: 3x4 右カメラ投影行列。左の修正座標系の点を右の修正座標系の画像平面座標系に投影します。
Q: 4x4 視差深度マップ マトリックス。

水平双眼カメラ (ほとんどの双眼カメラ) の場合、P1、P2、Q は次のように定義されます。

> 2 5 @ 5 4 0 0 0 1 0 ] 、 P2 = [ f 0 cx 2 T x ⋅ f 0 fcy 0 0 0 1 0 ] 、 Q = [ 1 0 0 − cx 1 0 − cy 0 0 0 f 0 0 − 1 T xcx 1 − cx 2 T x ] \begin{aligned} \texttt{P2} &= \begin{bmatrix} f & 0 & cx_2 & T_x \cdot f \\ . 0&f&cy&0\\0&0&1&0 \end{bmatrix },\\\texttt{P2}&=\begin{bmatrix}f&0&cx_2&T_x\cdot f\\0& f&cy&0\\0&0&1&0 \end{bmatrix},\\\texttt{Q}&= \begin{bmatrix}1&0&0&-cx_1 \\0&1&0& -cy\\0&0&0&f\\0&0&-\frac{1}{T_x}&\frac{cx_1-cx_2}{T_x}\end{bmatrix}\end{aligned}P2P2Q= f000f0cx _2サイ1T×f00 = f000f0cx _2サイ1T×f00 = 10000100000T×1c x1サイfT×cx _1c x2

ファンクションコードテスト

EuRoC MAV データセットの双眼カメラ パラメーターを使用して、この関数をテストします。

#include <opencv2/core.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
    
    
    if(argc != 3){
    
    
        cerr << "Error: please input 2 images." << endl;
        return -1;
    }

    // Euroc 数据集双目相机内参
    Mat Kl = ( Mat_<float>(3,3) << 458.654, 0., 367.215, 
                                    0., 457.296, 248.375, 
                                    0., 0., 1.);
    Mat Kr = ( Mat_<float>(3,3) << 457.587, 0., 379.999, 
                                    0., 456.134, 255.238, 
                                    0., 0., 1.);

    Mat Dl = ( Mat_<float>(1,4) << -0.28340811, 0.07395907, 0.00019359, 1.76187114e-05 );       // 畸变参数
    Mat Dr = ( Mat_<float>(1,4) << -0.28368365,  0.07451284, -0.00010473, -3.55590700e-05 );

    // 双目相机相对位姿
    Mat R_rl = ( Mat_<double>(3,3) <<    9.99997256e-01,   2.31206719e-03,   3.76008102e-04, 
	                                    -2.31713572e-03,   9.99898049e-01,   1.40898358e-02,
	                                    -3.43393121e-04,  -1.40906685e-02,   9.99900663e-01 );
    Mat t_rl = ( Mat_<double>(3,1) << -0.11007381,  0.00039912, -0.0008537 );       // 变换的数据类型需要时double的,不然之后执行opencv的函数会报错
    
    Mat img_src_l = imread(argv[1], IMREAD_UNCHANGED);    // 读取左右目图像
    Mat img_src_r = imread(argv[2], IMREAD_UNCHANGED);

    cout << "read images finished. " << endl;

    int width = img_src_l.cols, height = img_src_l.rows;
    Mat Rl, Rr, Pl, Pr, Q;
    Mat undistmap1l, undistmap2l, undistmap1r, undistmap2r;

    stereoRectify( Kl, Dl, Kr, Dr, Size(width, height), R_rl, t_rl, Rl, Rr, Pl, Pr, Q, cv::CALIB_ZERO_DISPARITY, 0 );
    cout << "stereo rectify finished. " << endl;
    initUndistortRectifyMap( Kl, Dl, Rl, Pl, cv::Size(width,height), CV_16SC2, undistmap1l, undistmap2l );
    initUndistortRectifyMap( Kr, Dr, Rr, Pr, cv::Size(width,height), CV_16SC2, undistmap1r, undistmap2r );

    // 将 R_21 和 t_21 转换为两个校正相机坐标系的变换
    cout << "R_rl before rectification: " << endl << R_rl << endl;
    R_rl = Rr * R_rl * Rl.t();
    cout << "R_rl after rectification: " << endl << R_rl << endl;

    cout << "t_rl before rectification: " << endl << t_rl.t() << endl;
    t_rl = Rr * t_rl;
    cout << "t_rl after rectification: " << endl << t_rl.t() << endl;

    // 打印投影矩阵
    cout << "Pl: " << endl << Pl << endl;
    cout << "Pr: " << endl << Pr << endl;

    // 得到校正图像
    Mat img_rtf_l, img_rtf_r;
    remap( img_src_l, img_rtf_l, undistmap1l, undistmap2l, cv::INTER_LINEAR );
    remap( img_src_r, img_rtf_r, undistmap1r, undistmap2r, cv::INTER_LINEAR );

    Mat img_src, img_rtf;
    hconcat(img_src_l, img_src_r, img_src);
    hconcat(img_rtf_l, img_rtf_r, img_rtf);

    cvtColor(img_src, img_src, COLOR_GRAY2BGR);
    cvtColor(img_rtf, img_rtf, COLOR_GRAY2BGR);

    // 绘制平行线
    for(int i = 1, iend = 8; i < iend; i++){
    
    
        int h = height/iend * i;
        line(img_src, Point2i(0, h), Point2i(width*2, h), Scalar(0,0,255));
        line(img_rtf, Point2i(0, h), Point2i(width*2, h), Scalar(0,0,255));
    }

    imshow("image_src", img_src);
    imshow("image_rtf", img_rtf);

    waitKey(0);

    return 0;
}

補正前後の左目カメラから右目カメラへの回転行列:

R_rl before rectification: 
[0.999997256, 0.00231206719, 0.000376008102;
 -0.00231713572, 0.999898049, 0.0140898358;
 -0.000343393121, -0.0140906685, 0.999900663]
R_rl after rectification: 
[0.999999999522207, -2.395107227679009e-12, 6.39099232960692e-12;
 -2.39902704968578e-12, 1.000000000493077, -4.695780200869464e-11;
 6.390414450718838e-12, -4.698293904886045e-11, 1.00000000036201]

補正された回転行列は恒等行列とほぼ等しく、2 台のカメラの撮像面が平行であることを示しています。

補正前後の左カメラから右カメラへの平行移動ベクトル:

t_rl before rectification: 
[-0.11007381, 0.00039912, -0.0008537]
t_rl after rectification: 
[-0.1100778440394819, -1.899859817072477e-16, 5.956099065901966e-16]

補正後の2台のカメラの位置はx方向にずれているだけであり、2台のカメラの撮像面は平行であるため、2台のカメラの撮像面は同一平面となる。

修正された射影行列:

Pl: 
[436.2345881250716, 0, 364.4412384033203, 0;
 0, 436.2345881250716, 256.9516830444336, 0;
 0, 0, 1, 0]
Pr: 
[436.2345881250716, 0, 364.4412384033203, -48.01976295625924;
 0, 436.2345881250716, 256.9516830444336, 0;
 0, 0, 1, 0]

左側の 3x3 行列は内部参照行列です。
パラメータがflagsに設定されているためCALIB_ZERO_DISPARITY、補正された 2 つのカメラの主点は等しく、したがって内部パラメータ行列も等しくなります。

補正前後の両眼画像に x 軸に平行な線を描きます。

ここに画像の説明を挿入
ここに画像の説明を挿入
補正後、左右の画像は同じエピポーラ ラインに平行になり、エピポーラ ライン上のすべての点の y 座標は等しくなります。

おすすめ

転載: blog.csdn.net/weixin_43196818/article/details/129184280