OpenCV2:特征匹配及其优化

OpenCV2简单的特征匹配中对使用OpenCV2进行特征匹配的步骤做了一个简单的介绍,其匹配出的结果是非常粗糙的,在这篇文章中对使用OpenCV2进行匹配的细化做一个简单的总结。主要包括以下几个内容:

  • DescriptorMatcher
  • DMatcher
  • KNN匹配
  • 计算两视图的基础矩阵F,并细化匹配结果
  • 计算两视图的单应矩阵H,并细化匹配结果

DescriptorMatcher 和 DMatcher

DescriptorMatcher是匹配特征向量的抽象类,在OpenCV2中的特征匹配方法都继承自该类(例如:BFmatcher,FlannBasedMatcher)。该类主要包含了两组匹配方法:图像对之间的匹配以及图像和一个图像集之间的匹配。

用于图像对之间匹配的方法的声明

// Find one best match for each query descriptor (if mask is empty).
    CV_WRAP void match( const Mat& queryDescriptors, const Mat& trainDescriptors,
                CV_OUT vector<DMatch>& matches, const Mat& mask=Mat() ) const;
    // Find k best matches for each query descriptor (in increasing order of distances).
    // compactResult is used when mask is not empty. If compactResult is false matches
    // vector will have the same size as queryDescriptors rows. If compactResult is true
    // matches vector will not contain matches for fully masked out query descriptors.
    CV_WRAP void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors,
                   CV_OUT vector<vector<DMatch> >& matches, int k,
                   const Mat& mask=Mat(), bool compactResult=false ) const;
    // Find best matches for each query descriptor which have distance less than
    // maxDistance (in increasing order of distances).
    void radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors,
                      vector<vector<DMatch> >& matches, float maxDistance,
                      const Mat& mask=Mat(), bool compactResult=false ) const;

方法重载,用于图像和图像集匹配的方法声明

CV_WRAP void match( const Mat& queryDescriptors, CV_OUT vector<DMatch>& matches,
                const vector<Mat>& masks=vector<Mat>() );
    CV_WRAP void knnMatch( const Mat& queryDescriptors, CV_OUT vector<vector<DMatch> >& matches, int k,
           const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
    void radiusMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
                   const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );

DMatcher 是用来保存匹配结果的,主要有以下几个属性

CV_PROP_RW int queryIdx; // query descriptor index
    CV_PROP_RW int trainIdx; // train descriptor index
    CV_PROP_RW int imgIdx;   // train image index

    CV_PROP_RW float distance;

在图像匹配时有两种图像的集合,查找集(Query Set)和训练集(Train Set),对于每个Query descriptor,DMatch中保存了和其最好匹配的Train descriptor。另外,每个train image会生成多个train descriptor。

如果是图像对之间的匹配的话,由于所有的train descriptor都是由一个train image生成的,所以在匹配结果DMatch中所有的imgIdx是一样的,都为0.

KNNMatch

匹配过程中很可能发生错误的匹配,错误的匹配主要有两种:匹配的特征点事错误的,图像上的特征点无法匹配。常用的删除错误的匹配有

  • 交叉过滤

    如果第一幅图像的一个特征点和第二幅图像的一个特征点相匹配,则进行一个相反的检查,即将第二幅图像上的特征点与第一幅图像上相应特征点进行匹配,如果匹配成功,则认为这对匹配是正确的。

    OpenCV中的BFMatcher已经包含了这种过滤   BFMatcher matcher(NORM_L2,true),在构造BFMatcher是将第二个参数设置为true。

  • 比率测试

    KNNMatch,可设置K = 2 ,即对每个匹配返回两个最近邻描述符,仅当第一个匹配与第二个匹配之间的距离足够小时,才认为这是一个匹配。

在抽象基类DescriptorMatcher中封装了knnMatch方法,具体使用方法如下:

void FeatureMatchTest::knnMatch(vector<DMatch>& matches) {

    const float minRatio = 1.f / 1.5f;
    const int k = 2;

    vector<vector<DMatch>> knnMatches;
    matcher->knnMatch(leftPattern->descriptors, rightPattern->descriptors, knnMatches, k);

    for (size_t i = 0; i < knnMatches.size(); i++) {
        const DMatch& bestMatch = knnMatches[i][0];
        const DMatch& betterMatch = knnMatches[i][1];

        float  distanceRatio = bestMatch.distance / betterMatch.distance;
        if (distanceRatio < minRatio)
            matches.push_back(bestMatch);
    }
}

 

RASIC方法计算基础矩阵,并细化匹配结果

如果已经知道了两视图(图像)间的多个点的匹配,就可以进行基础矩阵F的计算了。OpenCV2中可以使用findFundamentalMat方法,其声明如下:

//! finds fundamental matrix from a set of corresponding 2D points
CV_EXPORTS_W Mat findFundamentalMat( InputArray points1, InputArray points2,
                                     int method=FM_RANSAC,
                                     double param1=3., double param2=0.99,
                                     OutputArray mask=noArray());

