图像处理之SURF特征点检测

SURF全称即为加速版的具有鲁棒性的特征算法。由于SURF与sift算法一样是尺度不变特征变换算法,所以具有一定的鲁棒性,我们可以通过调节参数使得SURF能够达到实时跟踪的效果。与sift不同的是,它采用了hessian矩阵行列式近似值图像,使得算法的描述子维数降低(下文会提到)。而且采用和harr特征,使得程序运行时间大大提高。

为了便于理解,可以参考:http://jacoxu.com/?p=146

算法流程大致如下:

1,获取一张近似hessian的行列式图。

hessian行列式图通过对图中每个像素点求其hessian矩阵(此时可以把图像近似看做是2元函数),该hessian的行列式的值作为对应像素点在hessian行列式图中的值。该值实际上是作为该像素点的判别式,通过该值得正负判断该点是不是极值点。当然在实际操作中,为了保证该点具有尺度无关性,所以在进行hessian矩阵构造之前需要进行高斯滤波。surf与sift类似,有金字塔图像,但是在金字塔图像中分了很多层,不同层的图像可以仅通过改变高斯模糊的模板尺寸得到,得到多张降采样的图像。

2:经过第一步,我们得到多张(层)图,也就是我们这么多hessian行列式图,我们选那些点作为特征点的候选呢?以3*3的滤波器为例,每个像素点与其三维邻域的26的点进行大小比较,如果它是该邻域中的最大或者最小值,则将其保留,并将其作为候选特征点。


3.经过步骤2可以初步得到特征点候选,但是由于其数量很多,所以需要进行进一步的删选,也就是特征点定位。个人认为就是去掉一些小于一定阈值的点,也就是最终只有几个比较强的特征点会被检测出来。

4.构造特征点描述算子

步骤3得到了比较强的特征点,那么这些特征点的主方向如何确定呢?surf中是统计特征点领域内的harr小波特征,统计60度扇形内所有水平harr小波特征和垂直harr小波特征的总和,然后扇形以一定间隔进行旋转,最后将最大值的那个扇形的方向作为特征点的主方向。

在surf中,在特征点周围提取一个正方形边框,大小为20*20.根据刚才所说的方法可以特岛这个区域的主方向。然后将该方框区域分成16个小区域,每个小区域就是5*5=25个像素点。分别计算该小区域的harr小波特性的水平方向之和、水平方向绝对值之和、垂直方向之和、垂直方向绝对值之和。那么该20*20区域就可以用16*4=64维的向量表示。

surf采用hessian矩阵获取图像局部最值相对sift更稳定。

///我们在使用该算法进行图像特征点匹配大致思路也如上述

贴出代码如下:

int main( int argc, char** argv )
{
	argv[1] = "E:\\Ball_picture\\P_3.jpg";
	argv[2] = "E:\\Ball_picture\\P_4.jpg";
	argc = 3;
  if( argc != 3 )
  { readme(); return -1; }

  Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

  if( !img_1.data || !img_2.data )
  { std::cout<< " --(!) Error reading images " << std::endl; return -1; }

  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;

  SurfFeatureDetector detector( minHessian );

  std::vector<KeyPoint> keypoints_1, keypoints_2;

  detector.detect( img_1, keypoints_1 );//图像1的特征点
  detector.detect( img_2, keypoints_2 );//图像2的特征点

  //-- Step 2: Calculate descriptors (feature vectors)
  SurfDescriptorExtractor extractor;

  Mat descriptors_1, descriptors_2;

  extractor.compute( img_1, keypoints_1, descriptors_1 );//计算图像的描述子
  extractor.compute( img_2, keypoints_2, descriptors_2 );

  //-- Step 3: Matching descriptor vectors using FLANN matcher
  FlannBasedMatcher matcher;
  std::vector< DMatch > matches;
  matcher.match( descriptors_1, descriptors_2, matches );

  double max_dist = 0; double min_dist = 100;

  //-- Quick calculation of max and min distances between keypoints
  for( int i = 0; i < descriptors_1.rows; i++ )
  { double dist = matches[i].distance;
    if( dist < min_dist ) min_dist = dist;
    if( dist > max_dist ) max_dist = dist;//非抑制极大值得到较好的匹配
  }

  printf("-- Max dist : %f \n", max_dist );
  printf("-- Min dist : %f \n", min_dist );

  //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist,
  //-- or a small arbitary value ( 0.02 ) in the event that min_dist is very
  //-- small)
  //-- PS.- radiusMatch can also be used here.
  std::vector< DMatch > good_matches;

  for( int i = 0; i < descriptors_1.rows; i++ )
  { if( matches[i].distance <= max(2*min_dist, 0.02) )
    { good_matches.push_back( matches[i]); }
  }

  //-- Draw only "good" matches
  Mat img_matches;
  drawMatches( img_1, keypoints_1, img_2, keypoints_2,
               good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
               vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

  //-- Show detected matches
  imshow( "Good Matches", img_matches );

  for( int i = 0; i < (int)good_matches.size(); i++ )
  { printf( "-- Good Match [%d] Keypoint 1: %d  -- Keypoint 2: %d  \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); }

  waitKey(0);

  return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值