SLAM拾萃(3):siftGPU

前言

  本週博客咱們給你們介紹一下SiftGPU。因爲特徵匹配是SLAM中很是耗時間的一步,許多人都想把它的時間降至最短,所以目前ORB成了很是受歡迎的特徵。而老牌SIFT,則一直給人一種「很嚴謹很精確,但計算很是慢」的印象。在一個普通的PC上,計算一個640$\times$480的圖中的SIFT大約須要幾百毫秒左右。若是特徵都要算300ms,加上別的ICP什麼的,一個SLAM就只能作成兩幀左右的速度了,這是很使人失望的。而ORB,FAST之類的特徵,因爲計算速度較快,在SLAM這種實時性要求較高的場合更受歡迎。linux

  那麼,今天咱們來講一個GPU版本的SIFT。它是由Wu Changchang同窗寫的。它可以明顯地提高你的程序提取SIFT的速度。同時,它的代碼大部分是基於OpenGL的,即便在沒有英偉達顯卡的機器上也能運行起來。但另外一方面,出於某種(歷史或人爲的)緣由,SiftGPU的代碼配置起來並不很容易(特別是在Linux下,彷佛SiftGPU做者是在win下開發的),代碼新人可能會以爲比較困難。如今咱們帶着你們實踐一下SiftGPU,我會給出一個例程供你們測試。ios

  首先,說說個人運行配置。我用的機器是Thinkpad T450, Intel+Nvidia GetForce 940m顯卡。但我我的只用Intel卡,因此我就不編譯Cuda了。各位有上好N卡的同窗也能夠搞個Cuda下來編,可能會提升一點速度(但我不保證)。我使用的操做系統是Ubuntu 14.04,OpenCV3.1版本。因此我假設你OpenCV已經裝好啦!(因此c++編譯器總有的吧!) 不過opencv是否是3.1版本是不要緊的,程序在2.x版本上也是能正常運行的。c++

  小蘿蔔:師兄你這真是宅男標配啊!你究竟是在講配置環境仍是在秀桌面啊!git


下載SiftGPU與依賴庫

   SiftGPU主頁:http://www.cs.unc.edu/~ccwu/siftgpu/ github

  請找到「SiftGPU-V400"那個下載連接,保存到你的電腦上。而後解壓縮,進入壓縮後的文件夾。假定你也在用Ubuntu,那麼你如今的目錄應該是 ~/Downloads/SiftGPU/ 。注意,爲了和我保持一致,請你暫時不要下載github上面那個版本,那個與它稍有不一樣。若是你就是喜歡github,能夠把這個編譯好,再考慮用github版本。小程序

  如今咱們來安裝依賴項。首先,確保你機器上有OpenGL,請安裝如下幾項工具:編輯器

1 sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev 

   而後,要安裝glew1.5.1以上版本。據我我的經驗,最好是去下載glew網站的版本。ide

  glew的網址:http://glew.sourceforge.net/函數

  請下載那個1.13.0版本,zip文件或tgz都可。下載到本地並解壓,而後進入該文件夾。個人在~/Downloads/glew-1.13.0工具

  glew是用makefile直接編譯的,不用cmake。因此咱們直接敲:

1 make 
2 sudo make install 

   便可。很快它就編譯好了。

  注意看make install輸出的信息。它默認把編譯好的庫文件libglew.so.1.13放到了/usr/lib64下。因爲以後咱們要用cmake去編,可是它可能找不到這個文件夾,因此咱們如今先告訴系統,該文件夾下有要找的連接庫:  

1 sudo ldconfig /usr/lib64/

   ok,如今咱們處理完了glew,轉去編譯SiftGPU。SiftGPU也是用Makefile編譯的。如今轉到SiftGPU所在文件夾。調用

1 make

   來完成編譯。若是順利的話,你會在bin/目錄裏獲得幾個二進制和一個libsiftgpu.so庫文件。咱們主要使用這個庫文件。如今看一下它的連接是否正確:

