经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C » 查看文章
[学习笔记]SiftGPU入门
来源:cnblogs  作者:RichardYao1995  时间:2018/11/6 14:10:26  对本文有异议

当有读者看到我这篇SiftGPU入门的学习笔记时,相信你已经读过了高博那篇《SLAM拾萃:SiftGPU》,那篇文章写于16年,已经过去两年的时间。在我尝试配置SiftGPU的环境时,遇到了几个问题,在网络上也少有较为系统的关于SiftGPU的介绍,因此觉得有必要记录下来,以便同样对此感兴趣的同学们少走弯路。

暑假的时候参加了高分举办的无人机大赛,在进行图像处理的时候用到过特征提取,当时主要是考虑SIFT和SURF两种方法,由于提取速度上的优势,我采用了SURF。比赛之后读过一些博客和文章,发觉SIFT的准确率应该更高一些,而我在比赛中也发现SURF偶尔会出现无法匹配的情况。OpenCV集成了SIFT算子,我们可以比较容易地利用其中的函数进行特征点的检测,而由于传统的SIFT算法速度较为缓慢,检测一张图片在台式机上通常都需要100+ms,因此传统的SIFT算法很难应用在无人机这种资源紧张而且对速度要求很高的平台上。目前我们组的无人平台上主要应用过ORBSLAM和VINS。

我的原计划是阅读Lowe的论文,理解算法的原理,而后对源码进行一定的优化以在特定的情景中加快检测速度,然而在一次组会中,老板提到了SiftGPU让我去了解一下,于是就有了这篇学习笔记。原作者Wu Changchang来自北卡罗来纳大学教堂山分校,高博文章中的下载链接基于此。

笔者使用的是Ubuntu 16.04的系统,工作机配置了CUDA9.0,笔记本配置的是CUDA9.2,OpenCV的版本都是3.2.0,关于OpenCV及其contrib的编译可以参照我的另一篇博文[环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题。SiftGPU大部分代码是基于OpenGL的,因此不编译CUDA也没有问题,两者速度的对比后面会提到。

 


下载和编译

源码编译之前我们需要安装一些包以及Glew。

  1. $ sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev libdevil-dev

Glew的源码位于其官网,下载最新版的即可,之后直接解压。

  1. $ cd Downloads/glew-2.1.0
  2. $ make
  3. $ sudo make install

我们需要告诉系统的cmake工具编译好的文件的位置。

  1. $ sudo ldconfig /usr/lib64/

接下来就是SiftGPU的编译了,Wu Changchang的源码链接已经失效,因此只能去万能的github上下载源码了。但是这个版本的SiftGPU有几处问题,编译之前需要更正,否则无法正确编译完成,为方便起见,我fork之后更新了需要更正的文件,放在我的github上,大家可以直接clone我更正后的代码,然后编译SiftGPU。

  1. $ cd Downloads/SiftGPU
  2. $ make

检查一下得到的bin/libsiftgpu.so的链接是否正确

  1. $ ldd bin/libsiftgpu.so

如果得到如下的图片,说明编译成功,每一个库都找到了对应的位置。

 笔者对pitzer的源码主要更改了两个文件,首先一个关于freeglut的问题,报错如下

  1. freeglut ERROR: Function <glutDestroyWindow> called without first calling 'glutInit'.

我们打开src/SiftGPU/LiteWindow.h,找到

  1. virtual ~LiteWindow() { if(glut_id > 0) glutDestroyWindow(glut_id); }

改为

  1. virtual ~LiteWindow()
  2. {
  3. if(glut_id > 0)
  4. {
  5.   int argc = 0;
  6.   char** argv;
  7.   glutInit(&argc, argv);
  8.   glutDestroyWindow(glut_id);
  9. }
  10. }

第二个文件是src/SiftGPU/SiftGPU.h,在头文件处加一个

  1. #include <stddef.h>

如果缺少这个头文件,会报如下的错误

  1. /home/yao/Environment/SiftGPU/src/SiftGPU/SiftGPU.h:336:40: error: declaration of operator new as non-function SIFTGPU_EXPORT void* operator new (size_t size);

测试与结果

OpenGL

编译好之后我们当然需要来测试一下,笔者主要使用cmake从命令行进行编译,使用编译器的同学如果是调用cmake工具的话,应该步骤相同。首先我们创建一个工程文件夹,名字就叫test_SiftGPU,在文件夹下创建CMakeLists.txt

  1. cmake_minimum_required(VERSION 2.8.3)
  2. project(test_SiftGPU)
  3. # OpenCV依赖
  4. find_package( OpenCV REQUIRED )
  5. # OpenGL
  6. find_package(OpenGL REQUIRED)
  7. # GLUT
  8. find_package(GLUT REQUIRED)
  9. # Glew
  10. find_package(GLEW REQUIRED)
  11. # SiftGPU:手动设置其头文件与库文件所在位置
  12. include_directories("/home/yao/Environment/SiftGPU/src/SiftGPU/" ${OpenGL_INCLUDE_DIR})
  13. set(SIFTGPU_LIBS "/home/yao/Environment/SiftGPU/bin/libsiftgpu.so")
  14. add_executable( test_SiftGPU main.cpp )
  15. target_link_libraries( testSiftGPU
  16. ${OpenCV_LIBS}
  17. ${SIFTGPU_LIBS}
  18. ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES}
  19. )

