我有一个立体相机系统,并使用
这些是相机坐标系下以毫米为单位的结果。两个相机距离棋盘大约550毫米,方格尺寸为13毫米。显然,我的结果与我预期的相差甚远(z坐标为负且极大)。
所以我的问题是:
以下是我的代码:
cv::calibrateCamera
和cv::stereoCalibrate
进行正确校准。我的重投影误差
似乎没问题:
- Cam0: 0.401427
- Cam1: 0.388200
- Stereo: 0.399642
cv::stereoRectify
检查我的校准,并使用cv::initUndistortRectifyMap
和cv::remap
转换我的图像。结果如下所示(我注意到的一些奇怪的事情是,当显示矫正后的图像时,通常会在一个或两个图像中出现原始图像的变形副本):
我还使用阈值HSV图像上的cv::findContours
正确估计了标记在像素坐标中的位置。
不幸的是,当我现在尝试cv::triangulatePoints
时,我的结果与我的估计坐标相比非常差,特别是在x方向上。P1 = { 58 (±1), 150 (±1), -90xx (±2xxx) } (bottom)
P2 = { 115 (±1), -20 (±1), -90xx (±2xxx) } (right)
P3 = { 1155 (±6), 575 (±3), 60xxx (±20xxx) } (top-left)
这些是相机坐标系下以毫米为单位的结果。两个相机距离棋盘大约550毫米,方格尺寸为13毫米。显然,我的结果与我预期的相差甚远(z坐标为负且极大)。
所以我的问题是:
- 我非常密切地遵循了stereo_calib.cpp示例,并且至少在视觉上获得了良好的结果(参见重投影误差)。为什么我的三角测量结果如此糟糕?
- 如何将我的结果转换到真实世界的坐标系中,以便我可以定量地检查我的结果?我需要像这里所示手动完成吗?还是OpenCV有相关函数可供使用?
以下是我的代码:
std::vector<std::vector<cv::Point2f> > imagePoints[2];
std::vector<std::vector<cv::Point3f> > objectPoints;
imagePoints[0].resize(s->nrFrames);
imagePoints[1].resize(s->nrFrames);
objectPoints.resize( s->nrFrames );
// [Obtain image points..]
// cv::findChessboardCorners, cv::cornerSubPix
// Calc obj points
for( int i = 0; i < s->nrFrames; i++ )
for( int j = 0; j < s->boardSize.height; j++ )
for( int k = 0; k < s->boardSize.width; k++ )
objectPoints[i].push_back( Point3f( j * s->squareSize, k * s->squareSize, 0 ) );
// Mono calibration
cv::Mat cameraMatrix[2], distCoeffs[2];
cameraMatrix[0] = cv::Mat::eye(3, 3, CV_64F);
cameraMatrix[1] = cv::Mat::eye(3, 3, CV_64F);
std::vector<cv::Mat> tmp0, tmp1;
double err0 = cv::calibrateCamera( objectPoints, imagePoints[0], cv::Size( 656, 492 ),
cameraMatrix[0], distCoeffs[0], tmp0, tmp1,
CV_CALIB_FIX_PRINCIPAL_POINT + CV_CALIB_FIX_ASPECT_RATIO );
std::cout << "Cam0 reproj err: " << err0 << std::endl;
double err1 = cv::calibrateCamera( objectPoints, imagePoints[1], cv::Size( 656, 492 ),
cameraMatrix[1], distCoeffs[1], tmp0, tmp1,
CV_CALIB_FIX_PRINCIPAL_POINT + CV_CALIB_FIX_ASPECT_RATIO );
std::cout << "Cam1 reproj err: " << err1 << std::endl;
// Stereo calibration
cv::Mat R, T, E, F;
double err2 = cv::stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
cameraMatrix[0], distCoeffs[0], cameraMatrix[1], distCoeffs[1],
cv::Size( 656, 492 ), R, T, E, F,
cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 100, 1e-5),
CV_CALIB_USE_INTRINSIC_GUESS + // because of mono calibration
CV_CALIB_SAME_FOCAL_LENGTH +
CV_CALIB_RATIONAL_MODEL +
CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);
std::cout << "Stereo reproj err: " << err2 << std::endl;
// StereoRectify
cv::Mat R0, R1, P0, P1, Q;
Rect validRoi[2];
cv::stereoRectify( cameraMatrix[0], distCoeffs[0],
cameraMatrix[1], distCoeffs[1],
cv::Size( 656, 492 ), R, T, R0, R1, P0, P1, Q,
CALIB_ZERO_DISPARITY, 1, cv::Size( 656, 492 ), &validRoi[0], &validRoi[1]);
// [Track marker..]
// cv::cvtColor, cv::inRange, cv::morphologyEx, cv::findContours, cv::fitEllipse, *calc ellipsoid centers*
// Triangulation
unsigned int N = centers[0].size();
cv::Mat pnts3D;
cv::triangulatePoints( P0, P1, centers[0], centers[1], pnts3D );
cv::Mat t = pnts3D.t();
cv::Mat pnts3DT = cv::Mat( N, 1, CV_32FC4, t.data );
cv::Mat resultPoints;
cv::convertPointsFromHomogeneous( pnts3DT, resultPoints );
最后,resultPoints
应该包含我的相机坐标系下的三维位置的列向量。
编辑:我删除了一些不必要的转换以缩短代码
编辑2:使用@marols建议的三角测量方法得到的结果如下图所示:
P1 = { 111, 47, 526 } (bottom-right)
P2 = { -2, 2, 577 } (left)
P3 = { 64, -46, 616 } (top)