参数说明:

points1,points2 两幅图像间相匹配的点,点的坐标要是浮点数(float或者double)

第三个参数method是用来计算基础矩阵的具体方法,是一个枚举值。

param1,param2保持默认值即可。

主要来说下mask参数,有N个匹配点用来计算基础矩阵,则该值有N个元素,每个元素的值为0或者1.值为0时,代表该匹配点事错误的匹配(离群值),只在使用RANSAC和LMeds方法时该值有效,

可以使用该值来删除错误的匹配。

另外,在匹配完成后使用得到的匹配点来计算基础矩阵时,首先需要将特征点对齐,并且将特征点转换为2D点,具体实现如下:

//Align all points
    vector<KeyPoint> alignedKps1, alignedKps2;
    for (size_t i = 0; i < matches.size(); i++) {
        alignedKps1.push_back(leftPattern->keypoints[matches[i].queryIdx]);
        alignedKps2.push_back(rightPattern->keypoints[matches[i].trainIdx]);
    }

    //Keypoints to points
    vector<Point2f> ps1, ps2;
    for (unsigned i = 0; i < alignedKps1.size(); i++)
        ps1.push_back(alignedKps1[i].pt);

    for (unsigned i = 0; i < alignedKps2.size(); i++)
        ps2.push_back(alignedKps2[i].pt);

使用RANSAC方法计算基础矩阵后可以得到一个status向量,用来删除错误的匹配

//优化匹配结果
    vector<KeyPoint> leftInlier;
    vector<KeyPoint> rightInlier;
    vector<DMatch> inlierMatch;

    int index = 0;
    for (unsigned i = 0; i < matches.size(); i++) {
        if (status[i] != 0){
            leftInlier.push_back(alignedKps1[i]);
            rightInlier.push_back(alignedKps2[i]);
            matches[i].trainIdx = index;
            matches[i].queryIdx = index;
            inlierMatch.push_back(matches[i]);
            index++;
        }
    }
    leftPattern->keypoints = leftInlier;
    rightPattern->keypoints = rightInlier;
    matches = inlierMatch;

计算单应矩阵H,并细化匹配结果

同基础矩阵类似,得到匹配的特征点后也可以计算单应矩阵。

//! computes the best-fit perspective transformation mapping srcPoints to dstPoints.
CV_EXPORTS_W Mat findHomography( InputArray srcPoints, InputArray dstPoints,
                                 int method=0, double ransacReprojThreshold=3,
                                 OutputArray mask=noArray());

参数说明:

srcPoints,dstPoints是两视图中匹配的点

method 是计算单应矩阵所使用的方法,是一个枚举值。

ransacReprojThreshold 是允许的最大反投影错误,只在使用RANSAC方法时有效。

mask 同findFundamentalMat 类似,指出匹配的点是不是离群值,用来优化匹配结果。

void FeatureMatchTest::refineMatcheswithHomography(vector<DMatch>& matches, double reprojectionThreshold, Mat& homography){
    const int minNumbermatchesAllowed = 8;
    if (matches.size() < minNumbermatchesAllowed)
        return;

    //Prepare data for findHomography
    vector<Point2f> srcPoints(matches.size());
    vector<Point2f> dstPoints(matches.size());

    for (size_t i = 0; i < matches.size(); i++) {
        srcPoints[i] = rightPattern->keypoints[matches[i].trainIdx].pt;
        dstPoints[i] = leftPattern->keypoints[matches[i].queryIdx].pt;
    }

    //find homography matrix and get inliers mask
    vector<uchar> inliersMask(srcPoints.size());
    homography = findHomography(srcPoints, dstPoints, CV_FM_RANSAC, reprojectionThreshold, inliersMask);

    vector<DMatch> inliers;
    for (size_t i = 0; i < inliersMask.size(); i++){
        if (inliersMask[i])
            inliers.push_back(matches[i]);
    }
    matches.swap(inliers);
}

 

匹配结果对比

基础矩阵后的过滤 单应矩阵后的过滤
交叉过滤 KNNMatch

 

代码说明

定义了Pattern结构用来保存匹配过程中需要用到的数据

struct Pattern
{
    cv::Mat image;
    std::vector<cv::KeyPoint>  keypoints;
    cv::Mat descriptors;

    Pattern(cv::Mat& img) :
        image(img) {}
};

 

将各种匹配方法封装到了一个类中,在该类的构造函数中填充Pattern取得匹配所需的数据

FeatureMatchTest::FeatureMatchTest(std::shared_ptr<Pattern> left, std::shared_ptr<Pattern> right, std::shared_ptr<cv::DescriptorMatcher> matcher) :
leftPattern(left), rightPattern(right), matcher(matcher) {

    //step1:Create detector
    int minHessian = 400;
    SurfFeatureDetector detector(minHessian);

    //step2:Detecte keypoint
    detector.detect(leftPattern->image, leftPattern->keypoints);
    detector.detect(rightPattern->image, rightPattern->keypoints);

    //step3:Compute descriptor
    detector.compute(leftPattern->image, leftPattern->keypoints, leftPattern->descriptors);
    detector.compute(rightPattern->image, rightPattern->keypoints, rightPattern->descriptors);
}
时间: 2025-01-11 21:27:18

