若该文为原创文章,未经容许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/107837715
各位读者,常识无穷而人力有穷,要么改需要,要么找专业人士,要么本人钻研
红瘦子 (红模拟) 的博文大全:开发技术汇合(蕴含 Qt 实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬联合等等)继续更新中 …(点击传送门)
OpenCV 开发专栏(点击传送门)
上一篇:《OpenCV 开发笔记(六十八):红瘦子 8 分钟带你应用特色点 Flann 最邻近差值匹配辨认(图文并茂 + 浅显易懂 + 程序源码)》
下一篇:继续补充中…
前言
红瘦子,来也!
特色点、匹配,那么如何应用特色点和匹配来辨认已有的物体,也就剩最要害的最初一步:寻找已知的物体了。
Demo
寻找已知物体
本篇章应用 sift/surf 特色点
sift 特色点
尺度不变特色变换(Scale-invariant feature transform,SIFT),是用于图像处理畛域的一种形容。这种形容具备尺度不变性,可在图像中检测出关键点,是一种部分特征描述子。
surf 特色点
SURF 算法采纳了很多办法来对每一步进行优化从而进步速度。剖析显示在后果成果相当的状况下 SURF 的速度是 SIFT 的 3 倍。SURF 长于解决具备含糊和旋转的图像,然而不长于解决视角变动和光照变动。(SIFT 特色是部分特色,其对旋转、尺度缩放、亮度变动放弃不变性,对视角变动、仿射变换、噪声也放弃肯定水平的稳定性)。
针对图像场景的特点,抉择不同的特色点,列出之前特色点相干的博文:
《OpenCV 开发笔记(六十三):红瘦子 8 分钟带你深刻理解 SIFT 特色点(图文并茂 + 浅显易懂 + 程序源码)》
《OpenCV 开发笔记(六十四):红瘦子 8 分钟带你深刻理解 SURF 特色点(图文并茂 + 浅显易懂 + 程序源码)》
《OpenCV 开发笔记(六十五):红瘦子 8 分钟带你深刻理解 ORB 特色点(图文并茂 + 浅显易懂 + 程序源码)》
本篇章使用暴力、最邻近差值匹配
暴力匹配
最佳特色匹配总是尝试所有可能的匹配,从而使得它总可能找到最佳匹配,这也是 BruteForce(暴力法)的原始含意,波及到的类为 BFMatcher 类。
《OpenCV 开发笔记(六十七):红瘦子 8 分钟带你深刻理解特色点暴力匹配(图文并茂 + 浅显易懂 + 程序源码)》
最近邻差值匹配
一种近似法,算法更快然而找到的是最近邻近似匹配,所以当咱们须要找到一个绝对好的匹配然而不须要最佳匹配的时候往往应用 FlannBasedMatcher。
《OpenCV 开发笔记(六十八):红瘦子 8 分钟带你应用特色点 Flann 最邻近差值匹配辨认(图文并茂 + 浅显易懂 + 程序源码)》
概述
对已知物体:过滤、去噪后、提取已知物体的特色点;
对场景:过滤、去噪后、提取场景的特色点;
对已知物体特色点汇合和场景中的特色点汇合去匹配,计算投影矩阵;
若胜利计算变换矩阵就示意辨认到物体;
通过原始的四个点地位进行变换矩阵计算,即可失去场景中的已知物体的四个顶点,该四个顶点连接起来就是已知物体的地位。
特色点汇合计算变换矩阵函数原型
Mat findHomography(InputArray srcPoints,
InputArray dstPoints,
int method = 0,
double ransacReprojThreshold = 3,
OutputArray mask=noArray(),
const int maxIters = 2000,
const double confidence = 0.995);
- 参数一:InputArray 类型的 srcPoints,源立体上的对应点,能够是 CV_32FC2 的矩阵类型或者 vector<Point2>;
- 参数二:InputArray 类型的 dstPoints;指标立体上的对应点,可 以 是
CV 32FC2 的矩阵类型或者 vector<Point2>;
- 参数三:int 类型的 method,用于计算单应矩阵的办法,如下图:
- 参数四:double 类型的 ransacReprojThreshold,最大容许重投影谬误将点对视为内联线(仅用于 RANSAC 和 RHO 办法);
- 参数五:OutputArray 类型的 mask,由鲁棒办法(RANSAC 或 LMEDS)设置的可选输入掩码。留神输出掩码值被疏忽。;
- 参数六:const int 类型的 maxIters,RANSAC 迭代的最大数量。;
- 参数七:const double 类型的 confidence,置信水平,介于 0 和 1 之间;
矩阵变换函数原型
void perspectiveTransform( InputArray src,
InputArray dst,
InputArray m);
- 参数一:InputArray 类型的 src,输出两通道或三通道浮点数组;每个元素是要转换的二维 / 三维向量。
- 参数二:InputArray 类型的 dst,与 src 大小和类型雷同的输入数组;
- 参数三:InputArray 类型的 h,3×3 或 4 ×4 浮点转换矩阵。
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/107837715
Demo 源码
void OpenCVManager::testFindKnownObject()
{
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(450, 10, 10, true, true);
cv::Ptr<cv::Feature2D> _pFeature2D;
cv::Ptr<cv::DescriptorMatcher> _pDescriptorMatcher;
int type = 0;
int findType = 0;
int k1x = 25;
int k1y = 25;
int k2x = 75;
int k2y = 25;
int k3x = 75;
int k3y = 75;
int k4x = 25;
int k4y = 75;
// 定义匹配器
cv::Ptr<cv::FlannBasedMatcher> pFlannBasedMatcher = cv::FlannBasedMatcher::create();
cv::Ptr<cv::BFMatcher> pBFMatcher = cv::BFMatcher::create();
// 定义后果寄存
std::vector<cv::DMatch> listDMatch;
// 存储特色点检测器检测特色后的形容字
cv::Mat descriptor1;
cv::Mat descriptor2;
bool moveFlag = true; // 挪动的标记,不必每次都匹配
std::vector<cv::Point2f> obj_corners(4);
std::vector<cv::Point2f> scene_corners(4);
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 findTypeOld = findType;
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 / 4 + width * 0 - 20, 40 + height * 0, str.c_str());
cvui::trackbar(windowMat, width / 2 + width * 0, 0 + height * 0, 165, &findType, 0, 1);
switch(findType)
{
case 0:
str = "BFMatcher";
_pDescriptorMatcher = pBFMatcher;
break;
case 1:
str = "FlannBasedMatcher";
_pDescriptorMatcher = pFlannBasedMatcher;
break;
default:
break;
}
cvui::printf(windowMat, width / 4 * 3 + width * 0 - 20, 40 + 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 || findTypeOld != findType)
{
typeOld = type;
findTypeOld = findType;
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 最近邻匹配
_pDescriptorMatcher->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);
// 定义两个局部变量
std::vector<cv::Point2f> obj;
std::vector<cv::Point2f> scene;
// 从匹配胜利的匹配对中获取关键点
for(int index = 0; index < listDMatch.size(); index++)
{obj.push_back(keyPoints1[listDMatch[index].queryIdx].pt);
scene.push_back(keyPoints2[listDMatch[index].trainIdx].pt);
}
// 计算透视变换
cv::Mat H = cv::findHomography(obj, scene, CV_RANSAC);
// 从待测图片中获取角点
obj_corners[0] = cv::Point2f(0,0);
obj_corners[1] = cv::Point2f(srcMat.cols,0);
obj_corners[2] = cv::Point2f(srcMat.cols, srcMat.rows);
obj_corners[3] = cv::Point2f(0, srcMat.rows);
// 进行透视变换
cv::perspectiveTransform(obj_corners, scene_corners, H);
}
// 绘制出角点之间的线
qDebug() << __FILE__ << __LINE__
<< scene_corners[0].x
<< scene_corners[0].y
<< scene_corners[1].x
<< scene_corners[1].y;
cv::line(windowMat,
scene_corners[0] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
scene_corners[1] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
cv::Scalar(0, 0, 255), 2);
cv::line(windowMat,
scene_corners[1] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
scene_corners[2] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
cv::Scalar(0, 0, 255), 2);
cv::line(windowMat,
scene_corners[2] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
scene_corners[3] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
cv::Scalar(0, 0, 255), 2);
cv::line(windowMat,
scene_corners[3] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
scene_corners[0] + cv::Point2f(srcMat.cols * 1, srcMat.rows * 0),
cv::Scalar(0, 0, 255), 2);
}
cv::imshow(windowName, windowMat);
// 更新
cvui::update();
// 显示
// esc 键退出
if(cv::waitKey(25) == 27)
{break;}
}
}
工程模板:对应版本号 v1.63.0
对应版本号 v1.63.0
上一篇:《OpenCV 开发笔记(六十八):红瘦子 8 分钟带你应用特色点 Flann 最邻近差值匹配辨认(图文并茂 + 浅显易懂 + 程序源码)》
下一篇:继续补充中…