当有读者看到我这篇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。

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

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

$ cd Downloads/glew-2.1.0
$ make
$ sudo make install

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

$ sudo ldconfig /usr/lib64/

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

$ cd Downloads/SiftGPU
$ make

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

$ ldd bin/libsiftgpu.so

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

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

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

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

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

改为

virtual ~LiteWindow()
{
if(glut_id > )
{
  int argc = ;
  char** argv;
  glutInit(&argc, argv);
  glutDestroyWindow(glut_id);
}
}

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

#include <stddef.h>

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

/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);

测试与结果

测试代码我放在了我的github上,有兴趣的同学可以下载下来测试。

OpenGL

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

cmake_minimum_required(VERSION 2.8.)
project(test_SiftGPU) # OpenCV依赖
find_package( OpenCV REQUIRED ) # OpenGL
find_package(OpenGL REQUIRED) # GLUT
find_package(GLUT REQUIRED) # Glew
find_package(GLEW REQUIRED) find_package(CUDA REQUIRED) # SiftGPU:手动设置其头文件与库文件所在位置
include_directories("/home/yao/Environment/SiftGPU/src/SiftGPU/" ${OpenGL_INCLUDE_DIR})
set(SIFTGPU_LIBS "/home/yao/Environment/SiftGPU/bin/libsiftgpu.so") add_executable( test_SiftGPU main.cpp )
target_link_libraries( testSiftGPU
${OpenCV_LIBS}
${SIFTGPU_LIBS}
${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES}
)

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

main.cpp我一开始使用了高博的代码,在用OpenCV读入图像的条件下,检测出一张640*480图像中所有的特征点需要不到10ms,但是我发现这个测试程序有一些问题,前一部分是测试直接读取一张图片进行Sift检测,后面是对同一张图片先读取再检测。当我注释掉前一段代码时,发现后一段代码无法运行,我必须取消注释检测那一句代码才可以运行,因此我推测后一段代码引用了前一段的结果。我重新改写了一份测试代码,测试速度还算是比较快,通过调参,可以在我的笔记本上达到30ms/帧的检测速度。

在定义部分,默认调用OpenGL,如果想要调用CUDA,换用另一个字符串指针即可,但是要预先安装好CUDA,以及配置好CUDA,配置过程在后面会写。


#include <SiftGPU.h>
#include <iostream>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <chrono>
#include <GL/gl.h>


using namespace std;
using namespace chrono;


int main( int argc, char** argv)
{
  //声明SiftGPU并初始化
  SiftGPU sift;
  char* myargv[5] = { "-m", "-s", "-unpa", "0"};
  //char* myargv[4] = {"-fo", "-1", "-cuda", "0"};
  sift.ParseParam(5, myargv);


  //检查硬件是否支持SiftGPU
  int support = sift.CreateContextGL();
  if ( support != SiftGPU::SIFTGPU_FULL_SUPPORTED )
  {
    std::cerr << "SiftGPU is not supported!" << std::endl;
    return 2;
  }


  sift.ParseParam(5, myargv);
  cv::Mat img = cv::imread("/home/yao/workspace/SIFT_detection/image/2.png");
  int width = img.cols;
  int height = img.rows;

  sift.AllocatePyramid(width, height);
  sift.SetTightPyramid(1);
  auto start_siftgpu = std::chrono::system_clock::now();
  sift.RunSIFT(width, height, img.data, GL_RGB, GL_UNSIGNED_BYTE);
  float time_cost = chrono::duration_cast<microseconds>(std::chrono::system_clock::now() - start_siftgpu).count() / 1000.0;
  std::cout << "siftgpu::runSIFT() cost time=" << time_cost << "ms" << std::endl;
  int num = sift.GetFeatureNum();
  std::cout << "Feature number=" << num << std::endl;
  std::vector<float> descriptors(128*num);
  std::vector<SiftGPU::SiftKeypoint> keys(num);

  auto start_siftfeature = std::chrono::system_clock::now();
  sift.GetFeatureVector(&keys[0], &descriptors[0]);

  return 0;
}

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

$ mkdir build
$ cd build
$ cmake ..
$ make
$ ./test_SiftGPU

结果如下图所示

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

CUDA

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

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

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

改为

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

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

$ make clean
$ make

我们切回到SiftGPU的测试程序,找到主程序main.cpp,取消注释下面这句

//char* myargv[4] = {"-fo", "-1", "-cuda", "0"};

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

调用CUDA检测处同样的一张图片中的所有特征点需要消耗31ms,与不调用CUDA的情况时间相差无几,依据SiftGPU的手册,分辨率低于1080p的时候,OpenGL速度较快,因此这种结果也可以接受。


总结

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

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

