• 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})
    

    就是设置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和头文件复制到项目的目录下。

  • 相关阅读:
    Codeforces Round #358 (Div. 2)
    Codeforces Round #357 (Div. 2)
    Codeforces Round #356 (Div. 2)
    第11章例题(紫书)
    第10章例题(紫书)
    Codeforces Round #354 (Div. 2)
    2016百度之星
    BestCoder 1st Anniversary
    BestCoder Round #41
    BestCoder Round #40
  • 原文地址:https://www.cnblogs.com/wangguchangqing/p/10132052.html
Copyright © 2020-2023  润新知