You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV立体相机三角化求特征世界坐标结果异常,求技术指导

问题描述

使用两台相机,以地面贴纸定义世界坐标系:黄色圆形贴纸为世界原点,绿色圆形贴纸位于(500mm, 0, 0),红色圆形贴纸位于(0, 500mm, 0),小黄贴纸位于(151mm, 194mm, 0)。目标是获取小红贴纸的毫米级世界坐标,该贴纸在相机1图像中的像素坐标为(1175, 440),在相机2图像中的像素坐标为(1401, 298)。

采用的流程是依次调用cv::calibrateCamera分别校准两台相机,再调用cv::stereoCalibrate、cv::stereoRectify,最后调用cv::triangulatePoints求解,但得到的齐次坐标为[-0.60382962, -0.0076272688, 0.79707688, 0.00036873418],转换为非齐次坐标[-1637.574, -20.685, 2161.657]后结果明显不合理。

以下是使用的代码:

// 四个真实世界校准点,单位为毫米
// 黄色圆形为原点
// 绿色圆形在x轴上,距离原点500mm
// 红色圆形在y轴上,距离原点500mm
// 第四个校准点是小黄贴纸,位于(151mm, 194mm, 0mm)
// z轴向上,垂直于地面
std::vector<cv::Point3f> _ObjectPointsWorldMM;
_ObjectPointsWorldMM.push_back(cv::Point3f(0.0f, 0.0f, 0.0f));
_ObjectPointsWorldMM.push_back(cv::Point3f(500.0f, 0.0f, 0.0f));
_ObjectPointsWorldMM.push_back(cv::Point3f(0.0f, 500.0f, 0.0f));
_ObjectPointsWorldMM.push_back(cv::Point3f(151.0f, 194.0f, 0.0f));
std::vector<std::vector<cv::Point3f>> ObjectPointsWorldMM;
ObjectPointsWorldMM.push_back(_ObjectPointsWorldMM);

//
// 相机1 calibrateCamera()
//

// 获取相机1图像
cv::Mat mCamera1Image = cv::imread(std::string("Camera1.jpg"), CV_LOAD_IMAGE_COLOR);

// 相机1图像中对应四个校准点的像素位置
std::vector<cv::Point2f> _Camera1ImagePointsPx;
_Camera1ImagePointsPx.push_back(cv::Point2f(791.0f, 220.0f)); // 对应黄色原点贴纸
_Camera1ImagePointsPx.push_back(cv::Point2f(864.0f, 643.0f)); // 对应绿色x=500mm贴纸
_Camera1ImagePointsPx.push_back(cv::Point2f(1277.0f, 113.0f)); // 对应红色y=500mm贴纸
_Camera1ImagePointsPx.push_back(cv::Point2f(1010.0f, 287.0f)); // 对应第四个校准点的小黄贴纸(见上文)
std::vector<std::vector<cv::Point2f>> Camera1ImagePointsPx;
Camera1ImagePointsPx.push_back(_Camera1ImagePointsPx);

// 校准相机1
cv::Mat mCamera1IntrinsicMatrix;
cv::Mat mCamera1DistortionCoefficients;
std::vector<cv::Mat> Camera1RotationVecs;
std::vector<cv::Mat> Camera1TranslationVecs;
const double dCamera1RMSReProjectionError = cv::calibrateCamera(
    ObjectPointsWorldMM, // 输入:四个校准点的世界毫米坐标
    Camera1ImagePointsPx, // 输入:相机1图像中对应校准点的像素位置
    mCamera1Image.size(), // 输入:相机1校准图像的尺寸
    mCamera1IntrinsicMatrix, mCamera1DistortionCoefficients, // 输出:相机内参矩阵和畸变系数
    Camera1RotationVecs, Camera1TranslationVecs // 输出:相机旋转和平移向量
    );

//
// 相机2 calibrateCamera()
//

// 获取相机2图像
cv::Mat mCamera2Image = cv::imread(std::string("Camera2.jpg"), CV_LOAD_IMAGE_COLOR);

// 验证假设
assert((mCamera1Image.size() == mCamera2Image.size()));

// 相机2图像中对应四个校准点的像素位置
std::vector<cv::Point2f> _Camera2ImagePointsPx;
_Camera2ImagePointsPx.push_back(cv::Point2f(899.0f, 439.0f)); // 对应黄色原点贴纸
_Camera2ImagePointsPx.push_back(cv::Point2f(1472.0f, 608.0f)); // 对应绿色x=500mm贴纸
_Camera2ImagePointsPx.push_back(cv::Point2f(1101.0f, 74.0f)); // 对应红色y=500mm贴纸
_Camera2ImagePointsPx.push_back(cv::Point2f(1136.0f, 322.0f)); // 对应第四个校准点的小黄贴纸(见上文)
std::vector<std::vector<cv::Point2f>> Camera2ImagePointsPx;
Camera2ImagePointsPx.push_back(_Camera2ImagePointsPx);

