文档章节

OpenCV开发笔记(六十七):红胖子8分钟带你深入了解特征点暴力匹配(图文并茂+浅显易懂+程序源码)

红模仿_红胖子
 红模仿_红胖子
发布于 07/14 22:33
字数 2466
阅读 55
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/107348874 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门)

OpenCV开发专栏(点击传送门)

上一篇:《OpenCV开发笔记(六十六):红胖子8分钟带你总结形态学操作-膨胀、腐蚀、开运算、闭运算、梯度、顶帽、黑帽(图文并茂+浅显易懂+程序源码)》 下一篇:持续补充中…

<br>

前言

  红胖子,来也!   前面讲解了特征点,那么匹配特征点,原始图像与目标图像之间存在哪些匹配的同类型的特征点,匹配点的多少则是作为相似度的输入,达到一定相似度则认为匹配到了。   本篇章结合sift特征点和暴力匹配,进行特征点匹配实现步骤原理讲解。

<br>

Demo

  在这里插入图片描述   在这里插入图片描述   在这里插入图片描述   在这里插入图片描述   第四个图片的匹配效果不好,想要效果好需要根据图像特点去选择特征点提取的方式,此处主要是为了讲解流程。

<br>

本文章博客地址:https://blog.csdn.net/qq21497936/article/details/107348874

最佳特征匹配(暴力匹配)

  最佳特征匹配总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配,这也是BruteForce(暴力法)的原始含义,涉及到的类为BFMatcher类。

本篇章使用的是Sift特征点

概述

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

SIFT算法特点以及SIFT相关函数的使用

  • SIFT特征是局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;
  • 区分性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;
  • 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;
  • 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;
  • 可扩展性,可以很方便的与其他形式的特征向量进行联合;

  有关sift、surf、orb和使用请查看博文   针对图像场景的特点,选择不同的特征点,列出之前特征点相关的博文:   《OpenCV开发笔记(六十三):红胖子8分钟带你深入了解SIFT特征点(图文并茂+浅显易懂+程序源码)》   《OpenCV开发笔记(六十四):红胖子8分钟带你深入了解SURF特征点(图文并茂+浅显易懂+程序源码)》   《OpenCV开发笔记(六十五):红胖子8分钟带你深入了解ORB特征点(图文并茂+浅显易懂+程序源码)

BFMatcher类的使用

定义

// 定义匹配器
cv::Ptr<cv::BFMatcher> pBFMatcher;
// 定义结果存放
std::vector<cv::DMatch> listDMatch;
// 存储特征点检测器检测特征后的描述字
cv::Mat descriptor1;
cv::Mat descriptor2;

特征点提取

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

匹配

// bfMatcher暴力匹配
pBFMatcher->match(descriptor1, descriptor2, listDMatch);

BFMatcher相关函数原型

static Ptr<BFMatcher> create( int normType=NORM_L2,
                          bool crossCheck=false ) ;
  • 参数一:int类型的normType,欧式距离,NORM类型中: NORM_L1,NORM_L2,NORM_HAMMING,NORM_HAMMING 2,L1、L2是SIFT和SURF描述符的最优选选择; NORM_HAMMING应与ORB、BRISK和BRIEF搭配使用; NORM_HAMMING2应该与ORB一起使用,当WTA_K==3或者4时。
  • 参数二:bool类型的crossCheck,如果是false,这将是BFMatcher在找到k时的默认行为:每个查询描述符的最近邻居。如果crossCheck==true,则使用k=1只返回对(i,j),对于第i个查询描述符,在匹配器的集合是最近的,反之亦然,即BFMatcher只返回一致的对。当存在异常值时,这种技术通常会产生最佳结果,而离群值数量很少够了。这是D.Lowe在筛纸中使用的比率测试的替代方法。
void BFMatcher::match( InputArray queryDescriptors,
                     InputArray trainDescriptors,
                     std::vector<DMatch>& matches,
                     InputArray mask=noArray() ) const;
  • 参数一:InputArray类型的queryDescriptors,查询描述符集,一般cv::Mat,某个特征提取的描述符。
  • 参数二:InputArray类型的trainDescriptors,训练描述符集,此处输入的应该是没有加入到类对象集合种的(该类有训练的数据集合),一般cv::Mat,某个特征提取的描述符。
  • 参数三:匹配匹配项。如果在掩码中屏蔽了查询描述符,则不会为此添加匹配项描述符。因此,匹配项的大小可能小于查询描述符计数。
  • 参数四:指定输入查询和训练矩阵之间允许的匹配的掩码描述符。

绘制匹配关系图函数原型

  • 参数一:InputArray类型的img1,图像1。
  • 参数二:std::vector<KeyPoint>类型的keypoints1,图像1的关键点。
  • 参数三:InputArray类型的img2,图像2。
  • 参数四:std::vector<KeyPoint>类型的keypoints2,图像2的关键点。
  • 参数五:std::vector<DMatch>类型的matchers1to2,从第一个图像匹配到第二个图像,这意味着keypoints1[i]在keypoints2中有一个对应的点[matches[i]]。
  • 参数六:InputOutputArray类型的outImg,为空时,默认并排绘制输出图像以及连接关键点;若不为空,则在图像上绘制关系点。
  • 参数七:Scalar类型的matcherColor,匹配颜色匹配(线和连接的关键点)的颜色。如果颜色为cv::Scalar::all(-1),则为随机颜色。
  • 参数八:Scalar类型的singlePointColor,颜色单个关键点(圆)的颜色,这意味着关键点没有匹配到的则认是该颜色。
  • 参数九:std::vector<char>类型的matchersMask,确定绘制的匹配项目,若是为空,则表示全部绘制。
  • 参数十:int类型的flags,查看枚举DrawMatchesFlags,如下:   在这里插入图片描述

