c++ opencv cv::solvePnPRansac 的使用和注意事项

最近在做关于RGBD人脸拟合的事情,遇到一个关于 solvePnPRansac 使用的问题,记录一下. 顺便记录几个函数,里面包含了自己对 eigen 使用的一些尝试. 终于对eigen由陌生到可以较为熟练地使用了.

问题描述

一开始的现象是同样的参数使用solvePnP是正确的,而使用solvePnPRansac 就有问题,报错如下

terminate called after throwing an instance of 'cv::Exception'
  what():  OpenCV(3.4.5) /data/soft/opencv-3.4.5/modules/core/src/matrix_wrap.cpp:1235: error: (-215:Assertion failed) !fixedSize() || ((Mat*)obj)->size.operator()() == Size(_cols, _rows) in function 'create'

开始挺摸不着头脑的, 猜测是传入的数据类型不对,后来才知道 rvec, tvec 不能指定类型 (在solvePnp中是可以的)

利用solve pnp求解到相机姿态下的方法

// 这个是针对已经把相机内参去掉的p2d
void Calc_perspective_pm(float * pm, int n, const float * p3d, const float *p2d, int type){
    const cv::Mat1f noK = cv::Mat1f::eye(3, 3); // 去掉相机内参之后,就是单位阵了

    const cv::Mat1f noK_dst(n, 2, const_cast<float *>(p2d));
    const cv::Mat1f src(n, 3, const_cast<float *>(p3d));
    //auto noK_dst = cv::Mat1f(n, 2, p2d);

    //cv::Mat1f rvec, tvec;  这个对于 ransac 不行
    cv::Mat rvec, tvec;
    cv::Mat1f distCoeffs = cv::Mat1f::zeros(1, 5);
    if(type==0){
        cv::solvePnP(src, noK_dst, noK, distCoeffs, rvec, tvec);
    }else if(type==1){
        //cv::solvePnPRansac(cv::Mat(src), cv::Mat(noK_dst), cv::Mat(noK), cv::Mat(distCoeffs), rvec, tvec);
        cv::solvePnPRansac(src, noK_dst, noK, distCoeffs, rvec, tvec,
                false, 100, 8.0, 0.99, cv::noArray(), cv::SOLVEPNP_P3P);

    }else{
        cout<<"ERR: Calc_perspective_pm not support type: "<<type<<endl;
        exit(1);
    }

    //@7-10
    //cout<<rvec.type()<<" "<<src.type()<<endl;
    if(rvec.type()==5){
        memcpy(pm, rvec.ptr<float>(), 3*sizeof(float));
        memcpy(pm+3, tvec.ptr<float>(), 3*sizeof(float));
    }else if(rvec.type()==6){ // after solvePnPRansac
        memcpy(pm, rvec.ptr<double>(), 3*sizeof(double));
        memcpy(pm+3, tvec.ptr<double>(), 3*sizeof(double));
    }else{
        cout<<"ERR: Calc_perspective_pm, something wrong in ocv "<<endl;
        exit(1);
    }
    /*
    如果要求相机在世界坐标系下的位置
cv::Mat R;
cv::Rodrigues(rvec, R); // R is 3x3

R = R.t();  // rotation of inverse
tvec = -R * tvec; // translation of inverse

cv::Mat T = cv::Mat::eye(4, 4, R.type()); // T is 4x4
T( cv::Range(0,3), cv::Range(0,3) ) = R * 1; // copies R into T
T( cv::Range(0,3), cv::Range(3,4) ) = tvec * 1; // copies tvec into T
    */

}

去除相机参数 (use eigen)

上面我给的函数需要提前把相机内参给去掉,这里我把这个函数提供出来,同时也是对 eigen 矩阵操作的记录

