博客
关于我
OpenCV开发笔记(六十八):红胖子8分钟带你使用特征点Flann最邻近差值匹配识别(图文并茂+浅显易懂+程序源码)
阅读量:445 次
发布时间:2019-03-06

本文共 9033 字,大约阅读时间需要 30 分钟。

若该文为原创文章,未经允许不得转载

原博主博客地址:
原博主博客导航:
本文章博客地址:
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

上一篇:《》

下一篇:持续补充中…

 

前言

  红胖子,来也!

  前面讲解了特征点,那么匹配特征点,就是匹配两者的相似度,相似度达到一定的阈值,则认为识别了。
  考虑性能,除开暴力匹配外,还有最近邻匹配。

 

Demo

  

  
  
  

 

最近邻匹配(FLANN)

  FlannBasedMatcher中FLANN的含义是Fast Library forApproximate Nearest Neighbors,目前最完整的(近似)最近邻匹配。不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制。

  从字面意思可知它是一种近似法,算法更快但是找到的是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。
  当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度,但是相应地算法速度或者算法精度会受到影响。

本篇章使用sift/surf特征点

sift特征点

  尺度不变特征变换(Scale-invariant feature transform,SIFT),是用于图像处理领域的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子。

surf特征点

  SURF算法采用了很多方法来对每一步进行优化从而提高速度。分析显示在结果效果相当的情况下SURF的速度是SIFT的3倍。SURF善于处理具有模糊和旋转的图像,但是不善于处理视角变化和光照变化。(SIFT特征是局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性)。

针对图像场景的特点,选择不同的特征点,列出之前特征点相关的博文:
  《》
  《》
  《》

FlannBasedMatcher类的使用

定义

// 定义匹配器cv::Ptr
pFlannBasedMatcher = cv::FlannBasedMatcher::create();// 定义结果存放std::vector
listDMatch;// 存储特征点检测器检测特征后的描述字cv::Mat descriptor1;cv::Mat descriptor2;

特征点提取

pFlannBasedMatcher->detectAndCompute(srcMat1, cv::Mat(), keyPoints1, descriptor1);pFlannBasedMatcher->detectAndCompute(srcMat1, cv::Mat(), keyPoints1, descriptor1);

匹配

// FlannBasedMatcher最近邻匹配pFlannBasedMatcher->match(descriptor1, descriptor2, listDMatch); FlannBasedMatcher相关函数原型static Ptr
create() ;

无参数

FlannBasedMatcher::match( InputArray queryDescriptors,                          InputArray trainDescriptors,                          std::vector
& matches, InputArray mask=noArray() ) const;
  • 参数一:InputArray类型的queryDescriptors,查询描述符集,一般cv::Mat,某个特征提取的描述符。
  • 参数二:InputArray类型的trainDescriptors,训练描述符集,此处输入的应该是没有加入到类对象集合种的(该类有训练的数据集合),一般cv::Mat,某个特征提取的描述符。
  • 参数三:std::vector类型的matches。如果在掩码中屏蔽了查询描述符,则不会为此添加匹配项描述符。因此,匹配项的大小可能小于查询描述符计数。
  • 参数四:InputArray类型的mask,指定输入查询和训练矩阵之间允许的匹配的掩码描述符。
    绘制匹配关系图函数原型
void drawMatches( InputArray img1,                  const std::vector
& keypoints1, InputArray img2, const std::vector
& keypoints2, const std::vector
& matches1to2, InputOutputArray outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const std::vector
& matchesMask=std::vector
(), int flags=DrawMatchesFlags::DEFAULT );
  • 参数一:InputArray类型的img1,图像1。
  • 参数二:std::vector类型的keypoints1,图像1的关键点。
  • 参数三:InputArray类型的img2,图像2。
  • 参数四:std::vector类型的keypoints2,图像2的关键点。
  • 参数五:std::vector类型的matchers1to2,从第一个图像匹配到第二个图像,这意味着keypoints1[i]在keypoints2中有一个对应的点[matches[i]]。
  • 参数六:InputOutputArray类型的outImg,为空时,默认并排绘制输出图像以及连接关键点;若不为空,则在图像上绘制关系点。
  • 参数七:Scalar类型的matcherColor,匹配颜色匹配(线和连接的关键点)的颜色。如果颜色为cv::Scalar::all(-1),则为随机颜色。
  • 参数八:Scalar类型的singlePointColor,颜色单个关键点(圆)的颜色,这意味着关键点没有匹配到的则认是该颜色。
  • 参数九:std::vector类型的matchersMask,确定绘制的匹配项目,若是为空,则表示全部绘制。
  • 参数十:int类型的flags,查看枚举DrawMatchesFlags,如下:
      
 