<br>

Demo

void OpenCVManager::testBFMatcher()
{
    QString fileName1 = "13.jpg";
    int width = 400;
    int height = 300;

    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    cv::resize(srcMat, srcMat, 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<cv::xfeatures2d::SIFT> _pSift = cv::xfeatures2d::SiftFeatureDetector::create();

    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<cv::BFMatcher> pBFMatcher;
    pBFMatcher = cv::BFMatcher::create();
    std::vector<cv::DMatch> listDMatch;
    cv::Mat descriptor1;
    cv::Mat descriptor2;

    bool moveFlag = true;  // 移动的标志,不用每次都匹配
    windowMat = cv::Scalar(0, 0, 0);
    while(true)
    {
        cv::Mat mat;
        {
            std::vector<cv::KeyPoint> keyPoints1;
            std::vector<cv::KeyPoint> keyPoints2;

            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::printf(windowMat, 0 + width * 0, 10 + height * 0, "k1x");
            cvui::trackbar(windowMat, 0 + width * 0, 20 + height * 0, 165, &k1x, 0, 100);
            cvui::printf(windowMat, 0 + width * 0, 70 + height * 0, "k1y");
            cvui::trackbar(windowMat, 0 + width * 0, 80 + height * 0, 165, &k1y, 0, 100);

            cvui::printf(windowMat, width / 2 + width * 0, 10 + height * 0, "k2x");
            cvui::trackbar(windowMat, width / 2 + width * 0, 20 + height * 0, 165, &k2x, 0, 100);
            cvui::printf(windowMat, width / 2 + width * 0, 70 + height * 0, "k2y");
            cvui::trackbar(windowMat, width / 2 + width * 0, 80 + height * 0, 165, &k2y, 0, 100);

            cvui::printf(windowMat, 0 + width * 0, 10 + height * 0 + height / 2, "k3x");
            cvui::trackbar(windowMat, 0 + width * 0, 20 + height * 0 + height / 2, 165, &k3x, 0, 100);
            cvui::printf(windowMat, 0 + width * 0, 70 + height * 0 + height / 2, "k3y");
            cvui::trackbar(windowMat, 0 + width * 0, 80 + height * 0 + height / 2, 165, &k3y, 0, 100);

            cvui::printf(windowMat, width / 2 + width * 0, 10 + height * 0 + height / 2, "k4x");
            cvui::trackbar(windowMat, width / 2 + width * 0, 20 + height * 0 + height / 2, 165, &k4x, 0, 100);
            cvui::printf(windowMat, width / 2 + width * 0, 70 + height * 0 + height / 2, "k4y");
            cvui::trackbar(windowMat, width / 2 + width * 0, 80 + 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)
            {
                moveFlag = true;
            }

            std::vector<cv::Point2f> srcPoints;
            std::vector<cv::Point2f> 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(srcMat,
                                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);
                _pSift->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);
                _pSift->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);

                // bfMatcher暴力匹配
                pBFMatcher->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;
        }
    }
}

<br>

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

  对应版本号v1.61.0

<br>

上一篇:《OpenCV开发笔记(六十六):红胖子8分钟带你总结形态学操作-膨胀、腐蚀、开运算、闭运算、梯度、顶帽、黑帽(图文并茂+浅显易懂+程序源码)》 下一篇:持续补充中…

红模仿_红胖子
粉丝 0
博文 27
码字总数 49806
作品 0
长沙
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
4.4K
3
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
【opencv】图形的绘制

1.矩形图像的绘制: 原函数:void cvRectangle(CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8,int shift=0) img就是需要绘制的图像 pt1 and pt......

其实我是兔子
2014/10/08
1.2K
1
beego API开发以及自动化文档

beego API开发以及自动化文档 beego1.3版本已经在上个星期发布了,但是还是有很多人不了解如何来进行开发,也是在一步一步的测试中开发,期间QQ群里面很多人都问我如何开发,我的业余时间实在...

astaxie
2014/06/25
2.7W
22

没有更多内容

加载失败,请刷新页面

加载更多

【实用技巧】MAC苹果电脑怎么远程?

一般电脑远程包括三方面: 1、如果从外部windows或者linux终端连接到mac苹果电脑; 2、从苹果电脑内如何远程外面的windows、linux和mac等; 3、苹果和安卓手机怎么远程连接苹果/Windows电脑。...

osc_doeya1ck
22分钟前
4
0
虚拟主机和VPS主机有哪些不同点呢

虚拟主机是一种在单一主机或主机群上,实现多网域服务的方法,可以运行多个网站或服务的技术。vps主机是将一台服务器分割成多个虚拟专享服务器的服务。实现VPS的技术分为容器技术和虚拟机技术...

osc_b88oux8w
24分钟前
19
0
合理的使用MySQL乐观锁与悲观锁

针对 MySQL的乐观锁与悲观锁的使用,基本都是按照业务场景针对性使用的。针对每个业务场景,对应的使用锁。 但是两种锁无非都是解决并发所产生的问题。下面我们来看看如何合理的使用乐观锁与...

php开源社区
24分钟前
9
0
fusionpbx 中文 汉化

  自己以前有从事过呼叫中心的工作经验,然而由于自己是从事后端开发,对于前端界面的开发还是有些吃力,但是自己却又想自己搭建一套呼叫中心,所以购买了一台云服务器并克隆了FusionPBX的...

osc_ydeb2o99
25分钟前
9
0
关于大O表示法和小O表示法

上节课老师讲了一下各种表示法,当时没咋听懂,后来查了一些资料弄懂了,记录一下。 主要是从维基百科上看的。http://en.wikipedia.org/wiki/Big_O_notation 大O表示法: f(x) = O(g(x)) 表示...

osc_3mzamgkq
27分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部