若该文为原创文章,未经容许不得转载
原博主博客地址: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,如下:
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;}
}
}
工程模板:对应版本号 v1.61.0
对应版本号 v1.61.0
上一篇:《OpenCV 开发笔记(六十六):红瘦子 8 分钟带你总结形态学操作 - 收缩、侵蚀、开运算、闭运算、梯度、顶帽、黑帽(图文并茂 + 浅显易懂 + 程序源码)》
下一篇:继续补充中…