【问题标题】:OpenCV: "camera calibrate" Why non-planar calibration rigs need initial guess?OpenCV:“相机校准”为什么非平面校准装置需要初步猜测?
【发布时间】:2021-10-18 06:12:23
【问题描述】:

根据 OpenCV 文档,“对于非平面校准装置,必须指定初始内在矩阵”的原因是关于“内在参数的初始化”

OPENCV: calibrateCamera() latest-doc:

目前,内部参数的初始化(当 CALIB_USE_INTRINSIC_GUESS 未设置时)仅用于平面校准模式(其中对象点的 Z 坐标必须全为零)。只要提供初始 cameraMatrix,也可以使用 3D 校准装置。

内部参数初始化”发生了什么?为什么 3D 绑定 不能(或很难)?

初始猜测会对相机校准有多大影响?

【问题讨论】:

  • 只是猜测一下。畸变系数全为 0。根据图像分辨率和视场计算的相机矩阵(涉及反正切,仅此而已)
  • 我试过了,fx/fy=1 fx = resolution_width/(2*tan(FOV_w/2)) cx = resolution_width/2 cy = resolution_height/2 没有成功。
  • 看起来正确。从相机矩阵的应用导出的方程是:tan(hfov/2) * fx = width/2。垂直 FoV 和对角 FoV 的方程类似(对于方形像素,fx=fy=fd)。如果这不起作用,也许可以展示你的结果。

标签: opencv camera-calibration


【解决方案1】:

根据 opencv 源代码,cvInitIntrinsicParams2D 使用消失点和单应性来估计焦距。同应性仅适用于平面场景。由于失真等原因,它仍然会出现错误。也许您可以将您的 3D 校准图案映射到平面上?我不知道初始猜测的质量对于校准过程的其余部分有多重要。

很可能,如果该装置中有清晰的 2D 平面(例如,对于大量点,相同的 z 坐标),则可以很容易地调整算法以首先从 3D 装置中提取 2D 平面。如果您能够提取这样的平面但无法调整代码:使用 API 中公开的高级 intrinsicGuess 函数,并将猜测的内在矩阵提供给您的校准。

这是源代码,在 modules/calib3d/src/calibration.cpp (opencv 4.5.2) 中可用:

    CV_IMPL void cvInitIntrinsicParams2D( const CvMat* objectPoints,
                         const CvMat* imagePoints, const CvMat* npoints,
                         CvSize imageSize, CvMat* cameraMatrix,
                         double aspectRatio )
{
    Ptr<CvMat> matA, _b, _allH;

    int i, j, pos, nimages, ni = 0;
    double a[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
    double H[9] = {0}, f[2] = {0};
    CvMat _a = cvMat( 3, 3, CV_64F, a );
    CvMat matH = cvMat( 3, 3, CV_64F, H );
    CvMat _f = cvMat( 2, 1, CV_64F, f );

    CV_Assert(npoints);
    CV_Assert(CV_MAT_TYPE(npoints->type) == CV_32SC1);
    CV_Assert(CV_IS_MAT_CONT(npoints->type));
    nimages = npoints->rows + npoints->cols - 1;

    if( (CV_MAT_TYPE(objectPoints->type) != CV_32FC3 &&
        CV_MAT_TYPE(objectPoints->type) != CV_64FC3) ||
        (CV_MAT_TYPE(imagePoints->type) != CV_32FC2 &&
        CV_MAT_TYPE(imagePoints->type) != CV_64FC2) )
        CV_Error( CV_StsUnsupportedFormat, "Both object points and image points must be 2D" );

    if( objectPoints->rows != 1 || imagePoints->rows != 1 )
        CV_Error( CV_StsBadSize, "object points and image points must be a single-row matrices" );

    matA.reset(cvCreateMat( 2*nimages, 2, CV_64F ));
    _b.reset(cvCreateMat( 2*nimages, 1, CV_64F ));
    a[2] = (!imageSize.width) ? 0.5 : (imageSize.width - 1)*0.5;
    a[5] = (!imageSize.height) ? 0.5 : (imageSize.height - 1)*0.5;
    _allH.reset(cvCreateMat( nimages, 9, CV_64F ));

    // extract vanishing points in order to obtain initial value for the focal length
    for( i = 0, pos = 0; i < nimages; i++, pos += ni )
    {
        CV_DbgAssert(npoints->data.i);
        CV_DbgAssert(matA && matA->data.db);
        CV_DbgAssert(_b && _b->data.db);
        double* Ap = matA->data.db + i*4;
        double* bp = _b->data.db + i*2;
        ni = npoints->data.i[i];
        double h[3], v[3], d1[3], d2[3];
        double n[4] = {0,0,0,0};
        CvMat _m, matM;
        cvGetCols( objectPoints, &matM, pos, pos + ni );
        cvGetCols( imagePoints, &_m, pos, pos + ni );

        cvFindHomography( &matM, &_m, &matH );
        CV_DbgAssert(_allH && _allH->data.db);
        memcpy( _allH->data.db + i*9, H, sizeof(H) );

        H[0] -= H[6]*a[2]; H[1] -= H[7]*a[2]; H[2] -= H[8]*a[2];
        H[3] -= H[6]*a[5]; H[4] -= H[7]*a[5]; H[5] -= H[8]*a[5];

        for( j = 0; j < 3; j++ )
        {
            double t0 = H[j*3], t1 = H[j*3+1];
            h[j] = t0; v[j] = t1;
            d1[j] = (t0 + t1)*0.5;
            d2[j] = (t0 - t1)*0.5;
            n[0] += t0*t0; n[1] += t1*t1;
            n[2] += d1[j]*d1[j]; n[3] += d2[j]*d2[j];
        }

        for( j = 0; j < 4; j++ )
            n[j] = 1./std::sqrt(n[j]);

        for( j = 0; j < 3; j++ )
        {
            h[j] *= n[0]; v[j] *= n[1];
            d1[j] *= n[2]; d2[j] *= n[3];
        }

        Ap[0] = h[0]*v[0]; Ap[1] = h[1]*v[1];
        Ap[2] = d1[0]*d2[0]; Ap[3] = d1[1]*d2[1];
        bp[0] = -h[2]*v[2]; bp[1] = -d1[2]*d2[2];
    }

    cvSolve( matA, _b, &_f, CV_NORMAL + CV_SVD );
    a[0] = std::sqrt(fabs(1./f[0]));
    a[4] = std::sqrt(fabs(1./f[1]));
    if( aspectRatio != 0 )
    {
        double tf = (a[0] + a[4])/(aspectRatio + 1.);
        a[0] = aspectRatio*tf;
        a[4] = tf;
    }

    cvConvert( &_a, cameraMatrix );
}

【讨论】:

  • 感谢您的快速回复!它确实回答了为什么“内在参数的初始化”不能在 3D 钻机上工作。
  • 看看编辑过的部分。可能有一个简单的技巧可以从您的 3D 装备中猜测内在函数。
  • 然而 > “也许您可以将您的 3D 校准图案映射到平面上?”
    我不确定这是否可行。因为它会破坏物体和图像点之间的联系。例如,如果对象面向相机的一侧。在图像上正确投影对象点不会发生转换。无论如何,谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-01-28
  • 2014-10-23
  • 1970-01-01
  • 2012-04-18
  • 2016-12-29
  • 1970-01-01
  • 1970-01-01
  • 2011-08-03
相关资源
最近更新 更多