www.pudn.com > SIFTPRO.rar > SIFTPRO.cpp


#ifdef _CH_ 
#pragma package  
#endif 
 
#ifndef _EiC 
#include  
 
#include "stdlib.h" 
#include "string.h"  
#include "malloc.h"  
#include "math.h"  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#endif 
 
#ifdef _EiC 
#define WIN32 
#endif 
 
#define NUMSIZE 2 
#define GAUSSKERN 3.5 
#define PI 3.14159265358979323846 
/** 
   Sigma of base image -- See D.L.'s paper. 
*/ 
#define INITSIGMA 0.5 
/** 
   Sigma of each octave -- See D.L.'s paper. 
*/ 
#define SIGMA sqrt(3)//1.6// 
/** 
   Number of scales per octave.  See D.L.'s paper. 
*/ 
#define SCALESPEROCTAVE 2 
#define MAXOCTAVES 4 
int     numoctaves; 
/** 
   Double image size before looking searching for keypoints 
   Doubling finds more keypoints, but takes longer. See 
   D.L.'s paper. 
*/ 
#define CONTRAST_THRESHOLD   0.02 
#define CURVATURE_THRESHOLD  10.0 
#define DOUBLE_BASE_IMAGE_SIZE 1 
#define peakRelThresh 0.8 
#define LEN 128 
 
CvMemStorage* storage = 0; // temporary storage 
//Gaussian金字塔感觉用二维指针较简单 
/* Data structure for a float image. 
*/ 
typedef struct ImageSt {        /*金字塔每一层*/ 
   
  float levelsigma; 
  int levelsigmalength; 
  float absolute_sigma; 
  CvMat *Level;               
} ImageLevels; 
typedef struct ImageSt1 {      /*金字塔每一阶梯*/ 
  int row, col;          /* Dimensions of image. */ 
  float subsample; 
  ImageLevels *Octave;               
} ImageOctaves; 
 
ImageOctaves *DOGoctaves;        //DOG pyr 
ImageOctaves *mag_thresh ; 
ImageOctaves *mag_pyr ; 
ImageOctaves *grad_pyr ; 
/* Data structure for a keypoint.  Lists of keypoints are linked 
   by the "next" field.*/ 
typedef struct KeypointSt  
{ 
  float row, col; /* 反馈回原图像大小,特征点的位置 */ 
  float sx,sy;    /* 金字塔中特征点的位置*/ 
  int octave,level;/*金字塔中,特征点所在的阶梯、层次*/ 
 
  float scale, ori,mag; /*所在层的尺度sigma,主方向orientation (range [-PI,PI]),以及幅值*/ 
  float *descrip;       /*特征描述字指针:128维或32维等*/ 
  struct KeypointSt *next;/* Pointer to next keypoint in list. */ 
} *Keypoint; 
//定义特征点具体变量 
Keypoint keypoints=NULL;      //用于临时存储特征点的位置等 
Keypoint keyDescriptors=NULL; //用于最后的确定特征点以及特征描述字 
//图像处理基本函数,其实其实可以用OPENCV的函数代替 
//void NormalizeImage(CvMat *src); 
CvMat * halfSizeImage(CvMat * im);     //缩小图像:下采样 
CvMat * doubleSizeImage(CvMat * im);   //扩大图像:最近临方法 
CvMat * doubleSizeImage2(CvMat * im);  //扩大图像:线性插值 
float getPixelBI(CvMat * im, float col, float row);//双线性插值函数 
void normalizeVec(float* vec, int dim);//向量归一化   
CvMat* GaussianKernel2D(float sigma);  //得到2维高斯核 
void normalizeMat(CvMat* mat) ;        //矩阵归一化 
float* GaussianKernel1D(float sigma, int dim) ; //得到1维高斯核 
float ConvolveLocWidth(float* kernel, int dim, CvMat * src, int x, int y) ;  //在具体像素处宽度方向进行高斯卷积 
void Convolve1DWidth(float* kern, int dim, CvMat * src, CvMat * dst) ;       //在整个图像宽度方向进行1D高斯卷积 
float ConvolveLocHeight(float* kernel, int dim, CvMat * src, int x, int y) ; //在具体像素处高度方向进行高斯卷积 
void Convolve1DHeight(float* kern, int dim, CvMat * src, CvMat * dst);       //在整个图像高度方向进行1D高斯卷积 
int BlurImage(CvMat * src, CvMat * dst, float sigma) ;                      //用高斯函数模糊图像 
//以下为算法核心部分 
//SIFT算法第一步:图像预处理 
CvMat *ScaleInitImage(CvMat * im) ;                  //金字塔初始化 
//SIFT算法第二步:建立高斯金字塔函数 
ImageOctaves* BuildGaussianOctaves(CvMat * image) ;  //建立高斯金字塔 
//SIFT算法第三步:特征点位置检测,最后确定特征点的位置 
int DetectKeypoint(int numoctaves, ImageOctaves *GaussianPyr); 
void DisplayKeypointLocation(IplImage* image, ImageOctaves *GaussianPyr); 
//SIFT算法第四步:计算高斯图像的梯度方向和幅值,计算各个特征点的主方向 
void ComputeGrad_DirecandMag(int numoctaves, ImageOctaves *GaussianPyr); 
int FindClosestRotationBin (int binCount, float angle);  //进行方向直方图统计 
void AverageWeakBins (double* bins, int binCount);       //对方向直方图滤波 
//确定真正的主方向 
bool InterpolateOrientation (double left, double middle,double right, double *degreeCorrection, double *peakValue); 
//确定各个特征点处的主方向函数 
void AssignTheMainOrientation(int numoctaves, ImageOctaves *GaussianPyr,ImageOctaves *mag_pyr,ImageOctaves *grad_pyr); 
//显示主方向 
void DisplayOrientation (IplImage* image, ImageOctaves *GaussianPyr); 
//SIFT算法第五步:抽取各个特征点处的特征描述字 
void ExtractFeatureDescriptors(int numoctaves, ImageOctaves *GaussianPyr); 
 
//为了显示图象金字塔,而作的图像水平垂直拼接 
CvMat* MosaicHorizen( CvMat* im1, CvMat* im2 ); 
CvMat* MosaicVertical( CvMat* im1, CvMat* im2 ); 
 
// The feature grid is has 4x4 cells - feat_grid describes the cell center positions    
#define GridSpacing 4 
 