1 ldd bin/libsiftgpu.so

   這個命令會輸出與它連接的庫的信息。請保證沒有出現某個連接(特別是剛纔的GLEW)沒有找到的狀況(不然這裏會經過,但後面會出現undefined reference)。像我這樣:

  若是這步正確無誤,恭喜你,SiftGPU已經編譯完成了!真是可喜可賀呀!

  小蘿蔔:而後呢?師兄我還沒看到什麼感受很厲害的東西啊?

  師兄:下面咱們來實際找一個圖片,寫一段小程序調用SiftGPU,提一下特徵試試。爲測試速度,咱們還要記錄一下代碼運行時間。


測試SiftGPU

  如今咱們來寫一個測試程序。因爲它比較短,我就不專門搞個github了。請你們跟着我作便可。

  首先,隨意新建一個目錄,好比test_siftgpu。咱們要寫一個c++程序,而後用cmake編譯它。如今新建一個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 
20 int main( int argc, char** argv)
21 {
22     //聲明SiftGPU並初始化
23     SiftGPU sift;
24     char* myargv[4] ={ "-fo", "-1", "-v", "1"};
25     sift.ParseParam(4, myargv);
26 
27     //檢查硬件是否支持SiftGPU
28     int support = sift.CreateContextGL();
29     if ( support != SiftGPU::SIFTGPU_FULL_SUPPORTED )
30     {
31         cerr<<"SiftGPU is not supported!"<<endl;
32         return 2;
33     }
34 
35     //測試直接讀取一張圖像
36     cout<<"running sift"<<endl;
37     boost::timer timer;
38     //在此填入你想測試的圖像的路徑!不要用個人路徑!不要用個人路徑!不要用個人路徑!
39     sift.RunSIFT( "/home/xiang/wallE-slam/data/rgb1.png" );
40     cout<<"siftgpu::runsift() cost time="<<timer.elapsed()<<endl;
41     
42     // 獲取關鍵點與描述子
43     int num = sift.GetFeatureNum();
44     cout<<"Feature number="<<num<<endl;
45     vector<float> descriptors(128*num);
46     vector<SiftGPU::SiftKeypoint> keys(num);
47     timer.restart();
48     sift.GetFeatureVector(&keys[0], &descriptors[0]);
49     cout<<"siftgpu::getFeatureVector() cost time="<<timer.elapsed()<<endl;
50 
51     // 先用OpenCV讀取一個圖像,而後調用SiftGPU提取特徵
52     cv::Mat img = cv::imread("/home/xiang/wallE-slam/data/rgb1.png", 0);
53     int width = img.cols;
54     int height = img.rows;
55     timer.restart();
56     // 注意咱們處理的是灰度圖,故照以下設置
57     sift.RunSIFT(width, height, img.data, GL_INTENSITY8, GL_UNSIGNED_BYTE);
58     cout<<"siftgpu::runSIFT() cost time="<<timer.elapsed()<<endl;
59 
60     return 0;
61 }

   Sift接口仍是至關簡單的。在這程序裏,咱們一共作了三件事。一是直接對一個圖像路徑提Sift,二是獲取Sift的關鍵點和描述子。三是對OpenCV讀取的一個圖像提取Sift。咱們分別測了三者的效果和時間。

  接下來,寫一個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/xiang/Downloads/SiftGPU/src/SiftGPU/" ${OpenGL_INCLUDE_DIR})
set(SIFTGPU_LIBS "/home/xiang/Downloads/SiftGPU/bin/libsiftgpu.so")

add_executable( testSIFTGPU main.cpp )

target_link_libraries( testSIFTGPU
    ${OpenCV_LIBS}
    ${SIFTGPU_LIBS}
    ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES}
)

   對於SiftGPU,因爲它自己沒有提供cmake的配置,咱們手動去設置了它的頭文件與庫文件的連接方式。你們能夠學習一下這種比較土的辦法……而後就是常見的cmake啦:

