OpenCV C++相机标定内参异常求助:数值异常原因排查
OpenCV C++相机标定异常问题排查
我最近在做OpenCV C++相机标定时遇到了棘手的问题:标定输出的内参矩阵里Fy和Cx值为0,而且(0,1)位置的倾斜参数还非零——这明显不符合相机的实际参数(正常倾斜参数应该接近0)。我用Matlab做过相同图像的标定,结果是正常的,所以肯定是我的代码存在问题,下面是我的核心代码片段,希望大家帮忙找找问题根源:
// Initialize and reset calibration params void InitCaliberation() { //numBoards = 0; numCornersHor = horizontalCorners; numCornersVer = verticalCorners; numSquares = horizontalCorners * verticalCorners; board_sz = Size(horizontalCorners, verticalCorners); frame_sz = Size(frameWidth, frameHeight); sqSizeInmm = sqSizemm; //25 mm object_points.clear(); image_points.clear(); corners.clear(); finishedCalberation = false; } //Process frames bool CheckCheckerboardFrame(Mat image, bool debug=false) { vector<Point3f> obj; Mat grayImage; cvtColor(image, grayImage, COLOR_BGR2GRAY); for (int j = 0; j < numSquares; j++) obj.push_back(Point3f(sqSizeInmm * j / numCornersHor, sqSizeInmm * j%numCornersHor, 0.0f)); bool found = findChessboardCorners(grayImage, board_sz, corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS); if (found) { //sub-pixel accurate location cornerSubPix(grayImage, corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 30, 0.1)); if(debug) drawChessboardCorners(image, board_sz, corners, found); else { image_points.push_back(corners); object_points.push_back(obj); } } return found; } // Calculate params void FinishCaliberation() { calibrateCamera(object_points, image_points, frame_sz, intrinsic, distCoeffs, R, T); finishedCalberation = true; cout <<"\nIntrinsic: "<< intrinsic.size<<endl; for (int i = 0; i < intrinsic.rows; i++) { for (int j = 0; j < intrinsic.cols; j++) cout << intrinsic.at<float>(i, j) << " "; cout << endl; } cout << "\nDist coeff: " << distCoeffs.size << endl; for (int i = 0; i < distCoeffs.rows; i++) { for (int j = 0; j < distCoeffs.cols; j++) cout << distCoeffs.at<float>(i, j) << " "; cout << endl; } //cout<< }
问题根源分析
我仔细排查后发现两个致命错误:
1. 3D物体点生成逻辑完全错误
在CheckCheckerboardFrame里生成obj向量的循环逻辑搞反了行与列的坐标对应关系,而且点的顺序和findChessboardCorners返回的2D角点顺序不匹配:
for (int j = 0; j < numSquares; j++) obj.push_back(Point3f(sqSizeInmm * j / numCornersHor, sqSizeInmm * j%numCornersHor, 0.0f));
正确的做法是按行优先遍历棋盘格,让3D点的x对应列数×方格尺寸,y对应行数×方格尺寸,保证和2D角点的顺序(从左到右、从上到下)一一对应:
vector<Point3f> obj; for (int row = 0; row < numCornersVer; row++) { for (int col = 0; col < numCornersHor; col++) { obj.push_back(Point3f(col * sqSizeInmm, row * sqSizeInmm, 0.0f)); } }
原代码的错误会导致点对匹配混乱,让OpenCV求解出完全不符合实际的内参矩阵。
2. 数据收集与Debug模式互斥
代码里的分支逻辑是严重失误:
if(debug) drawChessboardCorners(image, board_sz, corners, found); else { image_points.push_back(corners); object_points.push_back(obj); }
这意味着只要开启Debug模式,所有找到棋盘格的帧都不会被加入标定数据集!如果采集过程是带着debug=true调用这个函数的,最后标定用的数据集可能为空或数据量极少,自然会算出Fy、Cx为0的异常结果。正确逻辑应该是:无论是否Debug,先收集数据,Debug仅用于预览画角点:
if (found) { cornerSubPix(grayImage, corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 30, 0.1)); // 优先收集标定数据 image_points.push_back(corners); object_points.push_back(obj); // Debug模式下才绘制角点预览 if(debug) { drawChessboardCorners(image, board_sz, corners, found); } }
其他优化建议
- 初始化内参矩阵:在
InitCaliberation里给intrinsic和distCoeffs设置合理初始值,给标定提供稳定的求解起点:intrinsic = Mat::eye(3, 3, CV_32F); distCoeffs = Mat::zeros(5, 1, CV_32F); - 确保采集足够的标定图像:至少需要10-20张不同角度、位置的棋盘格图像,让棋盘格在图像中占据不同区域(靠近边缘、不同缩放比例),保证标定结果稳定。
- 核对棋盘格参数:确认
numCornersHor和numCornersVer与实际使用的棋盘格角点数量一致(比如常见的8×6角点棋盘格,需设置numCornersHor=8,numCornersVer=6)。
内容的提问来源于stack exchange,提问作者enigma