int main( void ) 
{ 
  //声明当前帧IplImage指针 
  IplImage* src = NULL;  
  IplImage* image1 = NULL;  
  IplImage* grey_im1 = NULL;  
  IplImage* DoubleSizeImage = NULL; 
   
  IplImage* mosaic1 = NULL;  
  IplImage* mosaic2 = NULL;  
 
  CvMat* mosaicHorizen1 = NULL; 
  CvMat* mosaicHorizen2 = NULL; 
  CvMat* mosaicVertical1 = NULL; 
 
  CvMat* image1Mat = NULL; 
  CvMat* tempMat=NULL; 
 
  ImageOctaves *Gaussianpyr; 
  int rows,cols; 
  
  #define Im1Mat(ROW,COL) ((float *)(image1Mat->data.fl + image1Mat->step/sizeof(float) *(ROW)))[(COL)] 
   
  //灰度图象像素的数据结构 
  #define Im1B(ROW,COL) ((uchar*)(image1->imageData + image1->widthStep*(ROW)))[(COL)*3] 
  #define Im1G(ROW,COL) ((uchar*)(image1->imageData + image1->widthStep*(ROW)))[(COL)*3+1] 
  #define Im1R(ROW,COL) ((uchar*)(image1->imageData + image1->widthStep*(ROW)))[(COL)*3+2] 
   
  storage = cvCreateMemStorage(0);  
   
  //读取图片 
  if( (src = cvLoadImage( "einstein.pgm", 1)) == 0 )  // test1.jpg einstein.pgm back1.bmp 
	return -1; 
  //为图像分配内存 
  image1 = cvCreateImage(cvSize(src->width, src->height),  IPL_DEPTH_8U,3); 
  grey_im1 = cvCreateImage(cvSize(src->width, src->height),  IPL_DEPTH_8U,1); 
  DoubleSizeImage = cvCreateImage(cvSize(2*(src->width), 2*(src->height)),  IPL_DEPTH_8U,3); 
  //为图像阵列分配内存,假设两幅图像的大小相同,tempMat跟随image1的大小 
  image1Mat = cvCreateMat(src->height, src->width, CV_32FC1); 
  //转化成单通道图像再处理 
  cvCvtColor(src, grey_im1, CV_BGR2GRAY); 
  //转换进入Mat数据结构,图像操作使用的是浮点型操作 
  cvConvert(grey_im1, image1Mat); 
   
  double t = (double)cvGetTickCount(); 
  //图像归一化 
  cvConvertScale( image1Mat, image1Mat, 1.0/255, 0 ); 
 
  int dim = min(image1Mat->rows, image1Mat->cols); 
  numoctaves = (int) (log((double) dim) / log(2.0)) - 2;    //金字塔阶数 
  numoctaves = min(numoctaves, MAXOCTAVES); 
 
  //第一步,预滤波除噪声,建立金字塔底层 
  tempMat = ScaleInitImage(image1Mat) ; 
  //第二步,建立Guassian金字塔和DOG金字塔 
  Gaussianpyr = BuildGaussianOctaves(tempMat) ; 
 
  t = (double)cvGetTickCount() - t; 
  printf( "the time of build Gaussian pyramid and DOG pyramid is %.1f\n", t/(cvGetTickFrequency()*1000.) ); 
   
  #define ImLevels(OCTAVE,LEVEL,ROW,COL) ((float *)(Gaussianpyr[(OCTAVE)].Octave[(LEVEL)].Level->data.fl + Gaussianpyr[(OCTAVE)].Octave[(LEVEL)].Level->step/sizeof(float) *(ROW)))[(COL)] 
  //显示高斯金字塔 
  for (int i=0; iwidth, mosaicVertical1->height),  IPL_DEPTH_8U,1); 
  cvConvertScale( mosaicVertical1, mosaicVertical1, 255.0, 0 ); 
  cvConvertScaleAbs( mosaicVertical1, mosaic1, 1, 0 ); 
 
//  cvSaveImage("GaussianPyramid of me.jpg",mosaic1); 
  cvNamedWindow("mosaic1",1); 
  cvShowImage("mosaic1", mosaic1); 
  cvWaitKey(0); 
  cvDestroyWindow("mosaic1"); 
  //显示DOG金字塔 
  for ( i=0; iwidth, mosaicVertical1->height),  IPL_DEPTH_8U,1); 
  cvConvertScale( mosaicVertical1, mosaicVertical1, 255.0/(max_val-min_val), 0 ); 
  cvConvertScaleAbs( mosaicVertical1, mosaic2, 1, 0 ); 
 
//  cvSaveImage("DOGPyramid of me.jpg",mosaic2); 
  cvNamedWindow("mosaic1",1); 
  cvShowImage("mosaic1", mosaic2); 
  cvWaitKey(0); 
   
  //SIFT算法第三步:特征点位置检测,最后确定特征点的位置 
  int keycount=DetectKeypoint(numoctaves, Gaussianpyr); 
  printf("the keypoints number are %d ;\n", keycount); 
  cvCopy(src,image1,NULL); 
  DisplayKeypointLocation( image1 ,Gaussianpyr); 
   
  cvPyrUp( image1, DoubleSizeImage, CV_GAUSSIAN_5x5 ); 
  cvNamedWindow("image1",1); 
  cvShowImage("image1", DoubleSizeImage); 
  cvWaitKey(0);   
  cvDestroyWindow("image1"); 
   
  //SIFT算法第四步:计算高斯图像的梯度方向和幅值,计算各个特征点的主方向 
  ComputeGrad_DirecandMag(numoctaves, Gaussianpyr); 
  AssignTheMainOrientation( numoctaves, Gaussianpyr,mag_pyr,grad_pyr); 
  cvCopy(src,image1,NULL); 
  DisplayOrientation ( image1, Gaussianpyr); 
   
//  cvPyrUp( image1, DoubleSizeImage, CV_GAUSSIAN_5x5 ); 
  cvNamedWindow("image1",1); 
//  cvResizeWindow("image1", 2*(image1->width), 2*(image1->height) ); 
  cvShowImage("image1", image1); 
  cvWaitKey(0); 
   
  //SIFT算法第五步:抽取各个特征点处的特征描述字 
  ExtractFeatureDescriptors( numoctaves, Gaussianpyr); 
  cvWaitKey(0); 
   
  //销毁窗口 
  cvDestroyWindow("image1"); 
  cvDestroyWindow("mosaic1"); 
  //释放图像 
  cvReleaseImage(&image1); 
  cvReleaseImage(&grey_im1); 
  cvReleaseImage(&mosaic1); 
  cvReleaseImage(&mosaic2); 
  return 0; 
} 
 
//下采样原来的图像,返回缩小2倍尺寸的图像 
CvMat * halfSizeImage(CvMat * im)  
{ 
	unsigned int i,j; 
	int w = im->cols/2; 
	int h = im->rows/2;  
	CvMat *imnew = cvCreateMat(h, w, CV_32FC1); 
 
	#define Im(ROW,COL) ((float *)(im->data.fl + im->step/sizeof(float) *(ROW)))[(COL)] 
    #define Imnew(ROW,COL) ((float *)(imnew->data.fl + imnew->step/sizeof(float) *(ROW)))[(COL)] 
	for ( j = 0; j < h; j++)  
		for ( i = 0; i < w; i++)  
			Imnew(j,i)=Im(j*2, i*2); 
	return imnew; 
} 
 
//上采样原来的图像,返回放大2倍尺寸的图像 
CvMat * doubleSizeImage(CvMat * im)  
{ 
	unsigned int i,j; 
	int w = im->cols*2; 
	int h = im->rows*2;  
	CvMat *imnew = cvCreateMat(h, w, CV_32FC1); 
 
	#define Im(ROW,COL) ((float *)(im->data.fl + im->step/sizeof(float) *(ROW)))[(COL)] 
    #define Imnew(ROW,COL) ((float *)(imnew->data.fl + imnew->step/sizeof(float) *(ROW)))[(COL)] 
	 
	for ( j = 0; j < h; j++)  
		for ( i = 0; i < w; i++)  
			Imnew(j,i)=Im(j/2, i/2); 
 
	return imnew; 
} 
 
//上采样原来的图像,返回放大2倍尺寸的线性插值图像 
CvMat * doubleSizeImage2(CvMat * im)  
{ 
	unsigned int i,j; 
	int w = im->cols*2; 
	int h = im->rows*2;  
	CvMat *imnew = cvCreateMat(h, w, CV_32FC1); 
 
	#define Im(ROW,COL) ((float *)(im->data.fl + im->step/sizeof(float) *(ROW)))[(COL)] 
    #define Imnew(ROW,COL) ((float *)(imnew->data.fl + imnew->step/sizeof(float) *(ROW)))[(COL)] 
		 
	// fill every pixel so we don't have to worry about skipping pixels later 
	for ( j = 0; j < h; j++)  
	{ 
		for ( i = 0; i < w; i++)  
		{ 
			Imnew(j,i)=Im(j/2, i/2); 
		} 
	} 
	/* 
	  A B C 
	  E F G 
	  H I J 
	  pixels A C H J are pixels from original image 
	  pixels B E G I F are interpolated pixels 
	*/ 
	// interpolate pixels B and I 
	for ( j = 0; j < h; j += 2) 
		for ( i = 1; i < w - 1; i += 2) 
			Imnew(j,i)=0.5*(Im(j/2, i/2)+Im(j/2, i/2+1)); 
	// interpolate pixels E and G 
	for ( j = 1; j < h - 1; j += 2) 
		for ( i = 0; i < w; i += 2) 
			Imnew(j,i)=0.5*(Im(j/2, i/2)+Im(j/2+1, i/2)); 
	// interpolate pixel F 
	for ( j = 1; j < h - 1; j += 2) 
		for ( i = 1; i < w - 1; i += 2) 
			Imnew(j,i)=0.25*(Im(j/2, i/2)+Im(j/2+1, i/2)+Im(j/2, i/2+1)+Im(j/2+1, i/2+1)); 
	return imnew; 
} 
 
