0
点赞
收藏
分享

微信扫一扫

【slam十四讲第二版】【课后习题】【第七讲~视觉里程计Ⅰ】

年夜雪 2022-04-07 阅读 83

【slam十四讲第二版】【课后习题】【第七讲~视觉里程计Ⅰ】

0 前言

由于课本前六讲基本都是公式推导,所以就写在了平板上,而后面的多是理论知识以及代码的实现,所以特此在csdn上记录分享,

1 课后习题

1.1. 除了本书介绍的ORB 特征点外,你还能找到哪些其他的特征点?请说说SIFT 或SURF 的原理,对比它们与ORB 之间的优劣。

  • 除了ORB之外,一般还有还有SIFTSURFBRISKAKAZE,这些都是在OpenCV中已经实现了的。

  • SIFT算法,又称为尺度不变特征转换(Scale-invariant feature transform),大致方法是首先搜索所有尺度下图像的位置,通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点,然后在候选位置通过拟合精细的模型来确定位置和尺度,再基于图像梯度,分配给关键点一个或多个方向,最后在每个关键点的周围邻域,在选定的尺度下测量图像局部梯度来描述关键点。使用斑点检测方法和浮点型特征描述子,在建立高斯差分空间金字塔的基础上提取初具有尺度不变性的特征点,然后对特征点邻域内的点的梯度方向进行直方图统计。特征点的主方向就是直方图中比重最大的方向,必要时可选一个辅方向。SIFT特征集旋转不变性,尺度不变性,对图像变形和光照鲁棒鲁等优点于一身,不足之处是计算量大,计算速度慢,需要GPU加速才可满足实时性需求。

  • SURF算法,又称为加速稳健特征(Speeded Up Robust Features),其方法和构建图像金字塔的方法相反,其主要方法是通过修改滤波器的尺寸和模糊度从而得到不同层级的影像,但是寻找特征点的方法和SIFT类似。使用基于DoH的斑点特征检测方法,在特征点的描述上,SURF算法通过积分图,利用两个方向上的Harr小波模型进行梯度计算,然后对邻域内点的梯度方向以扇形的方式进行统计,得到特征点的主方向。其算法速度快且稳定性好。

  • SIFT和SURF能够比较稳定地提供较高的准确率,其中SIFT比SURF更准确一点,但是二者都特别慢。相较而言ORB速度更快,但是更容易出现问题,而且错误率远比其他二者大。

  • 计算速度: ORB>>SURF>>SIFT(各差一个量级)

  • 旋转鲁棒性: SURF>ORB~SIFT(表示差不多)

  • 模糊鲁棒性: SURF>ORB~SIFT

  • 尺度变换鲁棒性: SURF>SIFT>ORB(ORB并不具备尺度变换性)

  • 综上所述,如果对计算实时性要求非常高,可选用ORB算法,但基本要保证正对拍摄;如果对实行性要求稍高,可以选择SURF;基本不用SIFT


1.2. 设计程序,调用OpenCV 中的其他种类特征点。统计在提取1000 个特征点时,在你的机器上所用的时间。

  • 参考文献《视觉SLAM十四讲》课后习题—ch7(更新中……)

  • 如果上述文章失效,那么可以自取工程包,是我存储的,当时缺少opencv_contrib没有实现,CMakeLists.txt可能会有我问题,如果你的opencv符合要求,可以试试:链接:https://pan.baidu.com/s/1XE1u3FS2lAEGU_jdyT0KnQ 提取码:ny2r

  • 但是这个工程的实现,需要首先我们要知道如果要调用opencv中的SIFT和SURF特征点,SIFT和SURF都在nonfree模块中,所以我们就需要nonfree模块。但是在opencv3中,SURF/SIFT 以及其它的一些东西被移动到了独立的库(opencv_contrib)中。所以首先我们需要安装opencv_contrib:(如果你用的是opencv2可以省略安装opencv_contrib这一步骤)

  • 如果你已经安装了opencv3,那么要重装,可以参考ubuntu18.04安装opencv+opencv_contrib安装详细教程(亲测有效,包含安装包3.4.1及boostdesc_bgm.i文件),这里我没有亲自验证,有时间再回来

  • 如果你需要opencv3.4.1和对应版本的opencv_contrib3.4.1安装包,自取:链接https://pan.baidu.com/s/1mEpZSEeTj4TWBCX2Rv3vUw 提取码:usgp