注意设置SiftGPU的路径时读者要改成自己的路径。此外高博的文章中写到需要为Glew写一个寻找其路径的cmake文件,但在我安装完Glew后cmake的modules文件夹下出现了FindGLEW.cmake这个文件,因此我们不需要专门为Glew写这个文件,直接加上GLEW的find_package代码,注意大写。

main.cpp我直接用高博的代码,后续的学习之后可能会写一个新的测试代码,更便于学习。

  1. // SiftGPU模块
  2. #include <SiftGPU.h>
  3.  
  4. //标准C++
  5. #include <iostream>
  6. #include <vector>
  7.  
  8. // OpenCV图像
  9. #include <opencv2/core/core.hpp>
  10. #include <opencv2/highgui/highgui.hpp>
  11.  
  12. // boost库中计时函数
  13. #include <boost/timer.hpp>
  14.  
  15. // OpenGL
  16. #include <GL/gl.h>
  17.  
  18. using namespace std;
  19. int main( int argc, char** argv)
  20. {
  21. //声明SiftGPU并初始化
  22. SiftGPU sift;
  23. char* myargv[4] ={ "-fo", "-1", "-v", "1"};
  24. sift.ParseParam(4, myargv);
  25. //检查硬件是否支持SiftGPU
  26. int support = sift.CreateContextGL();
  27. if ( support != SiftGPU::SIFTGPU_FULL_SUPPORTED )
  28. {
  29. cerr<<"SiftGPU is not supported!"<<endl;
  30. return 2;
  31. }
  32. //测试直接读取一张图像
  33. cout<<"running sift"<<endl;
  34. boost::timer timer;
  35. //在此填入你想测试的图像的路径!不要用我的路径!不要用我的路径!不要用我的路径!
  36. sift.RunSIFT( "/home/yao/workspace/SIFT_detection/image/1.png" );
  37. cout<<"siftgpu::runsift() cost time="<<timer.elapsed()<<endl;
  38. // 获取关键点与描述子
  39. int num = sift.GetFeatureNum();
  40. cout<<"Feature number="<<num<<endl;
  41. vector<float> descriptors(128*num);
  42. vector<SiftGPU::SiftKeypoint> keys(num);
  43. timer.restart();
  44. sift.GetFeatureVector(&keys[0], &descriptors[0]);
  45. cout<<"siftgpu::getFeatureVector() cost time="<<timer.elapsed()<<endl;
  46. // 先用OpenCV读取一个图像,然后调用SiftGPU提取特征
  47. cv::Mat img = cv::imread("/home/yao/workspace/SIFT_detection/image/1.png", 0);
  48. int width = img.cols;
  49. int height = img.rows;
  50. timer.restart();
  51. // 注意我们处理的是灰度图,故照如下设置
  52. sift.RunSIFT(width, height, img.data, GL_INTENSITY8, GL_UNSIGNED_BYTE);
  53. cout<<"siftgpu::runSIFT() cost time="<<timer.elapsed()<<endl;
  54. return 0;
  55. }