//双线性插值,返回像素间的灰度值 
float getPixelBI(CvMat * im, float col, float row)  
{ 
	int irow, icol; 
	float rfrac, cfrac; 
	float row1 = 0, row2 = 0; 
	int width=im->cols; 
	int height=im->rows; 
	#define ImMat(ROW,COL) ((float *)(im->data.fl + im->step/sizeof(float) *(ROW)))[(COL)] 
	 
	irow = (int) row; 
	icol = (int) col; 
	 
	if (irow < 0 || irow >= height 
	    || icol < 0 || icol >= width) 
		return 0; 
	if (row > height - 1) 
		row = height - 1; 
	if (col > width - 1) 
		col = width - 1; 
	rfrac = 1.0 - (row - (float) irow); 
	cfrac = 1.0 - (col - (float) icol); 
	if (cfrac < 1)  
	{ 
		row1 = cfrac * ImMat(irow,icol) + (1.0 - cfrac) * ImMat(irow,icol+1); 
	}  
	else  
	{ 
		row1 = ImMat(irow,icol); 
	} 
	if (rfrac < 1)  
	{ 
		if (cfrac < 1)  
		{ 
			row2 = cfrac * ImMat(irow+1,icol) + (1.0 - cfrac) * ImMat(irow+1,icol+1); 
		} else  
		{ 
			row2 = ImMat(irow+1,icol); 
		} 
	} 
	return rfrac * row1 + (1.0 - rfrac) * row2; 
} 
 
//矩阵归一化 
void normalizeMat(CvMat* mat)  
{ 
    #define Mat(ROW,COL) ((float *)(mat->data.fl + mat->step/sizeof(float) *(ROW)))[(COL)] 
	float sum = 0; 
 
	for (unsigned int j = 0; j < mat->rows; j++)  
		for (unsigned int i = 0; i < mat->cols; i++)  
			sum += Mat(j,i); 
	for ( j = 0; j < mat->rows; j++)  
		for (unsigned int i = 0; i < mat->rows; i++)  
			Mat(j,i) /= sum; 
} 
 
//向量归一化 
void normalizeVec(float* vec, int dim)  
{ 
    unsigned int i; 
	float sum = 0; 
	for ( i = 0; i < dim; i++) 
		sum += vec[i]; 
	for ( i = 0; i < dim; i++) 
		vec[i] /= sum; 
} 
 
//得到向量的欧式长度,2-范数 
float GetVecNorm( float* vec, int dim ) 
{ 
	float sum=0.0; 
	for (unsigned int i=0;idata.fl + mat->step/sizeof(float) *(ROW)))[(COL)] 
	float s2 = sigma * sigma; 
	int c = dim / 2; 
	//printf("%d %d\n", mat.size(), mat[0].size()); 
    float m= 1.0/(sqrt(2.0 * CV_PI) * sigma); 
	for (int i = 0; i < (dim + 1) / 2; i++)  
	{ 
		for (int j = 0; j < (dim + 1) / 2; j++)  
		{ 
			//printf("%d %d %d\n", c, i, j); 
			float v = m * exp(-(1.0*i*i + 1.0*j*j) / (2.0 * s2)); 
			Mat(c+i,c+j) =v; 
			Mat(c-i,c+j) =v; 
			Mat(c+i,c-j) =v; 
			Mat(c-i,c-j) =v; 
		} 
	} 
//	normalizeMat(mat); 
	return mat; 
} 
 
//x方向像素处作卷积 
float ConvolveLocWidth(float* kernel, int dim, CvMat * src, int x, int y)  
{ 
	#define Src(ROW,COL) ((float *)(src->data.fl + src->step/sizeof(float) *(ROW)))[(COL)] 
    unsigned int i; 
	float pixel = 0; 
    int col; 
	int cen = dim / 2; 
	//printf("ConvolveLoc(): Applying convoluation at location (%d, %d)\n", x, y); 
	for ( i = 0; i < dim; i++)  
	{ 
		col = x + (i - cen); 
		if (col < 0) 
			col = 0; 
		if (col >= src->cols) 
			col = src->cols - 1; 
		pixel += kernel[i] * Src(y,col); 
	} 
	if (pixel > 1) 
		pixel = 1; 
	return pixel; 
} 
 
//x方向作卷积 
void Convolve1DWidth(float* kern, int dim, CvMat * src, CvMat * dst)  
{ 
	#define DST(ROW,COL) ((float *)(dst->data.fl + dst->step/sizeof(float) *(ROW)))[(COL)] 
	unsigned int i,j; 
 
	for ( j = 0; j < src->rows; j++)  
	{ 
		for ( i = 0; i < src->cols; i++)  
		{ 
			//printf("%d, %d\n", i, j); 
			DST(j,i) = ConvolveLocWidth(kern, dim, src, i, j); 
		} 
	} 
} 
 
//y方向像素处作卷积 
float ConvolveLocHeight(float* kernel, int dim, CvMat * src, int x, int y)  
{ 
	#define Src(ROW,COL) ((float *)(src->data.fl + src->step/sizeof(float) *(ROW)))[(COL)] 
    unsigned int j; 
	float pixel = 0; 
	int cen = dim / 2; 
	//printf("ConvolveLoc(): Applying convoluation at location (%d, %d)\n", x, y); 
	for ( j = 0; j < dim; j++)  
	{ 
		int row = y + (j - cen); 
		if (row < 0) 
			row = 0; 
		if (row >= src->rows) 
			row = src->rows - 1; 
		pixel += kernel[j] * Src(row,x); 
	} 
	if (pixel > 1) 
		pixel = 1; 
	return pixel; 
} 
 
//y方向作卷积 
void Convolve1DHeight(float* kern, int dim, CvMat * src, CvMat * dst)  
{ 
    #define Dst(ROW,COL) ((float *)(dst->data.fl + dst->step/sizeof(float) *(ROW)))[(COL)] 
    unsigned int i,j; 
	for ( j = 0; j < src->rows; j++)  
	{ 
		for ( i = 0; i < src->cols; i++)  
		{ 
			//printf("%d, %d\n", i, j); 
			Dst(j,i) = ConvolveLocHeight(kern, dim, src, i, j); 
		} 
	} 
} 
 
//卷积模糊图像 
int BlurImage(CvMat * src, CvMat * dst, float sigma)  
{ 
	float* convkernel; 
	int dim = (int) max(3.0f, 2.0 * GAUSSKERN * sigma + 1.0f); 
    CvMat *tempMat; 
	// make dim odd 
	if (dim % 2 == 0) 
		dim++; 
	tempMat = cvCreateMat(src->rows, src->cols, CV_32FC1); 
	convkernel = GaussianKernel1D(sigma, dim); 
 
	Convolve1DWidth(convkernel, dim, src, tempMat); 
	Convolve1DHeight(convkernel, dim, tempMat, dst); 
	cvReleaseMat(&tempMat); 
	return dim; 
} 
 
