You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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里给intrinsicdistCoeffs设置合理初始值,给标定提供稳定的求解起点:
    intrinsic = Mat::eye(3, 3, CV_32F);
    distCoeffs = Mat::zeros(5, 1, CV_32F);
    
  • 确保采集足够的标定图像:至少需要10-20张不同角度、位置的棋盘格图像,让棋盘格在图像中占据不同区域(靠近边缘、不同缩放比例),保证标定结果稳定。
  • 核对棋盘格参数:确认numCornersHornumCornersVer与实际使用的棋盘格角点数量一致(比如常见的8×6角点棋盘格,需设置numCornersHor=8numCornersVer=6)。

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

火山引擎 最新活动