SIFT算法不用我多解释了,这是一个很强大的算法,主要用于图像配准和物体识别等领域,但是其计算量相比也比较大,性价比比较高的算法包括PCA-SIFT和SURF其中OpenCV提供了SURF算法,但是为了方便理解。这里给出了Rob Hess所实现的SIFT算法的实现以及注释,结合我自己的理解,如果,您有关于SIFT算法不理解的地方咱们可以一起交流一下。或者您认为不详细的地方提出来。
SIFT算法的主要实现在sift.c这个文件,其主要流程为:
(1)首先创建初始图像,即通过将图像转换为32位的灰度图,然后将图像使用三次插值来方大,之后通过高斯模糊处理
(2)在此基础上进行高斯金字塔的构建以及高斯差分金字塔的构建
(3)对图像进行极值点检测
(4)计算特征向量的尺度
(5)调整图像大小
(6)计算特征的方向
(7)计算描述子,其中包括计算二维方向直方图并转换直方图为特征描述子
首先给出sift算法的整体框架代码:
输入参数:
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__ ); /* build scale space pyramid; smallest dimension of top level is ~4 pixels */ /* 构建高斯尺度空间金字塔,顶层最小的为4像素 */ init_img = create_init_img( img, img_dbl, sigma ); octvs = log( double MIN( init_img->width, init_img->height ) ) / log(2.0) - 2; //构建高斯金字塔和高斯差分金字塔 gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma ); dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls ); storage = cvCreateMemStorage( 0 ); //尺度空间极值点检测 features = scale_space_extrema( dog_pyr, octvs, intvls, contr_thr, curv_thr, storage ); //画出去除低对比度的极值点 //draw_extrempoint(img , features); //计算特征向量的尺度 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 ); /* sort features by decreasing scale and move from CvSeq to array */ cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL ); n = features->total; *feat = static_cast<feature *>( calloc( n, sizeof(struct feature) ) ); *feat = static_cast<feature *>( 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; }
(1)初始化图像
输入参数:
这里不需要解释了
该函数主要用来初始化图像,转换图像为32位灰度图以及进行高斯模糊。
static IplImage* create_init_img( IplImage* img, int img_dbl, double sigma ) { IplImage* gray, * dbl; float 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)构建高斯金字塔
输入参数:
octvs是高斯金字塔的组
invls是高斯金字塔的层数
sigma是初始的高斯模糊参数,后续也通过它计算每一层所使用的sigma
static IplImage*** build_gauss_pyr( IplImage* base, int octvs,int intvls, double sigma )
{
IplImage*** gauss_pyr;
double* sig = static_cast<double *>( calloc( intvls + 3, sizeof(double)) );
double sig_total, sig_prev, k;
int i, o;
gauss_pyr = static_cast<IplImage ***>( calloc( octvs, sizeof( IplImage** ) ) );
for( i = 0; i < octvs; i++ )
gauss_pyr[i] = static_cast<IplImage **>( calloc( intvls + 3, sizeof( IplImage* ) ) );
/*
precompute Gaussian sigmas using the following formula:
预计算每次高斯模糊的sigma
\sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2
*/
sig[0] = sigma;
k = pow( 2.0, 1.0 / intvls );
for( i = 1; i < intvls + 3; i++ )
{
sig_prev = pow( k, i - 1 ) * sigma;
sig_total = sig_prev * k;
sig[i] = sqrt( sig_total * sig_total - sig_prev * sig_prev );
}
for( o = 0; o < octvs; o++ )
for( i = 0; i < intvls + 3; i++ )
{
//对每一层进行降采样,形成高斯金字塔的每一层
if( o == 0 && i == 0 )
gauss_pyr[o][i] = cvCloneImage(base);
/* base of new octvave is halved image from end of previous octave */
//每一组的第一层都是通过对前面一组的最上面一层的降采样实现的
else if( i == 0 )
gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] );
/* blur the current octave's last image to create the next one */
//每一组的其他层则使通过使用不同sigma的高斯模糊来进行处理
else
{
gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]),
IPL_DEPTH_32F, 1 );
cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i],
CV_GAUSSIAN, 0, 0, sig[i], sig[i] );
}
}
free( sig );
return gauss_pyr;
}
降采样处理
输入参数:
不解释
这就是降采样,其实就是将图像通过最近邻算法缩小为原来的一半
static IplImage* downsample( IplImage* img ) { IplImage* smaller = cvCreateImage( cvSize(img->width / 2, img->height / 2), img->depth, img->nChannels ); cvResize( img, smaller, CV_INTER_NN ); return smaller; }
输入参数:
不解释了参见上面的说明即可
实际上差分金字塔的构成是通过对相邻层的图像进行相减获得的
static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls )
{
IplImage*** dog_pyr;
int i, o;
dog_pyr = static_cast<IplImage ***>( calloc( octvs, sizeof( IplImage** ) ) );
for( i = 0; i < octvs; i++ )
dog_pyr[i] = static_cast<IplImage **>( calloc( intvls + 2, sizeof(IplImage*) ) );
for( o = 0; o < octvs; o++ )
for( i = 0; i < intvls + 2; i++ )
{
dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]),
IPL_DEPTH_32F, 1 );
cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL );
}
return dog_pyr;
}
(4)极值点检测
输入参数:
contr_thr是去除对比度低的点所采用的阈值
curv_thr是去除边缘特征的阈值
static CvSeq* scale_space_extrema( IplImage*** dog_pyr, int octvs, int intvls, double contr_thr, int curv_thr, CvMemStorage* storage ) { CvSeq* features; double prelim_contr_thr = 0.5 * contr_thr / intvls; struct feature* feat; struct detection_data* ddata; int o, i, r, c; features = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct feature), storage ); for( o = 0; o < octvs; o++ ) for( i = 1; i <= intvls; i++ ) for(r = SIFT_IMG_BORDER; r < dog_pyr[o][0]->height-SIFT_IMG_BORDER; r++) for(c = SIFT_IMG_BORDER; c < dog_pyr[o][0]->width-SIFT_IMG_BORDER; c++) /* perform preliminary check on contrast */ if( ABS( pixval32f( dog_pyr[o][i], r, c ) ) > prelim_contr_thr ) if( is_extremum( dog_pyr, o, i, r, c ) ) { feat = interp_extremum(dog_pyr, o, i, r, c, intvls, contr_thr); if( feat ) { ddata = feat_detection_data( feat ); if( ! is_too_edge_like( dog_pyr[ddata->octv][ddata->intvl], ddata->r, ddata->c, curv_thr ) ) { cvSeqPush( features, feat ); } else free( ddata ); free( feat ); } } return features; }SIFT_IMG_BORDER是预定义的图像边缘;
通过和对比度阈值比较去掉低对比度的点;
而通过is_extremum来判断是否为极值点,如果是则通过极值点插值的方式获取亚像素的极值点的位置。
然后通过is_too_eage_like和所给的主曲率阈值判断是否为边缘点
*判断是否为极值点
其原理为:通过和高斯金字塔的上一层的9个像素+本层的除了本像素自己的其他的8个像素和下一层的9个像素进行比较看是否为这26个像素中最小的一个或者是否为最大的一个,如果是则为极值点。
static int is_extremum( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) { float val = pixval32f( dog_pyr[octv][intvl], r, c ); int i, j, k; /* check for maximum */ if( val > 0 ) { for( i = -1; i <= 1; i++ ) for( j = -1; j <= 1; j++ ) for( k = -1; k <= 1; k++ ) if( val < pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) return 0; } /* check for minimum */ else { for( i = -1; i <= 1; i++ ) for( j = -1; j <= 1; j++ ) for( k = -1; k <= 1; k++ ) if( val > pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) return 0; } return 1; }
static struct feature* interp_extremum( IplImage*** dog_pyr, int octv, int intvl, int r, int c, int intvls, double contr_thr ) { struct feature* feat; struct detection_data* ddata; double xi, xr, xc, contr;//分别为亚像素的intval,row,col的偏移offset,和对比度 int i = 0; while( i < SIFT_MAX_INTERP_STEPS )//重新确定极值点并重新定位的操作只能循环 5次 { interp_step( dog_pyr, octv, intvl, r, c, &xi, &xr, &xc ); if( ABS( xi ) < 0.5 && ABS( xr ) < 0.5 && ABS( xc ) < 0.5 )//如果满足条件就停止寻找 break; //否则继续寻找极值点 c += cvRound( xc ); r += cvRound( xr ); intvl += cvRound( xi ); if( intvl < 1 || intvl > intvls || c < SIFT_IMG_BORDER || r < SIFT_IMG_BORDER || c >= dog_pyr[octv][0]->width - SIFT_IMG_BORDER || r >= dog_pyr[octv][0]->height - SIFT_IMG_BORDER ) { return NULL; } i++; } //确保极值点是经过最大5步找到的 /* ensure convergence of interpolation */ if( i >= SIFT_MAX_INTERP_STEPS ) return NULL; //获取找到的极值点的对比度 contr = interp_contr( dog_pyr, octv, intvl, r, c, xi, xr, xc ); //判断极值点是否小于某一个阈值 if( ABS( contr ) < contr_thr / intvls ) return NULL; //若小于,则认为是极值点 feat = new_feature(); ddata = feat_detection_data( feat ); feat->img_pt.x = feat->x = ( c + xc ) * pow( 2.0, octv ); feat->img_pt.y = feat->y = ( r + xr ) * pow( 2.0, octv ); ddata->r = r; ddata->c = c; ddata->octv = octv; ddata->intvl = intvl; ddata->subintvl = xi; return feat; }
static void interp_step( IplImage*** dog_pyr, int octv, int intvl, int r, int c, double* xi, double* xr, double* xc ) { CvMat* dD, * H, * H_inv, X; double x[3] = { 0 }; //计算三维偏导数 dD = deriv_3D( dog_pyr, octv, intvl, r, c ); //计算三维海森矩阵 H = hessian_3D( dog_pyr, octv, intvl, r, c ); H_inv = cvCreateMat( 3, 3, CV_64FC1 ); cvInvert( H, H_inv, CV_SVD ); cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); cvGEMM( H_inv, dD, -1, NULL, 0, &X, 0 ); cvReleaseMat( &dD ); cvReleaseMat( &H ); cvReleaseMat( &H_inv ); *xi = x[2]; *xr = x[1]; *xc = x[0]; }
*计算三维偏导数
static CvMat* deriv_3D( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) { CvMat* dI; double dx, dy, ds; dx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) - pixval32f( dog_pyr[octv][intvl], r, c-1 ) ) / 2.0; dy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) - pixval32f( dog_pyr[octv][intvl], r-1, c ) ) / 2.0; ds = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) - pixval32f( dog_pyr[octv][intvl-1], r, c ) ) / 2.0; dI = cvCreateMat( 3, 1, CV_64FC1 ); cvmSet( dI, 0, 0, dx ); cvmSet( dI, 1, 0, dy ); cvmSet( dI, 2, 0, ds ); return dI; }
static CvMat* hessian_3D( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) { CvMat* H; double v, dxx, dyy, dss, dxy, dxs, dys; v = pixval32f( dog_pyr[octv][intvl], r, c ); dxx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) + pixval32f( dog_pyr[octv][intvl], r, c-1 ) - 2 * v ); dyy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) + pixval32f( dog_pyr[octv][intvl], r-1, c ) - 2 * v ); dss = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) + pixval32f( dog_pyr[octv][intvl-1], r, c ) - 2 * v ); dxy = ( pixval32f( dog_pyr[octv][intvl], r+1, c+1 ) - pixval32f( dog_pyr[octv][intvl], r+1, c-1 ) - pixval32f( dog_pyr[octv][intvl], r-1, c+1 ) + pixval32f( dog_pyr[octv][intvl], r-1, c-1 ) ) / 4.0; dxs = ( pixval32f( dog_pyr[octv][intvl+1], r, c+1 ) - pixval32f( dog_pyr[octv][intvl+1], r, c-1 ) - pixval32f( dog_pyr[octv][intvl-1], r, c+1 ) + pixval32f( dog_pyr[octv][intvl-1], r, c-1 ) ) / 4.0; dys = ( pixval32f( dog_pyr[octv][intvl+1], r+1, c ) - pixval32f( dog_pyr[octv][intvl+1], r-1, c ) - pixval32f( dog_pyr[octv][intvl-1], r+1, c ) + pixval32f( dog_pyr[octv][intvl-1], r-1, c ) ) / 4.0; H = cvCreateMat( 3, 3, CV_64FC1 ); cvmSet( H, 0, 0, dxx ); cvmSet( H, 0, 1, dxy ); cvmSet( H, 0, 2, dxs ); cvmSet( H, 1, 0, dxy ); cvmSet( H, 1, 1, dyy ); cvmSet( H, 1, 2, dys ); cvmSet( H, 2, 0, dxs ); cvmSet( H, 2, 1, dys ); cvmSet( H, 2, 2, dss ); return H; }
static double interp_contr( IplImage*** dog_pyr, int octv, int intvl, int r, int c, double xi, double xr, double xc ) { CvMat* dD, X, T; double t[1], x[3] = { xc, xr, xi }; cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); cvInitMatHeader( &T, 1, 1, CV_64FC1, t, CV_AUTOSTEP ); dD = deriv_3D( dog_pyr, octv, intvl, r, c ); cvGEMM( dD, &X, 1, NULL, 0, &T, CV_GEMM_A_T ); cvReleaseMat( &dD ); return pixval32f( dog_pyr[octv][intvl], r, c ) + t[0] * 0.5; }其中cvGEMM是矩阵的通用计算函数,至于CV_GEMM_A_T是计算dD的转置矩阵放入T中
static int is_too_edge_like( IplImage* dog_img, int r, int c, int curv_thr ) { double d, dxx, dyy, dxy, tr, det; /* principal curvatures are computed using the trace and det of Hessian */ d = pixval32f(dog_img, r, c); dxx = pixval32f( dog_img, r, c+1 ) + pixval32f( dog_img, r, c-1 ) - 2 * d; dyy = pixval32f( dog_img, r+1, c ) + pixval32f( dog_img, r-1, c ) - 2 * d; dxy = ( pixval32f(dog_img, r+1, c+1) - pixval32f(dog_img, r+1, c-1) - pixval32f(dog_img, r-1, c+1) + pixval32f(dog_img, r-1, c-1) ) / 4.0; tr = dxx + dyy; det = dxx * dyy - dxy * dxy; /* negative determinant -> curvatures have different signs; reject feature */ if( det <= 0 ) return 1; if( tr * tr / det < ( curv_thr + 1.0 )*( curv_thr + 1.0 ) / curv_thr ) return 0; return 1; }
static void calc_feature_scales( CvSeq* features, double sigma, int intvls ) { struct feature* feat; struct detection_data* ddata; double intvl; int i, n; n = features->total; for( i = 0; i < n; i++ ) { feat = CV_GET_SEQ_ELEM( struct feature, features, i ); ddata = feat_detection_data( feat ); intvl = ddata->intvl + ddata->subintvl; feat->scl = sigma * pow( 2.0, ddata->octv + intvl / intvls ); ddata->scl_octv = sigma * pow( 2.0, intvl / intvls ); } }
static void adjust_for_img_dbl( CvSeq* features ) { struct feature* feat; int i, n; n = features->total; for( i = 0; i < n; i++ ) { feat = CV_GET_SEQ_ELEM( struct feature, features, i ); feat->x /= 2.0; feat->y /= 2.0; feat->scl /= 2.0; feat->img_pt.x /= 2.0; feat->img_pt.y /= 2.0; } }
static void calc_feature_oris( CvSeq* features, IplImage*** gauss_pyr ) { struct feature* feat; struct detection_data* ddata; double* hist; double omax; int i, j, n = features->total; //遍历整个检测出来的特征点,计算每个特征点的直方图,然后平滑直方图去除突变,然后找到每一个特征点的主方向,并加入到好的方向特征数组中去 for( i = 0; i < n; i++ ) { feat = static_cast<feature *>( malloc( sizeof( struct feature ) ) ); cvSeqPopFront( features, feat ); ddata = feat_detection_data( feat ); //计算给定的某个像素的灰度方向直方图 hist = ori_hist( gauss_pyr[ddata->octv][ddata->intvl], ddata->r, ddata->c, SIFT_ORI_HIST_BINS, cvRound( SIFT_ORI_RADIUS * ddata->scl_octv ), SIFT_ORI_SIG_FCTR * ddata->scl_octv ); for( j = 0; j < SIFT_ORI_SMOOTH_PASSES; j++ ) smooth_ori_hist( hist, SIFT_ORI_HIST_BINS ); omax = dominant_ori( hist, SIFT_ORI_HIST_BINS ); //描述子向量元素门限化 add_good_ori_features( features, hist, SIFT_ORI_HIST_BINS, omax * SIFT_ORI_PEAK_RATIO, feat ); free( ddata ); free( feat ); free( hist ); } }
static double* ori_hist( IplImage* img, int r, int c, int n, int rad, double sigma) { double* hist; double mag, ori, w, exp_denom, PI2 = CV_PI * 2.0; int bin, i, j; hist = static_cast<double *>( calloc( n, sizeof( double ) ) ); exp_denom = 2.0 * sigma * sigma; for( i = -rad; i <= rad; i++ ) for( j = -rad; j <= rad; j++ ) if( calc_grad_mag_ori( img, r + i, c + j, &mag, &ori ) ) { w = exp( -( i*i + j*j ) / exp_denom ); bin = cvRound( n * ( ori + CV_PI ) / PI2 ); bin = ( bin < n )? bin : 0; hist[bin] += w * mag; } return hist; }
static int calc_grad_mag_ori( IplImage* img, int r, int c, double* mag, double* ori ) { double dx, dy; if( r > 0 && r < img->height - 1 && c > 0 && c < img->width - 1 ) { dx = pixval32f( img, r, c+1 ) - pixval32f( img, r, c-1 ); dy = pixval32f( img, r-1, c ) - pixval32f( img, r+1, c ); *mag = sqrt( dx*dx + dy*dy ); *ori = atan2( dy, dx ); return 1; } else return 0; }
static void smooth_ori_hist( double* hist, int n ) { double prev, tmp, h0 = hist[0]; int i; prev = hist[n-1]; for( i = 0; i < n; i++ ) { tmp = hist[i]; hist[i] = 0.25 * prev + 0.5 * hist[i] + 0.25 * ( ( i+1 == n )? h0 : hist[i+1] ); prev = tmp; } }
static double dominant_ori( double* hist, int n ) { double omax; int maxbin, i; omax = hist[0]; maxbin = 0; for( i = 1; i < n; i++ ) if( hist[i] > omax ) { omax = hist[i]; maxbin = i; } return omax; }
mag_thr描述子向量门限一般取0.2
static void add_good_ori_features( CvSeq* features, double* hist, int n, double mag_thr, struct feature* feat ) { struct feature* new_feat; double bin, PI2 = CV_PI * 2.0; int l, r, i; for( i = 0; i < n; i++ ) { l = ( i == 0 )? n - 1 : i-1; r = ( i + 1 ) % n; //描述子向量门限化,一般门限取0.2 if( hist[i] > hist[l] && hist[i] > hist[r] && hist[i] >= mag_thr ) { bin = i + interp_hist_peak( hist[l], hist[i], hist[r] ); bin = ( bin < 0 )? n + bin : ( bin >= n )? bin - n : bin; new_feat = clone_feature( feat ); new_feat->ori = ( ( PI2 * bin ) / n ) - CV_PI; cvSeqPush( features, new_feat ); free( new_feat ); } } }
static void compute_descriptors( CvSeq* features, IplImage*** gauss_pyr, int d, int n) { struct feature* feat; struct detection_data* ddata; double*** hist; int i, k = features->total; for( i = 0; i < k; i++ ) { feat = CV_GET_SEQ_ELEM( struct feature, features, i ); ddata = feat_detection_data( feat ); //计算二维方向直方图 hist = descr_hist( gauss_pyr[ddata->octv][ddata->intvl], ddata->r, ddata->c, feat->ori, ddata->scl_octv, d, n ); //将二维方向直方图转换为特征描述子 hist_to_descr( hist, d, n, feat ); release_descr_hist( &hist, d ); } }
static double*** descr_hist( IplImage* img, int r, int c, double ori, double scl, int d, int n ) { double*** hist; double cos_t, sin_t, hist_width, exp_denom, r_rot, c_rot, grad_mag, grad_ori, w, rbin, cbin, obin, bins_per_rad, PI2 = 2.0 * CV_PI; int radius, i, j; hist = static_cast<double ***>( calloc( d, sizeof( double** ) ) ); for( i = 0; i < d; i++ ) { hist[i] =static_cast<double **>( calloc( d, sizeof( double* ) ) ); for( j = 0; j < d; j++ ) hist[i][j] = static_cast<double *>( calloc( n, sizeof( double ) ) ); } cos_t = cos( ori ); sin_t = sin( ori ); bins_per_rad = n / PI2; exp_denom = d * d * 0.5; hist_width = SIFT_DESCR_SCL_FCTR * scl; radius = hist_width * sqrt(2.0) * ( d + 1.0 ) * 0.5 + 0.5; for( i = -radius; i <= radius; i++ ) for( j = -radius; j <= radius; j++ ) { /* 即将坐标移至关键点主方向 计算采用的直方图数组中相对于方向旋转的坐标 Calculate sample's histogram array coords rotated relative to ori. Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. r_rot = 1.5) have full weight placed in row 1 after interpolation. */ c_rot = ( j * cos_t - i * sin_t ) / hist_width; r_rot = ( j * sin_t + i * cos_t ) / hist_width; rbin = r_rot + d / 2 - 0.5; cbin = c_rot + d / 2 - 0.5; if( rbin > -1.0 && rbin < d && cbin > -1.0 && cbin < d ) if( calc_grad_mag_ori( img, r + i, c + j, &grad_mag, &grad_ori )) { grad_ori -= ori; while( grad_ori < 0.0 ) grad_ori += PI2; while( grad_ori >= PI2 ) grad_ori -= PI2; obin = grad_ori * bins_per_rad; w = exp( -(c_rot * c_rot + r_rot * r_rot) / exp_denom ); interp_hist_entry( hist, rbin, cbin, obin, grad_mag * w, d, n ); } } return hist; }
static void interp_hist_entry( double*** hist, double rbin, double cbin, double obin, double mag, int d, int n ) { double d_r, d_c, d_o, v_r, v_c, v_o; double** row, * h; int r0, c0, o0, rb, cb, ob, r, c, o; r0 = cvFloor( rbin ); c0 = cvFloor( cbin ); o0 = cvFloor( obin ); d_r = rbin - r0; d_c = cbin - c0; d_o = obin - o0; /* The entry is distributed into up to 8 bins. Each entry into a bin is multiplied by a weight of 1 - d for each dimension, where d is the distance from the center value of the bin measured in bin units. */ for( r = 0; r <= 1; r++ ) { rb = r0 + r; if( rb >= 0 && rb < d ) { v_r = mag * ( ( r == 0 )? 1.0 - d_r : d_r ); row = hist[rb]; for( c = 0; c <= 1; c++ ) { cb = c0 + c; if( cb >= 0 && cb < d ) { v_c = v_r * ( ( c == 0 )? 1.0 - d_c : d_c ); h = row[cb]; for( o = 0; o <= 1; o++ ) { ob = ( o0 + o ) % n; v_o = v_c * ( ( o == 0 )? 1.0 - d_o : d_o ); h[ob] += v_o; } } } } } }
static void hist_to_descr( double*** hist, int d, int n, struct feature* feat ) { int int_val, i, r, c, o, k = 0; for( r = 0; r < d; r++ ) for( c = 0; c < d; c++ ) for( o = 0; o < n; o++ ) feat->descr[k++] = hist[r][c][o]; feat->d = k; normalize_descr( feat ); for( i = 0; i < k; i++ ) if( feat->descr[i] > SIFT_DESCR_MAG_THR ) feat->descr[i] = SIFT_DESCR_MAG_THR; normalize_descr( feat ); /* convert floating-point descriptor to integer valued descriptor */ for( i = 0; i < k; i++ ) { int_val = SIFT_INT_DESCR_FCTR * feat->descr[i]; feat->descr[i] = MIN( 255, int_val ); } }
static void normalize_descr( struct feature* feat ) { double cur, len_inv, len_sq = 0.0; int i, d = feat->d;//为描述子长度128维 //如何进行归一化特征描述子来降低对光照的影响 //主要就是将每一个特征的平方求和,然后开方,然后去其倒数,然后乘以每一个特征描述子的梯度值,这样就得到了归一化的特征描述子 for( i = 0; i < d; i++ ) { cur = feat->descr[i]; len_sq += cur*cur; } len_inv = 1.0 / sqrt( len_sq ); for( i = 0; i < d; i++ ) feat->descr[i] *= len_inv; }
114 128 101.350424 136.130888 40.169873 0.771085 orientation 0 0 0 0 3 1 0 0 2 23 46 15 18 3 0 0 6 20 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 88 36 0 0 81 95 57 47 185 114 2 7 185 155 19 6 19 6 1 22 22 0 0 0 0 0 0 1 0 0 0 0 37 8 0 0 91 12 0 1 185 144 11 35 185 50 0 0 23 28 8 95 40 1 0 0 0 0 0 4 0 0 0 0 0 0 0 0 11 5 0 0 4 2 0 0 49 20 0 0 1 0 0 1 0 0 0 0 0 0 0 0 127.871534 71.100559 15.768594 -2.024589 1 2 2 72 63 12 1 1 133 93 1 4 2 7 4 44 133 115 0 0 0 0 0 20 9 4 0 0 0 0 0 0 23 0 1 9 107 20 1 8 133 5 0 0 0 1 5 133 132 14 0 0 0 0 8 133 14 1 0 0 0 0 0 8 26 0 0 0 126 37 8 22 133 47 0 0 0 0 3 52 131 41 0 0 0 0 2 36 1 0 0 0 0 0 0 2 2 0 0 0 34 105 80 24 111 15 0 0 0 1 55 66 79 21 0 0 0 0 0 5 0 0 0 0 0 0 0 0下面给出说明:
114 特征数目 128 向量维度 关键点坐标 101.350424 y坐标 136.130888 x坐标 40.169873 scale 尺度 0.771085 orientation 关键点的梯度方向 16个种子点的8个方向向量的信息共128个信息 0 0 0 0 3 1 0 0 2 23 46 15 18 3 0 0 6 20 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 88 36 0 0 81 95 57 47 185 114 2 7 185 155 19 6 19 6 1 22 22 0 0 0 0 0 0 1 0 0 0 0 37 8 0 0 91 12 0 1 185 144 11 35 185 50 0 0 23 28 8 95 40 1 0 0 0 0 0 4 0 0 0 0 0 0 0 0 11 5 0 0 4 2 0 0 49 20 0 0 1 0 0 1 0 0 0 0 0 0 0 0 下一组关键点向量 127.871534 y坐标 71.100559 x坐标 15.768594 尺度=sigma*2^(高斯模糊) -2.024589 梯度方向m(x,y)。。。。等等