Sift特征应该是使用最多的局部特征了,但是相比其他的一些特征描述符,计算sift特征描述符的时间较长。Changchang Wu使用GPU加速,实现了GPU版的sift特征提取SiftGPU。 SiftGPU应该是在Windows环境下完成的,其在Windows下的配置较为简单。

本文首先解释了,在Ubuntu下SiftGPU的编译,并简单的实现了一个类,封装SiftGPU的特征提取和匹配。在最后简单的介绍了下,SiftGPU在Windows下的使用。

Ubuntu下的安装与使用

  • 安装依赖库
sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev
  • 编译glew 下载地址 glew
make
sudo make install

安装位置为/usr/lib64

编译SiftGPU

从Git上下载SiftGPU的源代码,下载的原始代码在编译的时候需要修改两个部分,可以从原作者处clone,也可以clone我修改后的代码

具体编译的过程如下:

  • 在执行make 编译,如果遇到fatal error: IL/il.h: No such file or directory,使用下面的命令安装dev image library. sudo apt-get install libdevil-dev

  • 原始的代码在编译的时候有一处错误,编译不过。

    error: declaration of ‘operator new’ as non-function SIFTGPU_EXPORT void* operator new (size_t size);

    需要在头文件src/SiftGPU/SiftGPU.h中添加一句

#include <stddef.h>
  • 原始代码编译生成的库,在使用的时候会出现错误: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);
}
}
  • 编译生成的库在/bin/libsiftgpu.so,可以使用ldd bin/libsiftgpu.so测试生成的库链接是否正确。

使用

首先配置下CMakeLists.txt如下:

cmake_minimum_required(VERSION 2.8.3)

project(test_siftgpu)

set(CMAKE_VERBOSE_MAKEFILE on)

set(OpenCV_DIR "/usr/local/opencv3.4.4/share/OpenCV")
find_package(OpenCV REQUIRED) find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) #find_package(Glew REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # set siftgpu
include_directories("/home/liqiang/Downloads/SiftGPU/src/SiftGPU")
include_directories(${OpenGL_INCLUDE_DIR}) link_directories(/usr/lib64) # GLEW set(SIFTGPU_LIBS "/home/liqiang/Downloads/SiftGPU/bin/libsiftgpu.so") add_executable(testSiftGPU main.cc) target_link_libraries(testSiftGPU ${OpenCV_LIBS} ${SIFTGPU_LIBS} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES})

就是设置lincludlib的位置,手动指定GLEW的位置link_directories(/usr/lib64) # GLEW和SiftGPU的库和头文件的位置include_directories("/home/liqiang/Downloads/SiftGPU/src/SiftGPU")set(SIFTGPU_LIBS "/home/liqiang/Downloads/SiftGPU/bin/libsiftgpu.so").

配置好CMakeLists.txt后,就可以编译下面的代码进行特征的提取和匹配了。

int main()
{
// Read image
auto detector = cv::xfeatures2d::SIFT::create(); Mat des;
vector<KeyPoint> kpts; string file1 = "/home/liqiang/Documents/shared/8.jpg";
auto t = getTickCount();
auto img = imread(file1);
detector->detectAndCompute(img,noArray(),kpts,des);
auto end = static_cast<double>(getTickCount() - t) / getTickFrequency();
cout << "OpenCV get sift consume:" << end << endl;
cout << "count:" << kpts.size() << endl; // Declare sift and initlize
SiftGPU sift;
char* myargv[4] = {"-fo","-1","-v","1"};
sift.ParseParam(4,myargv); // Check hardware is support siftGPU
int support = sift.CreateContextGL();
if(support != SiftGPU::SIFTGPU_FULL_SUPPORTED){
cerr << "SiftGPU is not supported!" << endl;
return 2;
} auto img1 = imread("/home/liqiang/Documents/shared/3.jpg");
auto img2 = imread("/home/liqiang/Documents/shared/4.jpg");
auto img3 = imread("/home/liqiang/Documents/shared/5.jpg");
auto img4 = imread("/home/liqiang/Documents/shared/6.jpg");
auto img5 = imread("/home/liqiang/Documents/shared/7.jpg"); auto f = [&sift](Mat &img,vector<float> &des,vector<SiftGPU::SiftKeypoint> &kpts){ auto t = getTickCount();
sift.RunSIFT(img.cols,img.rows,img.data,GL_RGB,GL_UNSIGNED_BYTE);
auto num1 = sift.GetFeatureNum(); des.resize(128 * num1);
kpts.resize(num1);
sift.GetFeatureVector(&kpts[0],&des[0]);
cout << "=======================================" << endl;
cout << "width x height : " << img.cols << "x" << img.rows << endl;
cout << "Features count:" << num1 << endl;
cout << "Extract features,consume:" << static_cast<double>(getTickCount() - t) / getTickFrequency() << endl;
}; vector<float> des1,des2,des3,des4,des5;
vector<SiftGPU::SiftKeypoint> kpts1,kpts2,kpts3,kpts4,kpts5; f(img1,des1,kpts1);
f(img2,des2,kpts2);
f(img3,des3,kpts3);
f(img4,des4,kpts4);
f(img5,des5,kpts5); SiftMatchGPU matcher;
matcher.VerifyContextGL(); matcher.SetDescriptors(0,kpts1.size(),&des1[0]);
matcher.SetDescriptors(1,kpts2.size(),&des2[0]); int (*match_buf)[2] = new int[kpts1.size()][2];
t = getTickCount();
int num_match = matcher.GetSiftMatch(kpts1.size(), match_buf);
cout << "Match keypoints count:" << num_match << endl;
end = static_cast<double>(getTickCount() - t) / getTickFrequency(); cout << "Match,consume:" << end << endl;
}

