【问题标题】:OpenCV stereoRectify distorts imageOpenCV stereoRectify 扭曲图像
【发布时间】:2017-02-12 14:50:00
【问题描述】:

我们有一个 ELP 1.0 百万像素双镜头 USB 立体相机,我们正在尝试使用 C++ 中的 OpenCV 3.1 对其进行校准。但是,校准的结果完全无法使用,因为调用 stereoRectify 会完全扭曲图像。这就是我们所做的:

在两个相机中找到校准(棋盘)图案,棋盘大小为 5x7,无论拍摄多少张图像,结果几乎相同

findChessboardCorners(img[k], boardSize, corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE)
cornerSubPix(img[k], corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01));

所有棋盘都被正确检测到并使用验证

drawChessboardCorners(img[k], boardSize, corners, bFound);

然后我们分别标定每个摄像头(不过这一步似乎对立体标定不重要),但是我们可以用它来分别验证每个摄像头

calibrateCamera(objectPoints, imagePoints[k], Size(320, 240), cameraMatrix[k], distCoeffs[k], rvecs, tvecs, 0)

然后我们进行立体校准

stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1], cameraMatrix[0], distCoeffs[0], cameraMatrix[1], distCoeffs[1],
    Size(320, 240), R, T, E, F, CALIB_USE_INTRINSIC_GUESS);

计算整流变换

stereoRectify(cameraMatrix[0], distCoeffs[0], cameraMatrix[1], distCoeffs[1], Size(320, 240), R, T, R1, R2, P1, P2, Q,
    CALIB_ZERO_DISPARITY, 1, Size(320, 240), &validRoI[0], &validRoI[1]);

为重映射初始化地图

Mat rmap[2][2];
initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, Size(FRAME_WIDTH, FRAME_HEIGHT), CV_16SC2, rmap[0][0], rmap[0][1]);
initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, Size(FRAME_WIDTH, FRAME_HEIGHT), CV_16SC2, rmap[1][0], rmap[1][1]);
...
remap(img, rimg, rmap[k][0], rmap[k][1], INTER_LINEAR);
imshow("Canvas", rimg);

结果是完全扭曲的图像。正如我在开始时所说,所有校准/棋盘模式都被正确检测到,如果我们不调用 stereoRectify 函数,则未失真的图像(重新映射后)看起来很完美。如果我们调用 stereoRectify 函数,问题就来了。

我们错过了什么吗? 校准图像的数量似乎没有任何影响(有时拍摄 2 张​​图像比 10 张图像提供更好的结果(但仍然不可用))

这是校准模式的示例。我们采取了几种不同的方向:

如果我们不调用 stereoRectify,这是校准的结果:

如果我们调用 stereoRectify,这是错误的结果(但大多数情况下它会变得更糟):

在此先感谢您对可能出现问题的任何帮助。