//SIFT算法第一步:扩大图像,预滤波剔除噪声,得到金字塔的最底层-第一阶的第一层 
CvMat *ScaleInitImage(CvMat * im)  
{ 
    double sigma,preblur_sigma; 
	CvMat *imMat; 
	CvMat * dst; 
	CvMat *tempMat; 
	//首先对图像进行平滑滤波,抑制噪声 
	imMat = cvCreateMat(im->rows, im->cols, CV_32FC1); 
    BlurImage(im, imMat, INITSIGMA); 
	//针对两种情况分别进行处理:初始化放大原始图像或者在原图像基础上进行后续操作 
	//建立金字塔的最底层 
	if (DOUBLE_BASE_IMAGE_SIZE)  
	{ 
		tempMat = doubleSizeImage2(imMat);//对扩大两倍的图像进行二次采样,采样率为0.5,采用线性插值 
        #define TEMPMAT(ROW,COL) ((float *)(tempMat->data.fl + tempMat->step/sizeof(float) * (ROW)))[(COL)] 
 
		dst = cvCreateMat(tempMat->rows, tempMat->cols, CV_32FC1); 
		preblur_sigma = 1.0;//sqrt(2 - 4*INITSIGMA*INITSIGMA); 
        BlurImage(tempMat, dst, preblur_sigma);	 
		 
		// The initial blurring for the first image of the first octave of the pyramid. 
        sigma = sqrt( (4*INITSIGMA*INITSIGMA) + preblur_sigma * preblur_sigma ); 
//		sigma = sqrt(SIGMA * SIGMA - INITSIGMA * INITSIGMA * 4); 
		//printf("Init Sigma: %f\n", sigma); 
		BlurImage(dst, tempMat, sigma);	      //得到金字塔的最底层-放大2倍的图像 
		cvReleaseMat( &dst );  
		return tempMat; 
	}  
	else  
	{ 
		dst = cvCreateMat(im->rows, im->cols, CV_32FC1); 
		//sigma = sqrt(SIGMA * SIGMA - INITSIGMA * INITSIGMA); 
		preblur_sigma = 1.0;//sqrt(2 - 4*INITSIGMA*INITSIGMA); 
		sigma = sqrt( (4*INITSIGMA*INITSIGMA) + preblur_sigma * preblur_sigma ); 
		//printf("Init Sigma: %f\n", sigma); 
		BlurImage(imMat, dst, sigma);        //得到金字塔的最底层:原始图像大小 
		return dst; 
	}	 
} 
//SIFT第二步,建立Gaussian金字塔 
//给定金字塔第一阶第一层图像后,计算高斯金字塔其他尺度图像, 
//每一阶的数目由变量SCALESPEROCTAVE决定 
//给定一个基本图像,计算它的高斯金字塔图像,返回外部向量是阶梯指针,内部向量是每一个阶梯内部的不同尺度图像 
ImageOctaves* BuildGaussianOctaves(CvMat * image)  
{ 
    ImageOctaves *octaves; 
	CvMat *tempMat; 
    CvMat *dst; 
	CvMat *temp; 
 
	int i,j; 
	double k = pow(2, 1.0/((float)SCALESPEROCTAVE));  //方差倍数 
	float preblur_sigma, initial_sigma , sigma1,sigma2,sigma,absolute_sigma,sigma_f; 
	//计算金字塔的阶梯数目 
	int dim = min(image->rows, image->cols); 
    int numoctaves = (int) (log((double) dim) / log(2.0)) - 2;    //金字塔阶数 
	//限定金字塔的阶梯数 
	numoctaves = min(numoctaves, MAXOCTAVES); 
	//为高斯金塔和DOG金字塔分配内存 
	octaves=(ImageOctaves*) malloc( numoctaves * sizeof(ImageOctaves) ); 
    DOGoctaves=(ImageOctaves*) malloc( numoctaves * sizeof(ImageOctaves) ); 
 
	printf("BuildGaussianOctaves(): Base image dimension is %dx%d\n", (int)(0.5*(image->cols)), (int)(0.5*(image->rows)) ); 
	printf("BuildGaussianOctaves(): Building %d octaves\n", numoctaves); 
 
    // start with initial source image 
	tempMat=cvCloneMat( image ); 
//	preblur_sigma = 1.0;//sqrt(2 - 4*INITSIGMA*INITSIGMA); 
    initial_sigma = sqrt(2);//sqrt( (4*INITSIGMA*INITSIGMA) + preblur_sigma * preblur_sigma ); 
 //   initial_sigma = sqrt(SIGMA * SIGMA - INITSIGMA * INITSIGMA * 4); 
     
	//在每一阶金字塔图像中建立不同的尺度图像 
	for ( i = 0; i < numoctaves; i++)  
	{    
		//首先建立金字塔每一阶梯的最底层,其中0阶梯的最底层已经建立好 
		printf("Building octave %d of dimesion (%d, %d)\n", i, tempMat->cols,tempMat->rows); 
        //为各个阶梯分配内存 
		octaves[i].Octave= (ImageLevels*) malloc( (SCALESPEROCTAVE + 3) * sizeof(ImageLevels) ); 
		DOGoctaves[i].Octave= (ImageLevels*) malloc( (SCALESPEROCTAVE + 2) * sizeof(ImageLevels) ); 
		//存储各个阶梯的最底层 
		(octaves[i].Octave)[0].Level=tempMat; 
		 
		octaves[i].col=tempMat->cols; 
		octaves[i].row=tempMat->rows; 
	    DOGoctaves[i].col=tempMat->cols; 
		DOGoctaves[i].row=tempMat->rows; 
		if (DOUBLE_BASE_IMAGE_SIZE) 
            octaves[i].subsample=pow(2,i)*0.5; 
		else 
            octaves[i].subsample=pow(2,i); 
 
		if(i==0) 	    
		{ 
			(octaves[0].Octave)[0].levelsigma = initial_sigma; 
		    (octaves[0].Octave)[0].absolute_sigma = initial_sigma; 
			printf("0 scale and blur sigma : %f \n", (octaves[0].subsample) * ((octaves[0].Octave)[0].absolute_sigma)); 
		} 
		else 
		{ 
			(octaves[i].Octave)[0].levelsigma = (octaves[i-1].Octave)[SCALESPEROCTAVE].levelsigma; 
            (octaves[i].Octave)[0].absolute_sigma = (octaves[i-1].Octave)[SCALESPEROCTAVE].absolute_sigma; 
		    printf( "0 scale and blur sigma : %f \n", ((octaves[i].Octave)[0].absolute_sigma) ); 
		} 
		sigma = initial_sigma; 
        //建立本阶梯其他层的图像 
	    for ( j =  1; j < SCALESPEROCTAVE + 3; j++)  
		{ 
		    dst = cvCreateMat(tempMat->rows, tempMat->cols, CV_32FC1);//用于存储高斯层 
		    temp = cvCreateMat(tempMat->rows, tempMat->cols, CV_32FC1);//用于存储DOG层 
		    // 2 passes of 1D on original 
//			if(i!=0) 
//			{ 
//			    sigma1 = pow(k, j - 1) * ((octaves[i-1].Octave)[j-1].levelsigma); 
//		        sigma2 = pow(k, j) * ((octaves[i].Octave)[j-1].levelsigma); 
//			    sigma = sqrt(sigma2*sigma2 - sigma1*sigma1); 
				sigma_f= sqrt(k*k-1)*sigma; 
//			} 
//			else 
//			{ 
//			    sigma = sqrt(SIGMA * SIGMA - INITSIGMA * INITSIGMA * 4)*pow(k,j); 
//			}		 
            sigma = k*sigma; 
			absolute_sigma = sigma * (octaves[i].subsample); 
		    printf("%d scale and Blur sigma: %f  \n", j, absolute_sigma); 
			 
			(octaves[i].Octave)[j].levelsigma = sigma; 
            (octaves[i].Octave)[j].absolute_sigma = absolute_sigma; 
            //产生高斯层 
      		int length=BlurImage((octaves[i].Octave)[j-1].Level, dst, sigma_f);//相应尺度 
            (octaves[i].Octave)[j].levelsigmalength = length; 
		    (octaves[i].Octave)[j].Level=dst; 
            //产生DOG层 
            cvSub( ((octaves[i].Octave)[j]).Level, ((octaves[i].Octave)[j-1]).Level, temp, 0 ); 
//        	cvAbsDiff( ((octaves[i].Octave)[j]).Level, ((octaves[i].Octave)[j-1]).Level, temp ); 
            ((DOGoctaves[i].Octave)[j-1]).Level=temp; 
		} 
		// halve the image size for next iteration 
		tempMat  = halfSizeImage( ( (octaves[i].Octave)[SCALESPEROCTAVE].Level ) ); 
	} 
	return octaves; 
} 
 
