OpenCV2马拉松第27圈——SIFT论文,原理及源码解读

计算机视觉讨论群162501053


简介

SIFT特征描述子是David G. Lowe 在2004年的ijcv会议上发表的论文中提出来的,论文名为<<Distinctive Image Featuresfrom Scale-Invariant Keypoints>>。这是一个很强大的算法,主要用于图像配准和物体识别等领域,但是其计算量相比也比较大,性价比比较高的算法包括PCA-SIFT和SURF,其中OpenCV提供了SURF算法,OpenCV2.3版本后的SIFT算法是Rob Hess的源码,github的项目地址是http://blogs.oregonstate.edu/hess/code/sift/


SIFT特征描述子的优点:

  1. 提取出来的特征对图像大小的变化和旋转具有不变形。
  2. 提取出来的特征对光照和3D相机拍摄视角(差不多就是仿射变换)具有部分不变性。
  3. 在空间域和频率域的局部特性非常好,降低了光照,噪声及一些其他杂乱干扰的影响。
  4. 从一幅图像中可以比较快速提取出大量的特征,并且特征是高度唯一性的,可以在很多特征多唯一识别,因此在图像拼接,物体识别中很有用。

SIFT算法的主要步骤:
  1. 尺度空间极值检测:建立高斯差分金字塔,搜索所有尺度上的极值点。
  2. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
  3. 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
  4. 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。


算法主要流程

  1. 首先创建初始图像,即通过将图像转换为32位的灰度图,然后将图像使用三次插值来方大,之后通过高斯模糊处理
  2. 在此基础上进行高斯金字塔的构建以及高斯差分金字塔的构建
  3. 对图像进行极值点检测
  4. 计算特征向量的尺度
  5. 调整图像大小
  6. 计算特征的方向
  7. 计算描述子,其中包括计算二维方向直方图并转换直方图为特征描述子
  8. 对描述子进行排序


算法框架

我们先不讲理论的东西,直接看下Rob Hess写的主要代码sift.c

int sift_features( IplImage* img, struct feature** feat )

{

  return _sift_features( img, feat, SIFT_INTVLS, SIFT_SIGMA, SIFT_CONTR_THR,

             SIFT_CURV_THR, SIFT_IMG_DBL, SIFT_DESCR_WIDTH,

             SIFT_DESCR_HIST_BINS );

}


这里有很多的数据结构和变量,我先列出我们以后要接触到的数据结构

feature是最重要的一个数据结构
struct feature
{
  double x;                      /**< x coord */
  double y;                      /**< y coord */
  double a;                      /**< Oxford-type affine region parameter */
  double b;                      /**< Oxford-type affine region parameter */
  double c;                      /**< Oxford-type affine region parameter */
  double scl;                    /**< scale of a Lowe-style feature */
  double ori;                    /**< orientation of a Lowe-style feature */
  int d;                         /**< descriptor length */
  double descr[FEATURE_MAX_D];   /**< descriptor */
  int type;                      /**< feature type, OXFD or LOWE */
  int category;                  /**< all-purpose feature category */
  struct feature* fwd_match;     /**< matching feature from forward image */
  struct feature* bck_match;     /**< matching feature from backmward image */
  struct feature* mdl_match;     /**< matching feature from model */
  CvPoint2D64f img_pt;           /**< location in image */
  CvPoint2D64f mdl_pt;           /**< location in model */
  void* feature_data;            /**< user-definable data */
};

detection_data也是经常出现的,是feature结构体里的feature_data,有专门的宏定义用于获取
/** holds feature data relevant to detection */
struct detection_data
{
  int r;
  int c;
  int octv;
  int intvl;
  double subintvl;
  double scl_octv;
};

以及一些常量和宏定义
/** default number of sampled intervals per octave */
#define SIFT_INTVLS 3

/** default sigma for initial gaussian smoothing */
#define SIFT_SIGMA 1.6

/** default threshold on keypoint contrast |D(x)| */
#define SIFT_CONTR_THR 0.04