【问题讨论】:

    标签: c++ opencv camera-calibration


    【解决方案1】:

    您是否尝试过在函数 stereoRectify 中更改参数 alpha 的值。我记得有一次我也获得了这样的结果并将 alpha 的值更改为 0 对我有用。 请告诉我您使用 alpha = -1、alpha=0.5 和 alpha =0 获得的结果

    【讨论】:

    • 感谢您的建议。它有很大帮助,但我注意到以下几点: - 至少需要将 CALIB_FIX_K3 传递到 CalibrateCamera 和 CALIB_SAME_FOCAL_LENGTH 信息 stereoCalibrate 否则结果完全一团糟 - 我从 calibrateCamera 得到 RMS 大约 0.1,但从 stereoCalibrate 得到非常高的 RMS 大约 20 - 尽管结果现在看起来不错,重新映射后图像似乎放大了一点 - 我有 320x240,如果我在棋盘检测之前手动将图像大小调整为 640x480,然后将结果缩小 2.0,结果似乎很完美
    • 您可以使用参数 alpha 控制图像的缩放级别,您是否正确掌握了相机的外部参数?因为在我的情况下,平移向量偏离了 3-4 厘米。虽然它不相关,但我要补充一点,我使用 CalTech ToolBox for Matlab 获得了更好的结果。
    • Alpha 参数确实会影响缩放级别。但是,与 alpha=0 不同的任何东西都会使图像边缘波动并用黑色区域包围它(这是可以理解的),但我得到的最好的结果(= 没有失真,没有扭曲等)是使用 alpha=0 并在之前升级棋盘图像findChessboardCorners 然后缩小检测到的角。
    • 好的,我们刚刚对 70 张图像进行了校准。 CalibrateCamera 返回的 RMS 约为 0.13,但 stereoCalibrate 仍返回 14 左右的高 RMS。我删除了那些给出高对极误差的图像,现在 RMS 约为 0.2,这似乎非常好。但是,对于校准后的图像,视差图要差得多——即使远处的物体也被标记为非常近。
    • 我们用棋盘在几个距离上进行了新的校准,结果非常好!使用从 stereoRectify 返回的 Q 矩阵计算深度: ((1/Q[3][2]) * Q[2][3]) / disparity)
    【解决方案2】:

    只是总结一下是否有人需要类似的帮助,这就是我为获得最佳外观结果所做的工作:

    在角点检测之前放大棋盘图像:

    Mat resized;
    resize(img[k], resized, Size(FRAME_WIDTH * 2, FRAME_HEIGHT * 2), 0.0, 0.0, INTER_LINEAR);
     findChessboardCorners(resized, boardSize, corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE
    

    缩小检测到的角落:

    for (int i = 0; i < corners.size(); ++i) {
        corners[i].x /= 2.0;
        corners[i].y /= 2.0;
    }
    

    分别校准每个相机:

    double rms = calibrateCamera(objectPoints, imagePoints[k], Size(FRAME_WIDTH, FRAME_HEIGHT), cameraMatrix[k], distCoeffs[k], rvecs, tvecs,
        CALIB_FIX_PRINCIPAL_POINT | CALIB_FIX_ASPECT_RATIO | CALIB_ZERO_TANGENT_DIST | CALIB_RATIONAL_MODEL | CALIB_FIX_K3 | CALIB_FIX_K4 | CALIB_FIX_K5);
    

    校准立体相机:

    stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1], cameraMatrix[0], distCoeffs[0], cameraMatrix[1], distCoeffs[1],
        Size(FRAME_WIDTH, FRAME_HEIGHT), R, T, E, F, 
        CALIB_FIX_INTRINSIC | CALIB_SAME_FOCAL_LENGTH,
        TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0));
    

    计算修正(alpha = 0.0):

    stereoRectify(cameraMatrix[0], distCoeffs[0], cameraMatrix[1], distCoeffs[1], Size(FRAME_WIDTH, FRAME_HEIGHT), 
        R, T, R1, R2, P1, P2, Q,
        CALIB_ZERO_DISPARITY, 0.0, Size(FRAME_WIDTH, FRAME_HEIGHT), &validRoI[0], &validRoI[1]);
    

    这些是校准结果矩阵

    内在函数:

    M1: !!opencv-matrix
       rows: 3
       cols: 3
       dt: d
       data: [ 2.6187262304487734e+02, 0., 1.5950000000000000e+02, 0.,
           2.6187262304487734e+02, 1.1950000000000000e+02, 0., 0., 1. ]
    D1: !!opencv-matrix
       rows: 1
       cols: 5
       dt: d
       data: [ -4.6768074176991381e-01, 2.0221327568191746e-01, 0., 0., 0. ]
    M2: !!opencv-matrix
       rows: 3
       cols: 3
       dt: d
       data: [ 2.6400975025525213e+02, 0., 1.5950000000000000e+02, 0.,
           2.6400975025525213e+02, 1.1950000000000000e+02, 0., 0., 1. ]
    D2: !!opencv-matrix
       rows: 1
       cols: 5
       dt: d
       data: [ -4.5713211677198845e-01, 2.8855737500717565e-01, 0., 0., 0. ]
    

    外在因素:

    R: !!opencv-matrix
       rows: 3
       cols: 3
       dt: d
       data: [ 9.9963073433190641e-01, 4.6310793035473068e-04,
           2.7169477545556639e-02, -6.9475632716349024e-04,
           9.9996348636555088e-01, 8.5172324905818230e-03,
           -2.7164541091274301e-02, -8.5329635354663789e-03,
           9.9959455592785362e-01 ]
    T: !!opencv-matrix
       rows: 3
       cols: 1
       dt: d
       data: [ -6.1830090720273198e+01, 1.6774590574449604e+00,
           1.8118983433925613e+00 ]
    

    我的另一个问题是对变量初始化是否有任何特殊要求或者这是否足够?

    Mat cameraMatrix[2] = { Mat::eye(3, 3, CV_64F), Mat::eye(3, 3, CV_64F) };
    Mat distCoeffs[2], R, T, E, F, R1, R2, P1, P2, Q;
    

    【讨论】:

    • 我认为你应该发布一个新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 2015-03-18
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    相关资源
    最近更新 更多