热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

学习笔记之——vs2015+opencv2.4.13实现SIFT、SURF、ORB

此博文为本人写的第一篇博文,写博文的主要目的呢有两个:第一就是对自己做过的工作进行总结;第二就是希望跟志同道合的人相互学习交流~本篇博文主

       此博文为本人写的第一篇博文,写博文的主要目的呢有两个:第一就是对自己做过的工作进行总结;第二就是希望跟志同道合的人相互学习交流~

        本篇博文主要是我自学SIFT、SURF、ORB三种算法(三种特征描述子)过程的笔记以及运行的代码。博文主要是对于三种算法的  归纳以及加入我自己的一些思考与理解。当然里面还有一些出现的问题不知道怎么解决的,也放到博文里面(希望有大神可以在评论不吝赐教),后续也会继续更新,请广大读者批评指正谢谢。

        在opencv3中,这三个算子都转移到一个名为xfeature2d的第三方库中,而在opencv2中这三个算子在nonfree库中,为此我还特意把opencv3.2改为opencv2.4.9和2.4.13。

       关于在vs下配置opencv可参考https://blog.csdn.net/poem_qianmo/article/details/19809337。个人感觉这个教程比较好,注意版本号不一样修改对应的lib就好了。

 

一.SIFT

Scale Invariant Feature Transform(SIFT) 尺度不变特征变换。SIFT特征用于描述图像这种的局部特征。是一种关键点(或者叫做特征点)的检测和描述的算法。SIFT算法应用于图像特征点的提取,首先建立图像的尺度空间表示,接着在尺度空间中搜索图像的极值点,通过这些极值点(也称关键点,特征点。包含三个主要信息:位置、尺度、方向),从而建立特征描述向量。通过特征描述向量来做图像识别与检测方面的问题。

SIFT由David Lowe提出,且已经申请了专利保护。