// 下一步是检测DOG金字塔中的局部最大值,找到之后,还要经过两个检验才能确认为特征点 
// 一是它必须有明显的差异,二是他不应该是边缘点,(也就是说,在极值点处的主曲率比应该小于某一个阈值) 
int DetectKeypoint(int numoctaves, ImageOctaves *GaussianPyr) 
{ 
    //计算用于DOG极值点检测的主曲率比的阈值 
	double curvature_threshold; 
	curvature_threshold= ((CURVATURE_THRESHOLD + 1)*(CURVATURE_THRESHOLD + 1))/CURVATURE_THRESHOLD; 
	#define ImLevels(OCTAVE,LEVEL,ROW,COL) ((float *)(DOGoctaves[(OCTAVE)].Octave[(LEVEL)].Level->data.fl + DOGoctaves[(OCTAVE)].Octave[(LEVEL)].Level->step/sizeof(float) *(ROW)))[(COL)] 
	 
	int   keypoint_count = 0;    
	for (int i=0; i= CONTRAST_THRESHOLD ) 
					{ 
					 
					if ( ImLevels(i,j,m,n)!=0.0 )  //1、首先是非零 
					{ 
						float inf_val=ImLevels(i,j,m,n); 
						if(( (inf_val <= ImLevels(i,j-1,m-1,n-1))&& 
							 (inf_val <= ImLevels(i,j-1,m  ,n-1))&& 
							 (inf_val <= ImLevels(i,j-1,m+1,n-1))&& 
							 (inf_val <= ImLevels(i,j-1,m-1,n  ))&& 
							 (inf_val <= ImLevels(i,j-1,m  ,n  ))&& 
							 (inf_val <= ImLevels(i,j-1,m+1,n  ))&& 
							 (inf_val <= ImLevels(i,j-1,m-1,n+1))&& 
							 (inf_val <= ImLevels(i,j-1,m  ,n+1))&& 
							 (inf_val <= ImLevels(i,j-1,m+1,n+1))&&    //底层的小尺度9 
 							  
							 (inf_val <= ImLevels(i,j,m-1,n-1))&& 
							 (inf_val <= ImLevels(i,j,m  ,n-1))&& 
							 (inf_val <= ImLevels(i,j,m+1,n-1))&& 
							 (inf_val <= ImLevels(i,j,m-1,n  ))&& 
							 (inf_val <= ImLevels(i,j,m+1,n  ))&& 
							 (inf_val <= ImLevels(i,j,m-1,n+1))&& 
							 (inf_val <= ImLevels(i,j,m  ,n+1))&& 
							 (inf_val <= ImLevels(i,j,m+1,n+1))&&     //当前层8 
							  
                             (inf_val <= ImLevels(i,j+1,m-1,n-1))&& 
							 (inf_val <= ImLevels(i,j+1,m  ,n-1))&& 
							 (inf_val <= ImLevels(i,j+1,m+1,n-1))&& 
							 (inf_val <= ImLevels(i,j+1,m-1,n  ))&& 
							 (inf_val <= ImLevels(i,j+1,m  ,n  ))&& 
							 (inf_val <= ImLevels(i,j+1,m+1,n  ))&& 
							 (inf_val <= ImLevels(i,j+1,m-1,n+1))&& 
							 (inf_val <= ImLevels(i,j+1,m  ,n+1))&& 
							 (inf_val <= ImLevels(i,j+1,m+1,n+1))     //下一层大尺度9							  
						   ) ||  
						   ( (inf_val >= ImLevels(i,j-1,m-1,n-1))&& 
							 (inf_val >= ImLevels(i,j-1,m  ,n-1))&& 
							 (inf_val >= ImLevels(i,j-1,m+1,n-1))&& 
							 (inf_val >= ImLevels(i,j-1,m-1,n  ))&& 
							 (inf_val >= ImLevels(i,j-1,m  ,n  ))&& 
							 (inf_val >= ImLevels(i,j-1,m+1,n  ))&& 
							 (inf_val >= ImLevels(i,j-1,m-1,n+1))&& 
							 (inf_val >= ImLevels(i,j-1,m  ,n+1))&& 
							 (inf_val >= ImLevels(i,j-1,m+1,n+1))&& 
							  
							 (inf_val >= ImLevels(i,j,m-1,n-1))&& 
							 (inf_val >= ImLevels(i,j,m  ,n-1))&& 
							 (inf_val >= ImLevels(i,j,m+1,n-1))&& 
							 (inf_val >= ImLevels(i,j,m-1,n  ))&& 
							 (inf_val >= ImLevels(i,j,m+1,n  ))&& 
							 (inf_val >= ImLevels(i,j,m-1,n+1))&& 
							 (inf_val >= ImLevels(i,j,m  ,n+1))&& 
							 (inf_val >= ImLevels(i,j,m+1,n+1))&&  
 
							 (inf_val >= ImLevels(i,j+1,m-1,n-1))&& 
							 (inf_val >= ImLevels(i,j+1,m  ,n-1))&& 
							 (inf_val >= ImLevels(i,j+1,m+1,n-1))&& 
							 (inf_val >= ImLevels(i,j+1,m-1,n  ))&& 
							 (inf_val >= ImLevels(i,j+1,m  ,n  ))&& 
							 (inf_val >= ImLevels(i,j+1,m+1,n  ))&& 
							 (inf_val >= ImLevels(i,j+1,m-1,n+1))&& 
							 (inf_val >= ImLevels(i,j+1,m  ,n+1))&& 
							 (inf_val >= ImLevels(i,j+1,m+1,n+1))  
						   ) )      //2、满足26个中极值点 
						{    
							//此处可存储 
							//然后必须具有明显的显著性,即必须大于CONTRAST_THRESHOLD=0.02 
							if ( fabs(ImLevels(i,j,m,n))>= CONTRAST_THRESHOLD ) 
							{ 
							    //最后显著处的特征点必须具有足够的曲率比,CURVATURE_THRESHOLD=10.0,首先计算Hessian矩阵 
								// Compute the entries of the Hessian matrix at the extrema location. 
								/* 
                                   1   0   -1 
	                               0   0   0 
	                              -1   0   1         *0.25 
                                */ 
                                // Compute the trace and the determinant of the Hessian. 
                                //Tr_H = Dxx + Dyy; 
                                //Det_H = Dxx*Dyy - Dxy^2; 
                                float Dxx,Dyy,Dxy,Tr_H,Det_H,curvature_ratio; 
                                Dxx = ImLevels(i,j,m,n-1) + ImLevels(i,j,m,n+1)-2.0*ImLevels(i,j,m,n); 
								Dyy = ImLevels(i,j,m-1,n) + ImLevels(i,j,m+1,n)-2.0*ImLevels(i,j,m,n); 
								Dxy = ImLevels(i,j,m-1,n-1) + ImLevels(i,j,m+1,n+1) - ImLevels(i,j,m+1,n-1) - ImLevels(i,j,m-1,n+1); 
                                Tr_H = Dxx + Dyy; 
                                Det_H = Dxx*Dyy - Dxy*Dxy; 
                                // Compute the ratio of the principal curvatures. 
                                curvature_ratio = (1.0*Tr_H*Tr_H)/Det_H; 
								if ( (Det_H>=0.0) && (curvature_ratio <= curvature_threshold) )  //最后得到最具有显著性特征的特征点 
								{ 
									//将其存储起来,以计算后面的特征描述字 
									keypoint_count++; 
									Keypoint k; 
                                    /* Allocate memory for the keypoint. */ 
                                    k = (Keypoint) malloc(sizeof(struct KeypointSt)); 
                                    k->next = keypoints; 
                                    keypoints = k; 
                                    k->row = m*(GaussianPyr[i].subsample); 
									k->col =n*(GaussianPyr[i].subsample); 
									k->sy = m;    //行 
									k->sx = n;    //列 
									k->octave=i; 
									k->level=j; 
                                    k->scale = (GaussianPyr[i].Octave)[j].absolute_sigma;       
								}//if >curvature_thresh 
							}//if >contrast 
						}//if inf value 
					}//if non zero 
					}//if >contrast 
				}  //for concrete image level col 
		}//for levels 
	}//for octaves 
	return keypoint_count; 
} 
 