/** default threshold on keypoint ratio of principle curvatures */
#define SIFT_CURV_THR 10

/** double image size before pyramid construction? */
#define SIFT_IMG_DBL 1

/** default width of descriptor histogram array */
#define SIFT_DESCR_WIDTH 4

/** default number of bins per histogram in descriptor array */
#define SIFT_DESCR_HIST_BINS 8

/* assumed gaussian blur for input image */
#define SIFT_INIT_SIGMA 0.5

/* width of border in which to ignore keypoints */
#define SIFT_IMG_BORDER 5

/* maximum steps of keypoint interpolation before failure */
#define SIFT_MAX_INTERP_STEPS 5

/* default number of bins in histogram for orientation assignment */
#define SIFT_ORI_HIST_BINS 36

/* determines gaussian sigma for orientation assignment */
#define SIFT_ORI_SIG_FCTR 1.5

/* determines the radius of the region used in orientation assignment */
#define SIFT_ORI_RADIUS 3.0 * SIFT_ORI_SIG_FCTR

/* number of passes of orientation histogram smoothing */
#define SIFT_ORI_SMOOTH_PASSES 2

/* orientation magnitude relative to max that results in new feature */
#define SIFT_ORI_PEAK_RATIO 0.8

/* determines the size of a single descriptor orientation histogram */
#define SIFT_DESCR_SCL_FCTR 3.0

/* threshold on magnitude of elements of descriptor vector */
#define SIFT_DESCR_MAG_THR 0.2

/* factor used to convert floating-point descriptor to unsigned char */
#define SIFT_INT_DESCR_FCTR 512.0

/* returns a feature's detection data */
#define feat_detection_data(f) ( (struct detection_data*)(f->feature_data) )

让我们看一看真正的 _sift_features 函数

输入参数:

img为输入图像;

feat为所要提取的特征指针;

intvl指的是高斯金字塔和差分金字塔的层数;

sigma指的是图像初始化过程中高斯模糊所使用的参数;

contr_thr是归一化之后的去除不稳定特征的阈值;

curv_thr指的是去除边缘的特征的主曲率阈值;

img_dbl是是否将图像放大为之前的两倍;

descr_with用来计算特征描述子的方向直方图的宽度;

descr_hist_bins是直方图中的条数


int _sift_features( IplImage* img, struct feature** feat, int intvls,

            double sigma, double contr_thr, int curv_thr,

            int img_dbl, int descr_width, int descr_hist_bins )

{

  IplImage* init_img;

  IplImage*** gauss_pyr, *** dog_pyr;

  CvMemStorage* storage;

  CvSeq* features;

  int octvs, i, n = 0;

  

  /* check arguments */

  if( ! img )

    fatal_error( "NULL pointer error, %s, line %d"__FILE__,__LINE__ );

  if( ! feat )

    fatal_error( "NULL pointer error, %s, line %d"__FILE__,__LINE__ );


  /* 算法第一步,初始化图像 */

  init_img = create_init_img( img, img_dbl, sigma );

  /* 算法第二步,建立高斯差分金字塔(也就是所谓的尺度空间)最顶层4pixels */

  octvs = log( MIN( init_img->width, init_img->height ) ) / log(2) -2;  //octvs是整个金字塔层数

  gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );  //建立高斯金字塔

  dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls );  //建立高斯差分金字塔,octvs是金字塔层数,intvls是层数(每层金字塔有几张图片)

  

  storage = cvCreateMemStorage( 0 );

  /* 算法第三步,寻找尺度空间极值,contr_thr是去除对比度低的点所采用的阀值,curv_thr是去除边缘特征所采取的阀值 */

  features = scale_space_extrema( dog_pyr, octvs, intvls, contr_thr,

                  curv_thr, storage );

  /* 算法第四步,计算特征向量的尺度 */

  calc_feature_scales( features, sigma, intvls );

  /* 算法第五步,调整图像的大小 */

  if( img_dbl )

    adjust_for_img_dbl( features );

  /* 算法第六步,计算特征点的主要方向 */

  calc_feature_oris( features, gauss_pyr );

  /* 算法第七步,计算描述子,其中包括计算二维方向直方图并转换直方图为特征描述子 */

  compute_descriptors( features, gauss_pyr, descr_width, descr_hist_bins );


  /* 算法第八步,按尺度大小对描述子进行排序 */

  cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL );

  n = features->total;

  *feat = calloc( n, sizeof(struct feature) );

  *feat = cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ );

for( i = 0; i < n; i++ )

    {

      free( (*feat)[i].feature_data );

      (*feat)[i].feature_data = NULL;

    }

  

  cvReleaseMemStorage( &storage );

  cvReleaseImage( &init_img );

  release_pyr( &gauss_pyr, octvs, intvls + 3 );

  release_pyr( &dog_pyr, octvs, intvls + 2 );

  return n;

}


接下来让我们一起慢慢解读SIFT算法,深入SIFT算法的实质。





算法第1步:创建初始图像

该函数主要用来初始化图像,转换图像为32位灰度图以及进行高斯模糊,
Lowe 建议在建立尺度空间前首先对原始图像长宽扩展 一倍,以保留原始图像信息,增加特征点数量

static IplImage* create_init_img( IplImage* img,int img_dbl,double sigma )

{

  IplImage* gray, * dbl;

  double sig_diff;


  gray = convert_to_gray32( img );

  if( img_dbl )

    {

      sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 );

      dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ),

               IPL_DEPTH_32F, 1 );

      cvResize( gray, dbl, CV_INTER_CUBIC );

      cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );

      cvReleaseImage( &gray );

      return dbl;

    }

  else

    {

      sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA );

      cvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );

      return gray;

    }

}





算法第2步:建立尺度空间(高斯差分金字塔)

关于图像金字塔和尺度空间的讨论,我在下面两篇文章中有涉及

OpenCV2马拉松第20圈——blob特征检测原理与实现

OpenCV2马拉松第7圈——图像金字塔


这里,我再好好整理一下。


也就是说:对一张图像用不同的sigma进行高斯模糊,再相减就得到了高斯差分.

因此,建立尺度空间的简述过程如下:先建立高斯金字塔,过程如下:首先建立第1层,对原图分别用不同的sigma进行高斯模糊。其中sigma按如下规则获取:

  k = pow( 2.01.0 / intvls );

  sig[0] = sigma;

  sig[1] = sigma * sqrt( k*k- 1 );

  for (i = 2; i < intvls +3; i++)         //  intvls是每层的照片数!  为什么+3等下介绍

      sig[i] = sig[i-1] * k;


然后对原图进行下采样(代码中是取上一层的最后一张图片),再用不同的sigma建立第二层......一直到某次下采样获得的图片像素小于我们的预定值(代码中是4像素)。 建立完高斯金字塔后,我们再分别用相邻的图片相减,就得到了我们的 尺度空间(也就是高斯差分金字塔),如下图。



直观上理解,左边有2层金字塔,每层有5张图片

为了在每一组中检测S个尺度的极值点,则DOG金字塔每组需S+2层图像,这是因为一个点不仅要跟周围8个点比较,同时也要跟前一张后一张的各9各点比较。而DOG金字塔由高斯金字塔相邻两层相减得到,则高斯金字塔每组需S+3层图像。如下:



下面用组来表示高斯金字塔层数,用层数来表示每一组的图片个数,也就是s


按照刚才的描述,我们首先建立高斯金字塔

输入参数:

octvs是高斯金字塔的组

invls是高斯金字塔的层数

sigma是初始的高斯模糊参数,后续也通过它计算每一层所使用的sigma

static IplImage*** build_gauss_pyr( IplImage* base,int octvs,

                 int intvls, double sigma )

{

  IplImage*** gauss_pyr;

  const int _intvls = intvls;

  double sig[_intvls+3], sig_total, k;  //+3的原因在之前已经讨论过

  int i, o;

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值