SiftGPU进行特征提取可以分为三步

  • 实例化SiftGPU,并设置其参数
    char* myargv[4] = {"-fo","-1","-v","1"};
sift.ParseParam(4,myargv);

关于SiftGPU的具体的参数说明,可以参考其/SiftGPU/doc/manual.pdf使用手册。

  • 调用RunSift函数进行特征提取,该函数有多种重载。 常用的有两个:

    • 直接传入图像的路径RunSift(const char *imgpaht)
    • 传入图像的数据RunSift(int width,int height,const void *data,unsigned int gl_format,unsigned int gl_type)

      上述代码中使用OpenCV读取图像,然后利用再调用RunSift提取特征。
  • 调用GetFeatureVector取得提取到的特征描述。

上面代码中,将上述三步封装在了一个Lambda表达式中,然后调用改表达式连续的提取了多张图片的sift特征。其运行结果如下:

使用测试的几张图象尺寸相同,内容上的变化也不是很大。 上述结果可以看到,使用OpenCV提取特征耗费的时间为:48ms,使用SiftGPU提取第一张图像的特征耗费的时间是:56ms,对比OpenCV甚至有点差距。 但是,SiftGPU在提取后几张图像的效率提升就比较明显了,只有十几毫秒。

在最后使用SiftGPU对提取的特征进行了匹配,也是很快的。

封装

对SiftGPU简单的封装了下,方便使用。代码如下:

class GpuFeatureDetector{

    enum InitStatus{
INIT_OK,
INIT_IS_NOT_SUPPORT,
INIT_VERIFY_FAILED
}; public:
GpuFeatureDetector() = default;
~GpuFeatureDetector() {
if(m_siftGpuDetector) delete m_siftGpuDetector;
if(m_siftGpuMatcher) delete m_siftGpuMatcher;
}
InitStatus create(){
m_siftGpuDetector = new SiftGPU(); char* myargv[4] = {"-fo","-1","-v","1"};
m_siftGpuDetector->ParseParam(4,myargv);
// Set edge threshold, dog threshold if(m_siftGpuDetector->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED){
cerr << "SiftGPU is not supported!" << endl;
return InitStatus::INIT_IS_NOT_SUPPORT;
} m_siftGpuMatcher = new SiftMatchGPU();
m_siftGpuMatcher->VerifyContextGL(); m_maxMatch = 4096; return INIT_OK;
} void detectAndCompute(const Mat &img,Mat &descriptors,vector<KeyPoint> &kpts){ assert(img.channels() == 3); // RGB m_siftGpuDetector->RunSIFT(img.cols,img.rows,img.data,GL_RGB,GL_UNSIGNED_BYTE);
auto num1 = m_siftGpuDetector->GetFeatureNum(); vector<float> des(128 * num1);
vector<SiftGPU::SiftKeypoint> keypoints(num1);
m_siftGpuDetector->GetFeatureVector(&keypoints[0],&des[0]); // Trans to Mat
Mat m(des);
descriptors = m.reshape(1,num1).clone(); for(const SiftGPU::SiftKeypoint &kp : keypoints){
KeyPoint t(kp.x,kp.y,kp.s,kp.o);
kpts.push_back(t);
}
} void transToRootSift(const cv::Mat &siftFeature,cv::Mat &rootSiftFeature){
for(int i = 0; i < siftFeature.rows; i ++){
// Conver to float type
Mat f;
siftFeature.row(i).convertTo(f,CV_32FC1); normalize(f,f,1,0,NORM_L1); // l1 normalize
sqrt(f,f); // sqrt-root root-sift
rootSiftFeature.push_back(f);
}
} int gpuMatch(const Mat &des1,const Mat &des2){ m_siftGpuMatcher->SetDescriptors(0,des1.rows,des1.data);
m_siftGpuMatcher->SetDescriptors(1,des2.rows,des2.data); int (*match_buf)[2] = new int[m_maxMatch][2]; auto matchNum = m_siftGpuMatcher->GetSiftMatch(m_maxMatch,match_buf); delete[] match_buf; return matchNum;
} int gpuMatch(const Mat &des1,const Mat &des2,vector<DMatch>& matches){
m_siftGpuMatcher->SetDescriptors(0,des1.rows,(float*)des1.data);
m_siftGpuMatcher->SetDescriptors(1,des2.rows,(float*)des2.data); int (*match_buf)[2] = new int[m_maxMatch][2]; auto matchNum = m_siftGpuMatcher->GetSiftMatch(m_maxMatch,match_buf); for(int i = 0 ;i < matchNum; i ++) {
DMatch dm(match_buf[i][0],match_buf[i][1],0);
matches.push_back(dm);
} delete[] match_buf;
return matchNum;
}
private:
SiftGPU *m_siftGpuDetector;
SiftMatchGPU *m_siftGpuMatcher; int m_maxMatch;
};

