欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题

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

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

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

[环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题

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

freeglut error: function <glutdestroywindow> called without first calling 'glutinit'.

我们打开src/siftgpu/litewindow.h,找到

virtual ~litewindow()   {  if(glut_id > 0) glutdestroywindow(glut_id);  }

改为

virtual ~litewindow()   
{  
    if(glut_id > 0) 
    {
      int argc = 0;
      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);

测试与结果

opengl

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

cmake_minimum_required(version 2.8.3)
project(test_siftgpu)

# opencv依赖
find_package( opencv required )

# opengl
find_package(opengl required)

# glut
find_package(glut required)

# glew
find_package(glew 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我直接用高博的代码,后续的学习之后可能会写一个新的测试代码,更便于学习。

// siftgpu模块
#include <siftgpu.h>

//标准c++
#include <iostream>
#include <vector>

// opencv图像
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

// boost库中计时函数
#include <boost/timer.hpp>

// opengl
#include <gl/gl.h>

using namespace std;

int main( int argc, char** argv)
{
    //声明siftgpu并初始化
    siftgpu sift;
    char* myargv[4] ={ "-fo", "-1", "-v", "1"};
    sift.parseparam(4, myargv);

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

    //测试直接读取一张图像
    cout<<"running sift"<<endl;
    boost::timer timer;
    //在此填入你想测试的图像的路径!不要用我的路径!不要用我的路径!不要用我的路径!
    sift.runsift( "/home/yao/workspace/sift_detection/image/1.png" );
    cout<<"siftgpu::runsift() cost time="<<timer.elapsed()<<endl;
    
    // 获取关键点与描述子
    int num = sift.getfeaturenum();
    cout<<"feature number="<<num<<endl;
    vector<float> descriptors(128*num);
    vector<siftgpu::siftkeypoint> keys(num);
    timer.restart();
    sift.getfeaturevector(&keys[0], &descriptors[0]);
    cout<<"siftgpu::getfeaturevector() cost time="<<timer.elapsed()<<endl;

    // 先用opencv读取一个图像,然后调用siftgpu提取特征
    cv::mat img = cv::imread("/home/yao/workspace/sift_detection/image/1.png", 0);
    int width = img.cols;
    int height = img.rows;
    timer.restart();
    // 注意我们处理的是灰度图,故照如下设置
    sift.runsift(width, height, img.data, gl_intensity8, gl_unsigned_byte);
    cout<<"siftgpu::runsift() cost time="<<timer.elapsed()<<endl;

    return 0;
}

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

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

结果如下图所示

[环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题

测试代码只调用了opengl,我笔记本的配置是i7-7700hq,显卡gtx1050,可以看到在opencv已经读取了图片的情况下,提取出一张图像中所有的sift特征点只需要31毫秒,直接读取一张图进行sift特征提取的时候,需要100多毫秒,这与传统的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", "-v", "1"};
sift.parseparam(4, myargv);

改为

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

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

[环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题

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


总结

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

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