PNP algorithm

这篇博客介绍了PNP算法,特别是通过OPENCV源码探讨了P3P和EPNP两种方法。作者强调了PNP算法在实践中的复杂性,并详细解释了OPENCV的SolvePNP函数,包括参数含义和解决P3P问题的部分。文章还涉及相机内参、畸变系数、旋转和平移矩阵,并提供了部分源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PNP algorithm

这个算法比我想想的要复杂的多,所以我还是从理论入手把,结合opencv的代码。单从代码讲会忽略好多东西。以后有时间慢慢改。

最近用到了PNP算法,看了好多,网上搜索了一番,发现有很多种解法,在这里,我就准备通过OPENCV源码,看看OPENCV里是怎么实现的(我看的OPENCV的源码是2.4,C++实现的)。

PNP用的比较多的就是P3P和EPNP两种,在这里,我也准备把OPENCV里如何实现这两种算法的过程详细的记录下来。至于PNP算法用来干什么的我就不说了,网上很多解释。

OPENCV源码

首先,看一下OPENCV里的SolvePNP函数的API:

 C++: bool solvePnP(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false, int flags=ITERATIVE )

其中参数为:
Parameters:

  • objectPoints – Array of object points in the object coordinate space, 3xN/Nx3 1-channel or 1xN/Nx1 3-channel, where N is the number of points. vector can be also passed here.
  • imagePoints – Array of corresponding image points, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel, where N is the number of points. vector can be also passed here.
  • cameraMatrix – Input camera matrix A = \vecthreethree{fx}{0}{cx}{0}{fy}{cy}{0}{0}{1} .
  • distCoeffs – Input vector of distortion coefficients (k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6]]) of 4, 5, or 8 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed.
  • rvec – Output rotation vector (see Rodrigues() ) that, together with tvec , brings points from the model coordinate system to the camera coordinate system.
  • tvec – Output translation vector.
  • useExtrinsicGuess – If true (1), the function uses the provided rvec and tvec values as initial approximations of the rotation and translation vectors, respectively, and further optimizes them.
  • flags –

    Method for solving a PnP problem:

    • CV_ITERATIVE Iterative method is based on Levenberg-Marquardt optimization. In this case the function finds such a pose that minimizes reprojection error, that is the sum of squared distances between the observed projections imagePoints and the projected (using projectPoints() ) objectPoints .
    • CV_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang “Complete Solution Classification for the Perspective-Three-Point Problem”. In this case the function requires exactly four object and image points.
    • CV_EPNP Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the paper “EPnP: Efficient Perspective-n-Point Camera Pose Estimation”.

每个参数的意义已经写的很清楚了,objectPoints是世界坐标系的点,imagePoints是图像坐标系的点,cameraMatrix是相机内参,distCoeffs是相机畸变系数,rvec是旋转矩阵,tvec是平移矩阵,支持三种方法P3P,EPNP,ITERATIVE,现在就来看P3P情况。

solvePnP源码:

