Bootstrap

opencv上gpu版surf特征点与orb特征点提取及匹配实例

opencv上gpu版surf特征点与orb特征点提取及匹配实例

标签: gpu版surfgpu orbsurf和orbgpu surf及orbsurf orb gpu
  464人阅读  评论(0)  收藏  举报
  分类:

一、前言

本文主要实现了使用OpenCV里的gpu版surf特征检测器和gpu版orb检测器,分别对图片进行特征点提取及匹配,并对寻获的特征点进行了距离筛选,将匹配较为好的特征点进行展示

二、实现代码

我不生产代码,我只是代码的搬运工和修改工

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //main.cpp//  
  2. #include <opencv2/core/core.hpp>  
  3. #include <opencv2/imgproc/imgproc.hpp>  
  4. #include <opencv2/highgui/highgui.hpp>  
  5. #include <opencv2/gpu/gpu.hpp>  
  6. #include <opencv2/nonfree/gpu.hpp>  
  7. #include <opencv2/nonfree/features2d.hpp>   
  8. #include <iostream>  
  9.   
  10. using namespace std;  
  11. using namespace cv;  
  12.   
  13. Mat rotatedImage(const Mat & _src, double _degree)  
  14. {  
  15.     int width_src = _src.cols;  
  16.     int height_src = _src.rows;  
  17.   
  18.     float center_x = width_src / 2.0;  
  19.     float center_y = height_src / 2.0;  
  20.       
  21.     double angle =  _degree  * CV_PI / 180.;   
  22.     double a = sin(angle), b = cos(angle);  
  23.   
  24.     Mat map_matrix = getRotationMatrix2D(Point2f(center_x, center_y), _degree, 1.0);//获得旋转矩阵  
  25.     int height_rotated = height_src*fabs(b) + width_src*fabs(a);  
  26.     int width_rotated = height_src*fabs(a) + width_src*fabs(b);  
  27.   
  28.     map_matrix.at<double>(0, 2) += (width_rotated - width_src) / 2.0; //将坐标移到中点  
  29.     map_matrix.at<double>(1, 2) += (height_rotated - height_src) / 2.0; //将坐标移到中点  
  30.   
  31.     Mat dst;  
  32.     warpAffine(_src, dst, map_matrix, Size(width_rotated, height_rotated),   
  33.         CV_INTER_CUBIC | CV_WARP_FILL_OUTLIERS, BORDER_CONSTANT, cvScalarAll(0));  
  34.   
  35.     return dst;  
  36. }  
  37.   
  38. //主要获得surf特征点、描述子、及特征点匹配  
  39. void surfExtractor(Mat& _src_Img, Mat& _dst_Img )  
  40. {  
  41.     gpu::GpuMat src_gpu(_src_Img);  
  42.     gpu::GpuMat dst_gpu(_dst_Img);  
  43.   
  44.     std::vector<KeyPoint> keypoints_src;  
  45.     std::vector<KeyPoint> keypoints_dst;  
  46.     std::vector<DMatch> matches;  
  47.   
  48.     gpu::SURF_GPU FeatureFinder_gpu(500);  
  49.   
  50.     gpu::GpuMat keypoints_gpu_src, keypoints_gpu_dst;  
  51.     gpu::GpuMat descriptors_gpu_src, descriptors_gpu_dst;  
  52.     std::vector<float> descriptors_v1, descriptors_v2;  
  53.     //计算特征点和特征描述子  
  54.     FeatureFinder_gpu(src_gpu, gpu::GpuMat(), keypoints_gpu_src, descriptors_gpu_src);  
  55.     FeatureFinder_gpu(dst_gpu, gpu::GpuMat(), keypoints_gpu_dst, descriptors_gpu_dst);  
  56.     //将特征点下载回cpu,便于画图使用  
  57.     FeatureFinder_gpu.downloadKeypoints(keypoints_gpu_src, keypoints_src);  
  58.     FeatureFinder_gpu.downloadKeypoints(keypoints_gpu_dst, keypoints_dst);  
  59.     //使用gpu提供的BruteForceMatcher进行特征点匹配  
  60.     gpu::BruteForceMatcher_GPU< L2<float> > matcher_lk;  
  61.     matcher_lk.match(descriptors_gpu_src, descriptors_gpu_dst, matches, gpu::GpuMat());  
  62.   
  63.     float max_distance = 0.2;   //定义特征点好坏衡量距离  
  64.     std::vector<DMatch> good_matches;  //收集较好的匹配点  
  65.   
  66.     for (int i = 0; i < descriptors_gpu_src.rows; i++) {  
  67.         if (matches[i].distance < max_distance) {  
  68.             good_matches.push_back(matches[i]);  
  69.         }  
  70.     }  
  71.   
  72.     Mat image_matches;  
  73.     drawMatches(_src_Img, keypoints_src, _dst_Img, keypoints_dst, good_matches,  
  74.         image_matches, Scalar(0, 255, 0) , Scalar::all(-1), vector<char>(), 0);  
  75.        
  76.     imshow("Gpu Surf", image_matches);  
  77.   
  78. }  
  79.   
  80. void orbExtractor(Mat& _src_Img, Mat& _dst_Img)  
  81. {  
  82.     gpu::GpuMat src_gpu(_src_Img);  
  83.     gpu::GpuMat dst_gpu(_dst_Img);  
  84.   
  85.     std::vector<KeyPoint> keypoints_src, keypoints_dst;  
  86.     gpu::GpuMat descriptors_gpu_src, descriptors_gpu_dst;  
  87.     std::vector<DMatch> matches;  
  88.   
  89.     gpu::ORB_GPU orb_finder(500);  
  90.     orb_finder.blurForDescriptor = true;   //设置模糊  
  91.   
  92.     cv::gpu::GpuMat fullmask_1(src_gpu.size(), CV_8U, 0xFF);  
  93.     cv::gpu::GpuMat fullmask_2(dst_gpu.size(), CV_8U, 0xFF);  
  94.   
  95.     orb_finder(src_gpu, fullmask_1, keypoints_src, descriptors_gpu_src);  
  96.     orb_finder(dst_gpu, fullmask_2, keypoints_dst, descriptors_gpu_dst);  
  97.   
  98.     //使用gpu提供的BruteForceMatcher进行特征点匹配  
  99.     gpu::BruteForceMatcher_GPU< HammingLUT > matcher_lk;  
  100.     matcher_lk.match(descriptors_gpu_src, descriptors_gpu_dst, matches, gpu::GpuMat());  
  101.   
  102.     float max_distance = 60;    //定义特征点好坏衡量距离  
  103.     std::vector<DMatch> good_matches;  //收集较好的匹配点  
  104.   
  105.     for (int i = 0; i < descriptors_gpu_src.rows; i++) {  
  106.         if (matches[i].distance < max_distance) {  
  107.             good_matches.push_back(matches[i]);  
  108.         }  
  109.     }  
  110.   
  111.     Mat image_matches;  
  112.     drawMatches(_src_Img, keypoints_src, _dst_Img, keypoints_dst, good_matches,  
  113.         image_matches, Scalar(255, 0, 0), Scalar::all(-1), vector<char>(), 0);  
  114.   
  115.     imshow("Gpu ORB", image_matches);  
  116.   
  117. }  
  118.   
  119.   
  120. int main()  
  121. {  
  122.     int num_devices = cv::gpu::getCudaEnabledDeviceCount();  
  123.     if (num_devices <= 0)  
  124.     {  
  125.         std::cerr << "There is no device." << std::endl;  
  126.         return -1;  
  127.     }  
  128.     int enable_device_id = -1;  
  129.     for (int i = 0; i < num_devices; i++)  
  130.     {  
  131.         cv::gpu::DeviceInfo dev_info(i);  
  132.         if (dev_info.isCompatible())  
  133.         {  
  134.             enable_device_id = i;  
  135.         }  
  136.     }  
  137.     if (enable_device_id < 0)  
  138.     {  
  139.         std::cerr << "GPU module isn't built for GPU" << std::endl;  
  140.         return -1;  
  141.     }  
  142.     gpu::setDevice(enable_device_id);  
  143.   
  144.     Mat src_Img = imread("book.bmp" , 0);  
  145.     Mat dst_Img = rotatedImage(src_Img, -30.0);  
  146.   
  147.     surfExtractor(src_Img, dst_Img);  
  148.     orbExtractor(src_Img, dst_Img);  
  149.   
  150.     cv::waitKey(0);  
  151.     return 0;    
  152. }  