//在图像中,显示SIFT特征点的位置 
void DisplayKeypointLocation(IplImage* image, ImageOctaves *GaussianPyr) 
{ 
    Keypoint p = keypoints; // p指向第一个结点 
 	while(p) // 没到表尾 
	{ 
		cvLine( image, cvPoint((int)((p->col)-3),(int)(p->row)),  
			cvPoint((int)((p->col)+3),(int)(p->row)), CV_RGB(255,255,0), 
             1, 8, 0 ); 
        cvLine( image, cvPoint((int)(p->col),(int)((p->row)-3)),  
			cvPoint((int)(p->col),(int)((p->row)+3)), CV_RGB(255,255,0), 
             1, 8, 0 ); 
//		cvCircle(image,cvPoint((uchar)(p->col),(uchar)(p->row)), 
//			(int)((GaussianPyr[p->octave].Octave)[p->level].absolute_sigma), 
//			CV_RGB(255,0,0),1,8,0); 
		p=p->next; 
	}	 
} 
 
// Compute the gradient direction and magnitude of the gaussian pyramid images 
void ComputeGrad_DirecandMag(int numoctaves, ImageOctaves *GaussianPyr) 
{ 
//	ImageOctaves *mag_thresh ; 
    mag_pyr=(ImageOctaves*) malloc( numoctaves * sizeof(ImageOctaves) ); 
	grad_pyr=(ImageOctaves*) malloc( numoctaves * sizeof(ImageOctaves) ); 
//	float sigma=( (GaussianPyr[0].Octave)[SCALESPEROCTAVE+2].absolute_sigma ) / GaussianPyr[0].subsample; 
//	int dim = (int) (max(3.0f, 2 * GAUSSKERN *sigma + 1.0f)*0.5+0.5); 
	#define ImLevels(OCTAVE,LEVEL,ROW,COL) ((float *)(GaussianPyr[(OCTAVE)].Octave[(LEVEL)].Level->data.fl + GaussianPyr[(OCTAVE)].Octave[(LEVEL)].Level->step/sizeof(float) *(ROW)))[(COL)] 
	for (int i=0; idata.fl + Mag->step/sizeof(float) *(ROW)))[(COL)]    
			#define ORI(ROW,COL) ((float *)(Ori->data.fl + Ori->step/sizeof(float) *(ROW)))[(COL)]   
            #define TEMPMAT1(ROW,COL) ((float *)(tempMat1->data.fl + tempMat1->step/sizeof(float) *(ROW)))[(COL)] 
			#define TEMPMAT2(ROW,COL) ((float *)(tempMat2->data.fl + tempMat2->step/sizeof(float) *(ROW)))[(COL)] 
		    for (int m=1;m<(GaussianPyr[i].row-1);m++)  
				for(int n=1;n<(GaussianPyr[i].col-1);n++) 
				{ 
					//计算幅值 
					TEMPMAT1(m,n) = 0.5*( ImLevels(i,j,m,n+1)-ImLevels(i,j,m,n-1) );  //dx 
                    TEMPMAT2(m,n) = 0.5*( ImLevels(i,j,m+1,n)-ImLevels(i,j,m-1,n) );  //dy 
                    MAG(m,n) = sqrt(TEMPMAT1(m,n)*TEMPMAT1(m,n)+TEMPMAT2(m,n)*TEMPMAT2(m,n));  //mag 
					//计算方向 
					ORI(m,n) =atan( TEMPMAT2(m,n)/TEMPMAT1(m,n) ); 
                    if (ORI(m,n)==CV_PI) 
						ORI(m,n)=-CV_PI; 
				} 
			((mag_pyr[i].Octave)[j-1]).Level=Mag; 
			((grad_pyr[i].Octave)[j-1]).Level=Ori; 
			cvReleaseMat(&tempMat1); 
			cvReleaseMat(&tempMat2); 
		}//for levels 
	}//for octaves 
} 
 
//SIFT算法第四步:计算各个特征点的主方向,确定主方向 
void AssignTheMainOrientation(int numoctaves, ImageOctaves *GaussianPyr,ImageOctaves *mag_pyr,ImageOctaves *grad_pyr) 
{ 
    // Set up the histogram bin centers for a 36 bin histogram. 
    int num_bins = 36; 
    float hist_step = 2.0*PI/num_bins; 
    float hist_orient[36]; 
    for (int i=0;i<36;i++) 
	   hist_orient[i]=-PI+i*hist_step; 
    float sigma1=( ((GaussianPyr[0].Octave)[SCALESPEROCTAVE].absolute_sigma) ) / (GaussianPyr[0].subsample);//SCALESPEROCTAVE+2 
	int zero_pad = (int) (max(3.0f, 2 * GAUSSKERN *sigma1 + 1.0f)*0.5+0.5); 
    //Assign orientations to the keypoints. 
   	#define ImLevels(OCTAVES,LEVELS,ROW,COL) ((float *)((GaussianPyr[(OCTAVES)].Octave[(LEVELS)].Level)->data.fl + (GaussianPyr[(OCTAVES)].Octave[(LEVELS)].Level)->step/sizeof(float) *(ROW)))[(COL)] 
 
    int keypoint_count = 0; 
	Keypoint p = keypoints; // p指向第一个结点 
 
	while(p) // 没到表尾 
	{ 
		int i=p->octave; 
		int j=p->level; 
		int m=p->sy;   //行 
		int n=p->sx;   //列 
		if ((m>=zero_pad)&&(m=zero_pad)&&(nrows)); 
	        //分配用于存储Patch幅值和方向的空间 
	        #define MAT(ROW,COL) ((float *)(mat->data.fl + mat->step/sizeof(float) *(ROW)))[(COL)] 
    	     
			//声明方向直方图变量 
			double* orienthist = (double *) malloc(36 * sizeof(double)); 
			for ( int sw = 0 ; sw < 36 ; ++sw)  
			{ 
	           orienthist[sw]=0.0;		 
			} 
			//在特征点的周围统计梯度方向 
            for (int x=m-dim,mm=0;x<=(m+dim);x++,mm++)  
	           for(int y=n-dim,nn=0;y<=(n+dim);y++,nn++) 
			   {		    
				   //计算特征点处的幅值 
				   double dx = 0.5*(ImLevels(i,j,x,y+1)-ImLevels(i,j,x,y-1));  //dx 
                   double dy = 0.5*(ImLevels(i,j,x+1,y)-ImLevels(i,j,x-1,y));  //dy 
                   double mag = sqrt(dx*dx+dy*dy);  //mag 
				   //计算方向 
				   double Ori =atan( 1.0*dy/dx ); 
				   int binIdx = FindClosestRotationBin(36, Ori);                   //得到离现有方向最近的直方块 
				   orienthist[binIdx] = orienthist[binIdx] + 1.0* mag * MAT(mm,nn);//利用高斯加权累加进直方图相应的块 
			   } 
			// Find peaks in the orientation histogram using nonmax suppression. 
            AverageWeakBins (orienthist, 36); 
			// find the maximum peak in gradient orientation 
 		    double maxGrad = 0.0; 
		    int maxBin = 0; 
		    for (int b = 0 ; b < 36 ; ++b)  
			{ 
		       if (orienthist[b] > maxGrad)  
			   { 
		           maxGrad = orienthist[b]; 
		           maxBin = b; 
			   } 
			} 
            // First determine the real interpolated peak high at the maximum bin 
		    // position, which is guaranteed to be an absolute peak. 
            double maxPeakValue=0.0; 
			double maxDegreeCorrection=0.0; 
			if ( (InterpolateOrientation ( orienthist[maxBin == 0 ? (36 - 1) : (maxBin - 1)], 
                    orienthist[maxBin], orienthist[(maxBin + 1) % 36], 
                    &maxDegreeCorrection, &maxPeakValue)) == false) 
   				printf("BUG: Parabola fitting broken"); 
		 
			// Now that we know the maximum peak value, we can find other keypoint 
		    // orientations, which have to fulfill two criterias: 
		    // 
		    //  1. They must be a local peak themselves. Else we might add a very 
		    //     similar keypoint orientation twice (imagine for example the 
		    //     values: 0.4 1.0 0.8, if 1.0 is maximum peak, 0.8 is still added 
		    //     with the default threshhold, but the maximum peak orientation 
		    //     was already added). 
		    //  2. They must have at least peakRelThresh times the maximum peak 
		    //     value. 
			bool binIsKeypoint[36]; 
            for ( b = 0 ; b < 36 ; ++b)  
			{ 
                binIsKeypoint[b] = false; 
                // The maximum peak of course is 
                if (b == maxBin)  
				{ 
                   binIsKeypoint[b] = true; 
	               continue; 
				} 
                // Local peaks are, too, in case they fulfill the threshhold 
                if (orienthist[b] < (peakRelThresh * maxPeakValue)) 
	               continue; 
               int leftI = (b == 0) ? (36 - 1) : (b - 1); 
               int rightI = (b + 1) % 36; 
               if (orienthist[b] <= orienthist[leftI] || orienthist[b] <= orienthist[rightI]) 
	               continue;	// no local peak 
               binIsKeypoint[b] = true; 
			} 
            // find other possible locations 
		    double oneBinRad = (2.0 * PI) / 36; 
		    for ( b = 0 ; b < 36 ; ++b)  
			{ 
			    if (binIsKeypoint[b] == false) 
			        continue; 
            	int bLeft = (b == 0) ? (36 - 1) : (b - 1); 
                int bRight = (b + 1) % 36; 
       			// Get an interpolated peak direction and value guess. 
                double peakValue; 
                double degreeCorrection; 
 
                double maxPeakValue, maxDegreeCorrection;               
				if (InterpolateOrientation ( orienthist[maxBin == 0 ? (36 - 1) : (maxBin - 1)], 
                    orienthist[maxBin], orienthist[(maxBin + 1) % 36], 
                    °reeCorrection, &peakValue) == false) 
				{ 
   				    printf("BUG: Parabola fitting broken"); 
				} 
             
                double degree = (b + degreeCorrection) * oneBinRad - PI; 
                if (degree < -PI) 
	                degree += 2.0 * PI; 
                else if (degree > PI) 
	                degree -= 2.0 * PI; 
				//存储方向,可以直接利用检测到的链表进行该步主方向的指定; 
				//分配内存重新存储特征点 
                Keypoint k; 
                /* Allocate memory for the keypoint Descriptor. */ 
                k = (Keypoint) malloc(sizeof(struct KeypointSt)); 
                k->next = keyDescriptors; 
                keyDescriptors = k; 
                k->descrip = (float*)malloc(LEN * sizeof(float)); 
                k->row = p->row; 
				k->col = p->col; 
				k->sy = p->sy;    //行 
				k->sx = p->sx;    //列 
				k->octave = p->octave; 
				k->level = p->level; 
                k->scale = p->scale;       
                k->ori = degree; 
                k->mag = peakValue;		 
			}//for 
			free(orienthist); 
		} 
		p=p->next; 
	}	 
} 
 
