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. BZOJ_2594_[Wc2006]水管局长数据加强版_LCT

    BZOJ_2594_[Wc2006]水管局长数据加强版_LCT Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供 ...

  2. Python初学者必看(1)

    python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言 ...

  3. gitlab pipelines job执行时日志较大报错

    问题描述 gitlab pipelines job执行时日志较大报错 Job's log exceeded limit of 4194304 bytes. 解决方案 出现该问题主要是因为gitlab ...

  4. Windows Ubuntu Bash申请免费通配符证书(Let's Encrypt)并绑定IIS

    什么是 Let’s Encrypt? 部署 HTTPS 网站的时候需要证书,证书由 CA 机构签发,大部分传统 CA 机构签发证书是需要收费的,这不利于推动 HTTPS 协议的使用. Let’s En ...

  5. stm32的NVIC是什么?

    NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器.    对于M3和M4内核的MCU,每个中断的优先级都是用寄存器中的8位来设置的.8位的话 ...

  6. c语言最大公约数及最小公倍数的详解

    今天我打算把,学习到的一些知识整理一下,方便给以后的学弟学妹做一个参考! 这一次是关于最大公约数和最小公倍数的知识:这是百度关于最大公约数的介绍 感谢我的一位学姐的博文,让我能够更快的明白! 求最小公 ...

  7. jackson json转对象 json转集合 对大小写支持

    @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, isGetterVisibi ...

  8. springboot+redis分布式锁-模拟抢单

    本篇内容主要讲解的是redis分布式锁,这个在各大厂面试几乎都是必备的,下面结合模拟抢单的场景来使用她:本篇不涉及到的redis环境搭建,快速搭建个人测试环境,这里建议使用docker:本篇内容节点如 ...

  9. 我眼中的 Nginx(六):深入 Nginx/Openresty 服务里的 DNS 解析

    张超:又拍云系统开发高级工程师,负责又拍云 CDN 平台相关组件的更新及维护.Github ID: tokers,活跃于 OpenResty 社区和 Nginx 邮件列表等开源社区,专注于服务端技术的 ...

  10. Error Code: 1044. Access denied for user 'root'@'%' to database

    mysql> SELECT host,user,password,Grant_priv,Super_priv FROM mysql.user; +--------------+--------- ...