若该文为原创文章,未经容许不得转载原博主博客地址: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分钟带你总结形态学操作-收缩、侵蚀、开运算、闭运算、梯度、顶帽、黑帽(图文并茂+浅显易懂+程序源码)》下一篇:继续补充中… 前言 红瘦子,来也! 后面解说了特色点,那么匹配特色点,原始图像与指标图像之间存在哪些匹配的同类型的特色点,匹配点的多少则是作为类似度的输出,达到肯定类似度则认为匹配到了。 本篇章联合sift特色点和暴力匹配,进行特色点匹配实现步骤原理解说。
Demo 第四个图片的匹配成果不好,想要成果好须要依据图像特点去抉择特色点提取的形式,此处次要是为了解说流程。
本文章博客地址: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,如下: Demovoid 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; } }}
...