bool cv::solvePnP( InputArray _opoints, InputArray _ipoints,
                  InputArray _cameraMatrix, InputArray _distCoeffs,
                  OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess, int flags )
{
    Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
    int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
    CV_Assert( npoints >= 0 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
    Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();

    Mat rvec, tvec;
    if( flags != CV_ITERATIVE )
        useExtrinsicGuess = false;

    if( useExtrinsicGuess )
    {
        int rtype = _rvec.type(), ttype = _tvec.type();
        Size rsize = _rvec.size(), tsize = _tvec.size();
        CV_Assert( (rtype == CV_32F || rtype == CV_64F) &&
                   (ttype == CV_32F || ttype == CV_64F) );
        CV_Assert( (rsize == Size(1, 3) || rsize == Size(3, 1)) &&
                   (tsize == Size(1, 3) || tsize == Size(3, 1)) );
    }
    else
    {
        _rvec.create(3, 1, CV_64F);
        _tvec.create(3, 1, CV_64F);
    }
    rvec = _rvec.getMat();
    tvec = _tvec.getMat();

    if (flags == CV_EPNP)
    {
        cv::Mat undistortedPoints;
        cv::undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
        epnp PnP(cameraMatrix, opoints, undistortedPoints);

        cv::Mat R;
        PnP.compute_pose(R, tvec);
        cv::Rodrigues(R, rvec);
        return true;
    }
    else if (flags == CV_P3P)
    {
        CV_Assert( npoints == 4);
        cv::Mat undistortedPoints;
        cv::undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
        p3p P3Psolver(cameraMatrix);

        cv::Mat R;
        bool result = P3Psolver.solve(R, tvec, opoints, undistortedPoints);
        if (result)
            cv::Rodrigues(R, rvec);
        return result;
    }
    else if (flags == CV_ITERATIVE)
    {
        CvMat c_objectPoints = opoints, c_imagePoints = ipoints;
        CvMat c_cameraMatrix = cameraMatrix, c_distCoeffs = distCoeffs;
        CvMat c_rvec = rvec, c_tvec = tvec;
        cvFindExtrinsicCameraParams2(&c_objectPoints, &c_imagePoints, &c_cameraMatrix,
                                     c_distCoeffs.rows*c_distCoeffs.cols ? &c_distCoeffs : 0,
                                     &c_rvec, &c_tvec, useExtrinsicGuess );
        return true;
    }
    else
        CV_Error(CV_StsBadArg, "The flags argument must be one of CV_ITERATIVE or CV_EPNP");
    return false;
}

函数前三行

    Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();//将世界坐标系点的数据和图像坐标系点的数据变为Mat格式
    int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));//检查Mat是不是由npoints个3维CV_32F/CV_64F向量组成的
    CV_Assert( npoints >= 0 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );// 对ipoints进行同样的检查,并对比两组点的个数是否相同

下一句:

    Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();//转换为Mat类型

P3P

我们先看P3P部分:

 else if (flags == CV_P3P)
    {
        // 看看是否有4组点对,CV_P3P使用第四个点对来获得更好的结果
        CV_Assert( npoints == 4);
        //获得图像坐标在畸变前的坐标,我们的目的是看P3P算法怎么实现的,这里不用管。
        cv::Mat undistortedPoints;
        cv::undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
        //初始化一个p3p类型的对象
        p3p P3Psolver(cameraMatrix);

        
PnP(Perspective-n-Point)算法是计算机视觉中的一种常见方法,用于估算相机的位姿。下面是一个使用MATLAB实现PnP算法的示例: 假设我们有一个相机和一些三维点云数据,我们要估算相机在三维空间中的位姿。我们可以使用PnP算法来完成这项任务。 在MATLAB中,可以使用Computer Vision Toolbox中的estimateCameraPose函数来实现PnP算法。假设我们已经用相机采集到了一些图像,然后使用MATLAB中的相机标定工具箱进行相机标定,得到了相机的内参矩阵K和畸变参数d。 现在,我们需要使用estimateCameraPose函数来估算相机的位姿。我们需要提供相机的内参矩阵K、畸变参数d、三维点云数据和对应的二维图像坐标。 代码示例: ``` % load camera intrinsic parameters and distortion coefficients load('cameraParams.mat'); % load 3D points load('points3D.mat'); % load 2D image points load('points2D.mat'); % estimate camera pose using PnP algorithm [R, t] = estimateCameraPose(points3D, points2D, cameraParams); % display camera pose cameraPose = [R, t']; disp(cameraPose); ``` 在这个例子中,我们假设相机的内参矩阵和畸变参数已经保存在了cameraParams.mat文件中,三维点云数据保存在了points3D.mat文件中,二维图像坐标保存在了points2D.mat文件中。 estimateCameraPose函数会返回一个旋转矩阵R和一个平移向量t,它们描述了相机在三维空间中的位姿。最后,我们将它们组合成一个4x4的变换矩阵,并输出结果。 请注意,PnP算法对于噪声和误差比较敏感,因此在实际应用中需要进行一些额外的处理和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值