乐趣区

关于opencv:OpenCV开发笔记六十八红胖子8分钟带你使用特征点Flann最邻近差值匹配识别图文并茂浅显易懂程序源码

若该文为原创文章,未经容许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/107357296
各位读者,常识无穷而人力有穷,要么改需要,要么找专业人士,要么本人钻研
红瘦子 (红模拟) 的博文大全:开发技术汇合(蕴含 Qt 实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬联合等等)继续更新中 …(点击传送门)

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

上一篇:《OpenCV 开发笔记(六十七):红瘦子 8 分钟带你深刻理解特色点暴力匹配(图文并茂 + 浅显易懂 + 程序源码)》
下一篇:继续补充中…

前言

  红瘦子,来也!
  后面解说了特色点,那么匹配特色点,就是匹配两者的类似度,类似度达到肯定的阈值,则认为辨认了。
  思考性能,除开暴力匹配外,还有最近邻匹配。

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 特色是部分特色,其对旋转、尺度缩放、亮度变动放弃不变性,对视角变动、仿射变换、噪声也放弃肯定水平的稳定性)。
针对图像场景的特点,抉择不同的特色点,列出之前特色点相干的博文:
《OpenCV 开发笔记(六十三):红瘦子 8 分钟带你深刻理解 SIFT 特色点(图文并茂 + 浅显易懂 + 程序源码)》
《OpenCV 开发笔记(六十四):红瘦子 8 分钟带你深刻理解 SURF 特色点(图文并茂 + 浅显易懂 + 程序源码)》
《OpenCV 开发笔记(六十五):红瘦子 8 分钟带你深刻理解 ORB 特色点(图文并茂 + 浅显易懂 + 程序源码)》

FlannBasedMatcher 类的应用

定义

// 定义匹配器
cv::Ptr<cv::FlannBasedMatcher> pFlannBasedMatcher = cv::FlannBasedMatcher::create();
// 定义后果寄存
std::vector<cv::DMatch> 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<FlannBasedMatcher> create() ;

无参数

FlannBasedMatcher::match( InputArray queryDescriptors,
                          InputArray trainDescriptors,
                          std::vector<DMatch>& matches,
                          InputArray mask=noArray()) const;
  • 参数一:InputArray 类型的 queryDescriptors,查问描述符集,个别 cv::Mat,某个特征提取的描述符。
  • 参数二:InputArray 类型的 trainDescriptors,训练描述符集,此处输出的应该是没有退出到类对象汇合种的(该类有训练的数据汇合),个别 cv::Mat,某个特征提取的描述符。
  • 参数三:std::vector<DMatch> 类型的 matches。如果在掩码中屏蔽了查问描述符,则不会为此增加匹配项描述符。因而,匹配项的大小可能小于查问描述符计数。
  • 参数四:InputArray 类型的 mask,指定输出查问和训练矩阵之间容许的匹配的掩码描述符。

绘制匹配关系图函数原型

void drawMatches( InputArray img1,
                  const std::vector<KeyPoint>& keypoints1,
                  InputArray img2,
                  const std::vector<KeyPoint>& keypoints2,
                  const std::vector<DMatch>& matches1to2,
                  InputOutputArray outImg,
                  const Scalar& matchColor=Scalar::all(-1),
                  const Scalar& singlePointColor=Scalar::all(-1),
                  const std::vector<char>& matchesMask=std::vector<char>(),
                  int flags=DrawMatchesFlags::DEFAULT );
  • 参数一: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,如下:

  

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<cv::xfeatures2d::SIFT> _pSift = cv::xfeatures2d::SiftFeatureDetector::create();
    cv::Ptr<cv::xfeatures2d::SURF> _pSurf = cv::xfeatures2d::SurfFeatureDetector::create();

    cv::Ptr<cv::Feature2D> _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<cv::FlannBasedMatcher> pFlannBasedMatcher = cv::FlannBasedMatcher::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 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<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(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

上一篇:《OpenCV 开发笔记(六十七):红瘦子 8 分钟带你深刻理解特色点暴力匹配(图文并茂 + 浅显易懂 + 程序源码)》
下一篇:继续补充中…

退出移动版