m_maxMatch 是进行匹配时,最多的匹配点的个数。默认的是4096.

简单的封装,并没有提供过多的参数设置。有以下功能:

  • sift特征的提取,并将提取到的结果转换为OpenCV的数据形式,便于和OpenCV一起使用
  • 将sift转换为RootSift
  • 利用SiftGPU进行特征的匹配,其匹配进行了比率测试,删除了不正确的匹配点。

其测试代码如下:

GpuFeatureDetector fp;
fp.create(); Mat des11,des22;
vector<KeyPoint> kpts11,kpts22; fp.detectAndCompute(img1,des11,kpts11);
fp.detectAndCompute(img2,des22,kpts22); vector<DMatch> matches;
cout << "matches:" << fp.gpuMatch(des11,des22,matches) << endl; Mat matchImg;
t = getTickCount();
drawMatches(img1,kpts11,img2,kpts22,matches,matchImg);
cout << static_cast<double>(getTickCount() - t) / getTickFrequency() << endl;
imshow("matches",matchImg);
waitKey();

运行结果

其过滤后的效果,还是不错的。

下图是相同的图像,使用opencv提取特征点后进行匹配(比例测试过滤,ratio=0.8,和gpu的一样)的结果

上述代码可从本人GitHub上clone https://github.com/brookicv/codeSnippet/tree/master/SiftGPU

Windows下的安装与使用

首先从从Git上下载源代码,在SiftGPU/msvc目录下有两个解决方案SiftGPU.slnSiftGPU_CUDA_Enabled.sln看名字就知道了,一个是使用GLSL的,另一个是使用CUDA的。 windows没有配置cuda的环境,这里就只编译SiftGPU.sln。打开该解决方案,如下图:

SiftGPU项目就是需要的,编译生成SiftGPU.dll。 其余的几个是测试项目和一些使用的例子。该项目的解决方案是vs2010的使用的Windows SDK为8.1,如果是windows10的系统会提示找不到相应的SDK,可以右键解决方案选择重定解决方案目标会重新设置使用Windows10的SDK。

这里只描述SiftGPU的编译过程,其余的几个项目配置类似。

  • 配置GLEW

    从http://glew.sourceforge.net/ 下载编译好的windows的二进制库,直接解压开来,得到include和lib目录。右键 SifGPU项目,选择属性,添加C++的包含目录 glew/include;添加库目录/glew/lib/Release/Win32,如果要生成64位的,这里要将目录配置到x64下面。

  • 配置DevIL

    DevIL是一个跨平台的图像库,这里需要使用期开发的SDK,下载地址http://openil.sourceforge.net/download.php 。 注意要选择DevIL 1.8.0 SDK for Windows,需要其头文件和lib。 下载后,如GLEW类似添加头文件和lib目录。 需要注意的是,由于在代码中,作者使用了相对路径来加载DevIL.lib,因为这里配置lib的路径,需要修改这部分代码。将GLTextImage.cpp中的49行附近的代码修改为如下

#ifndef SIFTGPU_NO_DEVIL
#include "IL/il.h"
#if defined(_WIN64)
#pragma comment(lib, "DevIL64.lib")
#elif defined(_WIN32)
#pragma comment(lib, "DevIL.lib")
#endif
#else
#include <string.h>
#endif

就是去掉了"DevIL.lib"前面的相对路径,改为只按名称来查找(上面配置了lib的目录)。

编译SiftGPU,生成的lib文件位于SiftGPU/lib/SiftGPU_d.lib

使用的话,只需要配置c++项目的头文件目录到SiftGPU/src/SiftGPU下,lib目录到SiftGPU/lib/。 或者,可以精简下,将SiftGPU_d.lib和头文件复制到项目的目录下。