Demo

void OpenCVManager::testFlannBasedMatcher(){    QString fileName1 = "21.jpg";    QString fileName2 = "24.jpg";    int width = 400;    int height = 300;    cv::Mat srcMat = cv::imread(fileName1.toStdString());    cv::Mat srcMat3 = cv::imread(fileName2.toStdString());    cv::resize(srcMat, srcMat, cv::Size(width, height));    cv::resize(srcMat3, srcMat3, cv::Size(width, height));    cv::String windowName = _windowTitle.toStdString();    cvui::init(windowName);    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, srcMat.rows * 3),                                srcMat.type());    cv::Ptr
_pSift = cv::xfeatures2d::SiftFeatureDetector::create(); cv::Ptr
_pSurf = cv::xfeatures2d::SurfFeatureDetector::create(); cv::Ptr
_pFeature2D; int type = 0; int k1x = 0; int k1y = 0; int k2x = 100; int k2y = 0; int k3x = 100; int k3y = 100; int k4x = 0; int k4y = 100; // 定义匹配器 cv::Ptr
pFlannBasedMatcher = cv::FlannBasedMatcher::create(); // 定义结果存放 std::vector
listDMatch; // 存储特征点检测器检测特征后的描述字 cv::Mat descriptor1; cv::Mat descriptor2; bool moveFlag = true; // 移动的标志,不用每次都匹配 windowMat = cv::Scalar(0, 0, 0); while(true) { cv::Mat mat; { std::vector
keyPoints1; std::vector
keyPoints2; int typeOld = type; int k1xOld = k1x; int k1yOld = k1y; int k2xOld = k2x; int k2yOld = k2y; int k3xOld = k3x; int k3yOld = k3y; int k4xOld = k4x; int k4yOld = k4y; mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1), cv::Range(srcMat.cols * 0, srcMat.cols * 1)); mat = cv::Scalar(0); cvui::trackbar(windowMat, 0 + width * 0, 0 + height * 0, 165, &type, 0, 1); cv::String str; switch(type) { case 0: str = "sift"; _pFeature2D = _pSift; break; case 1: str = "surf"; _pFeature2D = _pSurf; break; default: break; } cvui::printf(windowMat, width / 2 + width * 0, 20 + height * 0, str.c_str()); cvui::printf(windowMat, 0 + width * 0, 60 + height * 0, "k1x"); cvui::trackbar(windowMat, 0 + width * 0, 70 + height * 0, 165, &k1x, 0, 100); cvui::printf(windowMat, 0 + width * 0, 120 + height * 0, "k1y"); cvui::trackbar(windowMat, 0 + width * 0, 130 + height * 0, 165, &k1y, 0, 100); cvui::printf(windowMat, width / 2 + width * 0, 60 + height * 0, "k2x"); cvui::trackbar(windowMat, width / 2 + width * 0, 70 + height * 0, 165, &k2x, 0, 100); cvui::printf(windowMat, width / 2 + width * 0, 120 + height * 0, "k2y"); cvui::trackbar(windowMat, width / 2 + width * 0, 130 + height * 0, 165, &k2y, 0, 100); cvui::printf(windowMat, 0 + width * 0, 30 + height * 0 + height / 2, "k3x"); cvui::trackbar(windowMat, 0 + width * 0, 40 + height * 0 + height / 2, 165, &k3x, 0, 100); cvui::printf(windowMat, 0 + width * 0, 90 + height * 0 + height / 2, "k3y"); cvui::trackbar(windowMat, 0 + width * 0, 100 + height * 0 + height / 2, 165, &k3y, 0, 100); cvui::printf(windowMat, width / 2 + width * 0, 30 + height * 0 + height / 2, "k4x"); cvui::trackbar(windowMat, width / 2 + width * 0, 40 + height * 0 + height / 2, 165, &k4x, 0, 100); cvui::printf(windowMat, width / 2 + width * 0, 90 + height * 0 + height / 2, "k4y"); cvui::trackbar(windowMat, width / 2 + width * 0, 100 + height * 0 + height / 2, 165, &k4y, 0, 100); if( k1xOld != k1x || k1yOld != k1y || k2xOld != k2x || k2yOld != k2y || k3xOld != k3x || k3yOld != k3y || k4xOld != k4x || k4yOld != k4y || typeOld != type) { moveFlag = true; } std::vector
srcPoints; std::vector
dstPoints; srcPoints.push_back(cv::Point2f(0.0f, 0.0f)); srcPoints.push_back(cv::Point2f(srcMat.cols - 1, 0.0f)); srcPoints.push_back(cv::Point2f(srcMat.cols - 1, srcMat.rows - 1)); srcPoints.push_back(cv::Point2f(0.0f, srcMat.rows - 1)); dstPoints.push_back(cv::Point2f(srcMat.cols * k1x / 100.0f, srcMat.rows * k1y / 100.0f)); dstPoints.push_back(cv::Point2f(srcMat.cols * k2x / 100.0f, srcMat.rows * k2y / 100.0f)); dstPoints.push_back(cv::Point2f(srcMat.cols * k3x / 100.0f, srcMat.rows * k3y / 100.0f)); dstPoints.push_back(cv::Point2f(srcMat.cols * k4x / 100.0f, srcMat.rows * k4y / 100.0f)); cv::Mat M = cv::getPerspectiveTransform(srcPoints, dstPoints); cv::Mat srcMat2; cv::warpPerspective(srcMat3, srcMat2, M, cv::Size(srcMat.cols, srcMat.rows), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(0)); mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1), cv::Range(srcMat.cols * 1, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, srcMat2, 1.0f, 0.0f, mat); if(moveFlag) { moveFlag = false; //特征点检测 // _pSift->detect(srcMat, keyPoints1); _pFeature2D->detectAndCompute(srcMat, cv::Mat(), keyPoints1, descriptor1); //绘制特征点(关键点) cv::Mat resultShowMat; cv::drawKeypoints(srcMat, keyPoints1, resultShowMat, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2), cv::Range(srcMat.cols * 0, srcMat.cols * 1)); cv::addWeighted(mat, 0.0f, resultShowMat, 1.0f, 0.0f, mat); //特征点检测 // _pSift->detect(srcMat2, keyPoints2); _pFeature2D->detectAndCompute(srcMat2, cv::Mat(), keyPoints2, descriptor2); //绘制特征点(关键点) cv::Mat resultShowMat2; cv::drawKeypoints(srcMat2, keyPoints2, resultShowMat2, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2), cv::Range(srcMat.cols * 1, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, resultShowMat2, 1.0f, 0.0f, mat); // FlannBasedMatcher最近邻匹配 pFlannBasedMatcher->match(descriptor1, descriptor2, listDMatch); // drawMatch绘制出来,并排显示了,高度一样,宽度累加(因为两个宽度相同,所以是两倍了) cv::Mat matchesMat; cv::drawMatches(srcMat, keyPoints1, srcMat2, keyPoints2, listDMatch, matchesMat); mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3), cv::Range(srcMat.cols * 0, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, matchesMat, 1.0f, 0.0f, mat); } } cv::imshow(windowName, windowMat); // 更新 cvui::update(); // 显示 // esc键退出 if(cv::waitKey(25) == 27) { break; } }}
 

工程模板:对应版本号v1.62.0

  对应版本号v1.62.0

 

上一篇:《》

下一篇:持续补充中…

你可能感兴趣的文章
MySQL5.6的zip包安装教程
查看>>
mysql5.7 for windows_MySQL 5.7 for Windows 解压缩版配置安装
查看>>
Webpack 基本环境搭建
查看>>
mysql5.7 安装版 表不能输入汉字解决方案
查看>>
MySQL5.7.18主从复制搭建(一主一从)
查看>>
MySQL5.7.19-win64安装启动
查看>>
mysql5.7.19安装图解_mysql5.7.19 winx64解压缩版安装配置教程
查看>>
MySQL5.7.37windows解压版的安装使用
查看>>
mysql5.7免费下载地址
查看>>
mysql5.7命令总结
查看>>
mysql5.7安装
查看>>
mysql5.7性能调优my.ini
查看>>
MySQL5.7新增Performance Schema表
查看>>
Mysql5.7深入学习 1.MySQL 5.7 中的新增功能
查看>>
Webpack 之 basic chunk graph
查看>>
Mysql5.7版本单机版my.cnf配置文件
查看>>
mysql5.7的安装和Navicat的安装
查看>>
mysql5.7示例数据库_Linux MySQL5.7多实例数据库配置
查看>>
Mysql8 数据库安装及主从配置 | Spring Cloud 2
查看>>
mysql8 配置文件配置group 问题 sql语句group不能使用报错解决 mysql8.X版本的my.cnf配置文件 my.cnf文件 能够使用的my.cnf配置文件
查看>>