1.3. 我们发现OpenCV 提供的ORB 特征点,在图像当中分布不够均匀。你是否能够找到或提出让特征点分布更加均匀的方法?

  • 用熵来度量局部信息量大小,将图像分为若干网格,计算每个网格图像的熵,在熵较大的区域内提取特征点。若局部依旧集中可再次划分网格,在没有特征的网格中,强行提取一个兴趣值最大的特征点。
  • 2016年出的ORB_SLAM2中已经用四叉树实现的特征点均匀分布算法。四叉树均匀划分的方法跟经典ORB算法变化不大,一般还是:1.构建图像金字塔;2.将每层图像划分网格,FAST算法提取特征点;3.对网格内的特征点进行评估,选取质量最好的留下作为代表关键点;4.用强度质心法计算每个关键点的方向;5对金字塔图层进行高斯滤波;6.使用BRIEF计算各个关键点的描述子(包含论文中的启发式搜索算法得到的256对匹配对坐标进行优化);7.保留关键点和描述子。

1.4. 研究FLANN 为何能够快速处理匹配问题。除了FLANN 之外,还能哪些可以加速匹配的手段?

  • FLANN的全称为Fast Library for Approximate Nearest Neighbors。它是一个对大数据集和高维特征进行最近邻近的算法的集合。在面对大数据集时它的性能要好于BFMatcher。匹配问题实际上就是一个特征向量求相似度问题。对于最简单的办法,就是逐个匹配对计算距离。明显这种遍历的方式是效率极低的,对于大数据情况下不适用。因此经典kd-tree的搜索回溯的搜索办法在这里派上了用场,减少了不必要的计算过程,提高了效率,但是对于多维数据而言,其效果往往不升反降。在经典kd-tree算法上提出了随机kd-tree算法,随即选出方差较大的维度,建立kd-tree,再进行搜索回溯。还有一种分级k-means tree,与之前不同的是先通过k-means算法(之后回环检测时会用到)来进行先数据聚类,然后再进行kd-tree的建立。这些方法相交于传统的直接匹配法优势都比较大。
  • 加速匹配的方法:预排序图像检索;GPU加速,可以使得匹配速度提高十多倍;再后来就是用FPGA加速,其匹配速度能提升10倍;再后来的VF-SIFT(very fast SIFT)算法,其核心思想是从SIFT特征中提取4个特征角,根据特征角区间的不同,避免了大量不必要的搜索,这样据说是普通搜索的1250倍。

1.5. 把演示程序使用的EPnP 改成其他PnP 方法,并研究它们的工作原理。

1.5.1 前言

  • 目前除了EPnP外,OpenCV还提供了另外两种方法:迭代法和P3P法,其中,在OpenCV3中还另外提供了DLS法和UPnP法。

  • EPnP(Efficient PnP)的思路是将空间中的任意3D点可以用4个不共面的3D点加权表示,然后通过n个3D点在相机平面的投影关系以及四个控制点的权重关系构建一个的矩阵,求这个矩阵的特征向量,就可以得到这个相机平面的坐标,再用POSIT正交投影变换算法得到相机位姿。

  • 迭代法实质是迭代求出重投影误差的最小解先用DLT直接线性变换求解,然后用LM算法进行优化,这个解显然不是正解,而且这个方法只能使用4个共面特征点才能求得。

  • P3P法,它需要3对3D-2D的匹配点,然后通过三角形投影关系得到的方程组,然后将方程组进行变换得到两个二元二次方程进行求解。

  • DLS(Direct Least-Squares)算法整体思路是首先对PnP非线性最小贰乘建模,重新定义LS模型以便于参数降维然后构造Maculy矩阵进行求解,将PnP问题重构为无约束LSM问题然后优化求解。

  • UPnP(Uncalibrated PnP)算法跟EPnP差不了太多,仅仅是多估计了焦距。因此,比较适合未标定场合。

  • 代码基础是【slam十四讲第二版】【课本例题代码向】【第七讲~视觉里程计Ⅰ】【1OpenCV的ORB特征】【2手写ORB特征】【3对极约束求解相机运动】【4三角测量】【5求解PnP】【3D-3D:ICP】中的第5小节

