SIFT算法C语言逐步实现详解
sift算法C代码详解
#ifndef SIFT_H#define SIFT_H#include "cxcore.h"/******************************** Structures ************************ *********///极值点检测中用到的结构//在SIFT特征提取过程中,此类型数据会被赋值给feature结构的feature_data成员struct detection_data{int r; //特征点所在的行int c; //特征点所在的列int octv; //高斯差分金字塔中,特征点所在的组int intvl; //高斯差分金字塔中,特征点所在的组中的层double subintvl; //特征点在层方向(σ方向,intvl方向)上的亚像素偏移量double scl_octv; //特征点所在的组的尺度};struct feature;/******************************* 一些默认参数 ************************* ****///高斯金字塔每组内的层数#define SIFT_INTVLS 3//第0层的初始尺度,即第0层高斯模糊所使用的参数#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//计算特征描述子过程中,计算方向直方图时,将特征点附近划分为d*d个区域,//每个区域生成一个直方图,SIFT_DESCR_WIDTH即d的默认值/** default width of descriptor histogram array */#define SIFT_DESCR_WIDTH 4//计算特征描述子过程中,每个方向直方图的bin个数/** default number of bins per histogram in descriptor array */#define SIFT_DESCR_HIST_BINS 8//输入图像的尺度为0.5/* 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//特征点方向赋值过程中,梯度方向直方图中柱子(bin)的个数/* default number of bins in histogram for orientation assignment */#define SIFT_ORI_HIST_BINS 36//特征点方向赋值过程中,搜索邻域的半径为:3 * 1.5 * σ/* determines gaussian sigma for orientation assignment */#define SIFT_ORI_SIG_FCTR 1.5//特征点方向赋值过程中,搜索邻域的半径为:3 * 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//特征点方向赋值过程中,梯度幅值达到最大值的80%则分裂为两个特征点/* orientation magnitude relative to max that results in new feature */#define SIFT_ORI_PEAK_RATIO 0.8//计算特征描述子过程中,特征点周围的d*d个区域中,每个区域的宽度为m*σ个像素,//SIFT_DESCR_SCL_FCTR即m的默认值,σ为特征点的尺度/* 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//定义了一个带参数的函数宏,用来提取参数f中的feature_data成员并转换为detecti on_data格式的指针/* returns a feature's detection data */#define feat_detection_data(f) ( (struct detection_data*)(f->feature_ data) )/*************************** Function Prototypes ******************** *********//*使用默认参数在图像中提取SIFT特征点参数:img:图像指针feat:用来存储特征点的feature数组的指针static IplImage* downsample( IplImage* );//通过对高斯金字塔中每相邻两层图像相减来建立高斯差分金字塔static IplImage*** build_dog_pyr( IplImage***, int, int );//在尺度空间中检测极值点,通过插值精确定位,去除低对比度的点,去除边缘点,返回检测到的特征点序列static CvSeq* scale_space_extrema( IplImage***, int, int, double, int, CvMemStorage*);//通过在尺度空间中将一个像素点的值与其周围3*3*3邻域内的点比较来决定此点是否极值点(极大值或极小都行)static int is_extremum( IplImage***, int, int, int, int );//通过亚像素级插值进行极值点精确定位(修正极值点坐标),并去除低对比度的极值点,将修正后的特征点组成feature结构返回static struct feature* interp_extremum( IplImage***, int, int, int, in t, int, double);//进行一次极值点差值,计算x,y,σ方向(层方向)上的子像素偏移量(增量)static void interp_step( IplImage***, int, int, int, int, double*, dou ble*, double* );//在DoG金字塔中计算某点的x方向、y方向以及尺度方向上的偏导数static CvMat* deriv_3D( IplImage***, int, int, int, int );//在DoG金字塔中计算某点的3*3海森矩阵static CvMat* hessian_3D( IplImage***, int, int, int, int );//计算被插值点的对比度:D + 0.5 * dD^T * Xstatic double interp_contr( IplImage***, int, int, int, int, double, d ouble, double );//为一个feature结构分配空间并初始化static struct feature* new_feature( void );//去除边缘响应,即通过计算主曲率比值判断某点是否边缘点static int is_too_edge_like( IplImage*, int, int, int );//计算特征点序列中每个特征点的尺度static void calc_feature_scales( CvSeq*, double, int );//将特征点序列中每个特征点的坐标减半(当设置了将图像放大为原图的2倍时,特征点检测完之后调用)static void adjust_for_img_dbl( CvSeq* );//计算每个特征点的梯度直方图,找出其主方向,若一个特征点有不止一个主方向,将其分为两个特征点static void calc_feature_oris( CvSeq*, IplImage*** );//计算指定像素点的梯度方向直方图,返回存放直方图的数组static double* ori_hist( IplImage*, int, int, int, int, double );//计算指定点的梯度的幅值magnitude和方向orientationstatic int calc_grad_mag_ori( IplImage*, int, int, double*, double* );//对梯度方向直方图进行高斯平滑,弥补因没有仿射不变性而产生的特征点不稳定的问题static void smooth_ori_hist( double*, int );//查找梯度直方图中主方向的梯度幅值,即查找直方图中最大bin的值static double dominant_ori( double*, int );//若当前特征点的直方图中某个bin的值大于给定的阈值,则新生成一个特征点并添加到特征点序列末尾static void add_good_ori_features( CvSeq*, double*, int, double, struc t feature* );//对输入的feature结构特征点做深拷贝,返回克隆生成的特征点的指针static struct feature* clone_feature( struct feature* );//计算特征点序列中每个特征点的特征描述子向量static void compute_descriptors( CvSeq*, IplImage***, int, int );//计算特征点附近区域的方向直方图,此直方图在计算特征描述子中要用到,返回值是一个d* d*n的三维数组static double*** descr_hist( IplImage*, int, int, double, double, int, int );static void interp_hist_entry( double***, double, double, double, doub le, int, int);//将某特征点的方向直方图转换为特征描述子向量,对特征描述子归一化并将所有元素转化为整型,存入指定特征点中static void hist_to_descr( double***, int, int, struct feature* );//归一化特征点的特征描述子,即将特征描述子数组中每个元素除以特征描述子的模static void normalize_descr( struct feature* );//比较函数,将特征点按尺度的降序排列,用在序列排序函数CvSeqSort中static int feature_cmp( void*, void*, void* );//释放计算特征描述子过程中用到的方向直方图的内存空间static void release_descr_hist( double****, int );//释放金字塔图像组的存储空间static void release_pyr( IplImage****, int, int );/*********************** Functions prototyped in sift.h ************** ********//**Finds SIFT features in an image using default parameter values. Alldetected features are stored in the array pointed to by \a feat.@param img the image in which to detect features@param feat a pointer to an array in which to store detected features@return Returns the number of features stored in \a feat or -1 on failu re@see _sift_features()*/int sift_features( IplImage* img, struct feature** feat ){return _sift_features( img, feat, SIFT_INTVLS, SIFT_SIGMA, SIF T_CONTR_THR,SIFT_CURV_THR, SIFT_IMG_DBL, SIFT_DESCR_WIDTH,SIFT_DESCR_HIST _BINS );}/*使用用户指定的参数在图像中提取SIFT特征点参数:img:输入图像feat:存储特征点的数组的指针,此数组的内存将在本函数中被分配,使用完后必须在调用出释放:free(*feat)intvls:每组的层数sigma:初始高斯平滑参数σcontr_thr:对比度阈值,针对归一化后的图像,用来去除不稳定特征curv_thr:去除边缘的特征的主曲率阈值img_dbl:是否将图像放大为之前的两倍descr_width:特征描述过程中,计算方向直方图时,将特征点附近划分为descr_width*d escr_width个区域,每个区域生成一个直方图descr_hist_bins:特征描述过程中,每个直方图中bin的个数返回值:提取的特征点个数,若返回-1表明提取失败*/int _sift_features( IplImage* img, struct feature** feat, int intvls,double sigma, double contr_thr, int c urv_thr,int img_dbl, int descr_width, int des cr_hist_bins ){IplImage* init_img;//原图经初始化后的图像IplImage*** gauss_pyr, *** dog_pyr;//三级指针,高斯金字塔图像组,Do G金字塔图像组CvMemStorage* storage;//存储器CvSeq* features;//存储特征点的序列,序列中存放的是struct feature类型的指针int octvs, i, n = 0;//输入参数检查if( ! img )fatal_error( "NULL pointer error, %s, line %d", __FILE __, __LINE__ );if( ! feat )fatal_error( "NULL pointer error, %s, line %d", __FILE __, __LINE__ );/*步骤一:建立尺度空间,即建立高斯差分(DoG)金字塔dog_pyr将原图转换为32位灰度图并归一化,然后进行一次高斯平滑,并根据参数img_dbl决定是否将图像尺寸放大为原图的2倍*/init_img = create_init_img( img, img_dbl, sigma );//计算高斯金字塔的组数octvsoctvs = log( MIN( init_img->width, init_img->height ) ) / log (2) - 2;//为了保证连续性,在每一层的顶层继续用高斯模糊生成3幅图像,所以高斯金字塔每组//有intvls+3层,DOG金字塔每组有intvls+2层//建立高斯金字塔gauss_pyr,是一个octvs*(intvls+3)的图像数组gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );//建立高斯差分(DoG)金字塔dog_pyr,是一个octvs*(intvls+2)的图像数组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 );//计算特征点序列features中每个特征点的尺度calc_feature_scales( features, sigma, intvls );if( img_dbl ) //若设置了将图像放大为原图的2倍adjust_for_img_dbl( features );//将特征点序列中每个特征点的坐标减半//(当设置了将图像放大为原图的2倍时,特征点检测完之后调用)/*步骤三:特征点方向赋值,完成此步骤后,每个特征点有三个信息:位置、尺度、方向*///计算每个特征点的梯度直方图,找出其主方向,若一个特征点有不止一个主方向,将其分为两个特征点calc_feature_oris( features, gauss_pyr );/*步骤四:计算特征描述子*///计算特征点序列中每个特征点的特征描述子向量compute_descriptors( features, gauss_pyr, descr_width, descr_h ist_bins );//按特征点尺度的降序排列序列中的元素的顺序,feature_cmp是自定义的比较函数cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL );//将CvSeq类型的特征点序列features转换为通用的struct feature类型的数组featn = features->total;//特征点个数*feat = calloc( n, sizeof(struct feature) );//分配控件//将序列features中的元素拷贝到数组feat中,返回数组指针给feat*feat = cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ );//释放特征点数组feat中所有特征点的feature_data成员,因为此成员中的数据在检测完特征//点后就没用了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;}/*将原图转换为32位灰度图并归一化,然后进行一次高斯平滑,并根据参数img_dbl决定是否将图像尺寸放大为原图的2倍参数:img:输入的原图像img_dbl:是否将图像放大为之前的两倍sigma:初始高斯平滑参数σ返回值:初始化完成的图像*/static IplImage* create_init_img( IplImage* img, int img_dbl, double s igma ){IplImage* gray, * dbl;float sig_diff;//调用函数,将输入图像转换为32位灰度图,并归一化gray = convert_to_gray32( img );if( img_dbl ) //若设置了将图像放大为原图的2倍{//将图像长宽扩展一倍时,便有了底-1层,该层尺度为: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 );//高斯平滑,高斯核在x,y方向上的标准差都是sig_diffcvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_di ff );cvReleaseImage( &gray );return dbl;}else//不用放大为原图的2倍{//计算第0层的尺度sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT _INIT_SIGMA );//高斯平滑,高斯核在x,y方向上的标准差都是sig_diffcvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_ diff );return gray;}}/*将输入图像转换为32位灰度图,并进行归一化参数:img:输入图像,3通道8位彩色图(BGR)或8位灰度图返回值:32位灰度图*/static IplImage* convert_to_gray32( IplImage* img ){IplImage* gray8, * gray32;gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 );//首先将原图转换为8位单通道图像if( img->nChannels == 1 )//若原图本身就是单通道,直接克隆原图gray8 = cvClone( img );else//若原图是3通道图像{gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );//创建8位单通道图像cvCvtColor( img, gray8, CV_BGR2GRAY );//将原图转换为8为单通道图像}//然后将8为单通道图像gray8转换为32位单通道图像,并进行归一化处理(除以255)cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 );//释放临时图像cvReleaseImage( &gray8 );//返回32位单通道图像return gray32;}/*根据输入参数建立高斯金字塔参数:base:输入图像,作为高斯金字塔的基图像octvs:高斯金字塔的组数intvls:每组的层数sigma:初始尺度返回值:高斯金字塔,是一个octvs*(intvls+3)的图像数组*/static IplImage*** build_gauss_pyr( IplImage* base, int octvs,int i ntvls, double sigma ){IplImage*** gauss_pyr;//为了保证连续性,在每一层的顶层继续用高斯模糊生成3幅图像,所以高斯金字塔每组有intvls+3层,DOG金字塔每组有intvls+2层double* sig = calloc( intvls + 3, sizeof(double));//每层的sigma 参数数组double sig_total, sig_prev, k;int i, o;//为高斯金字塔gauss_pyr分配空间,共octvs个元素,每个元素是一组图像的首指针gauss_pyr = calloc( octvs, sizeof( IplImage** ) );//为第i组图像gauss_pyr[i]分配空间,共intvls+3个元素,每个元素是一个图像指针for( i = 0; i < octvs; i++ )gauss_pyr[i] = calloc( intvls + 3, sizeof( IplImage* ) );/* precompute Gaussian sigmas using the following formul a:sigma_{total}^2 = sigma_{i}^2 + sigma_{i-1}^2 *///计算每次高斯模糊的sigma参数sig[0] = sigma;//初始尺度k = pow( 2.0, 1.0 / intvls );for( i = 1; i < intvls + 3; i++ ){sig_prev = pow( k, i - 1 ) * sigma;//i-1层的尺度sig_total = sig_prev * k;//i层的尺度sig[i] = sqrt( sig_total * sig_total - sig_prev * sig_p rev );}//逐组逐层生成高斯金字塔for( o = 0; o < octvs; o++ )//遍历组for( i = 0; i < intvls + 3; i++ )//遍历层{if( o == 0 && i == 0 )//第0组,第0层,就是原图像gauss_pyr[o][i] = cvCloneImage(base);else if( i == 0 )//新的一组的首层图像是由上一组最后一层图像下采样得到gauss_pyr[o][i] = downsample( gauss_pyr [o-1][intvls] );else//对上一层图像进行高斯平滑得到当前层图像{ //创建图像gauss_pyr[o][i] = cvCreateImage( cvGetS ize(gauss_pyr[o][i-1]),IPL_DEPTH_32F, 1 );//对上一层图像gauss_pyr[o][i-1]进行参数为sig[i]的高斯平滑,得到当前层图像gauss_pyr[o][i]cvSmooth( gauss_pyr[o][i-1], gauss_pyr [o][i], CV_GAUSSIAN, 0, 0, sig[i], sig[i] );}}free( sig );//释放sigma参数数组return gauss_pyr;//返回高斯金字塔}/*对输入图像做下采样生成其四分之一大小的图像(每个维度上减半),使用最近邻差值方法参数:img:输入图像返回值:下采样后的图像*/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;}/*通过对高斯金字塔中每相邻两层图像相减来建立高斯差分金字塔参数:gauss_pyr:高斯金字塔octvs:组数intvls:每组的层数返回值:高斯差分金字塔,是一个octvs*(intvls+2)的图像数组*/static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, in t intvls ){IplImage*** dog_pyr;int i, o;//为高斯差分金字塔分配空间,共octvs个元素,每个元素是一组图像的首指针dog_pyr = calloc( octvs, sizeof( IplImage** ) );//为第i组图像dog_pyr[i]分配空间,共(intvls+2)个元素,每个元素是一个图像指针for( i = 0; i < octvs; i++ )dog_pyr[i] = calloc( intvls + 2, sizeof(IplImage*) );//逐组逐层计算差分图像for( o = 0; o < octvs; o++ )//遍历组for( i = 0; i < intvls + 2; i++ )//遍历层{ //创建DoG金字塔的第o组第i层的差分图像dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss _pyr[o][i]), IPL_DEPTH_32F, 1 );//将高斯金字塔的第o组第i+1层图像和第i层图像相减来得到DoG金字塔的第o组第i层图像cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog _pyr[o][i], NULL );}return dog_pyr;//返回高斯差分金字塔}/*在尺度空间中检测极值点,通过插值精确定位,去除低对比度的点,去除边缘点,返回检测到的特征点序列参数:dog_pyr:高斯差分金字塔octvs:高斯差分金字塔的组数intvls:每组的层数contr_thr:对比度阈值,针对归一化后的图像,用来去除不稳定特征cur_thr:主曲率比值的阈值,用来去除边缘特征storage:存储器返回值:返回检测到的特征点的序列*/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;//在存储器storage上创建存储极值点的序列,其中存储feature结构类型的数据features = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct featur e), storage );/*遍历高斯差分金字塔,检测极值点*///SIFT_IMG_BORDER指明边界宽度,只检测边界线以内的极值点for( o = 0; o < octvs; o++ )//第o组for( i = 1; i <= intvls; i++ )//遍i层for(r = SIFT_IMG_BORDER; r < dog_pyr[o][0]->hei ght-SIFT_IMG_BORDER; r++)//第r行for(c = SIFT_IMG_BORDER; c < dog_pyr[o] [0]->width-SIFT_IMG_BORDER; c++)//第c列//进行初步的对比度检查,只有当归一化后的像素值大于对比度阈值prelim_contr_thr时才继续检测此像素点是//否可能是极值//调用函数pixval32f获取图像dog_p yr[o][i]的第r行第c列的点的坐标值,然后调用ABS宏求其绝对值if( ABS( pixval32f( dog_pyr[o] [i], r, c ) ) > prelim_contr_thr )//通过在尺度空间中将一个像素点的值与其周围3*3*3邻域内的点比较来决定此点是否极值点(极大值或//极小都行)if( is_extremum( dog_py r, o, i, r, c ) )//若是极值点{//由于极值点的检测是在离散空间中进行的,所以检测到的极值点并不一定是真正意义上的极//值点//因为真正的极值点可能位于两个像素之间,而在离散空间中只能精确到坐标点精度上//通过亚像素级插值进行极值点精确定位(修正极值点坐标),并去除低对比度的极值点,将修//正后的特征点组成feature结构返回feat = interp_ex tremum(dog_pyr, o, i, r, c, intvls, contr_thr);//返回值非空,表明此点已被成功修正if( feat ){//调用宏feat_detection_data来提取参数feat中的feature_data成员并转换为detec // tion_data类型的指针ddata = feat_detection_data( feat );//去除边缘响应,即通过计算主曲率比值判断某点是否边缘点,返回值为0表示不是边//缘点,可做特征点if( ! is _too_edge_like( dog_pyr[ddata->octv][ddata->intvl], ddata->r, ddat a->c, cu rv_thr ) ){ cvSeqPush( features, feat );//向特征点序列features末尾插入新检测到 //的特征点feat}elsefree( ddata );free( fe at );}}return features;//返回特征点序列}/*通过在尺度空间中将一个像素点的值与其周围3*3*3邻域内的点比较来决定此点是否极值点(极大值或极小都行)参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列返回值:若指定的像素点是极值点(极大值或极小值),返回1;否则返回0*/static int is_extremum( IplImage*** dog_pyr, int octv, int intvl, int r, int c ){//调用函数pixval32f获取图像dog_pyr[octv][intvl]的第r行第c列的点的坐标值float val = pixval32f( dog_pyr[octv][intvl], r, c );int i, j, k;//检查是否最大值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[oc tv][intvl+i], r + j, c + k ) )return 0;}//检查是否最小值else{for( i = -1; i <= 1; i++ )//层for( j = -1; j <= 1; j++ )//行for( k = -1; k <= 1; k++ )//列if( val > pixval32f( dog_pyr[oc tv][intvl+i], r + j, c + k ) )return 0;}return 1;}/*通过亚像素级插值进行极值点精确定位(修正极值点坐标),并去除低对比度的极值点,将修正后的特征点组成feature结构返回参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列intvls:每组的层数contr_thr:对比度阈值,针对归一化后的图像,用来去除不稳定特征返回值:返回经插值修正后的特征点(feature类型);若经有限次插值依然无法精确到理想情况或者该点对比度过低,返回NULL*/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;//与特征检测有关的结构,存在feature结构的feature_data成员中double xi, xr, xc, contr;//xi,xr,xc分别为亚像素的intvl(层),row (y),col(x)方向上的增量(偏移量)int i = 0;//插值次数//SIFT_MAX_INTERP_STEPS指定了关键点的最大插值次数,即最多修正多少次,默认是5while( i < SIFT_MAX_INTERP_STEPS ){//进行一次极值点差值,计算σ(层方向,intvl方向),y,x方向上的子像素偏移量(增量)interp_step( dog_pyr, octv, intvl, r, c, &xi, &xr, &xc );//若在任意方向上的偏移量大于0.5时,意味着差值中心已经偏移到它的临近点上,所以必须改变当前关键点的位置坐标if( ABS( xi ) < 0.5 && ABS( xr ) < 0.5 && ABS( xc ) < 0.5 )//若三方向上偏移量都小于0.5,表示已经够精确,则不用继续插值break;//修正关键点的坐标,x,y,σ三方向上的原坐标加上偏移量取整(四舍五入)c += cvRound( xc );//x坐标修正r += cvRound( xr );//y坐标修正intvl += cvRound( xi );//σ方向,即层方向//若坐标修正后超出范围,则结束插值,返回NULLif( 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_BORDE R ){return NULL;}i++;}//若经过SIFT_MAX_INTERP_STEPS次插值后还没有修正到理想的精确位置,则返回NULL,即舍弃此极值点if( i >= SIFT_MAX_INTERP_STEPS )return NULL;//计算被插值点的对比度:D + 0.5 * dD^T * Xcontr = interp_contr( dog_pyr, octv, intvl, r, c, xi, xr, xc );if( ABS( contr ) < contr_thr / intvls )//若该点对比度过小,舍弃,返回NULLreturn NULL;//为一个特征点feature结构分配空间并初始化,返回特征点指针feat = new_feature();//调用宏feat_detection_data来提取参数feat中的feature_data成员并转换为detection_data类型的指针ddata = feat_detection_data( feat );//将修正后的坐标赋值给特征点feat//原图中特征点的x坐标,因为第octv组中的图的尺寸比原图小2^octv倍,所以坐标值要乘以2^octvfeat->img_pt.x = feat->x = ( c + xc ) * pow( 2.0, octv );//原图中特征点的y坐标,因为第octv组中的图的尺寸比原图小2^octv倍,所以坐标值要乘以2^octvfeat->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;//特征点在层方向(σ方向,intvl方向)上的亚像素偏移量return feat;//返回特征点指针}/*进行一次极值点差值,计算x,y,σ方向(层方向)上的子像素偏移量(增量)参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列xi:输出参数,层方向上的子像素增量(偏移)xr:输出参数,y方向上的子像素增量(偏移)xc:输出参数,x方向上的子像素增量(偏移)*/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 };//在DoG金字塔中计算某点的x方向、y方向以及尺度方向上的偏导数,结果存放在列向量dD中dD = deriv_3D( dog_pyr, octv, intvl, r, c );//在DoG金字塔中计算某点的3*3海森矩阵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 );//X = - H^(-1) * dD,H的三个元素分别是x,y,σ方向上的偏移量(具体见SIF T算法说明)cvGEMM( H_inv, dD, -1, NULL, 0, &X, 0 );cvReleaseMat( &dD );cvReleaseMat( &H );cvReleaseMat( &H_inv );*xi = x[2];//σ方向(层方向)偏移量*xr = x[1];//y方向上偏移量*xc = x[0];//x方向上偏移量}/*在DoG金字塔中计算某点的x方向、y方向以及尺度方向上的偏导数参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列返回值:返回3个偏导数组成的列向量{ dI/dx, dI/dy, dI/ds }^T*/static CvMat* deriv_3D( IplImage*** dog_pyr, int octv, int intvl, int r, int c ){CvMat* dI;double dx, dy, ds;//求差分来代替偏导,这里是用的隔行求差取中值的梯度计算方法//求x方向上的差分来近似代替偏导数dx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) -pixval32f( dog_pyr[octv][intvl], r, c-1 ) ) / 2.0;//求y方向上的差分来近似代替偏导数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;}/*在DoG金字塔中计算某点的3*3海森矩阵/ Ixx Ixy Ixs \| Ixy Iyy Iys |\ Ixs Iys Iss /参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列返回值:返回3*3的海森矩阵*/static CvMat* hessian_3D( IplImage*** dog_pyr, int octv, int intvl, in t r, int c ){CvMat* H;double v, dxx, dyy, dss, dxy, dxs, dys;v = pixval32f( dog_pyr[octv][intvl], r, c );//该点的像素值//用差分近似代替倒数(具体公式见各种梯度的求法)//dxx = f(i+1,j) - 2f(i,j) + f(i-1,j)//dyy = f(i,j+1) - 2f(i,j) + f(i,j-1)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;}/*计算被插值点的对比度:D + 0.5 * dD^T * X参数:dog_pyr:高斯差分金字塔octv:像素点所在的组intvl:像素点所在的层r:像素点所在的行c:像素点所在的列xi:层方向上的子像素增量xr:y方向上的子像素增量xc:x方向上的子像素增量返回值:插值点的对比度*/static double interp_contr( IplImage*** dog_pyr, int octv, int intvl, int r,int c, double xi, dou ble xr, double xc ){CvMat* dD, X, T;double t[1], x[3] = { xc, xr, xi };//偏移量组成的列向量X,其中是x,y,σ三方向上的偏移量cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP );//矩阵乘法的结果T,是一个数值cvInitMatHeader( &T, 1, 1, CV_64FC1, t, CV_AUTOSTEP );//在DoG金字塔中计算某点的x方向、y方向以及尺度方向上的偏导数,结果存放在列向量dD中dD = deriv_3D( dog_pyr, octv, intvl, r, c );//矩阵乘法:T = dD^T * XcvGEMM( dD, &X, 1, NULL, 0, &T, CV_GEMM_A_T );cvReleaseMat( &dD );//返回计算出的对比度值:D + 0.5 * dD^T * X (具体公式推导见SIFT算法说明)return pixval32f( dog_pyr[octv][intvl], r, c ) + t[0] * 0.5;}/*为一个feature结构分配空间并初始化返回值:初始化完成的feature结构的指针*/static struct feature* new_feature( void ){struct feature* feat;//特征点指针struct detection_data* ddata;//与特征检测相关的结构feat = malloc( sizeof( struct feature ) );//分配空间memset( feat, 0, sizeof( struct feature ) );//清零ddata = malloc( sizeof( struct detection_data ) );memset( ddata, 0, sizeof( struct detection_data ) );feat->feature_data = ddata;//将特征检测相关的结构指针赋值给特征点的f eature_data成员feat->type = FEATURE_LOWE;//默认是LOWE类型的特征点return feat;}/*去除边缘响应,即通过计算主曲率比值判断某点是否边缘点参数:dog_img:此特征点所在的DoG图像r:特征点所在的行c:特征点所在的列cur_thr:主曲率比值的阈值,用来去除边缘特征返回值:0:此点是非边缘点;1:此点是边缘点*/static int is_too_edge_like( IplImage* dog_img, int r, int c, int curv _thr ){double d, dxx, dyy, dxy, tr, det;/*某点的主曲率与其海森矩阵的特征值成正比,为了避免直接计算特征值,这里只考虑特征值的比值可通过计算海森矩阵的迹tr(H)和行列式det(H)来计算特征值的比值设a是海森矩阵的较大特征值,b是较小的特征值,有a = r*b,r是大小特征值的比值tr(H) = a + b; det(H) = a*b;tr(H)^2 / det(H) = (a+b)^2 / ab = (r+1)^2/rr越大,越可能是边缘点;伴随r的增大,(r+1)^2/r 的值也增大,所以可通过(r +1)^2/r 判断主曲率比值是否满足条件*//* principal curvatures are computed using the trace and det of Hessian */d = pixval32f(dog_img, r, c);//调用函数pixval32f获取图像dog_img 的第r行第c列的点的坐标值//用差分近似代替偏导,求出海森矩阵的几个元素值/* / dxx dxy \\ dxy dyy / */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; re ject feature */if( det <= 0 )return 1;//返回1表明此点是边缘点//通过式子:(r+1)^2/r 判断主曲率的比值是否满足条件,若小于阈值,表明不是边缘点if( tr * tr / det < ( curv_thr + 1.0 )*( curv_thr + 1.0 ) / curv _thr )return 0;//不是边缘点return 1;//是边缘点}/*计算特征点序列中每个特征点的尺度参数:features:特征点序列sigma:初始高斯平滑参数,即初始尺度intvls:尺度空间中每组的层数*/static void calc_feature_scales( CvSeq* features, double sigma, int in tvls ){struct feature* feat;struct detection_data* ddata;double intvl;int i, n;n = features->total;//总的特征点个数//遍历特征点for( i = 0; i < n; i++ ){//调用宏,获取序列features中的第i个元素,并强制转换为struct feature类型feat = CV_GET_SEQ_ELEM( struct feature, features, i );//调用宏feat_detection_data来提取参数feat中的feature_da ta成员并转换为detection_data类型的指针ddata = feat_detection_data( feat );//特征点所在的层数ddata->intvl加上特征点在层方向上的亚像素偏移量,得到特征点的较为精确的层数intvl = ddata->intvl + ddata->subintvl;//计算特征点的尺度(公式见SIFT算法说明),并赋值给scl成员feat->scl = sigma * pow( 2.0, ddata->octv + intvl / int vls );//计算特征点所在的组的尺度,给detection_data的scl_octv成员赋值ddata->scl_octv = sigma * pow( 2.0, intvl / intvls );}}/*将特征点序列中每个特征点的坐标减半(当设置了将图像放大为原图的2倍时,特征点检测完之后调用)参数:features:特征点序列*/static void adjust_for_img_dbl( CvSeq* features ){struct feature* feat;int i, n;n = features->total;//总的特征点个数//遍历特征点for( i = 0; i < n; i++ )。
SIFT算法实现C语言
经典算法SIFT实现即代码解释:以下便是sift源码库编译后的效果图:为了给有兴趣实现sift算法的朋友提供个参考,特整理此文如下。
要了解什么是sift算法,请参考:九、图像特征提取与匹配之SIFT算法。
ok,咱们下面,就来利用Rob Hess维护的sift 库来实现sift算法:首先,请下载Rob Hess维护的sift 库:/hess/code/sift/下载Rob Hess的这个压缩包后,如果直接解压缩,直接编译,那么会出现下面的错误提示:编译提示:error C1083: Cannot open include file: 'cxcore.h': No such file or directory,找不到这个头文件。
这个错误,是因为你还没有安装opencv,因为:cxcore.h和cv.h是开源的OPEN CV头文件,不是VC++的默认安装文件,所以你还得下载OpenCV并进行安装。
然后,可以在OpenCV文件夹下找到你所需要的头文件了。
据网友称,截止2010年4月4日,还没有在VC6.0下成功使用opencv2.0的案例。
所以,如果你是VC6.0的用户请下载opencv1.0版本。
vs的话,opencv2.0,1.0任意下载。
以下,咱们就以vc6.0为平台举例,下载并安装opencv1.0版本、gsl等。
当然,你也可以用vs编译,同样下载opencv(具体版本不受限制)、gsl等。
请按以下步骤操作:一、下载opencv1.0/projects/opencvlibrary/files/opencv-win/1.0/OpenCV_1.0.exe/download二、安装opencv1.0,配置Windows环境变量1、安装注意:假如你是将OpenCV安装到C:/Program Files/OpenCV(如果你安装的时候选择不是安装在C盘,则下面所有对应的C盘都改为你所安装在的那个“X盘”,即可),在安装时选择"将/OpenCV/bin加入系统变量",打上“勾”。
SIFT算法详解
SIFT算法详解Scale Invariant Feature Transform(SIFT)Just For Funzdd zddmail@对于初学者,从David G.Lowe的论文到实现,有许多鸿沟,本文帮你跨越。
1、SIFT综述尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由David Lowe在1999年所发表,2004年完善总结。
其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。
此算法有其专利,专利拥有者为英属哥伦比亚大学。
局部影像特征的描述与侦测可以帮助辨识物体,SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。
对于光线、噪声、些微视角改变的容忍度也相当高。
基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。
使用SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。
在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。
SIFT特征的信息量大,适合在海量数据库中快速准确匹配。
SIFT算法的特点有:1. SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;2. 独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;4. 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;5. 可扩展性,可以很方便的与其他形式的特征向量进行联合。
SIFT算法可以解决的问题:目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。
SIFT算法实现及代码详解
SIFT算法实现及代码详解SIFT(Scale-Invariant Feature Transform)是一种计算机视觉算法,用于在图像中寻找局部特征。
由于SIFT算法具有尺度不变性和旋转不变性等优点,因此广泛应用于目标检测、图像拼接、图像匹配等领域。
1.尺度空间构建:通过对原始图像进行多次高斯模糊,得到图像的尺度空间。
2.高斯差分金字塔构建:对尺度空间进行差分操作,得到高斯差分金字塔。
3.极值点检测:在高斯差分金字塔中检测局部极值点,用于确定关键点的位置。
4.关键点精确定位:通过对关键点进行插值,得到更精确的关键点位置。
5.方向分配:为每个关键点分配主方向,用于后续的旋转不变性。
6.关键点描述:对每个关键点周围的区域计算特征向量,用于描述关键点的特征。
7.特征匹配:通过比较特征向量的相似度,实现图像间的特征匹配。
下面是SIFT算法的Python实现(使用OpenCV库):```pythonimport cv2def SIFT(image):#尺度空间构建gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)sift = cv2.xfeatures2d.SIFT_create# keypoints为检测到的关键点列表,descriptors为关键点的特征描述向量return keypoints, descriptors#读取图像image1 = cv2.imread('image1.png')image2 = cv2.imread('image2.png')#提取图像特征keypoints1, descriptors1 = SIFT(image1)keypoints2, descriptors2 = SIFT(image2)#特征匹配bf = cv2.BFMatcher(cv2.NORM_L2)matches = bf.match(descriptors1, descriptors2)matches = sorted(matches, key = lambda x:x.distance)#绘制匹配结果result = cv2.drawMatches(image1, keypoints1, image2, keypoints2, matches, None, flags=2)cv2.imshow('Result', result)cv2.waitKey(0)cv2.destroyAllWindows```上述代码中,首先定义了一个`SIFT`函数用于提取图像的特征。
sift算法的原理和步骤
sift算法的原理和步骤SIFT算法的原理和步骤SIFT算法是一种用于图像特征提取的算法,它能够从图像中提取出具有独特性、稳定性和可重复性的关键点,用于图像匹配、目标跟踪等任务。
本文将介绍SIFT算法的原理和步骤。
一、原理1. 尺度空间尺度空间是指同一物体在不同尺度下的表现形式。
SIFT算法采用高斯金字塔来实现尺度空间的构建,即将原始图像不断缩小并平滑处理,得到一系列模糊程度不同的图像。
2. 关键点检测在尺度空间中,SIFT算法采用DoG(Difference of Gaussian)来检测关键点。
DoG是指两个不同尺寸的高斯滤波器之间的差值,可以有效地提取出具有高斯拉普拉斯变换极值点(LoG)特征的区域。
3. 方向确定对于每个关键点,在其周围区域内计算梯度幅值和方向,并统计梯度直方图。
最终确定该关键点最显著的梯度方向作为其主方向。
4. 描述子生成以关键点为中心,生成一个16x16的方形区域,并将其分为4x4的小块。
对于每个小块,计算其内部像素的梯度方向直方图,并将其串联成一个128维的向量,作为该关键点的描述子。
5. 匹配通过计算不同图像之间的关键点描述子之间的距离来进行匹配。
采用最近邻法(Nearest Neighbor)和次近邻法(Second Nearest Neighbor)来进行筛选,从而得到最终的匹配结果。
二、步骤1. 构建高斯金字塔对于原始图像,采用高斯滤波器进行平滑处理,并将其缩小一定比例后再次平滑处理,得到一系列不同尺度下的图像。
这些图像构成了高斯金字塔。
2. 构建DoG金字塔在高斯金字塔中,相邻两层之间做差得到一组DoG金字塔。
通过在DoG金字塔上寻找局部极值点来检测关键点。
3. 确定关键点主方向对于每个关键点,在其周围区域内计算梯度幅值和方向,并统计梯度直方图。
最终确定该关键点最显著的梯度方向作为其主方向。
4. 生成描述子以关键点为中心,生成一个16x16的方形区域,并将其分为4x4的小块。
SIFT算法详解及应用
SIFT算法详解及应用SIFT(Scale-Invariant Feature Transform)是一种图像处理算法,它能够在不同尺度、旋转、光照条件下进行特征点匹配。
SIFT算法是计算机视觉领域的一个重要算法,广泛应用于目标识别、图像拼接、图像检索等方面。
首先,尺度空间极值检测是指在不同尺度上检测图像中的极值点,即图像中的局部最大值或最小值。
这样可以使特征点能够对应不同尺度的目标,使算法对尺度变化有鲁棒性。
为了实现这一步骤,SIFT算法使用了高斯差分金字塔来检测尺度空间中的极值点。
接下来是关键点定位,即确定在尺度空间极值点的位置以及对应的尺度。
SIFT算法通过比较每个极值点与其周围点的响应值大小来判断其是否为关键点。
同时,为了提高关键点的稳定性和准确性,算法还会对关键点位置进行亚像素精确化。
然后是关键点方向的确定,即为每个关键点分配一个主方向。
SIFT算法使用图像梯度方向的直方图来确定关键点的方向。
这样可以使得特征描述子具有旋转不变性,使算法在目标旋转的情况下仍能进行匹配。
最后是关键点的描述。
SIFT算法使用局部图像的梯度信息来描述关键点,即构建关键点的特征向量。
特征向量的构建过程主要包括将关键点周围的图像划分为若干个子区域,计算每个子区域的梯度直方图,并将所有子区域的直方图拼接成一个特征向量。
这样可以使得特征向量具有局部不变性和对光照变化的鲁棒性。
SIFT算法的应用非常广泛。
首先,在目标识别领域,SIFT算法能够检测和匹配图像中的关键点,从而实现目标的识别和定位。
其次,在图像拼接方面,SIFT算法能够提取图像中的特征点,并通过匹配这些特征点来完成图像的拼接。
此外,SIFT算法还可以应用于图像检索、三维重建、行人检测等领域。
总结起来,SIFT算法是一种具有尺度不变性和旋转不变性的图像处理算法。
它通过提取图像中的关键点,并构建关键点的描述子,实现了对不同尺度、旋转、光照条件下的目标识别和图像匹配。
SIFT算法原理:SIFT算法详细介绍
前面们介绍了Harris和Shi-Tomasi角检测算法,这两种算法具有旋转不变性,但不具有尺度不变性,以下图为例,在左侧小图中可以检测到角,但图像被放后,在使用同样的窗口,就检测不到角了。
所以,们来介绍一种计算机视觉的算法,尺度不变特征转换即SIFT(Scale-invariantfeaturetransform)。
它用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值,并提取出其位置、尺度、旋转不变量,此算法由DavidLowe在1999年所发表,2004年完善总结。
应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对等领域。
SIFT算法的实质在不同的尺度空间上查找关键(特征),并计算出关键的方向。
SIFT 所查找到的关键一些十分突出,不会因光照,仿变换和噪音等因素而变化的,如角、边缘、暗区的亮及亮区的暗等。
1.1基本流程Lowe将SIFT算法分解为如下四步:尺度空间极值检测:搜索所有尺度上的图像位置。
通过高斯差分函数来识别潜在的对于尺度和旋转不变的关键。
关键定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。
关键的选择依据于它们的稳定程度。
关键方向确定:基于图像局部的梯度方向,分配给每个关键位置一个或多个方向。
所有后面的对图像数据的操作都相对于关键的方向、尺度和位置进行变换,从而保证了对于这些变换的不变性。
关键描述:在每个关键周围的邻域内,在选定的尺度上测量图像局部的梯度。
这些梯度作为关键的描述符,它允许比较的局部形状的变形或光照变化。
们就沿着Lowe的步骤,对SIFT算法的实现过程进行介绍:1.2尺度空间极值检测在不同的尺度空间不能使用相同的窗口检测极值,对小的关键使用小的窗口,对的关键使用的窗口,为了达到上述目的,们使用尺度空间滤波器。
高斯核可以产生多尺度空间的核函数。
-《Scale-spacetheory:Abasictoolforanalysingstructuresatdifferentscales》。
sift算法详解
2、高斯模糊
SIFT 算法是在不同的尺度空间上查找关键点,而尺度空间的获取需要使用高斯模糊来 实现,Lindeberg 等人已证明高斯卷积核是实现尺度变换的唯一变换核,并且是唯一的线性 核。本节先介绍高斯模糊算法。
2.1 二维高斯函数
高斯模糊是一种图像滤波器,它使用正态分布(高斯函数)计算模糊模板,并使用该模板 与原图像做卷积运算,达到模糊图像的目的。 N 维空间正态分布方程为:
G (r ) =
1 2πσ
2
N
e −r
2
/(2 σ 2 )
(1-1)
其中, σ 是正态分布的标准差, σ 值越大,图像越模糊(平滑)。r 为模糊半径,模糊半 径是指模板元素到模板中心的距离。如二维模板大小为 m*n,则模板上的元素(x,y)对应的高 斯计算公式为:
G ( x, y ) =
1
2πσ 2
3.1 尺度空间理论
尺度空间(scale space)思想最早是由 Iijima 于 1962 年提出的,后经 witkin 和 Koenderink 等人的推广逐渐得到关注,在计算机视觉领域使用广泛。 尺度空间理论的基本思想是: 在图像信息处理模型中引入一个被视为尺度的参数, 通过 连续变化尺度参数获得多尺度下的尺度空间表示序列, 对这些序列进行尺度空间主轮廓的提 取,并以该主轮廓作为一种特征向量,实现边缘、角点检测和不同分辨率上的特征提取等。 尺度空间方法将传统的单尺度图像信息处理技术纳入尺度不断变化的动态分析框架中, 更容易获取图像的本质特征。 尺度空间中各尺度图像的模糊程度逐渐变大, 能够模拟人在距 离目标由近到远时目标在视网膜上的形成过程。 尺度空间满足视觉不变性。该不变性的视觉解释如下:当我们用眼睛观察物体时,一方 面当物体所处背景的光照条件变化时, 视网膜感知图像的亮度水平和对比度是不同的, 因此 要求尺度空间算子对图像的分析不受图像的灰度水平和对比度变化的影响, 即满足灰度不变 性和对比度不变性。另一方面,相对于某一固定坐标系,当观察者和物体之间的相对位置变 化时,视网膜所感知的图像的位置、大小、角度和形状是不同的,因此要求尺度空间算子对 图像的分析和图像的位置、 大小、 角度以及仿射变换无关, 即满足平移不变性、 尺度不变性、 欧几里德不变性以及仿射不变性。
SIFT算法实现C语言
SIFT算法实现C语言SIFT算法(Scale-Invariant Feature Transform)是一种用于图像特征提取和匹配的计算机视觉算法。
它的主要思想是通过检测和描述图像中的关键点,提取出具有尺度不变性和旋转不变性的特征点,从而适应不同尺度和旋转的图像匹配。
本文将介绍如何用C语言实现SIFT算法。
1.数据结构首先,我们需要定义一些用于存储图像信息和特征点信息的数据结构。
以下是一个简单的数据结构定义:```ctypedef structint x;int y;float scale;float orientation;float descriptor[128];} Keypoint;typedef structunsigned char *data;int width;int height;} Image;```在这个数据结构中,Keypoint用于存储关键点的位置、尺度、方向和描述子,Image用于存储图像的像素数据、宽度和高度。
2.关键点检测关键点检测是SIFT算法的第一步。
我们可以使用一个简单的角点检测算法,如Harris角点检测,来找到图像中具有显著度较高的角点。
以下是一个简单的关键点检测函数的实现:```cvoid detectKeypoints(Image *input, Keypoint *output, int*numKeypoints)// Harris corner detection implementation//...```这个函数接受一个图像作为输入,并在图像上检测并输出关键点。
输出参数包括关键点的数组output和关键点的数量numKeypoints。
3.关键点描述关键点描述是SIFT算法的第二步。
对于每个关键点,我们需要计算一个描述子,用于描述关键点周围的图像区域。
以下是一个简单的关键点描述函数的实现:```c//...```这个函数接受一个图像和关键点数组作为输入,并为每个关键点计算描述子。
SIFT算法C语言逐步实现详解
SIFT算法C语言逐步实现详解(上)引言:在我写的关于sift算法的前倆篇文章里头,已经对sift算法有了初步的介绍:九、图像特征提取与匹配之SIFT算法,而后在:九(续)、sift算法的编译与实现里,我也简单记录下了如何利用opencv,gsl等库编译运行sift程序。
但据一朋友表示,是否能用c语言实现sift算法,同时,尽量不用到opencv,gsl等第三方库之类的东西。
而且,Rob Hess维护的sift 库,也不好懂,有的人根本搞不懂是怎么一回事。
那么本文,就教你如何利用c语言一步一步实现sift算法,同时,你也就能真正明白sift算法到底是怎么一回事了。
ok,先看一下,本程序最终运行的效果图,sift 算法分为五个步骤(下文详述),对应以下第二--第六幅图:sift算法的步骤要实现一个算法,首先要完全理解这个算法的原理或思想。
咱们先来简单了解下,什么叫sift算法:sift,尺度不变特征转换,是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由David Lowe 在1999年所发表,2004年完善总结。
所谓,Sift算法就是用不同尺度(标准差)的高斯函数对图像进行平滑,然后比较平滑后图像的差别,差别大的像素就是特征明显的点。
以下是sift算法的五个步骤:一、建立图像尺度空间(或高斯金字塔),并检测极值点首先建立尺度空间,要使得图像具有尺度空间不变形,就要建立尺度空间,sift算法采用了高斯函数来建立尺度空间,高斯函数公式为:上述公式G(x,y,e),即为尺度可变高斯函数。
而,一个图像的尺度空间L(x,y,e) ,定义为原始图像I(x,y)与上述的一个可变尺度的2维高斯函数G(x,y,e) 卷积运算。
即,原始影像I(x,y)在不同的尺度e下,与高斯函数G(x,y,e)进行卷积,得到L(x,y,e),如下:以上的(x,y)是空间坐标,e,是尺度坐标,或尺度空间因子,e的大小决定平滑程度,大尺度对应图像的概貌特征,小尺度对应图像的细节特征。
SIFT算法详解
SIFT算法详解SIFT(Scale-Invariant Feature Transform)算法是一种用于计算图像特征的算法,最早由David Lowe于1999年提出,是一种用于在不同尺度和旋转下具有不变性的特征描述算法。
1.关键点检测:SIFT算法首先对图像进行多尺度空间的检测,即在图像中寻找尺度空间极值点。
为了实现尺度不变性,SIFT算法采用了高斯金字塔的方法来对图像进行多尺度模糊处理。
金字塔的每一层可以通过对上一层图像进行下采样和卷积来生成。
然后,在每一层金字塔上使用一种拉普拉斯算子来计算尺度空间的极值点,这些极值点包括图像的边缘点、角点和斑点等。
极值点的检测是通过对每个像素点的周围像素点进行梯度计算和方向估计来实现的。
2.关键点描述:在关键点检测之后,SIFT算法通过计算每个关键点的局部图像梯度直方图来对关键点进行描述。
首先,将关键点周围的领域分为若干个子区域,一般为16个,每个子区域可以通过将关键点周围的区域划分为4×4个小区域来实现。
然后,对每个小区域中的像素点计算其梯度幅值和方向,并将其分到相应的方向直方图中。
通过对所有子区域的梯度方向直方图进行拼接,就可以得到整个关键点的描述子。
描述子的维度一般为128维,特征向量的每个维度包含了关键点的领域中的梯度方向信息。
3.特征匹配:在关键点描述之后,SIFT算法使用一种基于特征向量距离的匹配方法来实现图像特征点的匹配。
常用的方法是计算两个特征向量之间的欧式距离,并使用一个阈值来筛选出较为相似的特征点。
为了提高匹配的准确性和稳定性,SIFT算法使用了一种自适应的阈值筛选方法,通过计算两个特征向量距离的比值来判断特征点的匹配性。
总结来说,SIFT算法通过对图像进行多尺度的检测、关键点的描述和特征的匹配,实现了对图像特征的提取和匹配。
这种算法对于图像的尺度变化、旋转变换和部分遮挡等具有一定的不变性,因此被广泛应用于图像处理、目标识别和三维重建等领域。
c++调用opencv的sift算法
c++调用opencv的sift算法OpenCV是一个广泛使用的计算机视觉库,其中包含了许多经典的图像处理和特征提取算法,包括SIFT(尺度不变特征变换)算法。
SIFT算法是由David Lowe在1999年提出的一种用于图像特征提取的算法,它通过检测图像中的关键点并计算这些关键点的局部特征描述子来实现。
要在C++中调用OpenCV的SIFT算法,首先需要安装并配置好OpenCV 库。
然后,我们可以按照以下步骤使用SIFT算法:1. 导入必要的OpenCV头文件:```cpp#include <opencv2/opencv.hpp>```2. 读取输入图像:```cppcv::Mat image = cv::imread('input.jpg',cv::IMREAD_GRAYSCALE);```3. 创建SIFT对象:```cppcv::Ptr<cv::Feature2D> sift = cv::SIFT::create();```4. 检测关键点和计算特征描述子:```cppstd::vector<cv::KeyPoint> keypoints;cv::Mat descriptors;sift->detectAndCompute(image, cv::noArray(), keypoints, descriptors);```5. 可选地,可以使用以下代码绘制关键点:```cppcv::Mat output;cv::drawKeypoints(image, keypoints, output);cv::imshow('Keypoints', output);cv::waitKey(0);```在这个例子中,我们首先加载输入图像并将其转换为灰度图像。
然后,我们创建了一个SIFT对象,并调用其detectAndCompute函数来检测关键点和计算特征描述子。
特征提取(sift算法)
特征提取(sift算法) sift算法在cv领域的重要性不⾔⽽喻,该作者的⽂章引⽤率在cv界是number1.本篇博客只是本⼈把sift算法知识点整理了下,以免忘记。
本⽂⽐较早的⼀篇博⽂中有使⽤opencv⾃带的sift做了个简单的实验,⽽这次主要是利⽤Rob Hess的sift源码来做实验,其实现在的opencv版本中带的sift算法也是Rob Hess的,只是稍微包装了下。
⾸先⽹上有不少⽂章介绍了sift算法,写得都不错,⽐如:该博客对sift算法理论做了介绍,且有⽰意图辅助理解,从该⽂中可以了解sift算法的⼤概流程.这篇⽂章对sift算法做了通俗易懂的解释. 这篇博客有教你怎样⽤c语⾔⼀步⼀步写sift算法。
该⽂也对sift做了详细的介绍,博客的作者还对sift匹配做了讲解。
下⾯还是简单看下sift算法的理论,具体的内容可以参考上⾯的⼏篇⽂章。
⼀、Sift描述⼦形成的步骤 1、构造⾼斯差分空间图像。
Sift特征点的检测时在DOG图像上进⾏的,DOG图像是将相邻尺度空间图像相减得到的。
且⾦字塔的每⼀层都要构造⼀个DOG空间图像。
默认参数是⾦字塔4层,即4个octave,每⼀个octave中有5张不同尺度的图⽚,不同octave的图⽚尺⼨⼤⼩不同,所以每⼀层中就会得到4幅DOG图像。
⾼斯⾦字塔的第1层第1副原图像是将原图像放⼤2倍且sigma(sigma=1.6)模糊,第2幅图像是k*sigma(k等于根号2)模糊,第3幅是k*k*sigma模糊,后⾯类推…⾼斯⾦字塔第2层第1幅图是选择⾦字塔上⼀层(这⾥是第1层)中尺度空间参数为k*k*sigma的那幅图(实际上是2倍的尺度空间)进⾏降采样(尺⼨⼤⼩为原来的1/4倍)得到,如果k不等于根号2,那么取原图的2*sigma降采样得到。
第2层第2幅图是在本层第⼀幅图尺度模糊系数增加k倍模糊后的图像,后⾯类似… ⽰意图如下所⽰:尺度不变当然是与图⽚尺⼨有关,即图⽚的尺⼨⼤⼩变化,但是其检测结果不变。
SIFT算法详解及应用
SIFT算法详解及应用SIFT(Scale-Invariant Feature Transform)是一种用于图像处理和计算机视觉中的特征提取算法。
它的主要目标是提取具有尺度和旋转不变性的局部特征点。
SIFT算法的独特之处在于它不依赖于特定的图像属性,而是通过一系列处理步骤构建出具有稳定性和描述性的特征点。
1. 尺度空间极值检测(Scale Space Extrema Detection):通过在不同的尺度上使用高斯差分函数,找到图像中的极值点作为潜在特征点。
2. 关键点定位(Keypoint Localization):在尺度空间中找到极值点后,使用插值方法精确定位特征点的位置。
同时,通过计算Hessian矩阵的主曲率来排除边缘响应。
3. 方向分配(Orientation Assignment):为每个特征点分配一个主要的方向,使得后续的特征描述能够具有旋转不变性。
4. 特征描述(Feature Description):根据每个特征点的主方向,构建特征描述子。
描述子被构建为一个128维的向量,它具有对尺度、旋转和光照变化的不变性。
5. 特征匹配(Feature Matching):通过比较特征描述子,找到两幅图像中具有相似特征的匹配点。
常用的方法是计算特征向量之间的欧式距离或相似性度量。
1.目标识别:SIFT算法可以检测并描述图像中的关键点,通过与预先训练好的模板特征进行匹配,可以在输入图像中快速准确地定位和识别目标物体。
2.图像拼接:SIFT算法可以提取图像中的特征点,并通过对这些特征点进行匹配来确定它们之间的对应关系。
这样,可以将多张图像拼接在一起,生成一个大的全景图像。
3.目标跟踪:SIFT算法可以提取图像中的关键点,并构建其特征描述子。
通过与之前的图像帧进行匹配,可以实现目标的跟踪和定位。
4.三维重建:使用多个图像拍摄同一场景,并通过SIFT算法提取特征点并进行匹配,可以推断出相机的位置和场景的结构,从而实现三维重建。
SIFT算法详解及应用(课件)
LOG ( x,
y,
)
22G
Gauss(x,
y, k ) Gauss(x, 2 (k 1)
• 目标遮挡(occlusion)
• 杂物场景(clutter)
• 噪声
2019/11/26
Back
7
SIFT算法实现细节
SIFT算法实现步骤简述
SIFT算法的实质可以归为在不同尺度空间上查找特征点(关键点)的问题。
原图像 目标图像
特征点 检测
特征点 描述
SIFT
特征点 检测
特征点 描述
SIFT
8
SIFT算法实现细节
SIFT算法实现步骤
1. 关键点检测 2. 关键点描述 3. 关键点匹配 4. 消除错配点
2019/11/26
9
关键点检测的相关概念
1. 哪些点是SIFT中要查找的关键点(特征点)?
这些点是一些十分突出的点不会因光照条件的改变而消失,比如角点、 边缘点、暗区域的亮点以及亮区域的暗点,既然两幅图像中有相同的景物, 那么使用某种方法分别提取各自的稳定点,这些点之间会有相互对应的匹配 点。
• 对一幅图像进行多次连续高斯模糊的效果与一次更大的高斯模糊可以 产生同样的效果,大的高斯模糊的半径是所用多个高斯模糊半径平方 和的平方根。例如,使用半径分别为 6 和 8 的两次高斯模糊变换得
到的效果等同于一次半径为 10 的高斯模糊效果, 62 82 10
根据这个关系,使用多个连续较小的高斯模糊处理不会比单个高斯较 大处理时间要少。
尺度不变特征变换匹配算法 Scale Invariant Feature Transform (SIFT)
宋丹
SIFT算法实现原理步骤
SIFT 算法实现步骤 :1 关键点检测、2 关键点描述、3 关键点匹配、4 消除错配点1关键点检测1.1 建立尺度空间根据文献《Scale-space theory: A basic tool for analysing structures at different scales 》我们可知,高斯核是唯一可以产生多尺度空间的核,一个图像的尺度空间,L (x,y,σ) ,定义为原始图像I(x,y)与一个可变尺度的2维高斯函数G(x,y,σ) 卷积运算。
高斯函数高斯金字塔高斯金子塔的构建过程可分为两步: (1)对图像做高斯平滑;(2)对图像做降采样。
为了让尺度体现其连续性,在简单 下采样的基础上加上了高斯滤波。
一幅图像可以产生几组(octave ) 图像,一组图像包括几层 (interval )图像。
高斯图像金字塔共o 组、s 层,则有:σ——尺度空间坐标;s ——sub-level 层坐标;σ0——初始尺度;S ——每组层数(一般为3~5)。
当图像通过相机拍摄时,相机的镜头已经对图像进行了一次初始的模糊,所以根据高斯模糊的性质:-第0层尺度 --被相机镜头模糊后的尺度高斯金字塔的组数: M 、N 分别为图像的行数和列数高斯金字塔的组尺度与组间尺度:组尺度是指同一组(octave )的尺度关系,组相邻层尺度化简为:组间尺度是指不同组直接的尺度关系,相邻组的尺度可化为:最后可将组和组间尺度归为:()22221()(),,exp 22i i i i x x y y G x y σπσσ⎛⎫-+-=- ⎪⎝⎭()()(),,,,*,L x y G x y I x y σσ=Octave 1Octave 2Octave 3Octave 4Octave 5σ2σ4σ8σ0()2s S s σσ=0σ=init σpre σ()()2log min ,3O M N ⎡⎤=-⎣⎦112S s s σσ+=1()2s SSo o s σσ++=222s S sSS o o σσ+=i —金字塔组数 n —每组层数上一组图像的底层是由前一组图像的倒数第二层图像隔点采样生成的。
SIFT算法原理SIFT算法详细介绍
SIFT算法原理SIFT算法详细介绍SIFT(Scale-Invariant Feature Transform)算法是一种用于图像特征提取和匹配的算法,被广泛应用于计算机视觉领域。
SIFT算法的核心思想是在不同尺度空间中检测和描述图像的局部特征,通过这些特征可实现图像的匹配、识别和定位。
1.尺度空间极值点检测:首先,SIFT算法通过高斯平滑滤波器构建高斯金字塔,每一层都是由上一层进行降采样得到的。
在不同尺度空间的图像上,通过比较每个像素点与其相邻像素点的灰度值,找出尺度空间极值点,作为关键点候选。
2.关键点定位:通过对尺度空间的极值点进行精确定位,以获取更加准确的关键点。
在关键点候选集合中,利用尺度空间的极值点与梯度方向直方图等局部特征进行过滤,剔除低对比度和边缘响应的关键点。
3.方向分配:为了使SIFT算法具有旋转不变性,需要为每个关键点分配主方向。
采用梯度直方图对关键点周围的图像区域进行统计,找到梯度方向直方图的峰值,作为该关键点的主方向。
4.关键点描述:在每个关键点周围的图像区域内,构建128维的特征向量,用于描述该关键点的局部特征。
通过计算每个像素点周围的梯度幅值和方向,并将其转换为特征向量的模式,构成关键点的描述符。
5.特征匹配:对于待匹配的图像,通过计算两个图像的特征向量之间的欧氏距离,进行特征匹配。
采用距离比值方法,选取最匹配和次匹配之间的距离比值小于预先设定的阈值的特征点,进行最终匹配。
6.特征筛选:通过应用RANSAC(随机抽样一致性)算法,对匹配结果进行筛选,剔除外点,保留内点,从而得到更准确的匹配结果。
SIFT算法的应用非常广泛,常用于目标识别、图像拼接、图像检索、三维重建和运动估计等领域。
在目标识别中,通过提取图像的SIFT特征,并与模板图像进行匹配,可以实现目标的识别和定位。
在图像拼接中,通过寻找多幅图像之间的共同特征点,并根据特征点的位置关系进行配准,可以实现图像的自动拼接。
(完整版)SIFT算法原理.doc
3.1.1 尺度空间极值检测尺度空间理论最早出现于计算机视觉领域,当时其目的是模拟图像数据的多尺度特征。
随后 Koendetink利用扩散方程来描述尺度空间滤波过程,并由此证明高斯核是实现尺度变换的唯一变换核。
Lindeberg, Babaud 等人通过不同的推导进一步证明高斯核是唯一的线性核。
因此,尺度空间理论的主要思想是利用高斯核对原始图像进行尺度变换,获得图像多尺度下的尺度空间表示序列,对这些序列进行尺度空间特征提取。
二维高斯函数定义如下:G ( x, y, ) 1 2e( x2y2)/2 2 ( 5)2一幅二维图像,在不同尺度下的尺度空间表示可由图像与高斯核卷积得到:L( x, y, G( x, y, )* I ( x, y) ( 6)其中 (x,y)为图像点的像素坐标, I(x,y)为图像数据 , L 代表了图像的尺度空间。
σ称为尺度空间因子,它也是高斯正态分布的方差,其反映了图像被平滑的程度,其值越小表征图像被平滑程度越小,相应尺度越小。
大尺度对应于图像的概貌特征,小尺度对应于图像的细节特征。
因此,选择合适的尺度因子平滑是建立尺度空间的关键。
在这一步里面,主要是建立高斯金字塔和DOG(Difference of Gaussian) 金字塔,然后在DOG 金字塔里面进行极值检测,以初步确定特征点的位置和所在尺度。
(1)建立高斯金字塔为了得到在不同尺度空间下的稳定特征点,将图像I (x, y) 与不同尺度因子下的高斯核G ( x, y,) 进行卷积操作,构成高斯金字塔。
高斯金字塔有o 阶,一般选择 4 阶,每一阶有s 层尺度图像,s 一般选择 5 层。
在高斯金字塔的构成中要注意,第 1 阶的第l 层是放大 2 倍的原始图像,其目的是为了得到更多的特征点;在同一阶中相邻两层的尺度因子比例系数是k ,则第 1 阶第 2 层的尺度因子是k ,然后其它层以此类推则可;第 2 阶的第l 层由第一阶的中间层尺度图像进行子抽样获得,其尺度因子是k 2 ,然后第 2 阶的第 2 层的尺度因子是第 1 层的k 倍即k 3 。
SIFT算法原理SIFT算法详细介绍
SIFT算法原理SIFT算法详细介绍SIFT(Scale-Invariant Feature Transform)是一种用于计算机视觉领域的算法,用于检测和描述图像中的局部特征。
它被广泛应用于目标识别、图像匹配和三维重建等任务中。
SIFT算法通过提取图像的稳定特征点来实现图像的尺度和旋转不变性。
下面详细介绍SIFT算法的原理和步骤。
1.尺度空间极值检测:SIFT算法首先在不同的尺度空间中通过高斯差分金字塔(Difference of Gaussian,DoG)寻找稳定的特征点。
通过对输入图像进行高斯滤波,得到一系列不同尺度的图像。
然后,通过对相邻的两个不同尺度的图像进行差分操作,得到高斯差分图像,即DoG金字塔。
接着,在DoG金字塔中寻找局部极值点,即该点的像素值在其周围的3×3×3邻域内最大或最小。
2.生成关键点:在尺度空间极值点检测后,通过插值计算亚像素精度的关键点位置,以获得更精确的特征点位置。
对比邻域像素的梯度幅值和方向,重新定位关键点位置。
3.消除边缘响应:排除低对比度的稳定特征点和位于边缘的特征点,以提高匹配的准确性。
通过计算Hessian矩阵的迹和行列式来判断是否为边缘响应。
4.计算主方向:为了使SIFT算法对旋转具有不变性,对每个关键点计算该点的主方向。
在关键点周围的邻域内,计算梯度幅值和方向直方图,选取主方向作为该特征点的方向描述符。
5.生成特征描述子:在关键点检测和主方向计算后,利用关键点附近的图像区域创建描述子。
以关键点为中心,将图像区域分为若干个子区域,并在每个子区域内计算局部特征。
对每个子区域,计算梯度幅值和方向直方图,形成一个向量。
最后将这些向量串联形成一个特征向量,作为该特征点的描述子。
6.特征点匹配:使用描述子来匹配不同图像中的特征点。
通过计算两个特征点描述子之间的距离来判断它们的相似性。
通常使用欧氏距离或余弦相似度来度量特征点之间的差异。
然后,根据距离进行特征点匹配,通过选取最佳匹配对的阈值来过滤不准确的匹配。
基于C的图像识别算法设计与调优
基于C的图像识别算法设计与调优图像识别技术是人工智能领域中的重要应用之一,随着深度学习技术的发展,基于C语言的图像识别算法设计与调优变得越来越重要。
本文将介绍基于C语言的图像识别算法设计与调优的相关内容,包括算法原理、实现步骤、调优方法等。
一、算法原理图像识别算法的核心是对图像进行特征提取和分类识别。
在基于C语言的图像识别算法设计中,常用的算法包括SIFT、SURF、HOG等。
这些算法通过提取图像的局部特征,然后通过机器学习模型进行分类识别。
SIFT(尺度不变特征变换)算法是一种基于局部特征的图像匹配算法,它通过检测图像中的关键点,并计算这些关键点周围的局部特征描述子来实现图像匹配和目标识别。
SURF(加速稳健特征)算法是SIFT算法的改进版本,它采用了一种快速计算特征点的方法,并且在保持精度的同时提高了计算速度。
HOG(方向梯度直方图)算法是一种用于目标检测和图像分类的特征描述方法,它通过计算图像中每个像素点的梯度方向和大小来描述图像的特征。
二、实现步骤基于C语言的图像识别算法设计通常包括以下几个步骤:图像预处理:包括图像去噪、灰度化、边缘检测等操作,以便提取更准确的特征。
特征提取:使用SIFT、SURF、HOG等算法提取图像的局部特征,并生成特征描述子。
特征匹配:将待识别图像的特征描述子与数据库中已知类别的特征描述子进行匹配,找到最相似的类别。
分类识别:根据匹配结果对待识别图像进行分类识别,并输出识别结果。
三、调优方法为了提高基于C语言的图像识别算法的准确率和效率,可以采用以下调优方法:参数调优:对于SIFT、SURF等算法,可以调整参数以获得更好的性能表现,如改变尺度空间、阈值设置等。
特征选择:在特征提取阶段,可以选择更具代表性和区分性的特征进行提取,避免冗余信息对分类结果造成干扰。
模型优化:可以采用深度学习技术对传统机器学习模型进行优化,如使用卷积神经网络(CNN)等模型提高准确率。
并行计算:利用多线程或并行计算技术加速图像处理和特征提取过程,提高系统响应速度。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
SIFT算法C语言逐步实现详解(上)引言:在我写的关于sift算法的前倆篇文章里头,已经对sift算法有了初步的介绍:九、图像特征提取与匹配之SIFT算法,而后在:九(续)、sift算法的编译与实现里,我也简单记录下了如何利用opencv,gsl等库编译运行sift程序。
但据一朋友表示,是否能用c语言实现sift算法,同时,尽量不用到opencv,gsl等第三方库之类的东西。
而且,Rob Hess维护的sift 库,也不好懂,有的人根本搞不懂是怎么一回事。
那么本文,就教你如何利用c语言一步一步实现sift算法,同时,你也就能真正明白sift算法到底是怎么一回事了。
ok,先看一下,本程序最终运行的效果图,sift 算法分为五个步骤(下文详述),对应以下第二--第六幅图:sift算法的步骤要实现一个算法,首先要完全理解这个算法的原理或思想。
咱们先来简单了解下,什么叫sift算法:sift,尺度不变特征转换,是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由David Lowe 在1999年所发表,2004年完善总结。
所谓,Sift算法就是用不同尺度(标准差)的高斯函数对图像进行平滑,然后比较平滑后图像的差别,差别大的像素就是特征明显的点。
以下是sift算法的五个步骤:一、建立图像尺度空间(或高斯金字塔),并检测极值点首先建立尺度空间,要使得图像具有尺度空间不变形,就要建立尺度空间,sift算法采用了高斯函数来建立尺度空间,高斯函数公式为:上述公式G(x,y,e),即为尺度可变高斯函数。
而,一个图像的尺度空间L(x,y,e) ,定义为原始图像I(x,y)与上述的一个可变尺度的2维高斯函数G(x,y,e) 卷积运算。
即,原始影像I(x,y)在不同的尺度e下,与高斯函数G(x,y,e)进行卷积,得到L(x,y,e),如下:以上的(x,y)是空间坐标,e,是尺度坐标,或尺度空间因子,e的大小决定平滑程度,大尺度对应图像的概貌特征,小尺度对应图像的细节特征。
大的e值对应粗糙尺度(低分辨率),反之,对应精细尺度(高分辨率)。
尺度,受e这个参数控制的表示。
而不同的L(x,y,e)就构成了尺度空间,具体计算的时候,即使连续的高斯函数,都被离散为(一般为奇数大小)(2*k+1) *(2*k+1)矩阵,来和数字图像进行卷积运算。
随着e的变化,建立起不同的尺度空间,或称之为建立起图像的高斯金字塔。
但,像上述L(x,y,e) = G(x,y,e)*I(x,y)的操作,在进行高斯卷积时,整个图像就要遍历所有的像素进行卷积(边界点除外),于此,就造成了时间和空间上的很大浪费。
为了更有效的在尺度空间检测到稳定的关键点,也为了缩小时间和空间复杂度,对上述的操作作了一个改建:即,提出了高斯差分尺度空间(DOG scale-space)。
利用不同尺度的高斯差分与原始图像I(x,y)相乘,卷积生成。
DOG算子计算简单,是尺度归一化的LOG算子的近似。
ok,耐心点,咱们再来总结一下上述内容:1、高斯卷积在组建一组尺度空间后,再组建下一组尺度空间,对上一组尺度空间的最后一幅图像进行二分之一采样,得到下一组尺度空间的第一幅图像,然后进行像建立第一组尺度空间那样的操作,得到第二组尺度空间,公式定义为L(x,y,e) = G(x,y,e)*I(x,y)图像金字塔的构建:图像金字塔共O组,每组有S层,下一组的图像由上一组图像降采样得到,效果图,图A如下(左为上一组,右为下一组):2、高斯差分在尺度空间建立完毕后,为了能够找到稳定的关键点,采用高斯差分的方法来检测那些在局部位置的极值点,即采用俩个相邻的尺度中的图像相减,即公式定义为:D(x,y,e) = ((G(x,y,ke) - G(x,y,e)) * I(x,y)= L(x,y,ke) - L(x,y,e)效果图,图B:SIFT的精妙之处在于采用图像金字塔的方法解决这一问题,我们可以把两幅图像想象成是连续的,分别以它们作为底面作四棱锥,就像金字塔,那么每一个截面与原图像相似,那么两个金字塔中必然会有包含大小一致的物体的无穷个截面,但应用只能是离散的,所以我们只能构造有限层,层数越多当然越好,但处理时间会相应增加,层数太少不行,因为向下采样的截面中可能找不到尺寸大小一致的两个物体的图像。
咱们再来具体阐述下构造D(x,y,e)的详细步骤:1、首先采用不同尺度因子的高斯核对图像进行卷积以得到图像的不同尺度空间,将这一组图像作为金子塔图像的第一层。
2、接着对第一层图像中的2倍尺度图像(相对于该层第一幅图像的2倍尺度)以2倍像素距离进行下采样来得到金子塔图像的第二层中的第一幅图像,对该图像采用不同尺度因子的高斯核进行卷积,以获得金字塔图像中第二层的一组图像。
3、再以金字塔图像中第二层中的2倍尺度图像(相对于该层第一幅图像的2倍尺度)以2倍像素距离进行下采样来得到金字塔图像的第三层中的第一幅图像,对该图像采用不同尺度因子的高斯核进行卷积,以获得金字塔图像中第三层的一组图像。
这样依次类推,从而获得了金字塔图像的每一层中的一组图像,如下图所示:4、对上图得到的每一层相邻的高斯图像相减,就得到了高斯差分图像,如下述第一幅图所示。
下述第二幅图中的右列显示了将每组中相邻图像相减所生成的高斯差分图像的结果,限于篇幅,图中只给出了第一层和第二层高斯差分图像的计算(下述俩幅图统称为图2):图像金字塔的建立:对于一幅图像I,建立其在不同尺度(scale)的图像,也成为子八度(octave),这是为了scale-invariant,也就是在任何尺度都能够有对应的特征点,第一个子八度的scale为原图大小,后面每个octave为上一个octave降采样的结果,即原图的1/4(长宽分别减半),构成下一个子八度(高一层金字塔)。
5、因为高斯差分函数是归一化的高斯拉普拉斯函数的近似,所以可以从高斯差分金字塔分层结构提取出图像中的极值点作为候选的特征点。
对DOG 尺度空间每个点与相邻尺度和相邻位置的点逐个进行比较,得到的局部极值位置即为特征点所处的位置和对应的尺度。
二、检测关键点为了寻找尺度空间的极值点,每一个采样点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。
如下图,图3所示,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。
因为需要同相邻尺度进行比较,所以在一组高斯差分图像中只能检测到两个尺度的极值点(如上述第二幅图中右图的五角星标识),而其它尺度的极值点检测则需要在图像金字塔的上一层高斯差分图像中进行。
依次类推,最终在图像金字塔中不同层的高斯差分图像中完成不同尺度极值的检测。
当然这样产生的极值点并不都是稳定的特征点,因为某些极值点响应较弱,而且DOG算子会产生较强的边缘响应。
三、关键点方向的分配为了使描述符具有旋转不变性,需要利用图像的局部特征为给每一个关键点分配一个方向。
利用关键点邻域像素的梯度及方向分布的特性,可以得到梯度模值和方向如下:其中,尺度为每个关键点各自所在的尺度。
在以关键点为中心的邻域窗口内采样,并用直方图统计邻域像素的梯度方向。
梯度直方图的范围是0~360度,其中每10度一个方向,总共36个方向。
直方图的峰值则代表了该关键点处邻域梯度的主方向,即作为该关键点的方向。
在计算方向直方图时,需要用一个参数等于关键点所在尺度1.5倍的高斯权重窗对方向直方图进行加权,上图中用蓝色的圆形表示,中心处的蓝色较重,表示权值最大,边缘处颜色潜,表示权值小。
如下图所示,该示例中为了简化给出了8方向的方向直方图计算结果,实际sift创始人David Lowe的原论文中采用36方向的直方图。
方向直方图的峰值则代表了该特征点处邻域梯度的方向,以直方图中最大值作为该关键点的主方向。
为了增强匹配的鲁棒性,只保留峰值大于主方向峰值80%的方向作为该关键点的辅方向。
因此,对于同一梯度值的多个峰值的关键点位置,在相同位置和尺度将会有多个关键点被创建但方向不同。
仅有15%的关键点被赋予多个方向,但可以明显的提高关键点匹配的稳定性。
至此,图像的关键点已检测完毕,每个关键点有三个信息:位置、所处尺度、方向。
由此可以确定一个SIFT特征区域。
四、特征点描述符通过以上步骤,对于每一个关键点,拥有三个信息:位置、尺度以及方向。
接下来就是为每个关键点建立一个描述符,使其不随各种变化而改变,比如光照变化、视角变化等等。
并且描述符应该有较高的独特性,以便于提高特征点正确匹配的概率。
首先将坐标轴旋转为关键点的方向,以确保旋转不变性。
接下来以关键点为中心取8×8的窗口。
上图,图5中左部分的中央黑点为当前关键点的位置,每个小格代表关键点邻域所在尺度空间的一个像素,箭头方向代表该像素的梯度方向,箭头长度代表梯度模值,图中蓝色的圈代表高斯加权的范围(越靠近关键点的像素梯度方向信息贡献越大)。
然后在每4×4的小块上计算8个方向的梯度方向直方图,绘制每个梯度方向的累加值,即可形成一个种子点,如图5右部分所示。
此图中一个关键点由2×2共4个种子点组成,每个种子点有8个方向向量信息。
这种邻域方向性信息联合的思想增强了算法抗噪声的能力,同时对于含有定位误差的特征匹配也提供了较好的容错性。
实际计算过程中,为了增强匹配的稳健性,Lowe建议对每个关键点使用4×4共16个种子点来描述,这样对于一个关键点就可以产生128个数据,即最终形成128维的SIFT特征向量。
此时SIFT特征向量已经去除了尺度变化、旋转等几何变形因素的影响,再继续将特征向量的长度归一化,则可以进一步去除光照变化的影响。
五、最后一步:当两幅图像的SIFT特征向量生成后,下一步我们采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。
取上图中,图像A中的某个关键点,并找出其与图像B中欧式距离最近的前两个关键点,在这两个关键点中,如果最近的距离除以次近的距离少于某个比例阈值,则接受这一对匹配点。
降低这个比例阈值,SIFT匹配点数目会减少,但更加稳定。
关于sift 算法的更多理论介绍请参看此文:/abcjennifer/article/details/7639681。
sift算法的逐步c实现ok,上文搅了那么多的理论,如果你没有看懂它,咋办列?没关系,下面,咱们来一步一步实现此sift算法,即使你没有看到上述的理论,慢慢的,你也会明白sift算法到底是怎么一回事,sift算法到底是怎么实现的...。
yeah,请看:前期工作:在具体编写核心函数之前,得先做几个前期的准备工作:0、头文件:1.#ifdef _CH_2.#pragma package <opencv>3.#endif4.5.#ifndef _EiC6.#include <stdio.h>7.8.#include "stdlib.h"9.#include "string.h"10.#include "malloc.h"11.#include "math.h"12.#include <assert.h>13.#include <ctype.h>14.#include <time.h>15.#include <cv.h>16.#include <cxcore.h>17.#include <highgui.h>18.#include <vector>19.#endif20.21.#ifdef _EiC22.#define WIN3223.#endif[cpp]view plaincopy1.#ifdef _CH_2.#pragma package <opencv>3.#endif4.5.#ifndef _EiC6.#include <stdio.h>7.8.#include "stdlib.h"9.#include "string.h"10.#include "malloc.h"11.#include "math.h"12.#include <assert.h>13.#include <ctype.h>14.#include <time.h>15.#include <cv.h>16.#include <cxcore.h>17.#include <highgui.h>18.#include <vector>19.#endif20.21.#ifdef _EiC22.#define WIN3223.#endif1、定义几个宏,及变量,以免下文函数中,突然冒出一个变量,而您却不知道怎么一回事:[cpp]view plaincopy1.#define NUMSIZE 22.#define GAUSSKERN3.53.#define PI 3.141592653589793238464.5.//Sigma of base image -- See D.L.'s paper.6.#define INITSIGMA 0.57.//Sigma of each octave -- See D.L.'s paper.8.#define SIGMA sqrt(3)//1.6//9.10.//Number of scales per octave. See D.L.'s paper.11.#define SCALESPEROCTAVE 212.#define MAXOCTAVES 413.int numoctaves;14.15.#define CONTRAST_THRESHOLD 0.0216.#define CURVATURE_THRESHOLD 10.017.#define DOUBLE_BASE_IMAGE_SIZE 118.#define peakRelThresh 0.819.#define LEN 12820.21.// temporary storage22.CvMemStorage* storage = 0;[cpp]view plaincopy1.#define NUMSIZE 22.#define GAUSSKERN3.53.#define PI 3.141592653589793238464.5.//Sigma of base image -- See D.L.'s paper.6.#define INITSIGMA 0.57.//Sigma of each octave -- See D.L.'s paper.8.#define SIGMA sqrt(3)//1.6//9.10.//Number of scales per octave. See D.L.'s paper.11.#define SCALESPEROCTAVE 212.#define MAXOCTAVES 413.int numoctaves;14.15.#define CONTRAST_THRESHOLD 0.0216.#define CURVATURE_THRESHOLD 10.017.#define DOUBLE_BASE_IMAGE_SIZE 118.#define peakRelThresh 0.819.#define LEN 12820.21.// temporary storage22.CvMemStorage* storage = 0;2、然后,咱们还得,声明几个变量,以及建几个数据结构(数据结构是一切程序事物的基础麻。