首先感谢Brook_icv的渊博知识,https://www.cnblogs.com/wangguchangqing/p/4333873.html
/*
 * Struct for matching: query descriptor index, train descriptor index, train image index and distance between descriptors.
 */ 
 /*
  * DMatch主要用来储存匹配信息的结构体,query是要匹配的描述子,train是被匹配的描述子,在Opencv中进行匹配时
  * void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<DMatch>& matches, const Mat& mask ) const
  * match函数的参数中位置在前面的为query descriptor,后面的是 train descriptor
  * 例如:query descriptor的数目为20,train descriptor数目为30,则DescriptorMatcher::match后的vector<DMatch>的size为20
  * 若反过来,则vector<DMatch>的size为30
  
 */ 
struct CV_EXPORTS_W_SIMPLE DMatch 
    //默认构造函数,FLT_MAX是无穷大 
    //#define FLT_MAX         3.402823466e+38F        /* max value */ 
    CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} 
    //DMatch构造函数 
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) : 
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} 
    //DMatch构造函数 
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : 
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} 
   
    //queryIdx为query描述子的索引,match函数中前面的那个描述子 
    CV_PROP_RW int queryIdx; // query descriptor index 
    //trainIdx为train描述子的索引,match函数中后面的那个描述子 
    CV_PROP_RW int trainIdx; // train descriptor index 
    //imgIdx为进行匹配图像的索引 
    //例如已知一幅图像的sift描述子,与其他十幅图像的描述子进行匹配,找最相似的图像,则imgIdx此时就有用了。 
    CV_PROP_RW int imgIdx;   // train image index 
    //distance为两个描述子之间的距离 
    CV_PROP_RW float distance; 
    //DMatch比较运算符重载,比较的是DMatch中的distance,小于为true,否则为false 
    // less is better 
    bool operator<( const DMatch &m ) const 
    
        return distance < m.distance; 
    
}; 
class KeyPoint{
        Point2f   pt;  //坐标
        float       size;//特征点邻域直径
        float       angle;//特征点的方向,值为【0,360】,负值表示不使用
        float       response;
        int         octave;//特征点所在的图像金字塔的组
        int         class_id;//用于聚类的id
}

  • 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 indexCV_PROP_RW int trainIdx; // train descriptor indexCV_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 pointsvector<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 pointsvector<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 findHomographyvector<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 maskvector<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 detectorint minHessian = 400;SurfFeatureDetector detector(minHessian);//step2:Detecte keypointdetector.detect(leftPattern->image, leftPattern->keypoints);detector.detect(rightPattern->image, rightPattern->keypoints);//step3:Compute descriptordetector.compute(leftPattern->image, leftPattern->keypoints, leftPattern->descriptors);detector.compute(rightPattern->image, rightPattern->keypoints, rightPattern->descriptors);
}

图像处理——特征检测涉及到的数据结构和匹配方法相关推荐

  1. java游戏房间匹配_一种游戏房间匹配方法与流程

    本发明涉及计算机技术领域,尤其涉及一种游戏房间匹配方法. 背景技术: 在目前的网络游戏中,游戏对象与游戏对象会通过互联网在线上进行竞技比赛,很多时候都需要多名玩家共同参与,现有的游戏对象的配对方法大多 ...

  2. OpenCV计算机图像处理 —— 凸性缺陷 + 点多边形测试 + 形状匹配 + 轮廓分层与cv.findContours()

    OpenCV计算机图像处理 -- 凸性缺陷 + 点多边形测试 + 形状匹配 + 轮廓分层与cv.findContours() 目录 OpenCV计算机图像处理 -- 凸性缺陷 + 点多边形测试 + 形 ...

  3. 【图像处理】——特征匹配(SIFT特征检测器+FLANN特征匹配方法+KNN近邻最优匹配筛选)——cv.xfeatures2d.SIFT_create()sift.detectAndCompute

    转载请注明地址 目录 1.特征检测和特征匹配方法 (1)特征检测算法 (2)特征匹配算法 (3)各种特征检测算法的比较 2.特征匹配的基本步骤(附带主要的函数) (1)图像预处理--灰度化(模板--查 ...

  4. OpenCV精进之路(十五):特征检测和特征匹配方法汇总

    一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图像中的特征点就非常重要. ...

  5. 特征检测和特征匹配方法汇总

    一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图像中的特征点就非常重要. ...

  6. 图像处理中涉及的灰度图、彩色图以及深度图概念

    图像处理中涉及最多的概念就是图像的类型,为了很好的理解图像的概念以及处理图片,我们就需要对常见的图像具有一定的概念. 我们首先介绍一下生活中常见的图像格式: 1.bmp格式:这是一种不常见的图像格式, ...

  7. 特征检测与特征匹配方法汇总

    一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图像中的特征点就非常重要. ...

  8. 特征检测和特征匹配方法

    特征检测和特征匹配方法 一幅图像中总存在着其独特的像素点,这些点我们可以认为就是这幅图像的特征,成为特征点.计算机视觉领域中的很重要的图像特征匹配就是一特征点为基础而进行的,所以,如何定义和找出一幅图 ...

  9. 图像处理HALCON中的模板匹配方法总结

    HALCON中的模板匹配方法总结 摘要 1. Shape-Based matching的基本流程 2. 基于形状匹配的参数关系与优化 摘要 德国MVTec公司开发的HALCON机器视觉开发软件,提供了 ...

最新文章

  1. 均方根误差不超过_描述数值预报随机误差的利器:随机物理扰动组合方案
  2. Et.parse(xml) #39gbk#39 codec cant decode byte
  3. ajax常见问题汇总(二)
  4. sql语句的进化--原始篇
  5. 转:两种转换mysql数据编码的方法-latin1转utf8
  6. 阿里 蚂蚁自研 IDE 研发框架 OpenSumi 正式开源
  7. Python入门--字符串的切片操作
  8. js 调用微信浏览器内置方法,启动支付
  9. 投资学翻译及感悟 Lazy Prices
  10. 在Cocos2d-x中使用CocosBuilder
  11. iOS AirPlay 投屏调研
  12. 39.JavaScript中Promise的基本概念、使用方法,回调地狱规避、链式编程
  13. html bootstrap主题,10大的 Metro 风格的 Bootstrap 主题和模板
  14. 数字电视业务PSI-SI学习系列
  15. 加班费计算(节假日、补班)
  16. 猿辅导国奖选手妈妈在线分享教育经:数学新生代的成长之路
  17. LNK1120: 一个无法解析的外部命令
  18. 网站开启https后很慢_网站开启https后地址栏安全锁灰色或黄色叹号
  19. 有哪些提供比较好的网页模板网站?
  20. 【计算机毕业设计】视频教学管理系统

热门文章

  1. JavaScript定义注册页面
  2. 数控加工零件工艺性分析
  3. java 银联支付_java服务器端移动银联支付的流程
  4. discuz mysql cpu 100_Discuz导致MYSQL CPU 占用 100%?
  5. 镜像翻转_【资讯】装动镜世界版假面骑士时王登场,镜像翻转也算新规?
  6. Eclipse使用入门
  7. Http 通过setHeader隐藏ip
  8. UE_材质_UV计算相关
  9. 在服务器上设置虚拟传入,如何配置 SMTP 虚拟服务器以进行邮件传递
  10. 前端工程师 后段工程师_如何像工程师一样思考