SlamBook14讲-PoseEstimation2d2d

参考高翔老师的示例代码

在FeatureExtraction的基础上计算本质矩阵E 并计算对极约束。

FeatureExtraction部分:

SlamBook14讲-FeatureExtraction_就差一点点_的博客-CSDN博客

本质矩阵E 包含了相机运动的旋转矩阵和平移矩阵。

本质矩阵E 的求解需要匹配好的n对点和内参,这里的points类型是InputArray。后者是opencv中一种输入的数组(不可更改 const),有很多重载版本,可以用vector储存。

cv::findEssentialMat(points1,points2,K);

基础矩阵是F = K^{-T}EK

基础矩阵的求解不需要相机内参K 其中FM_8POINT代表8点法求解

cv::findFundamentalMat(points1,points2,cv::FM_8POINT);

求解出本质矩阵E 后 便可计算出R 和T

cv::recoverPose(EssentialMat,points1,points2,K,R,t);

可以通过验证对极约束是否接近0来判断求解是否正确,即 p_{2}^{T}E p_{1} = 0

其中p1和p2是P分别在相机两个位置归一化平面的投影(是需要通过像素坐标换算到世界坐标的,变换方法是通过相机的内参进行换算。归一化坐标为[X/Z,Y/Z,1]),只要大多数对极约束接近0,则说明计算的本质矩阵E 和 是正确的。也可以用计算出的R和T,用t\wedge R代替E ,验证T 和R的正确性。其中t\wedge是李代数的部分内容,可以理解为矩阵的叉积。t\wedge  是通过t生成的反对称矩阵。

源码有些地方写的有些麻烦,主要是为了锻炼自己多用vector的迭代器和引用传递,算是复习C++

附上源码(包含了特征点提取与匹配的部分):

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>


void PoseEstimation2d2d(std::vector<cv::KeyPoint>,std::vector<cv::KeyPoint>,cv::Mat&,cv::Mat&);
void EpipolarConstraint(cv::Mat,const std::vector<cv::KeyPoint>&, const std::vector<cv::KeyPoint>& , cv::Mat,std::vector<cv::Mat>&,std::vector<cv::Mat>&);

