SiftGPU在Ubuntu和Windows下的编译与使用
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})
就是设置linclud
和lib
的位置,手动指定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.sln
和SiftGPU_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下的编译与使用的更多相关文章
- 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 ...
- 原创 C++应用程序在Windows下的编译、链接:第一部分 概述
本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 cl.exe是windows平台下的编译器,link.ex ...
- 【FFmpeg】Windows下FFmpeg编译
由于FFmpeg是基于Linux开发的开源项目,源代码和Windows下最常见的Visual Studio提供的C/C++编译器不兼容,因此它不能使用MSVC++编译,需要在Windows下配置一个类 ...
- C++应用程序在Windows下的编译、链接(一)概述
C++应用程序在Windows下的编译.链接(一)概述 本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 c ...
- ACE在windows下的编译及配置(VS2010)
ACE在windows下的编译及配置(VS2010) 分类: -[小西南]- 2013-08-06 16:17 2354人阅读 评论( ...
- [转]QGis2.9在windows下的编译以及二次开发包下载
今天心血来潮,将QGis在github上的代码更新后,又编译了一下.留意到源代码包里面的INSTALL文件有更新,于是本次编译完全基于官方的编译说明.编译过程非常顺利,除了在CMake的第一次conf ...
- windows下rabbitmq-c编译(带openssl、无需MinGW)
因为项目原因,需要使用到rabbitmq的c客户端库.首先,参见上一篇windows下openssl编译,如果已经使用cmake编译过了,则先delete cache(File-Delete Cach ...
- Windows下PythonQt编译(vs2015+Qt5.11.2+PythonQt 3.2)
后记: 由于自己low,没有下载罪行的python3.2导致编译上遇到种种问题,后文可以参考,建议看: <Windows7 VS2015 下编译 PythonQt3.2> https:// ...
- Windows下PythonQt编译(vs2015+Qt5.11.2+PythonQt 3.2)探索
时间:2018年10月20日 笔者最近在做Qt方面的开发工作,需用到脚本程序对程序内部进行扩展,就很自然的想到了PythonQt,下面介绍PythonQt在Windows下的的安装编译心得,水平有限, ...
随机推荐
- C++中函数重载和函数覆盖的区别
C++中经常会用到函数的重载和覆盖,二者也在很多场合都拿出来进行比较,这里我就对二者的区别做点总结: 函数重载: 函数重载指的是函数名相同.函数特征值不同的一些函数,这里函数的特征值指的是函数的参数的 ...
- UOJ_274_[清华集训2016]温暖会指引我们前行_LCT
UOJ_274_[清华集训2016]温暖会指引我们前行_LCT 任务描述:http://uoj.ac/problem/274 本题中的字典序不同在于空串的字典序最大. 并且题中要求排序后字典序最大. ...
- BZOJ_1070_[SCOI2007]修车_费用流
BZOJ_1070_[SCOI2007]修车_费用流 Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同 的车进行维修所用的时间 ...
- 浅谈HTTP协议
1 HTTP概念 把握三个点: 1 HTTP协议(超文本传输协议) HTTP是一个基于TCP/IP通信协议来传递数据,默认端口80 2 HTTP是无连接(限制每次连接只处理一个请求),无状态的(对于事 ...
- 在linux服务器之间复制文件和目录命令scp
scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...
- Guess 任意猜
age_of_oldboy = 56 count = 0 while count <3: guess_age = int(input("guess age:")) if gu ...
- Github泄露扫描系统
Github leaked patrol Github leaked patrol为一款github泄露巡航工具: 提供了WEB管理端,后台数据库支持SQLITE3.MYSQL和POSTGRES 双引 ...
- C++的代理类
怎样在一个容器中包含类型不同,但是彼此有关系的对象?众所周知,C++的容器只能存放类型相同的元素,所以直接在一个容器中存储不同类型的对象本身是不可能的,只能通过以下两种方案实现: 1. 提供一个间接层 ...
- Java SPI机制用法demo
①构建一个maven工程 包含如下目录结构: src/main/java src/main/resources src/test/java src/test/resources ②在src/main/ ...
- knowledge, Experience & Creativity
In a training session, the trainer asked the audience "knowledge is power, how many of you agre ...