1.5.2 pnps.cpp


// Created by czy on 2022/4/7
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <chrono>

using namespace std;

void find_feature_matches(const cv::Mat& img_1, const cv::Mat& img_2,
                          std::vector<cv::KeyPoint>& keypoints_1, std::vector<cv::KeyPoint>& keypoints_2,
                          std::vector<cv::DMatch>& matches);


// 像素坐标转相机归一化坐标
cv::Point2d pixel2cam(const cv::Point2d& p, const cv::Mat& K);

int main(int argc, char ** argv) {
    //读取图片
    if (argc != 5) {
        cout << "usage : pose_estimation_3d2d img1 img2 depth1 depth2" << endl;
        return 1;
    }

    //-- 读取图像
    cv::Mat img_1 = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR);//读取彩色图
    cv::Mat img_2 = cv::imread(argv[2], CV_LOAD_IMAGE_COLOR);
    assert(img_1.data != nullptr && img_2.data != nullptr);//若读取的图片没有内容,就终止程序

    vector<cv::KeyPoint> keypoints_1, keypoints_2;
    vector<cv::DMatch> matches;
    find_feature_matches(img_1, img_2, keypoints_1, keypoints_2, matches);//得到两个图片的特征匹配点
    cout << "一共找到了" << matches.size() << "组匹配点" << endl;

    // 建立3D点,把深度图信息读进来,构造三维点
    cv::Mat d1 = cv::imread(argv[3], -1);// 深度图为16位无符号数,单通道图像
    cv::Mat K = (cv::Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);

    vector<cv::Point3f> pts_3d;//创建容器pts_3d存放3d点(图1对应的特征点的相机坐标下的3d点)
    vector<cv::Point2f> pts_2d;//创建容器pts_2d存放图2的特征点

    for (cv::DMatch m: matches) {
        //把对应的图1的特征点的深度信息拿出来
        ushort d = d1.ptr<unsigned short>(int(keypoints_1[m.queryIdx].pt.y))[int(keypoints_1[m.queryIdx].pt.x)];
        if (d == 0) // bad depth
            continue;
        float dd = d / 5000.0;//用dd存放换算过尺度的深度信息
        cv::Point2d p1 = pixel2cam(keypoints_1[m.queryIdx].pt, K);//p1里面放的是图1特征点在相机坐标下的归一化坐标(只包含 x,y)
        pts_3d.push_back(cv::Point3f(p1.x * dd, p1.y * dd, dd));//得到图1特征点在相机坐标下的3d坐标
        pts_2d.push_back(keypoints_2[m.trainIdx].pt);//得到图2特张点的像素坐标
    }

    cout << "3d-2d pairs: " << pts_3d.size() << endl;//3d-2d配对个数得用pts_3d的size


    cout << "使用cv_PnP求解 位姿" << endl;

    cv::Mat r, t;
    cv::Mat R;
    chrono::steady_clock::time_point t1,t2;
    chrono::duration<double> time_used;

    cout << "***********************************SOLVEPNP_ITERATIVE***********************************" << endl;
    t1 = chrono::steady_clock::now();
    //Mat()这个参数指的是畸变系数向量
    cv::solvePnP(pts_3d, pts_2d, K, cv::Mat(), r, t, false,cv::SOLVEPNP_ITERATIVE); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
    t2 = chrono::steady_clock::now();
    time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cv::Rodrigues(r, R);//r为旋转向量形式,利用cv的Rodrigues()函数将旋转向量转换为旋转矩阵
    cout << "solve pnp in opencv cost time: " << time_used.count() << " seconds." << endl;
    cout << "R=" << endl << R << endl;
    cout << "t=" << endl << t << endl;
    cout << "calling bundle adjustment" << endl;
    cout << "***********************************SOLVEPNP_ITERATIVE***********************************" << endl;

    cout << "***********************************SOLVEPNP_EPNP***********************************" << endl;
    t1 = chrono::steady_clock::now();
    //Mat()这个参数指的是畸变系数向量
    cv::solvePnP(pts_3d, pts_2d, K, cv::Mat(), r, t, false,cv::SOLVEPNP_EPNP); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
    t2 = chrono::steady_clock::now();
    time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cv::Rodrigues(r, R);//r为旋转向量形式,利用cv的Rodrigues()函数将旋转向量转换为旋转矩阵
    cout << "solve pnp in opencv cost time: " << time_used.count() << " seconds." << endl;
    cout << "R=" << endl << R << endl;
    cout << "t=" << endl << t << endl;
    cout << "calling bundle adjustment" << endl;
    cout << "***********************************SOLVEPNP_EPNP***********************************" << endl;

    cout << "***********************************SOLVEPNP_UPNP***********************************" << endl;
    t1 = chrono::steady_clock::now();
    //Mat()这个参数指的是畸变系数向量
    cv::solvePnP(pts_3d, pts_2d, K, cv::Mat(), r, t, false,cv::SOLVEPNP_UPNP); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
    t2 = chrono::steady_clock::now();
    time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cv::Rodrigues(r, R);//r为旋转向量形式,利用cv的Rodrigues()函数将旋转向量转换为旋转矩阵
    cout << "solve pnp in opencv cost time: " << time_used.count() << " seconds." << endl;
    cout << "R=" << endl << R << endl;
    cout << "t=" << endl << t << endl;
    cout << "calling bundle adjustment" << endl;
    cout << "***********************************SOLVEPNP_UPNP***********************************" << endl;

    cout << "***********************************SOLVEPNP_DLS***********************************" << endl;
    t1 = chrono::steady_clock::now();
    //Mat()这个参数指的是畸变系数向量
    cv::solvePnP(pts_3d, pts_2d, K, cv::Mat(), r, t, false,cv::SOLVEPNP_DLS); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
    t2 = chrono::steady_clock::now();
    time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cv::Rodrigues(r, R);//r为旋转向量形式,利用cv的Rodrigues()函数将旋转向量转换为旋转矩阵
    cout << "solve pnp in opencv cost time: " << time_used.count() << " seconds." << endl;
    cout << "R=" << endl << R << endl;
    cout << "t=" << endl << t << endl;
    cout << "calling bundle adjustment" << endl;
    cout << "***********************************SOLVEPNP_DLS***********************************" << endl;

    cout << "***********************************SOLVEPNP_P3P***********************************" << endl;

    vector<cv::Point3f> pts_p3p_3d;//创建容器pts_3d存放3d点(图1对应的特征点的相机坐标下的3d点)
    vector<cv::Point2f> pts_p3p_2d;//创建容器pts_2d存放图2的特征点
    
    //取出其中的4个点对
    for (int i = 0; i < 4; i++)
    {
        pts_p3p_3d.push_back(pts_3d[i]);
        pts_p3p_2d.push_back(pts_2d[i]);
    }