//寻找与方向直方图最近的柱,确定其index  
int FindClosestRotationBin (int binCount, float angle) 
{ 
	angle += CV_PI; 
	angle /= 2.0 * CV_PI; 
	// calculate the aligned bin 
	angle *= binCount; 
	int idx = (int) angle; 
	if (idx == binCount) 
		idx = 0; 
	return (idx); 
} 
 
// Average the content of the direction bins. 
void AverageWeakBins (double* hist, int binCount) 
{ 
	// TODO: make some tests what number of passes is the best. (its clear 
	// one is not enough, as we may have something like 
	// ( 0.4, 0.4, 0.3, 0.4, 0.4 )) 
	for (int sn = 0 ; sn < 2 ; ++sn)  
	{ 
		double firstE = hist[0]; 
		double last = hist[binCount-1]; 
		for (int sw = 0 ; sw < binCount ; ++sw)  
		{ 
			double cur = hist[sw]; 
			double next = (sw == (binCount - 1)) ? firstE : hist[(sw + 1) % binCount]; 
			hist[sw] = (last + cur + next) / 3.0; 
			last = cur; 
		} 
	} 
} 
 
// Fit a parabol to the three points (-1.0 ; left), (0.0 ; middle) and 
// (1.0 ; right). 
// Formulas: 
// f(x) = a (x - c)^2 + b 
// c is the peak offset (where f'(x) is zero), b is the peak value. 
// In case there is an error false is returned, otherwise a correction 
// value between [-1 ; 1] is returned in 'degreeCorrection', where -1 
// means the peak is located completely at the left vector, and -0.5 just 
// in the middle between left and middle and > 0 to the right side. In 
// 'peakValue' the maximum estimated peak value is stored. 
bool InterpolateOrientation (double left, double middle,double right, double *degreeCorrection, double *peakValue) 
{ 
	double a = ((left + right) - 2.0 * middle) / 2.0;   //抛物线捏合系数a 
//	degreeCorrection = peakValue = Double.NaN; 
 
	// Not a parabol 
	if (a == 0.0) 
		return false; 
	double c = (((left - middle) / a) - 1.0) / 2.0; 
	double b = middle - c * c * a; 
	if (c < -0.5 || c > 0.5) 
		return false; 
	*degreeCorrection = c; 
	*peakValue = b; 
	return true; 
} 
 
//显示特征点处的主方向 
void DisplayOrientation (IplImage* image, ImageOctaves *GaussianPyr) 
{ 
	Keypoint p = keyDescriptors; // p指向第一个结点 
 	while(p) // 没到表尾 
	{ 
		float scale=(GaussianPyr[p->octave].Octave)[p->level].absolute_sigma; 
		float autoscale = 3.0;  
		float uu=autoscale*scale*cos(p->ori); 
		float vv=autoscale*scale*sin(p->ori); 
		float x=(p->col)+uu; 
		float y=(p->row)+vv; 
		cvLine( image, cvPoint((int)(p->col),(int)(p->row)),  
		      cvPoint((int)x,(int)y), CV_RGB(255,255,0), 
               1, 8, 0 ); 
		// Arrow head parameters 
        float alpha = 0.33; // Size of arrow head relative to the length of the vector 
        float beta = 0.33;  // Width of the base of the arrow head relative to the length 
		 
		float xx0= (p->col)+uu-alpha*(uu+beta*vv); 
        float yy0= (p->row)+vv-alpha*(vv-beta*uu); 
        float xx1= (p->col)+uu-alpha*(uu-beta*vv); 
        float yy1= (p->row)+vv-alpha*(vv+beta*uu); 
        cvLine( image, cvPoint((int)xx0,(int)yy0),  
		      cvPoint((int)x,(int)y), CV_RGB(255,255,0), 
               1, 8, 0 ); 
		cvLine( image, cvPoint((int)xx1,(int)yy1),  
		      cvPoint((int)x,(int)y), CV_RGB(255,255,0), 
               1, 8, 0 ); 
		p=p->next; 
	}	 
} 
 
