OpenCV立体矫正(distort)图像

4
我们有一款ELP 1.0 Megapixel双镜头USB立体相机,正在尝试使用OpenCV 3.1在C++中进行校准。然而,校准的结果完全无法使用,因为调用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张图像提供更好的结果(但仍然无法使用))。
这是校准图案的示例。我们采取了几种不同的方向:

enter image description here

如果我们不调用stereoRectify,这是校准的结果: 输入图像描述 如果我们调用stereoRectify,这是错误的结果(但大多数情况下它会变得更糟): 输入图像描述 提前感谢任何帮助找到问题所在。
2个回答

7

如果有人需要类似的帮助,我总结一下,以下是我为获得最佳效果所做的操作:

在角点检测之前将棋盘图像升级:

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;

我认为你应该发布一个新问题。 - Abc

3

嘿,你尝试过改变函数stereoRectify中参数alpha的值吗?我记得我曾经也获得了类似的结果,将alpha的值更改为0对我有用。请告诉我你使用alpha=-1、alpha=0.5和alpha=0时得到的结果。


谢谢您的建议,它帮了我很多,但我注意到以下几点:
  • 在调用CalibrateCamera时,至少需要传递CALIB_FIX_K3参数,在调用stereoCalibrate时需要传递CALIB_SAME_FOCAL_LENGTH参数,否则结果会完全混乱。
  • 我从calibrateCamera得到的RMS约为0.1,但从stereoCalibrate得到的RMS非常高,约为20。
  • 尽管现在的结果看起来不错,但是remap后图像似乎有点缩放。
  • 我的图像尺寸为320x240,如果在棋盘检测之前手动将图像调整为640x480,然后将结果缩小2.0倍,则结果似乎完美无缺。
- bigmuscle
你可以通过参数alpha控制图像的缩放级别,你确定相机的外参是正确的吗?因为在我的情况下,平移向量偏差了3-4厘米。虽然这并不相关,但我想补充一下,我使用Matlab的CalTech工具箱获得了更好的结果。 - Optimus 1072
1
Alpha参数确实会影响缩放级别。然而,任何不同于alpha=0的值都会使图像边缘波动,并用黑色区域包围它(这是可以理解的),但我得到的最好结果(即没有扭曲、没有扭曲等)是在findChessboardCorners之前将棋盘图像放大,然后缩小检测到的角落,同时alpha=0。 - bigmuscle
好的,我们刚刚使用了70张图像进行了校准。CalibrateCamera返回的RMS约为0.13,但是stereoCalibrate仍然返回高达14的RMS。我删除了那些产生高极线误差的图像,现在RMS约为0.2,这似乎非常好。然而,经过校准的图像的视差图要差得多 - 即使远处的物体也被标记为非常接近。 - bigmuscle
你尝试从视差图计算深度并检查差异了吗? - Optimus 1072
1
我们使用棋盘在多个距离上进行了新的校准,结果非常好!深度是使用从stereoRectify返回的Q矩阵计算的:((1/Q[3][2]) * Q[2][3]) / disparity)。 - bigmuscle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接