SiftGPU在Ubuntu和Windows下的编译与使用的更多相关文章

  1. libjingler-0.6.2在windows和ubuntu 10.04下的编译(Google Talk)

    Libjingle版本:0.6.2 所需的资源:         gtest-1.6.0.zip         http://download.csdn.net/detail/cl_gamer/48 ...

  2. 原创 C++应用程序在Windows下的编译、链接:第一部分 概述

    本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 cl.exe是windows平台下的编译器,link.ex ...

  3. 【FFmpeg】Windows下FFmpeg编译

    由于FFmpeg是基于Linux开发的开源项目,源代码和Windows下最常见的Visual Studio提供的C/C++编译器不兼容,因此它不能使用MSVC++编译,需要在Windows下配置一个类 ...

  4. C++应用程序在Windows下的编译、链接(一)概述

    C++应用程序在Windows下的编译.链接(一)概述 本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 c ...

  5. ACE在windows下的编译及配置(VS2010)

    ACE在windows下的编译及配置(VS2010) 分类:             -[小西南]-              2013-08-06 16:17     2354人阅读     评论( ...

  6. [转]QGis2.9在windows下的编译以及二次开发包下载

    今天心血来潮,将QGis在github上的代码更新后,又编译了一下.留意到源代码包里面的INSTALL文件有更新,于是本次编译完全基于官方的编译说明.编译过程非常顺利,除了在CMake的第一次conf ...

  7. windows下rabbitmq-c编译(带openssl、无需MinGW)

    因为项目原因,需要使用到rabbitmq的c客户端库.首先,参见上一篇windows下openssl编译,如果已经使用cmake编译过了,则先delete cache(File-Delete Cach ...

  8. Windows下PythonQt编译(vs2015+Qt5.11.2+PythonQt 3.2)

    后记: 由于自己low,没有下载罪行的python3.2导致编译上遇到种种问题,后文可以参考,建议看: <Windows7 VS2015 下编译 PythonQt3.2> https:// ...

  9. Windows下PythonQt编译(vs2015+Qt5.11.2+PythonQt 3.2)探索

    时间:2018年10月20日 笔者最近在做Qt方面的开发工作,需用到脚本程序对程序内部进行扩展,就很自然的想到了PythonQt,下面介绍PythonQt在Windows下的的安装编译心得,水平有限, ...

随机推荐

  1. jsp 基础知识之指令元素

    由于考研和结业的事情,这里荒废了许久,而如今重新捡起来,是因为带到公司的碳素笔没有油了......    jsp的指令元素:通常以<%@开始,以%>结尾. jsp主要包括三种指令元素:pa ...

  2. 毕业原版=[约克大学毕业证书]York原件一模一样证书

    约克大学毕业证[微/Q:2544033233◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归& ...

  3. 隐马尔可夫模型(HMM)总结

    摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项(算法过程,调参等注意事项) 5.实现和具体例子 6.适用场合 内容: 1.算法概述 隐马尔科夫模型(Hidden Markov ...

  4. 基于SpringBoot从零构建博客网站 - 技术选型和整合开发环境

    技术选型和整合开发环境 1.技术选型 博客网站是基于SpringBoot整合其它模块而开发的,那么每个模块选择的技术如下: SpringBoot版本选择目前较新的2.1.1.RELEASE版本 持久化 ...

  5. Python一行代码实现快速排序

    上期文章排序算法——(2)Python实现十大常用排序算法为大家介绍了十大常用排序算法的前五种(冒泡.选择.插入.希尔.归并),因为快速排序的重要性,所以今天将单独为大家介绍一下快速排序! 一.算法介 ...

  6. Linux - 修改系统的max open files、max user processes (附ulimit的使用方法)

    目录 1 问题说明 2 修改max open files 3 修改max user processes 4 附录: ulimit命令说明 1 问题说明 Linux 系统默认的max open file ...

  7. 『性能』测试一下 MSSqlHelper 的性能

    本文没啥技术含量,就是测试一下 MSSqlHelper 在 使用反射.不使用反射 的性能对比. 之后,不要问为什么不用 ORM 这类的东西 —— 会有另外的文章 介绍 自己这些年 自己的ORM 升级历 ...

  8. windows下,读取快捷方式lnk所指向的路径

    BOOL GetLnkFileName( OUT PWSTR pLnkName, OUT PWSTR OepnFileNameBuufer, IN DWORD OpenFileNameBufferSi ...

  9. 并发系列(7)之 ScheduledThreadPoolExecutor 详解

    文本将主要讲述 ThreadPoolExecutor 一个特殊的子类 ScheduledThreadPoolExecutor,主要用于执行周期性任务:所以在看本文之前最好先了解一下 ThreadPoo ...

  10. C# .NET Web API 如何自訂 ModelBinder

    各位好!這次要來替大家介紹的是如何在 .NET  Web API 中自訂一個 ModelBinder 透過自定義的 ModelBinder 我們可以很簡單的將 QueryString 傳過來的參數綁定 ...