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

C++版cv::calibrateCamera校准失败,Python版正常求排查

相机校准C++与Python结果差异问题排查

在Windows环境下开展相机校准工作时,出现以下异常:

  • 使用vcpkg安装的OpenCV 4.8.0#15(C++版)校准,得到的内参矩阵K与相机实际参数完全无关,RMSE(均方根误差)约为200.0;
  • 使用相同图片、基于opencv-python 4.8.0.72的Python版校准则运行正常,RMSE仅为0.05。

已更换电脑、重装所有依赖包并重新构建项目,但问题依旧。此前有相机校准经验,这是首次在多台电脑上遇到该问题,需排查问题根源是库本身的问题,还是代码语法或操作不当导致。


C++校准代码

// 棋盘格校准参数与准则
const int rows=11;
const int columns=8;
const cv::Size tableSize(rows, columns);
const float world_scaling=15.0;
const cv::Size conv_size(11,11);
const cv::TermCriteria criteria(cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER, 100, 1.0E-5);

// 生成世界坐标系下的棋盘格点
std::vector<cv::Point3f> objp(rows * columns);
for (size_t i = 0; i < rows; ++i) {
    for (size_t j = 0; j < columns; ++j) {
        objp[i * columns + j] = cv::Point3f(j * world_scaling, i * world_scaling, 0.0);
    }
}

// 读取图像
auto images = readImg("../data/cam1");
const int width = images[0].cols;
const int height = images[0].rows;

std::vector<std::vector<cv::Point3f>> objPoints1;
std::vector<std::vector<cv::Point2f>> imgPoints1;
objPoints1.clear();
imgPoints1.clear();

// 检测每张图像的棋盘格角点
for (size_t i = 0; i < images.size(); i++)
{
    std::vector<cv::Point2f> corners1;
    bool found_1 = cv::findChessboardCorners(images[i], tableSize, corners1, cv::CALIB_CB_ADAPTIVE_THRESH);
    if (found_1) {
        cv::cornerSubPix(images[i], corners1, conv_size, cv::Size(-1, -1), criteria);
        objPoints1.push_back(objp);
        imgPoints1.push_back(corners1);
    }
}

// 单相机校准
cv::Mat cameraMatrix1;
cv::Mat distCoeffs1;
std::vector<cv::Mat> rvecs1, tvecs1;
const cv::Size size(width, height);

double ret = cv::calibrateCamera(objPoints1, imgPoints1, size, cameraMatrix1, distCoeffs1, rvecs1, tvecs1);

Python校准代码

rows=11
columns=8
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.00001)
world_scaling = 15.0

# 生成世界坐标系下的棋盘格点
objp = np.zeros((rows*columns,3), np.float32)
objp[:,:2] = np.mgrid[0:rows,0:columns].T.reshape(-1,2)
objp = world_scaling * objp

# 存储图像点与世界点
imgpoints_l = [] # 图像平面的2D点
objpoints_l = [] # 真实世界的3D点

# 遍历灰度图像检测角点
for img_l in list_images:
    found_l, corners_l = cv.findChessboardCorners(img_l, (rows, columns), flags=cv.CALIB_CB_ADAPTIVE_THRESH)
    if found_l:
       corners_l = cv.cornerSubPix(img_l, corners_l, (11,11), (-1, -1), criteria)
       objpoints_l.append(objp)
       imgpoints_l.append(corners_l)

# 单相机校准
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints_l, imgpoints_l, (width,height), None, None)

可能的问题根源排查

1. 世界点生成顺序不匹配

对比C++和Python的世界点生成逻辑:

  • C++中,循环顺序为行优先(i遍历rows,j遍历columns),点的索引为i * columns + j,对应坐标(j*scaling, i*scaling);
  • Python中,np.mgrid[0:rows,0:columns].T.reshape(-1,2)的实际顺序是列优先:mgrid生成的是(行索引, 列索引)的网格,转置后变为(列索引, 行索引),reshape后点的顺序是(0,0), (0,1), ..., (0,columns-1), (1,0), ...,这与C++的点顺序完全相反。

世界点与图像角点的对应顺序错误会直接导致校准结果完全失效,RMSE大幅偏高,这是最可能的原因。

2. 图像读取格式不一致

Python代码明确使用灰度图进行角点检测,但C的readImg函数是否正确将图像转为单通道灰度图?如果C读取的是彩色图(3通道),即使findChessboardCorners能处理,也可能因通道亮度差异导致角点检测的精度或顺序异常。

3. 世界点容器初始化问题

C++代码中需确认objp是否正确创建了大小为rows * columns的std::vector<cv::Point3f>。如果容器大小错误,会导致世界点数据混乱,进而影响校准结果。

4. 校准函数隐式参数差异

虽然C++和Python的calibrateCamera默认参数基本一致,但需确认是否存在隐式的校准标志位差异。例如,是否在某一端默认启用了额外的校准约束(如固定焦距),但此情况导致RMSE200的概率较低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:25:58