-
目录
11.3.2、找到最佳匹配:DescriptorMatch::match方法
11.4.2、进行透视矩阵变换:perspectiveTransform函数
十一、特征检测与匹配
-
11.1、SURF特征点检测
-
11.1.1、SURF算法简述
- 是尺度不变特征变换算法(SIFI)的加速版,比其块好几倍,在多幅图像下具有更好的稳定性。SURF最大的特征是采用了harr特征以及积分图像的概念,大大加快了程序的运行时间。可以应用于计算机视觉的物体识别和3D重构。
-
11.1.2、SURF算法原理
-
1、构建Hessian矩阵构造高斯金字塔尺度空间
- sift采用的是DOG图像,surf采用的是Hessian矩阵行列式近似值图像。海森矩阵式一个自变量为向量的实值函数的二阶偏导数组成的方块矩阵,假设函数f(x,y),Hessian矩阵H是函数、偏导数组成。图像中某个像素点的Hessian矩阵如下:
即每一个像素点都可以求出一个Hessian矩阵。H矩阵的判别式为
判别式的值是H矩阵的特征值,可以利用判定结果的符号将所有点分类,根据判别式取值正负,来判别该点是不是极值点。
- 在surf算法中,用图像像素I(x,y)即为函数值f(x,y),选用二阶标准高斯函数作为滤波器,通过特定核间的卷积计算二阶偏导数,计算出H矩阵的三个矩阵元素L_xx、L_xy、L_yy,从而计算出H矩阵:
由于特征点需要具备尺度无关性,所以在构造H矩阵前,需要进行高斯滤波,经过滤波后在进行H的计算,公式如下:
L(x,t)是一副图像在不同解析度下的表示,可以利用高斯核G(t)于图像函数I(x)在点x的卷积来实现,高斯核的计算公式为:
g(x)为高斯函数,t为高斯方差,就可以为每个像素计算出H行列式的绝对值,并用这个值来判别特征点。可以使用近似值来代替L(x,t),为平衡准确值与近似值间的误差引入权值,权值随尺度变化,则H矩阵判别式为:
其中0.9是一个经验值。
- 由于求H时要先高斯平滑,然后求二阶导数,这在离散的像素点时用模板卷积形成的,这两种操作合在一起用一个模板就可以了,比如y方向的模板:
左图时用高斯平滑然后再y方向上求二阶导数的模板,为了加快运算用了近似处理,右图采用积分图来计算加快速度。x和y方向的二阶混合偏导模板:
这只得到了一张近似Hessian的行列式图。在金字塔图像中有很多层,每一层叫做一个octave,每一个octave又有几张尺度不同的图片
- 在sift中,同一个octavt中图片尺寸相同,尺度不同(模糊程度),不同的octave图片尺寸不同。在进行高斯模糊时,sift的高斯模板时始终不变的,指示在不同的octave改变图像的大小
- 在surf中,图片大小一直不变,同一个octave中不同图片用到的高斯模板尺度也不同。算法允许尺度空间多层图像同时被处理,不需对图像进行二次抽样,从而提高算法性能。surf算法使原始图像保持不变而只改变了滤波器大小。
- sift采用的是DOG图像,surf采用的是Hessian矩阵行列式近似值图像。海森矩阵式一个自变量为向量的实值函数的二阶偏导数组成的方块矩阵,假设函数f(x,y),Hessian矩阵H是函数、偏导数组成。图像中某个像素点的Hessian矩阵如下:
-
2、利用非极大值抑制初步确定特征点
- 将经过H矩阵处理过的每个像素点与其三维领域的26个点进行大小比较,如果是最大或最小值,就保留下来,当作初步的特征点。检测过程中使用与该尺度层图像解析度相对应大小的滤波器 进行检测。以 3X3 的滤波器为例, 该尺度层图像中 9个像素点之一的检测特征点 与自身尺度层中其余8个点和l在其之上及之下的两个尺度层的各9个点进行比较, 共26 个点,图 中标记的 "x" 的像素点的特征值若大于周围像素,则可确定 i衷点为该区域的特征点。
- 将经过H矩阵处理过的每个像素点与其三维领域的26个点进行大小比较,如果是最大或最小值,就保留下来,当作初步的特征点。检测过程中使用与该尺度层图像解析度相对应大小的滤波器 进行检测。以 3X3 的滤波器为例, 该尺度层图像中 9个像素点之一的检测特征点 与自身尺度层中其余8个点和l在其之上及之下的两个尺度层的各9个点进行比较, 共26 个点,图 中标记的 "x" 的像素点的特征值若大于周围像素,则可确定 i衷点为该区域的特征点。
-
3、精确定位极值点
- 采用三维线性插值法得到亚像素级的特征点,去掉那些值小于阈值的点,增加极值使检测到的特征点数量减少,最终只有几个特征最强点被检测出来。
-
4、选取特征点的主方向
- sift选取主方向是采用在特征点领域内统计其梯度直方图,取直方图bin值最大的以及超过最大bin值80%的那些方向作为主方向
- 而surf中,不统计其梯度直方图,而是统计特征点领域内的harr小波特征。即在特征点的领域内,统计60度扇形内所有点的水平harr小波特征和垂直特征总和,harr小波尺寸变长为4s,一个扇形得到一个值。60度扇形以一定间隔进行旋转,将最大值那个扇形的方向作为该特征点的主方向。
-
5、构造surf特征点描述算子
- 在sift中,是在特征点周围取 16x16 的邻域, 并把该领域化为 4x4个的小区 域, 每个小区域统计8个方向梯度, 最后得到4x4x8=128 维的向量, 该向量作为 该点的描述子
- 在 surf 中,也是在特征点周围取一个正方形框, 框的边长为 20s (s是所检测 到该特征点所在的尺度〉。该框带方向,方向当然就是第4步检测出来的主方向了。 然后把该框分为 16个子区域, 每个子区域统计25个像素的水平方向和垂直方向 的 haar 小波特征, 这里的水平和垂直方向都是相对主方向而言的。该baar小波特 征为水平方向值之和,水平方向绝对值之和,垂直方向之和, 垂直方向绝对值之 和。
这样每个小区域就有4个值,每个特征点就是16x4=64维向量,少了一半,加快了匹配速度。
-
6、总结
- Surf 采用 Henssian 矩阵获取图像局部最值十分稳定, 但是在求主方向阶段太 过于依赖局部区域像素的梯度方向,有可能使找到的主方向不准确。后面的特征 向量提取以及匹配部严重依赖于主方向,即使不大偏差角度也可以造成后面特征 匹配的放大误差,从而使匹配不成功。 另外图像金字塔的层取得不够紧密也会使 得尺度有误差,后面的特征向量提取同样依赖相应的尺度, 发明者在这个问题 L 的折中解决方法是取适量的层然后进行插值
-
-
11.1.3、SURF类源码剖析
-
11.1.4、绘制关键点:drawKeypoints函数
-
void drawKeypoints(const Mat& image, const vector<KeyPoint>& keypoints, Mat& outImage, const Scalar& color=Scalar::all(-1),int flags= DrawMatchesFlags::DEFAULT)
1:输入图像;2:根据源图像得到的特征点,是一个输出参数;3:输出图像;4:关键点的颜色;5:绘制关键点的特征描述符,
-
-
11.1.5、KeyPoint类
- 是一个为特征点检测而生的数据结构,表示特征点
class KeyPoint { Point2f pt;//坐标 float size;//特征点邻域直径 float angle;//特征点的方向,值为[0,360],负值表示不使用 float response; int octave;//特征点所在的金字塔的组 int class_id;//用于聚类的id }
- 是一个为特征点检测而生的数据结构,表示特征点
-
11.1.6、示例程序:SURF特征点检测
- 使用FeatureDetector接口发现感兴趣点
- 使用SurfFeatureDetector以及函数detect实现检测过程
- 使用drawKeypoints绘制关键点
-
int main() { system("color 2F"); Mat srcImage1 = imread("1.jpg",1); Mat srcImage2 = imread("2.jpg",1); imshow("原始图1",srcImage1); imshow("原始图2",srcImage2); int minHessian = 400;//阈值特征点检测算子 SurfFeatureDetector detector(minHessian);//类对象 //模板类是存放动态数组,增加和压缩数据 vector<KeyPoint> keypoints_1,keypoints_2; //调用函数检测关键点保存在vector detector.detect(srcImage1,keypoints_1); detector.detect(srcImage2,keypoints_2); //绘制关键点 Mat img_keypoints_1;Mat img_keypoints_2; drawKeypoints(srcImage1,keypoints_1,img_keypoints_1, Scalar::all(-1),DrawMatchesFlags::DEFAULT); drawKeypoints(srcImage2,keypoints_2,img_keypoints_2, Scalar::all(-1),DrawMatchesFlags::DEFAULT); imshow("特征点检测效果图1",img_keypoints_1); imshow("特征点检测效果图2",img_keypoints_2); waitKey(0); return 0; }
-
-
11.2、SURF特征提取
-
11.2.1 、绘制匹配点:drawMatches函数
-
void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<DMatch>& matcheslto2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<char>& matchesMask=vector<char>(), int flags=DrawMatchesFlags::DEFAULT)
void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<vector<DMatch>>& matcheslto2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<vector<char>>& matchesMask=vector<vector<char>>(), int flags=DrawMatchesFlags::DEFAULT)
1:第一幅源图像;2:根据第一幅图像得到的特征点,是一个输出参数;3:第二幅源图像;4:根据第一幅图像得到的特征点;5:第一幅图像到第二幅图像的匹配点,表示每一个图1中的特征点都在图2中有一一对应的点;6:输出图像;7:匹配的输出颜色,-1表示颜色随机生成的;8:单一特征点的颜色;9:确定哪些匹配是会绘制出来的掩膜,掩膜为空,表示所有匹配都绘制;10:特征绘制的标识符,可以在如下的结构体中选:
struct DrawMatchesFlags { enum { //构建输出图像矩阵,使用现存的图像绘制匹配对和特征点 //且对每一个关键点,只绘制中间点 DEFAULT=0, //不构建输出图像矩阵,而是在输出图像上绘制匹配点 DRAW_OVER_OUTIMG=1, //单点特征点不被绘制 NOT_DRAW_SINGLE_POINTS=2, //对每一个关键点,绘制带大小和方向的关键点圆圈 DRAW_RICH_KEYPOINTS=4 }; };
-
-
11.2.3、示例程序:SURF特征提取
-
使用DescriptorExtractor接口寻找关键点对应的特征向量
-
使用SurfDescriptorExtractor和函数compute完成计算
-
使用BruteForceMatcher来匹配特征向量
-
使用函数drawMatches绘制检测到的匹配点
-
int main() { system("color 2F"); ShowHelpText(); Mat srcImage1 = imread("1.jpg",1); Mat srcImage2 = imread("2.jpg",1); //使用SURF算子检测关键点 int minHessian = 700;//阈值特征点检测算子 SurfFeatureDetector detector(minHessian);//类对象 //模板类是存放动态数组,增加和压缩数据 vector<KeyPoint> keypoints_1,keypoints_2; //调用函数检测关键点保存在vector detector.detect(srcImage1,keypoints_1); detector.detect(srcImage2,keypoints_2); //计算描述符(特征向量) SurfDescriptorExtractor extractor; Mat descriptors1,descriptors2; extractor.compute(srcImage1,keypoints_1,descriptors1); extractor.compute(srcImage2,keypoints_2,descriptors2); /*结果是一个Mat矩阵,行数与特征点向量中元素个数是一致的。每行都是一个 N维描述子向量,比如SURF算法默认的描述子维度64,该向量描绘了特征点周围的强度样式 。两个特征点越相似,它们的特征向量也越靠近。 匹配同一场景下的两幅图像:首先检测每幅图像中特征,然后提取描述子。 第一幅图像中的每一个特征描述子向量都会与第二幅图中的描述子进行比较, 得分最高的一对描述子(两个向量的距离最近)将被视为那个特征的最佳匹配。 该过程对于第一幅图像中的所有特征进行重复,这就是BruteForceMatcber 中实行的最基本的策略。 相关代码如下*/ //使用bruteForce //实例化一个匹配器 BruteForceMatcher<L2<float>> matcher; vector<DMatch> matches; //匹配两幅图中的描述子 matcher.match(descriptors1,descriptors2,matches); /*BruteForceMatcher 是由 DescriptorMatcher 派 生 出来的 一 个 类 ,而 Descri ptorMatcher 定义了不同的匹配策略的共同接口。 调用 match 方法后, 在其 第三个参数输出一个cv::DMatch 向量。 于是我们定义一个 std::vector<DMatch>类 型的 matches,调用 match 方法之后, 便可以使用 drawMatches 方法对匹配到的点进行绘制, 井最终显示出来。相关代码如下。*/ //绘制关键点 Mat imgMatches; drawKeypoints(srcImage1,keypoints_1,srcImage2,keypoints_2, matches,imgMatches); imshow("匹配图",imgMatches); waitKey(0); return 0; }
-
-
-
11.3、使用FLANN进行特征点匹配
-
11.3.2、找到最佳匹配:DescriptorMatch::match方法
-
void DescriptorMatcher::match( const Mat& queryDescriptors,//查询描述符集 const Mat& trainDescriptors,//训练描述符集 vector<DMatch>& matches,//得到的匹配 /*若查询描述符有在掩膜中被标记出来,则没有匹配 添加到描述符中。匹配量可能会比查询描述符数量少*/ const Mat& mask=Mat())//指定输入查询和训练描述符允许匹配的掩膜
void DescriptorMatcher::match( const Mat& queryDescriptors,//查询描述符集 const Mat& trainDescriptors,//训练描述符集 vector<DMatch>& matches,//得到的匹配 /*若查询描述符有在掩膜中被标记出来,则没有匹配 添加到描述符中。匹配量可能会比查询描述符数量少*/ const vector<Mat>& masks=vector<Mat>()) /*一组掩膜,每个masks[i]从第i个图像指定 输入查询和训练描述符允许匹配的掩膜*/
-
-
11.3.3、使用FLANN进行特征点匹配
-
int main(int argc, char** argv) { Mat img_1 = imread("1.jpg",1); Mat img_2 = imread("2.jpg",1); //利用SURF检测器检测的关键点 int minHessian = 300;//阈值特征点检测算子 SurfFeatureDetector detector(minHessian);//类对象 //模板类是存放动态数组,增加和压缩数据 vector<KeyPoint> keypoints_1,keypoints_2; //调用函数检测关键点保存在vector detector.detect(srcImage1,keypoints_1); detector.detect(srcImage2,keypoints_2); //计算描述符(特征向量) SurfDescriptorExtractor extractor; Mat descriptors1,descriptors2; extractor.compute(srcImage1,keypoints_1,descriptors1); extractor.compute(srcImage2,keypoints_2,descriptors2); //采用FLANN算法匹配描述符向量 flannBasedMatcher matcher; vector<DMatch> matches; matcher.match(descriptors1,descriptors2,matches); double max_dist=0;double min_dist =100; //快速计算关键点之间的最大和最小距离 for(int i=0;i<descriptors1.rows;i++) { double dist=matches[i].distance; if(dist<min_dist)min_dist=dist; if(dist>max_dist)max_dist=dist; } //输出距离信息 printf(">最大距离(Max dist): %f \n",max_dist); printf(">最小距离(Min dist): %f \n",min_dist); //存下符合条件的匹配结果 vector<DMatch> good_matches; for(int i=0;i<descriptors1.rows;i++) { if(matches[i].distance<2*min_dist) { good_matches.push_back(matches[i]); } } //绘制出符合条件的匹配点 Mat img_matches; drawMatches(img_1,keypoints_1,img_2,keypoints_2, good_matches,img_matches,Scalar::all(-1), Scalar::all(-1),vector<char>(), drawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //输出相关匹配点信息 for(int i=0;i<good_matches.size();i++) { printf(">符合条件的匹配点【%d】特征点1: %d -- 特征点1: %d \n",i,good_matches[i].queryIdx, good_matches[i].trainIdx); } imshow("匹配效果图",img_matches); waitKey(0); return 0; }
-
-
11.3.4、FLANN结合SURF进行关键点的描述和匹配
-
int main(int argc, char** argv) { system("color 6F"); Mat trainImage = imread("1.jpg"),trainImage_gray; imshow("原始图",trainImage); cvtColor(trainImage,trainImage_gray,CV_BGR2GRAY); //检测SURF关键点,提取训练图像描述符 vector<KeyPoint> train_keyPoint; Mat trainDescriptor; SurfFeatureDetector featureDetector(80); featureDetector.detect(trainImage_gray,train_keyPoint); SurfDescriptorExtractor featureExtractor; featureExtractor.compute(trainImage_gray,train_keyPoint, trainDescriptor); //创建基于FLANN的描述符匹配对象 flannBasedMatcher matcher; vector<Mat> train_desc_collection(1,trainDescriptor); matcher.add(train_desc_collection); matcher.train(); //创建视频对象,定义帧率 VideoCapture cap(0); unsigned int frameCount =0;//帧数 //不断循环,直到q被按下 while(char(waitKey(1)) != 'q') { //参数设置 int time0 = getTickCount(); Mat testImage,testImage_gray; cap>>testImage;//采集视频到变量 if(testImage.empty()) continue; //转化图像到灰度 cvtColor(testImage,testImage_gray,CV_BGR2GRAY); //检测s关键点,提取测试图像描述符 vector<KeyPoint> test_keyPoint; Mat testDescriptor; featureDetector,detect(testImage_gray,test_keyPoint); featureExtractor.compute(testImage_gray,test_keyPoint, testDescriptor); //匹配训练和测试描述符 vector<vector<DMatch>> matches; matcher.knnMatch(testDescriptor,matches,2); //根据劳氏算法得到优秀的匹配点 vector<DMatch> goodMatches; for(unsigned int i=0;i<matches.size();i++) { if(matches[i][0].distance<0.6*matches[i][1].distance) goodMatches.push_back(matches[i][0]); } //绘制匹配点 Mat dstImage; drawMatches(testImage,test_keyPoint,trainImage, train_keyPoint,goodMatches,dstImage); imshow("匹配窗口",dstImage); //输出帧率信息 cout<<"当前帧率:"<<getTickFrequency() / (getTickCount()-time0)<<endl; } return 0; }
-
-
11.3.5、SIFT配合暴力匹配进行关键点描述和提取
- SURF和SIFT算法各项指标对比
-
int main(int argc, char** argv) { system("color 5F"); Mat trainImage = imread("1.jpg"),trainImage_gray; imshow("原始图",trainImage); cvtColor(trainImage,trainImage_gray,CV_BGR2GRAY); //检测SIFT关键点,提取训练图像描述符 vector<KeyPoint> train_keyPoint; Mat trainDescription; SiftFeatureDetector featureDetector; featureDetector.detect(trainImage_gray,train_keyPoint); SiftDescriptorExtractor featureExtractor; featureExtractor.compute(trainImage_gray,train_keyPoint, trainDescription); //创建基于描述符的暴力匹配 BFMatcher matcher; vector<Mat> train_desc_collection(1,trainDescription); matcher.add(train_desc_collection); matcher.train(); //创建视频对象,定义帧率 VideoCapture cap(0); unsigned int frameCount =0;//帧数 //不断循环,直到q被按下 while(char(waitKey(1)) != 'q') { //参数设置 double time0 = getTickCount(); Mat captureImage,captureImage_gray; cap>>captureImage;//采集视频到变量 if(captureImage.empty()) continue; //转化图像到灰度 cvtColor(captureImage,captureImage_gray,CV_BGR2GRAY); //检测s关键点,提取测试图像描述符 vector<KeyPoint> test_keyPoint; Mat testDescriptor; featureDetector,detect(captureImage_gray,test_keyPoint); featureExtractor.compute(captureImage_gray,test_keyPoint, testDescriptor); //匹配训练和测试描述符 vector<vector<DMatch>> matches; matcher.knnMatch(testDescriptor,matches,2); //根据劳氏算法得到优秀的匹配点 vector<DMatch> goodMatches; for(unsigned int i=0;i<matches.size();i++) { if(matches[i][0].distance<0.6*matches[i][1].distance) goodMatches.push_back(matches[i][0]); } //绘制匹配点 Mat dstImage; drawMatches(captureImage,test_keyPoint,trainImage, train_keyPoint,goodMatches,dstImage); imshow("匹配窗口",dstImage); //输出帧率信息 cout<<"当前帧率:"<<getTickFrequency() / (getTickCount()-time0)<<endl; } return 0; }
- SURF和SIFT算法各项指标对比
-
-
11.4、寻找已知物体
-
在FLANN特征匹配的基础上,还可以进一步利用Homography映射找出已知物体。具体来说就是利用findHomography函数通过匹配的关键点找出相应的变换,再利用perspectiveTransform函数映射点群
-
11.4.1、寻找透视变换:findHomograthy
-
Mat findHomography( InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray())
此函数的作用就是找到并返回源图像和目标图像之间的透视变换H。1:源平面上的对应点;2:目标平面上的对应点;3:用于计算单应矩阵的方法:
4:默认值3,处理点对为内围层时,允许重投影误差的最大值。5:,有默认值 noArray, 是一个可选 的参数,输入掩码值会被忽略掉。
-
-
11.4.2、进行透视矩阵变换:perspectiveTransform函数
-
void perspectiveTransform( InputArray src, OutputArray dst, InputArray m)
1:输入图像;2:这个参数用于存放函数调用后的输出结果;3:变换矩阵
-
-
11.4.3、寻找已知物体
-
int main() { Mat srcImage1=imread("1.jpg",1); Mat srcImage2=imread("2.jpg",1); //使用SURF算子检测关键点 int minHessian = 400;//阈值特征点检测算子 SurfFeatureDetector detector(minHessian);//类对象 //模板类是存放动态数组,增加和压缩数据 vector<KeyPoint> keypoints_object,keypoints_scene; //调用函数检测关键点保存在vector detector.detect(srcImage1,keypoints_object); detector.detect(srcImage2,keypoints_scene); //计算描述符(特征向量) SurfDescriptorExtractor extractor; Mat descriptors_object,descriptors_scene; extractor.compute(srcImage1,keypoints_object,descriptors_object); extractor.compute(srcImage2,keypoints_scene,descriptors_scene); //采用FLANN算法匹配描述符向量 flannBasedMatcher matcher; vector<DMatch> matches; matcher.match(descriptors_object,descriptors_scene,matches); double max_dist=0;double min_dist =100; //计算关键点之间的最大和最小距离 for(int i=0;i<descriptors_object.rows;i++) { double dist=matches[i].distance; if(dist<min_dist)min_dist=dist; if(dist>max_dist)max_dist=dist; } //输出距离信息 printf(">最大距离(Max dist): %f \n",max_dist); printf(">最小距离(Min dist): %f \n",min_dist); //存下符合条件的匹配结果 vector<DMatch> good_matches; for(int i=0;i<descriptors_object.rows;i++) { if(matches[i].distance<3*min_dist) { good_matches.push_back(matches[i]); } } //绘制出符合条件的匹配点 Mat img_matches; drawMatches(srcImage1,keypoints_object,srcImage2, keypoints_scene, good_matches,img_matches,Scalar::all(-1), Scalar::all(-1),vector<char>(), drawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //定义两个局部变量 vector<Point2f> obj; vector<Point2f> scene; //从匹配成功的匹配对中获取关键点 for(unsigned int i=0;i<good_matches.size();i++) { obj.push_back(keypoints_object[good_matches[i].queryIdx].pt); scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt); } Mat H = findHomography(obj,scene,CV_RANSAC);//计算透视变换 //从待测图片中获取角点 vector<Point2f> obj_corners(4); obj_corners[0]=cvPoint(0,0); obj_corners[1]=cvPoint(srcImage1.cols,0); obj_corners[2]=cvPoint(srcImage1.cols,srcImage1.rows); obj_corners[3]=cvPoint(0,srcImage1.rows); vector<Point2f> scene_corners(4); //进行透视变换 perspectiveTransform(obj_corners,scene_corners,H); //绘制出角点之间的直线 line(img_matches,scene_corners[0]+ Point2f(static_cast<float>(srcImage1.cols),0),scene_corners[1]+ Point2f(static_cast<float>(srcImage1.cols),0),Scalar(255,0,123),4); line(img_matches,scene_corners[1]+ Point2f(static_cast<float>(srcImage1.cols),0),scene_corners[2]+ Point2f(static_cast<float>(srcImage1.cols),0),Scalar(255,0,123),4); line(img_matches,scene_corners[2]+ Point2f(static_cast<float>(srcImage1.cols),0),scene_corners[3]+ Point2f(static_cast<float>(srcImage1.cols),0),Scalar(255,0,123),4); line(img_matches,scene_corners[3]+ Point2f(static_cast<float>(srcImage1.cols),0),scene_corners[0]+ Point2f(static_cast<float>(srcImage1.cols),0),Scalar(255,0,123),4); //显示最终结果 imshow("Good Matches & Object detection",img_matches); waitKey(0); return 0; }
-
-
-
11.5、ORB特征提取
-
11.5.1、ORB算法概述
- ORB 是sift 的 100倍, 是 surf 的 10 倍。
-
11.5.2、相关概念认知
-
Brief描述子
- 在特征点附近随机选取若干点对,将这些点对的灰度值大小,组合成一个二进制串,并将这个二进制串作为该特征点的特征描述子
- 优点在于速度,缺点:不具备旋转不变性、对噪声敏感、不具备尺度不变性
-
-
11.5.3、ORB类相关类源码简单分析
-
11.5.4、ORB算法描述与匹配
-
int main() { Mat srcImage = imread("1.jpg"); imshow("原始图",srcImage); Mat grayImage; cvtColor(srcImage,grayImage,CV_BGR2GRAY); //---------检测SIFT特征点并在图像中提取物体的描述符 //参数定义 OrbFeatureDetector featureDetector; vector<KeyPoint> keyPoints; Mat descriptors; //调用detect函数检测出特征关键点并保存 featureDetector.detect(grayImage,keyPoints); //计算描述符 OrbDescriptorExtractor featureExtractor; featureExtractor.compute(grayImage,keyPoints,descriptors); //基于FLANN的描述符对象匹配 flann::Index flannIndex(descriptors, flann::LshIndexParams(12,20,2),cvflann::FLANN_DIST_HAMMING); //初始化视频采集对象 VideoCapture cap(0); cap.set(CV_CAP_PROP_FRAME_WIDTH,360);//设置采集视频的宽度 cap.set(CV_CAP_PROP_FRAME_HEIGHT,360);//设置采集视频的高度 unsigned int frameCount=0;//帧数 //轮询,按下esc退出 while(1) { double time0=static_cast<double>(getTickCount()); Mat captureImage,captureImage_gray; cap>>captureImage;//采集视频帧 if(captureImage.empty()) continue; cvtColor(captureImage,captureImage_gray,CV_BGR2GRAY);//转化为灰度图 //检测SIFT关键点并提取测试图像中的描述符 vector<KeyPoint> captureKeyPoints; Mat captureDescription; //调用detect函数检测特征关键点 featureDetector.detect(captureImage_gray,captureKeyPoints); //计算描述符 featureExtractor.compute(captureImage_gray, captureKeyPoints,captureDescription); //匹配和测试描述符,获取两个最邻近的描述符 Mat matchIndex(captureDescription.rows,2,CV_32SC1), matchDistance(captureDescription.rows,2,CV_32FC1); //调用k临近算法 flannIndex,knnSearch(captureDescription,matchIndex, matchDistance,2,flann::SearchParams()); //根据劳氏计算出优秀的匹配 vector<DMatch> goodMatches; for(int i=0;i<matchDistance.rows;i++) { if(matchDistance.at<float>(i,0) <0.6* matchDistance.at<float>(i,1)) { DMatch dmatches(i,matchIndex.at<int>(i,0), matchDistance.at<float>(i,0)); goodMatches.push_back(dmatches); } } Mat resultImage; drawMatches(captureImage,captureKeyPoints,srcImage, keyPoints,goodMatches,resultImage); imshow("匹配窗口",resultImage); cout<<"当前帧率:"<<getTickFrequency() / (getTickCount()-time0)<<endl; if(char(waitKey(1))==27) break; } return 0; }
-
-
-
-
11.6、本章小结
-