在ubuntu+clion+opencv3.1.0中运行opencv中第三方库的程序
背景:
在学习高翔视觉SLAM十四讲的时候,学到视觉里程计方面,主要是学习特征检测与匹配,视觉SLAM方面介绍这块比较少,推荐学习opencv.推荐《opencv3编程入门》毛星云写的,学习opencv入门比较好的书籍。当然你的英文水平好的话可以学习官方文档教程。
注意:由于目前发行的opencv3关于特征检测算子(SURF、SIFT、ORB)已经从官方行的opencv3中移除,转移到第三方库xfeatures2d.所以想要在opencv3运行这些程序的话需要遵照下面的步骤。
一.配置opencv3.4.3(含contrib3.4.3)(按照对应版本下载)
1.下载
https://github.com/opencv/opencv/releases 请下载opencv3.4.3 Source code(zip);
https://github.com/opencv/opencv_contrib/releases/tag/3.4.3 请下载opencv_contrib3.4.3 Source code(zip);
编译contrib主要是为了使用xfeatures2d中的API,角点检测、特征提取与匹配。
2.安装
2.1安装依赖(ubuntu一般已经安装好,只是为了检查一下)
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
2.2解压
我这里并没有用命令解压,直接手动解压到 Downloads
1.将opencv-3.4.3文件夹复制到home,重命名为opencv;
2.再将opencv_contrib-3.4.3文件夹复制到opencv文件夹,重命名为opencv_contrib;
3.在opencv中新建一个build文件夹,作为编译目的路径,到此准备工作ok~如下图所示。
3.编译
3.1安装cmake-gui(用图形界面编译安装不易出错,简介方便)
sudo apt-get install cmake-gui
安装好了之后,进入opencv文件打开终端进入cmake-gui图形界面
1.设置好源路径和目的路径后,点击Configure(选择Unix Makefiles),然后点击finish。
1)在CMAKE_BUILD_TYPE 值处输入RELEASE(若本身为Release则不用更改;这里说明一下,CMAKE_INSTALL_PREFIX为安装路径,系统默认为/usr/local,如若对ubuntu不熟悉,则不要更改,默认就好)。
2)在OPENCV_EXTRA_MODULES_PATH处加入opencv_contrib路径。
注意,不是选opencv_contrib文件夹,而是需要选中opencv_contrib里面的modules文件夹!
3.确认无误后,点击Configure;显示Configuring done后,点击Generate生成文件;这时资源文件就出现在build文件夹中,我们可以关闭cmake了。
3.3make并install
1.在opencv/build文件夹中打开终端,输入指令编译(此次时间漫长20分钟左右)。
sudo make
2.100%后,继续输入指令安装。
sudo make install
3.sudo make install 执行完毕后OpenCV编译过程就结束了,接下来就需要配置一些OpenCV的编译环境首先将OpenCV的库添加到路径,从而可以让系统找到。
sudo gedit /etc/ld.so.conf.d/opencv.conf
4.在文件中添加,然后保存关闭,提示的错误不用管它。
/usr/local/lib
5.执行命令,使配置路径生效
sudo ldconfig
6.配置bash
sudo gedit /etc/bash.bashrc
7.在文件最后加入两行,然后保存。
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
8.执行命令,使配置路径生效
source /etc/bash.bashrc
9.更新系统路径
sudo updatedb
10.到此位置,我们的opencv+opencv_contrib配置完成,已经可以调用opencv库函数啦。
实际操作演示
【特征点的检测与匹配】是计算机视觉中非常重要的技术之一。在物体检测、视觉跟踪、三维重建等领域都有很广泛的应用。
opencv提供了10种特征检测方法:
【FAST】
【STAR】
【SIFT】
【SURF】
【ORB】
【MSER】
【GFTT】
【HARRIS】
【Dense】
【SimpleBlob】
1【SURF特征点检测】
SURF—加速版的具有鲁棒性的特征算法(SIFT—尺寸不变特征变换算法的加速版),SURF最大特征在于采用harr特征以及积分图像的概念,
大大加速了程序的运行时间。应用于计算机视觉的物体识别、3D重建中。
缺点:严重依赖局部像素的梯度方向,容易出现方向不准等问题。
算法原理----①:选用二阶标准高斯函数作为滤波器(Hessian矩阵构造高斯金字塔尺度空间)
②:利用非极大值抑制初步确定特征点
③:精确定位极值点(采用三维线性插值法得到亚像素级的特征点)
④:选取特征点的主方向(梯度直方图,选大于等于bin值的那些方向)
⑤:构造surf特征点描述算子
绘制关键点:drawKeypoints()函数
void drawKeypoints(
const Mat&image, //输入图像
const vector&keypoints, //根据原图像得到的特征点
Mat& outImage, //输出图像
const Scalar& color=Scalar::all(-1), //关键点颜色(默认Scalar::all(-1))
int flags=DrawMatchesFlags::DEFAULT //绘制关键点的特征标识符(默认DrawMatchesFlags::DEFAULT)
);
KeyPoint类-----一个为特征点检测而生的数据结构,用于表示特征点。
class KeyPoint{
Point2f pt;//坐标
float size;//特征点邻域直径
float angle;//特征点的方向,值为[0,360],负值表示不使用
float response;
int octave;//特征点所在的图像金字塔的组
int class_id;//用于聚类的id
}
综合示例:SURF特征点检测
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(SURF)
set(CMAKE_CXX_STANDARD 11)
find_package( OpenCV REQUIRED )
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(SURF main.cpp)
target_link_libraries( SURF ${OpenCV_LIBS} )
//------------------------【SURF特征点检测】-----------------------------
//描述:使用FeatureDetector接口来发现感兴趣点
// 使用SurfFeatureDetector以及其函数detect来实现检测过程
// 使用函数drawKeypoints绘制检测到的关键点
//-----------------------------------------------------------------------
#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
int main()
{
Mat srcImage1 = imread("1.jpg", 1);
Mat srcImage2 = imread("2.jpg", 1);
if (!srcImage1.data || !srcImage2.data){cout << "读取图片出错" << endl;return false;}
imshow("原始图1", srcImage1);
imshow("原始图2", srcImage2);
int minHessian = 100;//SURF算子检测关键点
//定义一个SurfFeatureDetector(SURF)特征检测类对象
Ptr detector = SurfFeatureDetector::create(minHessian);
vector key_points_1, key_points_2;//vector模板类,存放任意的动态数组
Mat dstImage1, dstImage2;
//调用detect函数检测出SURF特征关键点,保存在vector容器中
detector->detectAndCompute(srcImage1, Mat(), key_points_1, dstImage1);
detector->detectAndCompute(srcImage2, Mat(), key_points_2, dstImage2);//可以分成detect和compute
Mat img_keypoints_1, img_keypoints_2;
//绘制特征关键点
drawKeypoints(srcImage1, key_points_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2, key_points_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
Ptr matcher = DescriptorMatcher::create("FlannBased");
vectormach;
matcher->match(dstImage1, dstImage2, mach);
double Max_dist = 0;
double Min_dist = 100;
for (int i = 0; i < dstImage1.rows; i++)
{
double dist = mach[i].distance;
if (dist < Min_dist)Min_dist = dist;
if (dist > Max_dist)Max_dist = dist;
}
cout << "最短距离" << Min_dist << endl;
cout << "最长距离" << Max_dist << endl;
vectorgoodmaches;
for (int i = 0; i < dstImage1.rows; i++)
{
if (mach[i].distance < 2 * Min_dist)goodmaches.push_back(mach[i]);
}
Mat img_maches;
drawMatches(srcImage1, key_points_1, srcImage2, key_points_2, goodmaches, img_maches);
for (int i = 0; i < goodmaches.size(); i++)
{
cout << "符合条件的匹配:" << goodmaches[i].queryIdx << "--" << goodmaches[i].trainIdx << endl;
}
imshow("效果图1", img_keypoints_1);
imshow("效果图2", img_keypoints_2);
imshow("匹配效果", img_maches);
waitKey(0);
return 0;
}
运行结果如下图所示
输入图像从参考下边那张图 ./1.jpg ./2.jpg
然后输入自己的工作目录
2 【FLANN特征检测与匹配】
检测关键点并计算描述符:detectAndCompute()
void detectAndCompute(
InputArray image, //图像
InputArray mask, //掩模
CV_OUT std::vector& keypoints,//输出关键点的集合
OutputArray descriptors,//计算描述符(descriptors[i]是为keypoints[i]的计算描述符)
bool useProvidedKeypoints=false //使用提供的关键点
);
从查询集中查找每个描述符的最佳匹配:DescriptorMatcher::match()方法。
void DescriptorMatcher::match(
const Mat& queryDescriptors, //查询描述符集
const Mat& trainDescriptors, //训练描述符集合
CV_OUT std::vector& matches, //匹配
InputArray mask=noArray() //指定输入查询和描述符的列表矩阵之间的允许匹配的掩码
);
综合示例:FLANN特征点检测
#include
#include
using namespace cv;
using namespace cv::xfeatures2d;
//FLANN对高维数据较快
int main()
{
Mat src1, src2;
src1 = imread("1.jpg");
src2 = imread("2.jpg");
if (src1.empty() || src2.empty()){printf("can ont load images....\n");return -1;}
imshow("image1", src1);
imshow("image2", src2);
int minHessian = 400;
//选择SURF特征
Ptrdetector = SURF::create(minHessian);
std::vectorkeypoints1;
std::vectorkeypoints2;
Mat descriptor1, descriptor2;
//检测关键点并计算描述符
detector->detectAndCompute(src1, Mat(), keypoints1, descriptor1);
detector->detectAndCompute(src2, Mat(), keypoints2, descriptor2);
//基于Flann的描述符匹配器
FlannBasedMatcher matcher;
std::vectormatches;
//从查询集中查找每个描述符的最佳匹配
matcher.match(descriptor1, descriptor2, matches);
double minDist = 1000;
double maxDist = 0;
for (int i = 0; i < descriptor1.rows; i++)
{
double dist = matches[i].distance;
printf("%f \n", dist);
if (dist > maxDist)
{
maxDist = dist;
}
if (dist < minDist)
{
minDist = dist;
}
}
//DMatch类用于匹配关键点描述符的
std::vectorgoodMatches;
for (int i = 0; i < descriptor1.rows; i++)
{
double dist = matches[i].distance;
if (dist < max(2.5*minDist, 0.02))
{
goodMatches.push_back(matches[i]);
}
}
Mat matchesImg;
drawMatches(src1, keypoints1, src2, keypoints2, goodMatches, matchesImg, Scalar::all(-1), Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow("output", matchesImg);
waitKey();
return 0;
}
【FLANN结合SURF进行关键点的描述和匹配】 :
//------------------【FLANN结合SURF进行关键点的描述和匹配】----------------------
#include
#include
#include
#include
#include
using namespace cv::xfeatures2d;
using namespace cv;
using namespace std;
int main()
{
//【1】载入原图
Mat srcImage = imread("1.jpg");
imshow("【原图】", srcImage);
//【2】对BGR空间的图像直接进行计算很费时间,所以,需要转换为灰度图
Mat srcGrayImage;
cvtColor(srcImage, srcGrayImage, CV_BGR2GRAY);
//【3】首先对两幅图像检测SURF关键点、提取测试图像描述符
vector keyPoint1;
Mat dstImage1, dstImage2;
Ptr surf = SURF::create(80);
surf->detect(srcGrayImage, keyPoint1);
Mat descriImage1;
surf->compute(srcGrayImage, keyPoint1, descriImage1);
//【4】先对原图的描述子进行保留-------邻近匹配
//FlannBasedMatcher FLMatcher;
//因为FlannBasedMatcher类的成员函数add()的参数是一个vector的容器,所以先定义一个这样的变量,并将原图的描述子放入容器中
//vector g_vdescriImage1(1, descriImage1);
//调用FlannBasedMatcher类的成员函数add,将原图的描述子放在FlannBasedMatcher的对象FLMatcher中
//FLMatcher.add(g_vdescriImage1);
//FLMatcher.train();
//【4】进行基于描述符的-------暴力匹配
BFMatcher matcher;
//因为FlannBasedMatcher类的成员函数add()的参数是一个vector的容器,所以先定义一个这样的变量,并将原图的描述子放入容器中
vector g_vdescriImage1(1, descriImage1);
//调用FlannBasedMatcher类的成员函数add,将原图的描述子放在FlannBasedMatcher的对象FLMatcher中
matcher.add(g_vdescriImage1);
matcher.train();
VideoCapture capture;
capture.open(0);
Mat frameImage, frameGrayImage;
while (waitKey(1) != 27)
{
capture >> frameImage;
//<1>为了提高计算效率,将图像转换为灰度图像
cvtColor(frameImage, frameGrayImage, CV_BGR2GRAY);
//<2>检测S关键点、提取测试图像描述符
vector keyPoints2;
surf->detect(frameGrayImage, keyPoints2);
Mat descriImage2;
surf->compute(frameGrayImage, keyPoints2, descriImage2);
//<3>将之前得到的原图的描述子和现在得到的描述子进行匹配(匹配训练和测试描述符)
//成员函数knnMatch的参数是二维的DMatch向量,所以首先定义一个该容器的向量
vector> knnDMatches;
//<4>用之前已经存放原图描述子的对象来计算------邻近匹配
//FLMatcher.knnMatch(descriImage2, knnDMatches, 2);
//<4>用之前已经存放原图描述子的对象来计算------暴力匹配
matcher.knnMatch(descriImage2, knnDMatches, 2);
//<5>根据劳氏算法,采集优秀的匹配点
vector goodMatches;
for (size_t i = 0; i < knnDMatches.size(); i++)
{
if (knnDMatches[i][0].distance < 0.6 * knnDMatches[i][1].distance)
{
goodMatches.push_back(knnDMatches[i][0]);
}
}
//<6>绘制匹配点并显示窗口
Mat dstImage;
drawMatches(frameImage, keyPoints2, srcImage, keyPoint1, goodMatches, dstImage);
imshow("【结果图】", dstImage);
}
return 0;
}
3、【寻找已知物体】
寻找已知物体:在Flann特征匹配的基础上,还可以进一步利用Homography映射找出已知物体。
集体来说就是利用findHomography函数通过匹配的关键点找出相应的变量,在利用perspectiveTransform函数映射点群。
寻找透视变换:findHomography()函数----找到并返回原图像和目标图像之间的透视变换H
Mat findHomography(
InputArray srcPoints, //原平面上的对应点
InputArray dstPoints, //目标平面上的对应点
int method=0,//用于计算单应矩阵的方法(默认0;CV_RANSAC---基于RANSAC的鲁棒性方法;CV_LMEDS---最小中值鲁棒性方法)
double ransacReprojThreshold=3,//(默认3)处理点对为内围层时,允许重投影误差的最大值
OutputArray mask=noArray()//可选参数
);
进行透视矩阵变换:perspectiveTransform()函数—进行向量透视矩阵变换
void perspectiveTransform(
InputArray src, //输入图像
OutputArray dst, //目标图像
InputArray m //变换矩阵
);
综合示例:寻找已知物体
//-------------------【寻找已知物体】---------------
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
int main()
{
Mat srcImage1 = imread("1.jpg");
Mat srcImage2 = imread("2.jpg");
imshow("【原图1】", srcImage1);
imshow("【原图2】", srcImage2);
Mat grayImage1, grayImage2;
cvtColor(srcImage1, grayImage1, CV_BGR2GRAY);
cvtColor(srcImage2, grayImage2, CV_BGR2GRAY);
//首先对两幅图像进行特征点的检测
//先准备参数
vector g_vKeyPoint1;
vector g_vKeyPoint2;
Ptr surf = SURF::create(400);
surf->detect(grayImage1, g_vKeyPoint1);
surf->detect(grayImage2, g_vKeyPoint2);
//利用得到的特征点计算特征描述子
//目的:对得到的每个特征点进行特征描述,整合到Mat类型的矩阵中(计算结果是Mat类型的)
//该得到的结果矩阵的行数就是特征点的个数,因为是对每个点进行描述,所以每行都会有一个描述的字子向量,共同构成Mat矩阵
Mat descriImage1, descriImage2;
surf->compute(grayImage1, g_vKeyPoint1, descriImage1);
surf->compute(grayImage2, g_vKeyPoint2, descriImage2);
//正式开始在两幅图像中进行匹配
//先得到一个匹配向量
FlannBasedMatcher FLMatcher;
vector g_vMatches;
//g_vMatches就是得到的匹配向量
FLMatcher.match(descriImage1, descriImage2, g_vMatches);
//用找最大最小值的方式找到 两幅图像中匹配的点的距离的最大值和最小值
//这里的 keyPoint1.size() 和 descriImage1.rows是一样的值,因为descriImage1的行数就是检测到的特征点的个数
double minDistance = g_vMatches[0].distance, maxDistance = g_vMatches[0].distance;
for (size_t i = 0; i < g_vKeyPoint1.size(); i++)
{
double currDistance = g_vMatches[i].distance;
if (currDistance < minDistance)
minDistance = currDistance;
if (currDistance > maxDistance)
maxDistance = currDistance;
}
//定义一个新的变量,用来存储 通过距离检测后 通过阀值的点
vector newMatches;
for (size_t i = 0; i < g_vKeyPoint1.size(); i++)
{
if (g_vMatches[i].distance < 2 * minDistance)
newMatches.push_back(g_vMatches[i]);
}
//用绘制函数对匹配向量进行绘制
Mat dstImage;
drawMatches(srcImage1, g_vKeyPoint1, srcImage2, g_vKeyPoint2, newMatches, dstImage
, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255))
, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), Mat(), 2);
imshow("【特征提取后的图像】", dstImage);
//=================================正式开始寻找已知物体============================
//为了调用 得到H矩阵findHomography函数,所以需要得到 匹配点所对应的特征点 然后作为参数传递给计算H矩阵的函数
//所以首先是进行 匹配点和对应的特征点的转换步骤
//将得到的点放入新的容器中,所以需要定义新的容器
vector g_vSrcPoint2f1;
vector g_vSrcPoint2f2;
for (size_t i = 0; i < newMatches.size(); i++)
{
g_vSrcPoint2f1.push_back(g_vKeyPoint1[newMatches[i].queryIdx].pt);
g_vSrcPoint2f2.push_back(g_vKeyPoint2[newMatches[i].trainIdx].pt);
}
//将得到的对应的特征点 计算H矩阵
Mat H = findHomography(g_vSrcPoint2f1, g_vSrcPoint2f2, 0);
////用得到的H矩阵 来进行透视矩阵变换 用到的是perspectiveTransform函数
//vector g_vCorners1(4);
//vector g_vCorners2(4);
//g_vCorners1[0] = Point2f(0, 0);
//g_vCorners1[1] = Point2f((float)srcImage1.cols, 0);
//g_vCorners1[2] = Point2f((float)srcImage1.cols, (float)srcImage1.rows);
//g_vCorners1[3] = Point2f(0, (float)srcImage1.rows);
//perspectiveTransform(g_vCorners1, g_vCorners2, H);
////在得到的两幅图像的合成图中绘制检测到的物体的直线
//line(dstImage, (Point)g_vCorners2[0] + Point(srcImage1.cols, 0), (Point)g_vCorners2[1] + Point(srcImage1.cols, 0)
// , Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);
//line(dstImage, (Point)g_vCorners2[1] + Point(srcImage1.cols, 0), (Point)g_vCorners2[2] + Point(srcImage1.cols, 0)
// , Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);
//line(dstImage, (Point)g_vCorners2[2] + Point(srcImage1.cols, 0), (Point)g_vCorners2[3] + Point(srcImage1.cols, 0)
// , Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);
//line(dstImage, (Point)g_vCorners2[3] + Point(srcImage1.cols, 0), (Point)g_vCorners2[0] + Point(srcImage1.cols, 0)
// , Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);
//imshow("【检测物体后的图像】", dstImage);
//进行角点检测
////开始进行强角点检测
////先配置需要的函数参数
vector dstPoint2f1;
goodFeaturesToTrack(grayImage1, dstPoint2f1, 200, 0.01, 10, Mat(), 3);
vector dstPoint2f2(dstPoint2f1.size());
//进行透视变换
perspectiveTransform(dstPoint2f1, dstPoint2f2, H);
//在计算得到的点中寻找最小包围矩形
//rectPoint变量中得到了矩形的四个顶点坐标
RotatedRect rectPoint = minAreaRect(dstPoint2f2);
//定义一个存储以上四个点的坐标的变量
Point2f fourPoint2f[4];
//将rectPoint变量中存储的坐标值放到 fourPoint的数组中
rectPoint.points(fourPoint2f);
//根据得到的四个点的坐标 绘制矩形
for (int i = 0; i < 3; i++)
{
line(srcImage2, fourPoint2f[i], fourPoint2f[i + 1]
, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 3);
}
line(srcImage2, fourPoint2f[0], fourPoint2f[3]
, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 3);
imshow("【检测到的物体】", srcImage2);
waitKey(0);
return 0;
}
4、【ORB特征提取】
优于其他两种方法。
//------------------【ORB】--------------------------
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat srcImage = imread("1.jpg");
imshow("【原图】", srcImage);
//对BGR空间的图像直接进行计算很费时间,所以,需要转换为灰度图
Mat srcGrayImage;
cvtColor(srcImage, srcGrayImage, CV_BGR2GRAY);
//首先对两幅图像进行特征点的检测和描述子的计算
vector keyPoint1;
Ptr orb = ORB::create(400);
//调用detect函数检测出特征关键点,保存在vector中
orb->detect(srcGrayImage, keyPoint1);
Mat descriImage1;
//计算描述符(特征向量)
orb->compute(srcGrayImage, keyPoint1, descriImage1);
//基于FLANN的描述符对象匹配
flann::Index flannIndex(descriImage1, flann::LshIndexParams(12, 20, 2), cvflann::FLANN_DIST_HAMMING);
//初始化视屏采集对象
VideoCapture capture;
capture.open(0);
capture.set(CV_CAP_PROP_FRAME_WIDTH, 360);//设置采集视频的宽度
capture.set(CV_CAP_PROP_FRAME_HEIGHT, 900);//设置采集视频的高度
Mat frameImage, frameGrayImage;
while (waitKey(1) != 27)
{
capture >> frameImage;
//为了提高计算效率,将图像转换为灰度图像
cvtColor(frameImage, frameGrayImage, CV_BGR2GRAY);
//计算特征点和描述子
vector keyPoints2;
orb->detect(frameGrayImage, keyPoints2);
Mat descriImage2;
orb->compute(frameGrayImage, keyPoints2, descriImage2);
//匹配和测试描述符,获取两个最临近的描述符
Mat matchIndex(descriImage2.rows, 2, CV_32SC1);
Mat matchDistance(descriImage2.rows, 2, CV_32SC1);
//调用k邻近算法
flannIndex.knnSearch(descriImage2, matchIndex, matchDistance, 2, flann::SearchParams());
//采集优秀的匹配点(根据劳氏算法)
vector goodMatches;
for (int i = 0; i < matchDistance.rows; i++)
{
//........................................................................
if (matchDistance.at(i, 0) < 0.6 * matchDistance.at(i, 1))
{
DMatch midDMatch(i, matchIndex.at(i, 0), matchDistance.at(i, 0));
goodMatches.push_back(midDMatch);
}
}
Mat dstImage;
drawMatches(frameImage, keyPoints2, srcImage, keyPoint1, goodMatches, dstImage);
imshow("【结果图】", dstImage);
}
return 0;
}
作者:qq_37894023
来源:CSDN
原文:https://blog.csdn.net/qq_37894023/article/details/85060047
版权声明:本文为博主原创文章,转载请附上博文链接!
作者:最疯狂
来源:CSDN
原文:https://blog.csdn.net/weixin_42714444/article/details/85243509
版权声明:本文为博主原创文章,转载请附上博文链接!