mkdir build
cd build
cmake ..
make

   等一下!是否是還忘了些什麼呢?嗯,若是你直接去cmake的話,會報一個find_package找不到glew的錯!由於咱們裝glew的時候是直接用make install裝的嘛,cmake怎麼會知道咱們幹了這件事呢?因此此時find_package(Glew REQUIRED)就會出錯啦!

  小蘿蔔:爲何出錯了你仍是很高興的樣子……

  師兄:對!如今呢咱們要本身寫一個FindGlew.cmake文件嘍。請打開你的編輯器,輸入:

 1 #
 2 # Try to find GLEW library and include path.
 3 # Once done this will define
 4 #
 5 # GLEW_FOUND
 6 # GLEW_INCLUDE_PATH
 7 # GLEW_LIBRARY
 8 # 
 9 
10 IF (WIN32)
11     FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
12         $ENV{PROGRAMFILES}/GLEW/include
13         ${PROJECT_SOURCE_DIR}/src/nvgl/glew/include
14         DOC "The directory where GL/glew.h resides")
15     FIND_LIBRARY( GLEW_LIBRARY
16         NAMES glew GLEW glew32 glew32s
17         PATHS
18         $ENV{PROGRAMFILES}/GLEW/lib
19         ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin
20         ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib
21         DOC "The GLEW library")
22 ELSE (WIN32)
23     FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
24         /usr/include
25         /usr/local/include
26         /sw/include
27         /opt/local/include
28         DOC "The directory where GL/glew.h resides")
29     FIND_LIBRARY( GLEW_LIBRARY
30         NAMES GLEW glew
31         PATHS
32         /usr/lib64
33         /usr/lib
34         /usr/local/lib64
35         /usr/local/lib
36         /sw/lib
37         /opt/local/lib
38         DOC "The GLEW library")
39 ENDIF (WIN32)
40 
41 IF (GLEW_INCLUDE_PATH)
42     SET( GLEW_FOUND 1 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
43 ELSE (GLEW_INCLUDE_PATH)
44     SET( GLEW_FOUND 0 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
45 ENDIF (GLEW_INCLUDE_PATH)
46 
47 MARK_AS_ADVANCED( GLEW_FOUND )

   而後呢,把這個文件放到cmake的modules文件夾中去!這樣cmake就會知道你在調用find_package(Glew)時怎麼找啦!

sudo cp ./FindGlew.cmake /usr/share/cmake-2.8/Modules/

   注意到這個文件所在的目錄一般是沒有寫權限的的哦!因此咱們要用sudo提高到管理員權限才行呢。

  這時,再調用cmake ..,就不會報上面的錯誤啦!而編譯也得以順利進行下去了。

  可是!可是!編譯仍是出錯了,錯誤以下:

/home/xiang/Downloads/SiftGPU/src/SiftGPU/SiftGPU.h:336:40: error: declaration of ‘operator newas non-function SIFTGPU_EXPORT void* operator new (size_t size);

   這是什麼緣由呢?g++的編譯錯誤很難懂,一直爲人詬病。師兄仔細查了查,發現SiftGPU做者重載了new運算符,可是它的參數"size_t size"中的"size_t"類型,在linux下編譯是須要指定一個頭文件的!因此咱們打開~/Downloads/SiftGPU/src/SiftGPU/SiftGPU.h文件,在上頭加入一個

#include <stddef.h>

  

  這樣編譯器就會找到size_t類型啦!編譯就能經過嘍!


SiftGPU運行結果

  如下就是在師兄電腦上的運行結果啦,你們能夠看一下:

  對於OpenCV已經讀入的數據,在640x480的分辨率下,用SiftGPU只需40多毫秒便可完成計算了呢!GPU真的是很強大啊!即便在沒有Cuda的狀況下都取得了近十倍的加速啊!效果拔羣!

  小蘿蔔:個人ORB只要30毫秒就好了,哼.


小結

  本篇介紹了SiftGPU,咱們帶領讀者完成了它的編譯,並在本身的程序內實現了調用。能夠看到它的加速效果仍是不錯的!

  另外,這也是個人一次嘗試,告訴讀者在編譯過程當中遇到問題該如何處理。我本能夠直接跳過這些buggy的部分,告訴你們運行的結果。但我以爲這樣子講可能對讀者更有幫助啦!


 

  若是你以爲個人博客有幫助,能夠進行幾塊錢的小額贊助,幫助我把博客寫得更好。

  

相關文章
相關標籤/搜索