三、运行结果

运行环境为vs2013+opencv2.4.9+cuda7.0,结果展示如下,orb算法寻找特征点及计算描述子速度较快,gpu版的surf特征点对输入图片大小有要求,不能太小



使用GPU提取SURF局部特征

GPU做SURF特征提取的接口和CPU版本有所不同,不过这一部分可以完全参考<opencv_source_directory>/samples/gpu/surf_keypoint_matcher.cpp的例子代码。 
我这里给出一个更加简化的例子,并添加一些中文注释和说明。

/*surf.cpp*/                                                                                                                                             

#include <opencv2/imgcodecs.hpp>
#include <opencv2/xfeatures2d/cuda.hpp>
#include <opencv2/cudafeatures2d.hpp>

using namespace std;

int GetMatchPointCount(const char * pic_path_1,const char * pic_path_2) {
  /*指定使用的GPU序号,相关的还有下面几个函数可以使用
    cv::cuda::getCudaEnabledDeviceCount();
    cv::cuda::getDevice();
    cv::cuda::DeviceInfo*/
  cv::cuda::setDevice(0);

  /*向显存加载两张图片。这里需要注意两个问题:
    第一,我们不能像操作(主)内存一样直接一个字节一个字节的操作显存,也不能直接从外存把图片加载到显存,一般需要通过内存作为媒介
    第二,目前opencv的GPU SURF仅支持8位单通道图像,所以加上参数IMREAD_GRAYSCALE*/
  cv::cuda::GpuMat gmat1;
  cv::cuda::GpuMat gmat2;
  gmat1.upload(cv::imread(pic_path_1,cv::IMREAD_GRAYSCALE));
  gmat2.upload(cv::imread(pic_path_2,cv::IMREAD_GRAYSCALE));

  /*下面这个函数的原型是:
  explicit SURF_CUDA(double 
      _hessianThreshold, //SURF海森特征点阈值
      int _nOctaves=4, //尺度金字塔个数
      int _nOctaveLayers=2, //每一个尺度金字塔层数
      bool _extended=false, //如果true那么得到的描述子是128维,否则是64维
      float _keypointsRatio=0.01f, 
      bool _upright = false 
      );
  要理解这几个参数涉及SURF的原理*/
  cv::cuda::SURF_CUDA surf(
      100,4,3
      );  

  /*分配下面几个GpuMat存储keypoint和相应的descriptor*/
  cv::cuda::GpuMat keypt1,keypt2;
  cv::cuda::GpuMat desc1,desc2;

  /*检测特征点*/
  surf(gmat1,cv::cuda::GpuMat(),keypt1,desc1);
  surf(gmat2,cv::cuda::GpuMat(),keypt2,desc2);

  /*匹配,下面的匹配部分和CPU的match没有太多区别,这里新建一个Brute-Force Matcher,一对descriptor的L2距离小于0.1则认为匹配*/
  auto matcher=cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2);
  vector<cv::DMatch> match_vec;
  matcher->match(desc1,desc2,match_vec);

  int count=0;
  for(auto & d:match_vec){
    if(d.distance<0.1) count++;
  }
  return count;
}

int main(int argc, const char* argv[])
{
  GetMatchPointCount(argv[1],argv[2]);
  return 0;
}  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

最后,编译这个例子,可以参考下面通用的编译命令:

g++ -std=c++11 surf.cpp `pkg-config --cflags opencv` `pkg-config --libs opencv`
  
  
  • 1
  • 1

更简化的编译命令:

g++ -std=c++11 surf.cpp -lopencv_xfeatures2d -lopencv_cudafeatures2d

;