// 校准相机2
cv::Mat mCamera2IntrinsicMatrix;
cv::Mat mCamera2DistortionCoefficients;
std::vector<cv::Mat> Camera2RotationVecs;
std::vector<cv::Mat> Camera2TranslationVecs;
const double dCamera2RMSReProjectionError = cv::calibrateCamera(
    ObjectPointsWorldMM, // 输入:四个校准点的世界毫米坐标
    Camera2ImagePointsPx, // 输入:相机2图像中对应校准点的像素位置
    mCamera2Image.size(), // 输入:相机2校准图像的尺寸
    mCamera2IntrinsicMatrix, mCamera2DistortionCoefficients, // 输出:相机内参矩阵和畸变系数
    Camera2RotationVecs, Camera2TranslationVecs // 输出:相机旋转和平移向量
    );

//
// stereoCalibrate()
//

// 校准立体相机系统
cv::Mat InterCameraRotationMatrix, InterCameraTranslationMatrix;
cv::Mat InterCameraEssentialMatrix, InterCameraFundamentalMatrix;
const double dStereoCalReProjectionError = cv::stereoCalibrate(
    ObjectPointsWorldMM, // 输入:四个校准点的世界毫米坐标
    Camera1ImagePointsPx, // 输入:相机1图像中对应校准点的像素位置
    Camera2ImagePointsPx, // 输入:相机2图像中对应校准点的像素位置
    mCamera1IntrinsicMatrix, mCamera1DistortionCoefficients, // 输入:相机1的内参矩阵和畸变系数
    mCamera2IntrinsicMatrix, mCamera2DistortionCoefficients, // 输入:相机2的内参矩阵和畸变系数
    mCamera1Image.size(), // 输入:每张图像的尺寸
    InterCameraRotationMatrix, InterCameraTranslationMatrix, // 输出:相机间旋转和平移矩阵
    InterCameraEssentialMatrix, InterCameraFundamentalMatrix // 输出:相机间本质矩阵和基础矩阵
    );

//
// stereoRectify()
//

// 计算已校准立体相机的校正变换
cv::Mat Camera1RectificationTransform, Camera2RectificationTransform;
cv::Mat Camera1ProjectionMatrix, Camera2ProjectionMatrix;
cv::Mat DisparityToDepthMappingMatrix;
cv::stereoRectify(
    mCamera1IntrinsicMatrix, mCamera1DistortionCoefficients, // 输入:相机1的内参矩阵和畸变系数
    mCamera2IntrinsicMatrix, mCamera2DistortionCoefficients, // 输入:相机2的内参矩阵和畸变系数
    mCamera1Image.size(), // 输入:每张图像的尺寸
    InterCameraRotationMatrix, InterCameraTranslationMatrix, // 输入:相机间旋转和平移矩阵
    Camera1RectificationTransform, Camera2RectificationTransform, // 输出:每个相机的3x3校正变换矩阵
    Camera1ProjectionMatrix, Camera2ProjectionMatrix, // 输出:每个相机的3x4投影矩阵
    DisparityToDepthMappingMatrix // 输出:4x4视差转深度映射矩阵
    );

//
// triangulatePoints()
//

// 相机1图像中待查找特征的像素位置
std::vector<cv::Point2f> FeaturePointsCamera1;
FeaturePointsCamera1.push_back(cv::Point2f(1175.0f, 440.0f)); // 小红贴纸的像素位置

// 相机2图像中待查找特征的像素位置
std::vector<cv::Point2f> FeaturePointsCamera2;
FeaturePointsCamera2.push_back(cv::Point2f(1401.0f, 298.0f)); // 小红贴纸的像素位置

// 执行三角化以找到特征位置
cv::Mat FeatureLocationHomog;
cv::triangulatePoints(
    Camera1ProjectionMatrix, Camera2ProjectionMatrix, // 输入:每个相机的3x4投影矩阵
    FeaturePointsCamera1, FeaturePointsCamera2, // 输入:每个相机的特征像素点
    FeatureLocationHomog // 输出:齐次坐标下的重建特征位置
    );

// 验证假设
assert((FeatureLocationHomog.cols == static_cast<int>(FeaturePointsCamera1.size())));
问题分析与解决建议

核心问题:校准点数量严重不足

cv::calibrateCamera需要足够多的校准点(通常至少15-20个,分布在画面不同区域)才能准确求解内参和畸变系数。仅用4个点校准单相机,会导致内参矩阵、畸变系数的求解严重不稳定,后续立体校准和三角化的结果自然不可靠。

其他可能的问题点

  • 像素坐标与世界点对应错误:需再次核对每个贴纸在两张图像中的像素坐标是否完全匹配,尤其是绿色/红色贴纸的位置是否搞反。
  • 未校正特征点畸变:cv::triangulatePoints输入的像素点应该是经过畸变校正后的坐标,直接使用原始像素点会引入畸变误差。
  • 立体校准参数设置不当:单独校准相机后,调用stereoCalibrate时未固定内参,可能导致已校准的内参被二次修改。

修正步骤

  1. 增加校准点数量:使用8x6以上的棋盘格,拍摄15-20张不同角度、不同位置的图像,重新进行单相机校准,确保重投影误差低于1像素。
  2. 校正特征点畸变:用cv::undistortPoints对小红贴纸的原始像素坐标进行畸变校正,再输入到triangulatePoints中。
  3. 优化立体校准流程:完成单相机校准后,调用stereoCalibrate时添加CALIB_FIX_INTRINSIC标志,固定已校准的内参。
  4. 验证校准结果:用已知的校准点(比如小黄贴纸)进行三角化测试,看是否能还原其世界坐标,验证流程正确性。

内容的提问来源于stack exchange,提问作者user2062604

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:29:50