//确定特征点的描述字。描述字是Patch网格内梯度方向的描述, 
//旋转网格到主方向,插值得到网格处梯度值。 
//一个特征点可以用2*2*8=32维的向量,也可以用4*4*8=128维的向量更精确的进行描述。 
void ExtractFeatureDescriptors(int numoctaves, ImageOctaves *GaussianPyr) 
{ 
	// The orientation histograms have 8 bins 
	float orient_bin_spacing = PI/4; 
    float orient_angles[8]={-PI,-PI+orient_bin_spacing,-PI*0.5, -orient_bin_spacing, 
	                         0.0, orient_bin_spacing, PI*0.5,  PI+orient_bin_spacing}; 
    //产生描述字中心各点坐标 
    float *feat_grid=(float *) malloc( 2*16 * sizeof(float)); 
	for (int i=0;ioctave].Octave)[p->level].absolute_sigma; 
		       
		float sine = sin(p->ori); 
	    float cosine = cos(p->ori);   
        //计算中心点坐标旋转之后的位置 
		float *featcenter=(float *) malloc( 2*16 * sizeof(float)); 
		for (int i=0;isx); 
			   featcenter[i*2*GridSpacing+j+1]=((-sine * x + cosine * y) + p->sy); 
			} 
		} 
		// calculate sample window coordinates (rotated along keypoint) 
		float *feat=(float *) malloc( 2*256 * sizeof(float)); 
		for ( i=0;i<64*GridSpacing;i++,i++) 
		{ 
			float x=feat_samples[i]; 
			float y=feat_samples[i+1]; 
			feat[i]=((cosine * x + sine * y) + p->sx); 
			feat[i+1]=((-sine * x + cosine * y) + p->sy); 
		} 
 		//Initialize the feature descriptor. 
        float *feat_desc = (float *) malloc( 128 * sizeof(float)); 
        for (i=0;i<128;i++) 
		{ 
		    feat_desc[i]=0.0; 
		//	printf("%f  ",feat_desc[i]);		 
		} 
        //printf("\n"); 
		for ( i=0;i<512;++i,++i) 
		{ 
            float x_sample = feat[i]; 
            float y_sample = feat[i+1]; 
	           // Interpolate the gradient at the sample position 
			   /* 
                     0   1   0 
                     1   *   1 
					 0   1   0   具体插值策略如图示 
			   */ 
			float sample12=getPixelBI(((GaussianPyr[p->octave].Octave)[p->level]).Level, x_sample, y_sample-1); 
			float sample21=getPixelBI(((GaussianPyr[p->octave].Octave)[p->level]).Level, x_sample-1, y_sample);  
			float sample22=getPixelBI(((GaussianPyr[p->octave].Octave)[p->level]).Level, x_sample, y_sample);  
			float sample23=getPixelBI(((GaussianPyr[p->octave].Octave)[p->level]).Level, x_sample+1, y_sample);  
			float sample32=getPixelBI(((GaussianPyr[p->octave].Octave)[p->level]).Level, x_sample, y_sample+1);  
	        //float diff_x = 0.5*(sample23 - sample21); 
            //float diff_y = 0.5*(sample32 - sample12); 
			float diff_x = sample23 - sample21; 
            float diff_y = sample32 - sample12; 
            float mag_sample = sqrt( diff_x*diff_x + diff_y*diff_y ); 
            float grad_sample = atan( diff_y / diff_x ); 
            if(grad_sample == CV_PI) 
               grad_sample = -CV_PI; 
            // Compute the weighting for the x and y dimensions. 
            float *x_wght=(float *) malloc( GridSpacing * GridSpacing * sizeof(float)); 
            float *y_wght=(float *) malloc( GridSpacing * GridSpacing * sizeof(float)); 
			float *pos_wght=(float *) malloc( 8*GridSpacing * GridSpacing * sizeof(float));; 
			for (int m=0;m<32;++m,++m) 
			{ 
                float x=featcenter[m]; 
			    float y=featcenter[m+1]; 
			    x_wght[m/2] = max(1 - (fabs(x - x_sample)*1.0/GridSpacing), 0); 
                y_wght[m/2] = max(1 - (fabs(y - y_sample)*1.0/GridSpacing), 0);  
			     
			} 
			for ( m=0;m<16;++m) 
                for (int n=0;n<8;++n) 
 					pos_wght[m*8+n]=x_wght[m]*y_wght[m]; 
			free(x_wght); 
            free(y_wght); 
		    //计算方向的加权,首先旋转梯度场到主方向,然后计算差异  
			float diff[8],orient_wght[128]; 
			for ( m=0;m<8;++m) 
			{	 
				float angle = grad_sample-(p->ori)-orient_angles[m]+CV_PI; 
				float temp = angle / (2.0 * CV_PI); 
				angle -= (int)(temp) * (2.0 * CV_PI); 
				diff[m]= angle - CV_PI; 
			} 
  			// Compute the gaussian weighting. 
            float x=p->sx; 
			float y=p->sy; 
			float g = exp(-((x_sample-x)*(x_sample-x)+(y_sample-y)*(y_sample-y))/(2*feat_window*feat_window))/(2*CV_PI*feat_window*feat_window); 
			 
			for ( m=0;m<128;++m) 
			{ 
				orient_wght[m] = max((1.0 - 1.0*fabs(diff[m%8])/orient_bin_spacing),0); 
                feat_desc[m] = feat_desc[m] + orient_wght[m]*pos_wght[m]*g*mag_sample; 
			} 
			free(pos_wght);			 
		} 
		free(feat); 
		free(featcenter); 
		float norm=GetVecNorm( feat_desc, 128); 
		for (int m=0;m<128;m++) 
		{ 
		    feat_desc[m]/=norm; 
			if (feat_desc[m]>0.2) 
				feat_desc[m]=0.2; 
		} 
        norm=GetVecNorm( feat_desc, 128); 
        for ( m=0;m<128;m++) 
		{ 
			feat_desc[m]/=norm; 
			printf("%f  ",feat_desc[m]);		 
		} 
		printf("\n"); 
		p->descrip = feat_desc; 
		p=p->next; 
	} 
	free(feat_grid); 
    free(feat_samples); 
} 
 
//为了显示图象金字塔,而作的图像水平拼接 
CvMat* MosaicHorizen( CvMat* im1, CvMat* im2 ) 
{ 
  int row,col; 
  CvMat *mosaic = cvCreateMat( max(im1->rows,im2->rows),(im1->cols+im2->cols),CV_32FC1); 
  #define Mosaic(ROW,COL) ((float*)(mosaic->data.fl + mosaic->step/sizeof(float)*(ROW)))[(COL)] 
  #define Im11Mat(ROW,COL) ((float *)(im1->data.fl + im1->step/sizeof(float) *(ROW)))[(COL)] 
  #define Im22Mat(ROW,COL) ((float *)(im2->data.fl + im2->step/sizeof(float) *(ROW)))[(COL)] 
  cvZero(mosaic); 
  /* Copy images into mosaic1. */ 
  for ( row = 0; row < im1->rows; row++) 
    for ( col = 0; col < im1->cols; col++) 
		Mosaic(row,col)=Im11Mat(row,col) ; 
  for (  row = 0; row < im2->rows; row++) 
    for (  col = 0; col < im2->cols; col++) 
        Mosaic(row, (col+im1->cols) )= Im22Mat(row,col) ; 
  return mosaic; 
} 
//为了显示图象金字塔,而作的图像垂直拼接 
CvMat* MosaicVertical( CvMat* im1, CvMat* im2 ) 
{ 
  int row,col; 
  CvMat *mosaic = cvCreateMat(im1->rows+im2->rows,max(im1->cols,im2->cols), CV_32FC1); 
  #define Mosaic(ROW,COL) ((float*)(mosaic->data.fl + mosaic->step/sizeof(float)*(ROW)))[(COL)] 
  #define Im11Mat(ROW,COL) ((float *)(im1->data.fl + im1->step/sizeof(float) *(ROW)))[(COL)] 
  #define Im22Mat(ROW,COL) ((float *)(im2->data.fl + im2->step/sizeof(float) *(ROW)))[(COL)] 
  cvZero(mosaic); 
 
  /* Copy images into mosaic1. */ 
  for ( row = 0; row < im1->rows; row++) 
    for ( col = 0; col < im1->cols; col++) 
		Mosaic(row,col)= Im11Mat(row,col) ; 
  for ( row = 0; row < im2->rows; row++) 
    for ( col = 0; col < im2->cols; col++) 
        Mosaic((row+im1->rows),col)=Im22Mat(row,col) ; 
   
  return mosaic; 
}