/*    cv::Mat pts_p3p = (cv::Mat_<double>(4,3) <<
            pts_p3p_3d[0].x,pts_p3p_3d[0].y,pts_p3p_3d[0].z,
            pts_p3p_3d[1].x,pts_p3p_3d[1].y,pts_p3p_3d[1].z,
            pts_p3p_3d[2].x,pts_p3p_3d[2].y,pts_p3p_3d[2].z,
            pts_p3p_3d[3].x,pts_p3p_3d[3].y,pts_p3p_3d[3].z
        );*/

    t1 = chrono::steady_clock::now();
    //Mat()这个参数指的是畸变系数向量
    cv::solvePnP(pts_p3p_3d, pts_p3p_2d, K, cv::Mat(), r, t, false,cv::SOLVEPNP_P3P); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
    t2 = chrono::steady_clock::now();
    time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cv::Rodrigues(r, R);//r为旋转向量形式,利用cv的Rodrigues()函数将旋转向量转换为旋转矩阵
    cout << "solve pnp in opencv cost time: " << time_used.count() << " seconds." << endl;
    cout << "R=" << endl << R << endl;
    cout << "t=" << endl << t << endl;
    cout << "calling bundle adjustment" << endl;
    cout << "***********************************SOLVEPNP_P3P***********************************" << endl;
}



