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
相关产品推荐
相关产品推荐