[学习笔记]SiftGPU入门的更多相关文章

  1. python学习笔记--Django入门四 管理站点--二

    接上一节  python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...

  2. WebSocket学习笔记——无痛入门

    WebSocket学习笔记——无痛入门 标签: websocket 2014-04-09 22:05 4987人阅读 评论(1) 收藏 举报  分类: 物联网学习笔记(37)  版权声明:本文为博主原 ...

  3. Java学习笔记之---入门

    Java学习笔记之---入门 一. 为什么要在众多的编程语言中选择Java? java是一种纯面向对象的编程语言 java学习起来比较简单,适合初学者使用 java可以跨平台,即在Windows操作系 ...

  4. DBFlow框架的学习笔记之入门

    什么是DBFlow? dbflow是一款android高性的ORM数据库.可以使用在进行项目中有关数据库的操作.github下载源码 1.环境配置 先导入 apt plugin库到你的classpat ...

  5. MongoDB学习笔记:快速入门

    MongoDB学习笔记:快速入门   一.MongoDB 简介 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.M ...

  6. 学习笔记_J2EE_SpringMVC_01_入门

    1.    概述 笔者作为一个不太正经的不专业佛教信仰者,习惯了解事物的因果关系,所以概述就有点BBB...了.如果不喜欢这些的,请自行跳过概述章节,直接进入第二章的操作实践:2 入门示例. 1.1. ...

  7. dubbo入门学习笔记之入门demo(基于普通maven项目)

    注:本笔记接dubbo入门学习笔记之环境准备继续记录; (四)开发服务提供者和消费者并让他们在启动时分别向注册中心注册和订阅服务 需求:订单服务中初始化订单功能需要调用用户服务的获取用户信息的接口(订 ...

  8. SpringBoot学习笔记<一>入门与基本配置

    毕业实习项目技术学习笔记 参考文献 学习视频 2小时学会Spring Boot:https://www.imooc.com/learn/767 学习资料 SpringBoot入门:https://bl ...

  9. [学习笔记] pymysql入门

    一.快速开始 对于会用MySQL的朋友来说,开始使用pymysql可以说真的so esay,只要用下面的代码,把想要对数据库的操作放在 sql = " " 里就可以了. 没有接触过 ...

随机推荐

  1. 安卓预览报错 Failed to load AppCompat ActionBar with unknown error

    报错信息 : Render ProblemFailed to load AppCompat ActionBar with unknown error. Failed to instantiate on ...

  2. [luogu2668] 斗地主

    题面 ​ 这好像就是道**暴搜题, 由于可以回溯, 所以顺序其实没有多大的关系, 见代码吧... 具体代码 #include <iostream> #include <cstring ...

  3. 【转载】Caffe + Ubuntu 14.04 + CUDA 6.5 新手安装配置指南

    洋洋洒洒一大篇,就没截图了,这几天一直在折腾这个东西,实在没办法,不想用Linux但是,为了Caffe,只能如此了,安装这些东西,遇到很多问题,每个问题都要折磨很久,大概第一次就是这样的.想想,之后应 ...

  4. “error : unknown filesystem”的解决的方法

    解决方法例如以下: 1:首先使用ls命令显示出ubuntu分区的安装信息: 1 grub rescue>ls 通常会罗列出全部磁盘的分区信息,比方(hd0,msdos1)(hd0,msdos2) ...

  5. CCF 201709-1打酱油

    问题描述 小明带着N元钱去买酱油.酱油10块钱一瓶,商家进行促销,每买3瓶送1瓶,或者每买5瓶送2瓶.请问小明最多可以得到多少瓶酱油. 输入格式 输入的第一行包含一个整数N,表示小明可用于买酱油的钱数 ...

  6. pyhton 面向对象之 小明左右手换牌

    '''#左右手交换牌 案列#小明手里有俩张牌,左手红桃♥K,右手黑桃♠A,小明交换俩手的牌后,手里分别是什么? 人类:    属性:小明,左手,右手    行为:展示手里的牌, 交换手里的牌手类:   ...

  7. 什么是控制反转(IOC)?什么是依赖注入?

    控制反转是应用于软件工程领域中的,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对象之间耦合关系在编译时通常是未知的.在传统编程方式中,业务逻辑的流程是应用程序中早已被设定好关联关系的对象来决定的 ...

  8. MariaDB中文乱码之解决思路

    首先出现乱码的原因就是编码不一致问题引起的,那么就从以下2个方面入手: 1.应用层:前提条件数据库服务端存储的中文数据是对的,但是页面上显示乱码,这里只需要检查你的项目的编码格式,设置成一致就行. 2 ...

  9. mysql获取随机题目、排序

    mysql排序问题(对字符串类型数据进行排序)对普通数字字符串字段排序:select * from qq ORDER BY score*1 DESC,time*1 ASC 一.在mysql操作中我们经 ...

  10. Redis简介和常见的面试题

    redis介绍及特点 Redis是由意大利人Salvatore Sanfilippo开发的一款内存内存高速缓存数据库. Reids全称为:Remote Dictionary Server(远程数据服务 ...