OpenCV2:特征匹配及其优化的相关文章

OpenCV2简单的特征匹配

特征的匹配大致可以分为3个步骤: 特征的提取 计算特征向量 特征匹配 对于3个步骤,在OpenCV2中都进行了封装.所有的特征提取方法都实现FeatureDetector接口,DescriptorExtractor接口则封装了对特征向量(特征描述符)的提取,而所有特征向量的匹配都继承了DescriptorMatcher接口. 简单的特征匹配 int main() { const string imgName1 = "x://image//01.jpg"; const string im

Robotics Lab3 ——图像特征匹配、跟踪与相机运动估计

Robotics Lab3 --图像特征匹配.跟踪与相机运动估计 图像特征匹配 图像特征点 携带摄像头的机器人在运动过程中,会连续性地获取多帧图像,辅助其感知周围环境和自身运动.时间序列上相连的两幅或多幅图像,通常存在相同的景物,只是它们在图像中的位置不同.而位置的变换恰恰暗含了相机的运动,这时就需要相邻图像间的相似性匹配. 选取一大块图像区域进行运动估计是不可取的.已知图像在计算机内部是以数字矩阵的形式存储的,[如灰度图的每个元素代表了单个像素的灰度值].而对于点的提取和匹配较为方便,且和数字

[翻译]鲁棒的尺度不变特征匹配在遥感图像配准中应用(Robust Scale-Invariant Feature Matching for Remote Sensing Image Registration)

李乔亮,汪国有,刘建国,会员,IEEE,和陈少波 2008年8月7日接收;2008年10月22日和2008年11月27日修改.2009年2月2日首版:当前版本出版于2009年4月17日.本项工作由中国国家基础研究项目60672060资助. 中国湖北省武汉市华中科技大学模式识别与人工智能国家重点实验室,邮编430074(邮箱:[email protected];   [email protected];  [email protected];  [email protected]) 数字对象识别编

特征匹配篇

1. ORB特征匹配 BruteForce-Hamming匹配 //使用ORB特征匹配两张图片,并进行运行时间,对称性测试,ratio测试 #include <iostream> #include <ctime> //#include <dirent.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/non

特征检测和特征匹配方法

一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图像中的特征点就非常重要.这篇文章我总结了视觉领域最常用的几种特征点以及特征匹配的方法. 在计算机视觉领域,兴趣点(也称关键点或特征点)的概念已经得到了广泛的应用, 包括目标识别. 图像配准. 视觉跟踪. 三维重建等. 这个概念的原理是, 从图像中选取某些特征点并对图像进行局部分析,而非观察整幅图像. 只要图像中有足够多可检

OpenCV探索之路(二十三):特征检测和特征匹配方法汇总

http://www.cnblogs.com/skyfsm/p/7401523.html 一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图像中的特征点就非常重要.这篇文章我总结了视觉领域最常用的几种特征点以及特征匹配的方法. 在计算机视觉领域,兴趣点(也称关键点或特征点)的概念已经得到了广泛的应用, 包括目标识别. 图像配准. 视觉跟踪. 三维重建等. 这个概念的原理是

BF和FLANN特征匹配

BF特征点匹配原理:   暴力匹配 (段匹配) 1 #include <opencv2/opencv.hpp> 2 #include <opencv2/xfeatures2d.hpp> 3 #include <iostream> 4 5 using namespace cv; 6 using namespace std; 7 using namespace cv::xfeatures2d; 8 9 int main(int argc, char** argv) { 10

【特征匹配】SIFT原理之KD树+BBF算法解析

继上一篇中已经介绍了SIFT原理点击打开链接,最后得到了一系列特征点,每个特征点对应一个128维向量.假如现在有两副图片都已经提取到特征点,现在要做的就是匹配上相似的特征点. 相似性查询有两种基本方式:1.范围查询:即给点查询点和查询阈值,从数据集中找出所有与查询点距离小于阈值的点. 2.K近邻查询:给点查询点及正整数K,从数据集中找到与查询点最近的K个数据,当K=1时,就是最近邻查询. 特征匹配算子可以分为两类:1.穷举法:即将数据集中的点与查询点逐一计算距离,如果图1提取到N1个特征点,图2

特征匹配

计算机视觉课堂笔记 回顾:特征提取中分为点(Harris等),线(Canny算子),区域(MSER)等特征的提取. 相应的特征匹配就会有特征点匹配,直线匹配,曲线匹配,区域匹配. 而在众多研究中以点匹配居多,点匹配的基本原则:利用图像点周围的信息来描述点,如灰度信息,颜色信息,梯度信息等,然后进行 相似性度量. 点匹配典型方法: 基于灰度分布的匹配:Cross-correlation: 基于梯度分布的匹配:SIFT:Daisy descriptor: 其他匹配方法:Eigenvector:ICP