// type: 0: 只有四个数, 且不是矩阵; 1: 只有四个数且是矩阵; 2: 按照2*3 矩阵来算
void Calc_remove_camK(int n, float *p2_5d, const float *p2d, const float *camK, int type) {
    using namespace Eigen;
    using namespace define_utils;

    MapMatXf P2_5d(p2_5d, 2, n);
    constMapMatXf P2d(p2d, 2, n);

    assert(type<3);
    if(type==2){
        MatrixXf inv_K2x2(2, 2);
        VectorXf txy(2);

        txy[0] = camK[2];
        txy[1] = camK[3+2];
        float ad_bc = camK[0]*camK[3+1] - camK[1]*camK[3];

        inv_K2x2(0, 0) = camK[3+1];
        inv_K2x2(0, 1) = -camK[1];
        inv_K2x2(1, 0) = -camK[3];
        inv_K2x2(1, 1) = camK[0];
        inv_K2x2 /= ad_bc;

        P2_5d = inv_K2x2 * P2d;
        P2_5d.colwise() -= inv_K2x2 * txy;
    }else{
        VectorXf fxy(2), txy(2);
        if(type==0){
            fxy(0) = camK[0];
            fxy(1) = camK[2];
            txy(0) = camK[1];
            txy(1) = camK[3];
        }else{
            fxy(0) = camK[0];
            fxy(1) = camK[3+1];
            txy(0) = camK[2];
            txy(1) = camK[3+2];
        }

        if(p2_5d!=p2d) P2_5d = P2d;
        P2_5d.colwise() -= txy;

        P2_5d.block(0, 0, 1, n) /= fxy(0);
        P2_5d.block(1, 0, 1, n) /= fxy(1);

    }

}

透视投影函数 (use eigen)

用了一些 eigen的矩阵操作技巧记录一下

// 自己的简化定义
namespace define_utils{
    using c_F = float;
    using c_Fp = float*;
    using c_cFp = const float*;
    using c_cIp = const int*;
    using c_uint32 = unsigned int;

    using MapVecXf = Eigen::Map<Eigen::VectorXf>;
    using constMapVecXf = Eigen::Map<const Eigen::VectorXf>;
    using MapMatXf = Eigen::Map<Eigen::MatrixXf>;
    using constMapMatXf = Eigen::Map<const Eigen::MatrixXf>;

    // dynamic = -1
    using constMapMatXf_RM = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> >;

}


// 里面注释掉的都是我想的eigen操作方式,但是不成功
void Calc_project3D_to_2D(int n, float *p2d, const float *p3d, const float *camK, int type) {
    using namespace Eigen;
    using namespace define_utils;

    constMapMatXf P3d(p3d, 3, n);
    MapMatXf P2d(p2d, 2, n);
    assert(type<3);

    if(type==0 || type==1){
        VectorXf fxy(2), txy(2);
        if(type==0){
            fxy(0) = camK[0];
            fxy(1) = camK[2];
            txy(0) = camK[1];
            txy(1) = camK[3];
        }else{
            fxy(0) = camK[0];
            fxy(1) = camK[3+1];
            txy(0) = camK[2];
            txy(1) = camK[3+2];
        }
        //P2d = P3d.block(0, 0, 2, n).colwise() * fxy; // error
        //P2d += txy *  P3d.block(2, 0, 1, n); then div z

        //P2d = P2d.rowwise() / P3d.block(2, 0, 1, n) + txy;

        P2d =  P3d.block(0, 0, 2, n).array().rowwise() / P3d.row(2).array();

        P2d.block(0, 0, 1, n) *= fxy(0);
        P2d.block(1, 0, 1, n) *= fxy(1);

        //P2d = P2d.colwise() + txy;
        P2d.colwise() += txy;

        //P2d.block(0, 0, 1, n) = P3d.row(0).array() / P3d.row(2).array() ; // * fxy(0);
        //P2d.block(1, 0, 1, n) = P3d.block(1, 0, 1, n) / P3d.block(2, 0, 1, n) * fxy(1);
        //P2d = P2d.rowwise() / P3d.block(2, 0, 1, n) + txy;


    }else{
        constMapMatXf_RM K(camK, 2, 3);
        //cout<<K<<endl;
        P2d = K * P3d;
        //P2d = P2d.array().rowwise() / P3d.row(2).array();
        //P2d.array().rowwise() /= P3d.row(2).array();
        P2d.array().rowwise() /= P3d.row(2).array();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值