//实现特征匹配
void find_feature_matches(const cv::Mat& img_1, const cv::Mat& img_2,
                          std::vector<cv::KeyPoint>& keypoints_1, std::vector<cv::KeyPoint>& keypoints_2,
                          std::vector<cv::DMatch>& matches)
{
    //-- 初始化
    cv::Mat descriptors_1, descriptors_2;
    // used in OpenCV3
    cv::Ptr<cv::FeatureDetector> detector = cv::ORB::create();
    cv::Ptr<cv::DescriptorExtractor> descriptor = cv::ORB::create();
    // use this if you are in OpenCV2
    // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" );
    // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" );

    cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");

    //-- 第一步:检测 Oriented FAST 角点位置
    detector->detect(img_1, keypoints_1);
    detector->detect(img_2, keypoints_2);

    //-- 第二步:根据角点位置计算 BRIEF 描述子
    descriptor->compute(img_1, keypoints_1, descriptors_1);
    descriptor->compute(img_2, keypoints_2, descriptors_2);

    //-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
    vector<cv::DMatch> match;
    // BFMatcher matcher ( NORM_HAMMING );
    matcher->match(descriptors_1, descriptors_2, match);

    //-- 第四步:匹配点对筛选
    double min_dist = 10000, max_dist = 0;

    //找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
    for (int i = 0; i < descriptors_1.rows;i++)
    {
        double dist = match[i].distance;
        if(dist > max_dist) max_dist = dist;
        if(dist < min_dist) min_dist = dist;
    }

    printf("-- Max dist : %f \n", max_dist);;
    printf("-- Min dist : %f \n", min_dist);

    //当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
    for ( int i = 0; i < descriptors_1.rows; i++ )
    {
        if ( match[i].distance <= max ( 2*min_dist, 30.0 ))
        {
            matches.push_back ( match[i] );
        }
    }
}

//实现像素坐标到相机坐标的转换(求出来的只是包含相机坐标下的x,y的二维点)
cv::Point2d pixel2cam(const cv::Point2d& p, const cv::Mat& K)
{
    return cv::Point2d(
            ((p.x - K.at<double>(0,2))/K.at<double>(0,0)),
            ((p.y - K.at<double>(1,2))/K.at<double>(1,1))
    );
}

1.5.3 CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(pnps)

set( CMAKE_CXX_STANDARD 14)
set( CMAKE_BUILD_TYPE Release )

find_package(OpenCV 3.1 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRECTORIES})

include_directories("/usr/include/eigen3")

add_executable(pnps src/pnps.cpp)

target_link_libraries( pnps
        ${OpenCV_LIBS}
        )

1.5.4 输出结果为

/home/bupo/shenlan/zuoye/cap7/pnps/cmake-build-release/pnps ./src/1.png ./src/2.png ./src/1_depth.png ./src/2_depth.png
[ INFO:0] Initialize OpenCL runtime...
-- Max dist : 95.000000 
-- Min dist : 7.000000 
一共找到了81组匹配点
3d-2d pairs: 77
使用cv_PnP求解 位姿
***********************************SOLVEPNP_ITERATIVE***********************************
solve pnp in opencv cost time: 0.000981819 seconds.
R=
[0.9979193252225089, -0.05138618904650331, 0.03894200717386666;
 0.05033852907733834, 0.9983556574295412, 0.02742286944793203;
 -0.04028712992734059, -0.02540552801469367, 0.9988651091656532]
t=
[-0.1255867099750398;
 -0.007363525258777434;
 0.06099926588678889]
calling bundle adjustment
***********************************SOLVEPNP_ITERATIVE***********************************
***********************************SOLVEPNP_EPNP***********************************
solve pnp in opencv cost time: 0.00010083 seconds.
R=
[0.9978654469053291, -0.05171629787598204, 0.03987448314938394;
 0.05055337177925334, 0.9982813811698801, 0.02964187260119165;
 -0.04133892202484725, -0.02756281087914256, 0.9987649297919227]
t=
[-0.1266609334454452;
 -0.01111413717711941;
 0.05673412814657697]
calling bundle adjustment
***********************************SOLVEPNP_EPNP***********************************
***********************************SOLVEPNP_UPNP***********************************
solve pnp in opencv cost time: 5.0345e-05 seconds.
R=
[0.9978654469053291, -0.05171629787598204, 0.03987448314938394;
 0.05055337177925334, 0.9982813811698801, 0.02964187260119165;
 -0.04133892202484725, -0.02756281087914256, 0.9987649297919227]
t=
[-0.1266609334454452;
 -0.01111413717711941;
 0.05673412814657697]