SIFT的算法流程图如下图所示:

  • Mat srcImage2 = imread("hand2.jpg", 1);

  •  
  • //CV_Assert用于判断输入数据的合法性,当该函数为false时,返回一个错误信息

  • CV_Assert(srcImage1.data != NULL && srcImage2.data != NULL);

  •  
  •  
  • //转换为灰度图并做归一化

  • Mat grayMat1, grayMat2;

  • cvtColor(srcImage1, grayMat1, CV_BGR2GRAY);

  • normalize(grayMat1, grayMat1, 0, 255, NORM_MINMAX);

  •  
  • cvtColor(srcImage2, grayMat2, CV_BGR2GRAY);

  • normalize(grayMat2, grayMat2, 0, 255, NORM_MINMAX);

  •  
  •  
  •  
  • //定义SIFT描述子

  • SiftFeatureDetector detector;

  • //这个对象顾名思义就是SIFT特征的探测器,用它来探测图片中SIFT点的特征,存到一个KeyPoint类型的vector中。

  •  
  • /*keypoint只是保存了opencv的sift库检测到的特征点的一些基本信息,但sift所提取出来的特征向量其实不是在这个里面,特征向量通过SiftDescriptorExtractor 提取,

  • 结果放在一个Mat的数据结构中。这个数据结构才真正保存了该特征点所对应的特征向量。*/

  •  
  • //得到keypoint只是达到了关键点的位置,方向等信息,并无该特征点的特征向量,要想提取得到特征向量就还要进行SiftDescriptorExtractor 的工作。

  •  
  •  
  • SiftDescriptorExtractor extractor;

  • //建立了SiftDescriptorExtractor 对象后,通过该对象,对之前SIFT产生的特征点进行遍历,找到该特征点所对应的128维特征向量。

  • //SiftDescriptorExtractor对应于SIFT算法中特征向量提取的工作,通过他对关键点周围邻域内的像素分块进行梯度运算,得到128维的特征向量。

  •  
  •  
  •  
  • //特征点的检测,并放入keypoint类型的vector中

  • vector keypoints1;

  • detector.detect(grayMat1, keypoints1);

  •  
  • vector keypoints2;

  • detector.detect(grayMat2, keypoints2);

  •  
  •  
  •  
  • //计算特征点描述子

  • Mat descriptors1;

  • extractor.compute(grayMat1, keypoints1, descriptors1);

  •  
  • Mat descriptors2;

  • extractor.compute(grayMat2, keypoints2, descriptors2);

  •  
  •  
  •  
  • //特征点匹配

  • //两幅图片的特征向量被提取出来后,我们就可以使用BruteForceMatcher对象对两幅图片的descriptor进行匹配,得到匹配的结果到matches中

  • vector matches;

  • BruteForceMatcher > matcher;

  • matcher.match(descriptors1, descriptors2, matches);

  •  
  •  
  •  
  • //二分排序

  • int N = 80;

  • nth_element(matches.begin(), matches.begin() + N - 1, matches.end());

  • //方法是,nth位置的元素放置的值就是把所有元素排序后在nth位置的值.把所有不大于nth的值放到nth的前面,把所有不小于nth的值放到nth后面.

  • matches.erase(matches.begin() + N, matches.end());//去除特征点不匹配情况。erase(pos,n); 删除从pos开始的n个字符。

  •  
  •  
  •  
  • //绘制检测结果

  • Mat resultMat;

  • drawMatches(srcImage1, keypoints1, srcImage2, keypoints2, matches, resultMat);

  • imshow("jieguo", resultMat);

  •  
  • waitKey( );

  • return 0;

  • }

  • 程序是可以正常运行的,出来的结果如下:

  •  
  • SIFT sift(200);//设置了200个特征点

  •  
  •  
  • vector key_points;//存放特征点,存放检测出来的特征点

  •  
  •  
  • Mat descriptors, mascara;// descriptors为描述符,mascara为掩码矩阵

  •  
  •  
  • sift(img, mascara, key_points, descriptors);//执行sift运算

  •  
  •  
  • Mat output_img; //输出图像矩阵

  • //在输出图像上绘制特征点

  • drawKeypoints(img, //输入图像

  • key_points, //特征点矢量

  • output_img, //输出图像

  • Scalar::all(-1), //绘制特征点的颜色,为随机

  • //以特征点为中心画圆,圆的半径表示特征点的大小,直线表示特征点的方向

  • DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

  •  
  • imshow("sift tu", output_img);

  •  
  •  
  • waitKey(0);

  • return 0;

  • }

  • 结果如下:

    这样每个小区域就有4个值,所以每个特征点就是16*4=64维的向量,相比sift而言,少了一半,这在特征匹配过程中会大大加快匹配速度。

     

    代码演示:

    opencv中SURF部分设计到三个类:SURF、SurfFeatureDetector、SurfDescriptorExtractor

    根据features2d.hpp头文件中的两句定义:


    1. typedef SURF SurfFeatureDetector;

    2. typedef SURF SurfDescriptorExtractor;

    其中,typedef声明是为现有类型创建一个新的名字,类型别名,即SURF类有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。也就是说,SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。下图给出了SURF相关类之间的关系:

     

  • SurfFeature(src1,src2,1000);

  •  
  • return 0;

  • }

  • 运行结果如下:

  • imshow("测试图",src);

  •  
  • //创建放置关键点的vector

  • vector keyPoints;

  •  
  • //创建FAST对象,并将阈值设定为55

  • FastFeatureDetector fast(55);

  •  
  • //获取特征点

  • fast.detect(src, keyPoints);

  •  
  • //在原图上画出特征点

  • drawKeypoints(src, keyPoints, src, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);

  • imshow("FAST feature", src);

  •  
  • waitKey(0);

  • return 0;

  • }

  • 结果图如下:

  • Mat srcImage2 = imread("hand2.jpg", 1);

  •  
  •  
  • //转换为灰度图

  • Mat grayImage1, grayImage2;

  • cvtColor(srcImage1, grayImage1, CV_RGB2GRAY);

  • cvtColor(srcImage2, grayImage2, CV_RGB2GRAY);

  •  
  • //定义特征点检测器(注意SurfFeatureDetector和SurfDescriptorExtractor是等价的)

  • SurfFeatureDetector detector(400);

  •  
  • //声明存放关键点的容器

  • vector keyPoints1, keyPoints2;

  •  
  • //特征点检测

  • detector.detect(grayImage1, keyPoints1);

  • detector.detect(grayImage2, keyPoints2);

  •  
  •  
  •  
  • //定义特征提取器,采用了BRIEF特征描述子

  • BriefDescriptorExtractor extractor(64);// //参数表示字节数,采用长度为64×8=512的向量表示

  • //注意bytes参数表示的是描述子占用的字节数不是描述子长度,如默认采用32字节对应描述子长度为32×8=256;

  •  
  • //声明存放特征向量的特征描述矩阵

  • Mat descriptorMat1, descriptorMat2;

  •  
  • //计算SURF特征描述矩阵(计算特征向量)

  • extractor.compute(grayImage1, keyPoints1, descriptorMat1);

  • extractor.compute(grayImage2, keyPoints2, descriptorMat2);

  •  
  • //用匹配器进行匹配。之前介绍过FLANN匹配器和暴力匹配器(BruteForceMatcher)

  • BFMatcher matcher(NORM_HAMMING); //汉明距离匹配特征点

  •  
  • //定义特征点匹配向量

  • vector matches;

  • matcher.match(descriptorMat1, descriptorMat2, matches);

  •  
  • //画图

  • Mat image;

  • drawMatches(srcImage1, keyPoints1, srcImage2, keyPoints2, matches, image);

  • imshow("BRIEF特征描述子出来的匹配结果", image);

  •  
  •  
  •  
  • 定义特征提取器,采用了SURF的特征描述子

  • //SurfDescriptorExtractor extractor1;// //参数表示字节数,采用长度为64×8=512的向量表示

  •  
  • 声明存放特征向量的特征描述矩阵

  • //Mat descriptorMat1_1, descriptorMat2_1;

  •  
  • 计算SURF特征描述矩阵(计算特征向量)

  • //extractor1.compute(grayImage1, keyPoints1, descriptorMat1_1);

  • //extractor1.compute(grayImage2, keyPoints2, descriptorMat2_1);

  •  
  • 用匹配器进行匹配。之前介绍过FLANN匹配器和暴力匹配器(BruteForceMatcher)

  • //BFMatcher matcher1; //汉明距离匹配特征点

  • //

  • 定义特征点匹配向量

  • //vector matches1;

  • //matcher1.match(descriptorMat1_1, descriptorMat2_1, matches1);

  •  
  • 画图

  • //Mat image1;

  • //drawMatches(srcImage1, keyPoints1, srcImage2, keyPoints2, matches1, image1);

  • //imshow("采用了SURF的特征描述子出来的匹配结果", image1);

  •  
  •  
  • waitKey(0);

  • return 0;

  • }

  •  
  •  
  • 结果如下图所示:

  • Mat src2 = imread("hand2.jpg", 1);

  •  
  • ORB_feature_and_compare(src1, src2);

  •  
  • return 0;

  • }

  • 结果如图所示:

    匹配的效果不是特别的好~~~至于原因我也没有去深究,如果读者有什么想法,可以在评论一下赐教(会不会是匹配的速度快,对应准确率就没那么高呢?)

     

    总结:三种算法其实非常相似,在opencv中表现只是特征提取的函数以及特征描述的函数不一样而已。

    三种算法用于识别两张图的目标是否相同,总结基本流程总结如下:

    1、分别找出这两张图中的特征点。通过特征检测器进行特征检测,检测的结果放于KeyPoint类型的vector中。

    2、描述这些特征点的属性。特征的描述也叫特征的提取,就是第一步获得的仅仅是一系列特征点,第二步就要生成特征向量,用特征提取器获得描述子,并放于特征描述矩阵中。

    3、比较这两张图片的特征点的属性,如果有足够多的特征点具有相同的属性,那么可以认为这两张图片中的目标是相同的。通过匹配器进行特征匹配(匹配器分为FLANN和暴力匹配),匹配结果放于DMatch类型的vector中。

    至此,本人学习SIFT、SURF和ORB三种特征算子的旅途就告一段落了,希望本博客能给有需要的人带来帮助,同时也欢迎广大读者批评赐教。感觉本人对于这三种算子的理解目前还是处于纸上谈兵的状态,希望能与大家多交流,后续我也会自己做一些项目,有新的心得体会会及时更新这篇博客。同时这篇博客理论部分很多都是参考其他博客与书籍,适当的地方都给出了引用,本博客无商业用途,仅仅作为本人的学习笔记~谢谢。

     

    Reference:(除了在上面分别指出的博客链接外,本博文还参考了以下资料)

    《OpenCV图像处理编程实例》

    《Opencv3编程入门》

     


    推荐阅读
    • 如何寻找程序员的兼职机会
      随着远程工作的兴起,越来越多的程序员开始寻找灵活的兼职工作机会。本文将介绍几个适合程序员、设计师、翻译等专业人士的在线平台,帮助他们找到合适的兼职项目。 ... [详细]
    • 本文将介绍几款常用的搜索引擎,包括Google、百度、搜狗和去哪儿网,旨在为用户提供更多高效的网络搜索工具。所有推荐的搜索引擎均为免费服务。 ... [详细]
    • 本文将详细介绍如何配置并整合MVP架构、Retrofit网络请求库、Dagger2依赖注入框架以及RxAndroid响应式编程库,构建高效、模块化的Android应用。 ... [详细]
    • 使用R语言进行Foodmart数据的关联规则分析与可视化
      本文探讨了如何利用R语言中的arules和arulesViz包对Foodmart数据集进行关联规则的挖掘与可视化。文章首先介绍了数据集的基本情况,然后逐步展示了如何进行数据预处理、规则挖掘及结果的图形化呈现。 ... [详细]
    • Web开发实践:创建连连看小游戏
      本文详细介绍了如何在Web环境中开发一款连连看小游戏,适合初学者和技术爱好者参考。通过本文,您将了解游戏的基本结构、连线算法以及实现方法。 ... [详细]
    • 正则表达式入门指南
      本文基于《正则表达式必知必会》(作者:Ben Forta,译者:杨涛),介绍了正则表达式的基本概念及其应用,包括搜索与替换功能,以及元字符的分类与使用。 ... [详细]
    • Docker基础入门与环境配置指南
      本文介绍了Docker——一款用Go语言编写的开源应用程序容器引擎。通过Docker,用户能够将应用及其依赖打包进容器内,实现高效、轻量级的虚拟化。容器之间采用沙箱机制,确保彼此隔离且资源消耗低。 ... [详细]
    • ACM经典书籍推荐
      本文介绍了几本在算法和计算机科学领域具有重要影响力的书籍,包括由Donald E. Knuth编著的《计算机程序设计艺术》第一卷,以及潘氏兄弟的数论经典教材等。这些书籍不仅是学习相关领域的宝贵资源,也是专业人士不可或缺的参考书。 ... [详细]
    • Linux内核中的内存反碎片技术解析
      本文深入探讨了Linux内核中实现的内存反碎片技术,包括其历史发展、关键概念如虚拟可移动区域以及具体的内存碎片整理策略。旨在为开发者提供全面的技术理解。 ... [详细]
    • 苹果官方在线商店(中国)提供了关于MacBook Pro的详细信息。通过先进的工厂校准技术,新MacBook Pro能够精确地适应多种色彩空间标准,如sRGB、BT.601、BT.709及P3-ST.2084(HDR),确保用户获得最佳视觉效果。 ... [详细]
    • 本文探讨了在AspNetForums平台中实施基于角色的权限控制系统的方法,旨在为不同级别的用户提供合适的访问权限,确保系统的安全性和可用性。 ... [详细]
    • Excel技巧:单元格中显示公式而非结果的解决方法
      本文探讨了在Excel中如何通过简单的方法解决单元格显示公式而非计算结果的问题,包括使用快捷键和调整单元格格式两种方法。 ... [详细]
    • 本文旨在探讨Swift中的Closure与Objective-C中的Block之间的区别与联系,通过定义、使用方式以及外部变量捕获等方面的比较,帮助开发者更好地理解这两种机制的特点及应用场景。 ... [详细]
    • 本文介绍了如何使用 Python 的 Pyglet 库加载并显示图像。Pyglet 是一个用于开发图形用户界面应用的强大工具,特别适用于游戏和多媒体项目。 ... [详细]
    • 本文提供了一个详尽的前端开发资源列表,涵盖了从基础入门到高级应用的各个方面,包括HTML5、CSS3、JavaScript框架及库、移动开发、API接口、工具与插件等。 ... [详细]
    author-avatar
    推球了
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有