然后就是轻车熟路的cmake编译过程了。

  1. $ mkdir build
  2. $ cd build
  3. $ cmake ..
  4. $ make
  5. $ ./test_SiftGPU

结果如下图所示

测试代码只调用了OpenGL,我笔记本的配置是i7-7700HQ,显卡GTX1050,可以看到在OpenCV已经读取了图片的情况下,提取出一张图像中所有的SIFT特征点只需要31毫秒,直接读取一张图进行SIFT特征提取的时候,需要100多毫秒,这与传统的SIFT提取消耗的时间相差不多。多数情况下,我们都是调用OpenCV进行图像的读取以及后续的处理,因此使用SiftGPU可以加快提取特征点的速度。在无人机平台上,图像处理速度一般要求在20HZ以上,因此SiftGPU获取特征点的策略可以应用于无人机平台,与ORB等算子速度相当。

CUDA

我们切换至CUDA下进行特征点提取,关于调用CUDA来完成SiftGPU的测试,github上的原作者写的比较含糊,网络上也鲜有教程,因此特做记录如下。

首先切换至SiftGPU的安装路径,找到makefile中的

  1. ifneq ($(simple_find_cuda), )
  2. siftgpu_enable_cuda = 0
  3. else
  4. siftgpu_enable_cuda = 0
  5. endif
  6. CUDA_INSTALL_PATH = /usr/local/cuda
  7. #change additional settings, like SM version here if it is not 1.0 (eg. -arch sm_13 for GTX280)
  8. #siftgpu_cuda_options = -Xopencc -OPT:unroll_size=200000
  9. #siftgpu_cuda_options = -arch sm_10

改为

  1. ifneq ($(simple_find_cuda), )
  2. siftgpu_enable_cuda = 1
  3. else
  4. siftgpu_enable_cuda = 0
  5. endif
  6. CUDA_INSTALL_PATH = /usr/local/cuda
  7. #change additional settings, like SM version here if it is not 1.0 (eg. -arch sm_13 for GTX280)
  8. #siftgpu_cuda_options = -Xopencc -OPT:unroll_size=200000
  9. siftgpu_cuda_options = -arch sm_50

其中最后一行的sm_50取决于读者电脑的GPU算力,笔者笔记本使用的GPU是Pascal架构的GTX1050,算力为5.2,因此采用sm_50这个参数,关于不同GPU的算力可以参考这篇博客。之后重新编译安装SiftGPU。

  1. $ make clean
  2. $ make

我们切回到SiftGPU的测试程序,找到主程序main.cpp

  1. char* myargv[4] ={ "-fo", "-1", "-v", "1"};
  2. sift.ParseParam(4, myargv);

改为

  1. char* myargv[5] ={ "-fo", "-1", "-v", "1", "-cuda"};
  2. sift.ParseParam(5, myargv);

然后cmake编译,就可以测试了,测试结果如下

直接读取图片进行SiftGPU的测试运行时间约为88ms,相较于使用OpenGL的测试速度提升了少许,但还没有质的飞跃,当然也可能跟我的笔记本GPU算力不够强大有关。OpenCV读取后的数据格式不符,目前还没有找到对应的函数,后续会补充更新的测试程序。


总结

SIFT在特征点检测领域是一个非常优秀的算子,用于匹配准确率高,缺点是速度慢,而Wu Changchang提出的SiftGPU算法加快了特征点的提取,在CUDA的加成下相较于只调用OpenGL的SiftGPU速度提升有限,因此对于没有装CUDA的同学们来说这算是一个利好。7700HQ的CPU+GTX1050的显卡可以将一幅640*480的图像只用31毫秒便找出了所有的特征点,因此在无人机的轻量级运算平台上的应用很可期。

本文主要是介绍了SiftGPU的编译和使用过程,改进了源码的几处错误以便于正确编译,尝试了调用CUDA的方法,给出了解决方案,对SiftGPU的使用提供了较为系统的方法。欢迎读者提出指正与问题,便于讨论与共同进步。

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号