calling bundle adjustment
***********************************SOLVEPNP_UPNP***********************************
***********************************SOLVEPNP_DLS***********************************
solve pnp in opencv cost time: 4.746e-05 seconds.
R=
[0.9978654469053291, -0.05171629787598204, 0.03987448314938394;
 0.05055337177925334, 0.9982813811698801, 0.02964187260119165;
 -0.04133892202484725, -0.02756281087914256, 0.9987649297919227]
t=
[-0.1266609334454452;
 -0.01111413717711941;
 0.05673412814657697]
calling bundle adjustment
***********************************SOLVEPNP_DLS***********************************
***********************************SOLVEPNP_P3P***********************************
solve pnp in opencv cost time: 2.0098e-05 seconds.
R=
[0.9982328467626991, -0.04009678360638762, 0.0438569445864655;
 0.03857211555269049, 0.9986400289063435, 0.03507541258100014;
 -0.04520371163773695, -0.03332177381773087, 0.9984218967169202]
t=
[-0.1276949195981435;
 -0.02542013264231638;
 0.04538521283166519]
calling bundle adjustment
***********************************SOLVEPNP_P3P***********************************

进程已结束,退出代码0

1.5.5 报错解决

  • OpenCV(3.4.1) Error: Assertion failed (npoints == 4) in solvePnP, file /home/bupo/opencv-3.4.1/modules/calib3d/src/solvepnp.cpp, line 113 terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(3.4.1) /home/bupo/opencv-3.4.1/modules/calib3d/src/solvepnp.cpp:113: error: (-215) npoints == 4 in function solvePnP
  • 这是由于P3P的方式只能是4个点作为输入,可以参考:

1.5.6 感悟

  • 处理速度上没法比较,可能数据太小,有时这个快,有时那个快

1.6. 在PnP 优化中,将第一个相机的观测也考虑进来,程序应如何书写?最后结果会有何变化?

参考:视觉slam十四讲第七章课后习题6


1.7. 在ICP 程序中,将空间点也作为优化变量考虑进来,程序应如何书写?最后结果会有何变化?

参考:视觉slam十四讲第七章课后习题7


1.8. 在特征点匹配过程中,不可避免地会遇到误匹配的情况。如果我们把错误匹配输入到PnP 或ICP 中,会发生怎样的情况?你能想到哪些避免误匹配的方法?

  • 目前书中用的是根据汉明距离的暴力匹配方法,然后根据经验参数(30或者是最小距离的两倍)对匹配子根据其距离进行筛选。
  • 如果误匹配情况输入到PnP或是ICP中,再加上迭代算法选择不正确,初值估计不准确,就很容易导致计算结果产生误差,更有甚者会让迭代过程不稳定,甚至报错。
  • 目前比较流行的避免误匹配方法有:
  1. 交叉匹配(在暴力匹配的基础上再匹配一次,如果两次结果一致,则认为是个特征点,如果不一致则滤掉,BFMatcher XX (NORM_HAMMING, true) )
  2. KNN匹配(K邻近匹配,匹配时候选择K个与特征点相似的点,一般K是2,如果区别足够大,则选择最相似的点作为匹配点,bfMatcher->knnMatch(descriptors1, descriptors2, knnMatches, 2) )
  3. RANSAC(随机采样一致性,利用两个图像之间的单应矩阵,根据重投影误差判定某个匹配是不是正确匹配,findHomography)
  • 等等,一般可以跟觉已有的成熟框架如ORB_SLAM2等等观察其对于不同场景所采取的避免误匹配的方法。同样,对于后端,在优化时可以用Huber损失函数等等增强优化算法的鲁棒性。

1.9. 使用Sophus 的SE3 类,自己设计g2o 的节点与边,实现PnP 和ICP 的优化。

  • 这个课本已经实现了的,参考文章:【slam十四讲第二版】【课本例题代码向】【第七讲~视觉里程计Ⅰ】【1OpenCV的ORB特征】【2手写ORB特征】【3对极约束求解相机运动】【4三角测量】【5求解PnP】【3D-3D:ICP】
  • 其中PnP的实现,看文章的5.2
  • 其中ICP的实现,看文章的6.2

1.10. 在Ceres 中实现PnP 和ICP 的优化。

参考文章:在ceres中实现ICP优化(仅优化位姿)

举报

相关推荐

0 条评论