int main(){

    cv::Mat img1 = cv::imread("../1.png",1);
    cv::Mat img2 = cv::imread("../2.png",1);

    std::vector<cv::KeyPoint> KeyPoint1,KeyPoint2;
    cv::Mat descriptors1,descriptors2;
    cv::Ptr<cv::FeatureDetector> detector = cv::ORB::create();//represented as vectors in a multidimensional space
    cv::Ptr<cv::DescriptorExtractor> descriptor = cv::ORB::create();//初始化描述子 descriptor为指针
    cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");

    detector->detect(img1,KeyPoint1);
    detector->detect(img2,KeyPoint2);

    descriptor->compute(img1,KeyPoint1,descriptors1);
    descriptor->compute(img2,KeyPoint2,descriptors2);

    // cv::Mat outimg1;
    // cv::drawKeypoints(img1,KeyPoint1,outimg1);
    // cv::imshow("img1特征点",outimg1);
    //可以看出descriptor描述子每个元素都是0-255 意味着二进制的编码
    //std::cout << descriptors1 << std::endl;
    std::vector<cv::DMatch> matches;
    matcher->match(descriptors1,descriptors2,matches);
    //可以看到结果不理想,很多误匹配的点,需要设置阈值筛选
    // cv::Mat matchimg;
    // cv::drawMatches(img1,KeyPoint1,img2,KeyPoint2,matches,matchimg);
    // cv::imshow("特征匹配",matchimg);

    //迭代器遍历 for遍历也行
    double min = 10000,max = 0;
    min = min_element( matches.begin(), matches.end(), [](const cv::DMatch& m1, const cv::DMatch& m2) {return m1.distance<m2.distance;} )->distance;
    max = max_element( matches.begin(), matches.end(), [](const cv::DMatch& m1, const cv::DMatch& m2) {return m1.distance<m2.distance;} )->distance;

    std::vector<cv::DMatch> GoodMatches;
    for(auto i = matches.begin(); i < matches.end(); i++){
        if(i->distance <= std::max( 2*min, 30.0 )){
            GoodMatches.push_back(*i);
        }
    }

    std::vector<cv::KeyPoint> GoodMatchPoint1,GoodMatchPoint2;
    for(auto i = GoodMatches.begin(); i < GoodMatches.end(); i++){
        GoodMatchPoint1.push_back(KeyPoint1[i->queryIdx]);
        GoodMatchPoint2.push_back(KeyPoint2[i->trainIdx]);
    }
    cv::Mat R,t;
    PoseEstimation2d2d(GoodMatchPoint1,GoodMatchPoint2,R,t);

    cv::Mat t_x = ( cv::Mat_<double> ( 3,3 ) <<
                0,                      -t.at<double> ( 2,0 ),     t.at<double> ( 1,0 ),
                t.at<double> ( 2,0 ),      0,                      -t.at<double> ( 0,0 ),
                -t.at<double> ( 1.0 ),     t.at<double> ( 0,0 ),      0 );
    cv::Mat t_r = t_x*R;
    std::cout << "t^R is " << std::endl << t_x*R << std::endl;

    // cv::Mat GoodMatchimg;
    // cv::drawMatches(img1,KeyPoint1,img2,KeyPoint2,GoodMatches,GoodMatchimg);
    // cv::imshow("特征匹配",GoodMatchimg);
    // cv::imwrite("../GoodResult.png", GoodMatchimg);
    // cv::waitKey(0);
    cv::Mat K = ( cv::Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
    cv::Point2d cam1;//点在归一化平面的坐标
    cv::Point2d cam2;

    std::vector<cv::Mat> x1;
    std::vector<cv::Mat> x2;

    EpipolarConstraint(t_r,GoodMatchPoint1,GoodMatchPoint2,K,x1,x2);
    

    return 0;
}

void EpipolarConstraint(cv::Mat E,const std::vector<cv::KeyPoint>& PixelPoint1,const std::vector<cv::KeyPoint> & PixelPoint2, cv::Mat K,std::vector<cv::Mat> & x1,std::vector<cv::Mat> & x2){
    cv::Point2d p1,p2;
    for(auto i = PixelPoint1.begin(); i < PixelPoint1.end(); i++){
        p1.x=(i->pt.x-K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 );
        p1.y=(i->pt.y-K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 );
        x1.push_back((cv::Mat_<double> ( 3,1 ) << p1.x, p1.y, 1)) ;
    }
    for(auto i = PixelPoint2.begin(); i < PixelPoint2.end(); i++){
        p2.x=(i->pt.x-K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 );
        p2.y=(i->pt.y-K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 );
        x2.push_back((cv::Mat_<double> ( 3,1 ) << p2.x, p2.y, 1)) ;
    }
    auto j = x2.begin();
    for(auto i = x1.begin(); i < x1.end(); i++,j++){
    cv::Mat EpipolarConstraint = (*j).t()* E * (*i);
    std::cout << "EpipolarConstraint is " <<  EpipolarConstraint << std::endl;
    }
}

void PoseEstimation2d2d(std::vector<cv::KeyPoint> GoodMatchPoint1,std::vector<cv::KeyPoint> GoodMatchPoint2,cv::Mat& R,cv::Mat& t){
    //初始化矩阵
    cv::Mat K = (cv::Mat_<double>(3,3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);//相机内参 fx fy cx cy

    std::vector<cv::Point2f> points1, points2;
    //深拷贝
    for(auto i = GoodMatchPoint1.begin(); i < GoodMatchPoint1.end();i++){
        points1.push_back(i->pt) ;
    }

    for(auto i = GoodMatchPoint2.begin(); i < GoodMatchPoint2.end();i++){
        points2.push_back(i->pt) ;
    }
    cv::Mat FundamentalMat;
    FundamentalMat = cv::findFundamentalMat(points1,points2,cv::FM_8POINT);
    //std::cout << "fundamental_matrix is " << std::endl << FundamentalMat << std::endl;

    cv::Mat EssentialMat;
    EssentialMat = cv::findEssentialMat(points1,points2,K);
    //EssentialMat = cv::findEssentialMat(points1,points2,521.0, cv::Point2d(325.1,249.7));
    std::cout << "EssentialMat is " << std::endl << EssentialMat << std::endl;
    
    cv::recoverPose(EssentialMat,points1,points2,K,R,t);
    //cv::recoverPose(EssentialMat,points1,points2,R,t,521.0,cv::Point2d(325.1,249.7));

    //std::cout << "R is " << std::endl << R << std::endl;
    //std::cout << "t is " << std::endl << t << std::endl;


}

最终的运行结果如下:

EssentialMat is 
[0.0109715107420577, 0.2483381379213602, 0.03170694792954255;
 -0.2088597167616505, 0.029084710794909, -0.6744719771361425;
 0.008249595749295274, 0.6614170563251771, 0.01676713455468244]
t^R is 
[-0.01551605929362698, -0.3512031627022603, -0.04484039578292499;
 0.2953722440781573, -0.04113199246326703, 0.9538474175082218;
 -0.01166669019199051, -0.9353849714383189, -0.02371230909015907]
EpipolarConstraint is [-0.0005416579768821039]
EpipolarConstraint is [-0.002159398557490991]
EpipolarConstraint is [0.000329564098265081]
EpipolarConstraint is [-1.147072796144066e-05]
EpipolarConstraint is [0.000207988216714694]
EpipolarConstraint is [0.0001093677532205128]
EpipolarConstraint is [0.0004208746919946507]
EpipolarConstraint is [-0.003177997347355854]
EpipolarConstraint is [-4.07601554744362e-05]
EpipolarConstraint is [-0.00144829153347319]
EpipolarConstraint is [-0.0009615799940303968]
EpipolarConstraint is [-0.0008059848523957747]
EpipolarConstraint is [-0.001424066451101234]
EpipolarConstraint is [-0.0004308558850970052]
EpipolarConstraint is [-0.0004796564950435983]
EpipolarConstraint is [-0.0001958686247416352]
EpipolarConstraint is [0.001542044376943941]
EpipolarConstraint is [0.003106913282011514]
EpipolarConstraint is [0.0006736754289436708]
EpipolarConstraint is [-0.001173033197502501]
EpipolarConstraint is [-0.003987317225260016]
EpipolarConstraint is [-0.001256013301241379]
EpipolarConstraint is [-0.001550737161933333]
EpipolarConstraint is [0.00201042506321103]
EpipolarConstraint is [-0.0006111309111405394]
EpipolarConstraint is [-3.046189484311179e-05]
EpipolarConstraint is [0.001273590112099141]
EpipolarConstraint is [-0.004166758257779282]
EpipolarConstraint is [-0.001107404024207642]
EpipolarConstraint is [-0.0005148070022724105]
EpipolarConstraint is [-0.001776337642038897]
EpipolarConstraint is [-1.012294414959314e-12]
EpipolarConstraint is [-0.001856761858613207]
EpipolarConstraint is [-0.0004830391085845209]
EpipolarConstraint is [0.0004692803762148395]
EpipolarConstraint is [-0.002957773919832811]
EpipolarConstraint is [-0.003344265746093363]
EpipolarConstraint is [-0.0001976593466801457]
EpipolarConstraint is [-0.002823913457831666]
EpipolarConstraint is [-0.0004305282653053466]
EpipolarConstraint is [0.001181778913841636]
EpipolarConstraint is [-1.695033002846458e-12]
EpipolarConstraint is [-0.002141849714928215]
EpipolarConstraint is [0.001149598706735769]
EpipolarConstraint is [-0.002121385557104991]
EpipolarConstraint is [-1.019170858818086e-12]
EpipolarConstraint is [0.0009022844709607424]
EpipolarConstraint is [-0.002101173356852802]
EpipolarConstraint is [-0.0003542202672723524]
EpipolarConstraint is [-2.942426762718442e-05]
EpipolarConstraint is [-0.003786956691643206]
EpipolarConstraint is [-0.001587983371808432]
EpipolarConstraint is [-0.00292944464841105]
EpipolarConstraint is [-0.001016011288387075]
EpipolarConstraint is [0.0001215793621033301]
EpipolarConstraint is [-0.0009821639412199053]
EpipolarConstraint is [0.001458461868649605]
EpipolarConstraint is [-1.237312320791217e-05]
EpipolarConstraint is [-0.002735751536037237]
EpipolarConstraint is [0.001414873832845623]
EpipolarConstraint is [0.001789545990435903]
EpipolarConstraint is [-0.002550349147929159]
EpipolarConstraint is [-0.006256229913356795]
EpipolarConstraint is [-0.001875732926272605]
EpipolarConstraint is [-0.002105779810921735]
EpipolarConstraint is [-1.543598582287586e-12]
EpipolarConstraint is [-0.004014001023516178]
EpipolarConstraint is [-0.004726927213163874]
EpipolarConstraint is [-0.001328876731452905]
EpipolarConstraint is [5.292988269900434e-14]
EpipolarConstraint is [-0.005081445458945427]
EpipolarConstraint is [-0.001976431721038091]
EpipolarConstraint is [-0.001662302184417874]
EpipolarConstraint is [0.004283653601028871]
EpipolarConstraint is [0.004085015243819348]
EpipolarConstraint is [0.0001472315485530409]
EpipolarConstraint is [-0.0009652492684803365]
EpipolarConstraint is [-0.002341399165420976]
EpipolarConstraint is [-0.006138218209024097]

对于结果有几点注意

E 的 尺度是不确定的 这意味着E 乘以任何一个常数都是正确的 因为不会影响对极约束的结果,称为本质矩阵的尺度不确定性。可以看到结果中t\wedge R与E是差了一个常数项系数的。 

如果相机的所有特征点几乎共面,本质矩阵会退化,应该使用单应矩阵,这里没有写。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值