www.pudn.com > OPENCV_SIFT_VC6.rar > ScaleSpace.cpp


// ScaleSpace.cpp: implementation of the CScaleSpace class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "TestSIFT.h" 
#include "ScaleSpace.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
// Variable s is from explanation in Lowe paper 
CScaleSpace::CScaleSpace(int s,double basePixScale) 
{ 
	m_scaleLevel = s; 
	m_dogCount = m_scaleLevel+2;	 
	m_gaussCount = m_scaleLevel+3; 
	m_gaussImgs = NULL; 
	m_dogImgs = NULL; 
	m_magnitudes = NULL; 
	m_directions = NULL; 
	m_basePixScale = basePixScale; 
} 
 
CScaleSpace::~CScaleSpace() 
{ 
	if (m_dogImgs != NULL) 
	{ 
		for (int i = 0; i < m_dogCount; i++) 
			cvReleaseImage (&m_dogImgs[i]); 
		delete m_dogImgs; 
	} 
	if (m_gaussImgs != NULL) 
	{ 
		for (int i = 0; i < m_gaussCount; i++) 
			cvReleaseImage (&m_gaussImgs[i]); 
		delete m_gaussImgs; 
	} 
} 
 
void CScaleSpace::buildDoG(IplImage *img,double startsigma,double firstScale) 
{ 
 
	m_gaussImgs = new IplImage*[m_gaussCount]; 
	for (int i = 0; i < m_gaussCount; i++) 
		m_gaussImgs[i] = cvCreateImage (cvGetSize(img),IPL_DEPTH_32F,1); 
	if (img->depth == IPL_DEPTH_32F) 
		cvCopy(img,m_gaussImgs[0]); 
	else 
	{ 
		for (int y = 0; y < cvGetSize(img).height; y++) 
			for (int x = 0; x < cvGetSize(img).width; x++) 
				cvSet2D (m_gaussImgs[0],y,x,cvGet2D(img,y,x)); 
	} 
	m_dogImgs = new IplImage*[m_dogCount]; 
	for (i = 0; i < m_dogCount; i++) 
		m_dogImgs[i] = cvCreateImage (cvGetSize(img),IPL_DEPTH_32F,1); 
	 
	/* This explaination is from Sebastian Nowozin's C# code 
	 * 
	 * Gaussian G(sigma), with relation 
	 * G(sigma_1) * G(sigma_2) = G(sqrt(sigma_1^2 + * sigma_2^2)) 
	 * 
	 * Then, we have: 
	 * 
	 * G(k^{p+1}) = G(k^p) * G(sigma), 
	 * and our goal is to compute every iterations sigma value so this 
	 * equation iteratively produces the next level.  Hence: 
	 * 
	 * sigma = \sqrt{\left(k^{p+1}\right)^2 - \left(k^p\right)^2} 
	 *   = \sqrt{k^{2p+2} - k^{2p}} 
	 *   = \sqrt{k^2p (k^2 - 1)} 
	 *   = k^p \sqrt{k^2 - 1} 
	 * 
	 * In the code below, 'w' is the running k^p, where p increases by one 
	 * each iteration.  kTerm is the constant \sqrt{k^2 - 1} term. 
	 */ 
	 // SToK (scales) = 2^(1/scales) == k 
	double k = exp (1.0/m_scaleLevel*log(2)); 
	double kTerm = sqrt (k*k - 1.0); 
	double w = startsigma; 
	for (i = 1 ; i < m_gaussCount ; i++) { 
		cvSmooth( m_gaussImgs[i-1], m_gaussImgs[i],CV_GAUSSIAN,0,0,w); 
		cvAbsDiff (m_gaussImgs[i-1],m_gaussImgs[i],m_dogImgs[i-1]); 
		w *= k; 
	}		 
} 
 
IplImage* CScaleSpace::getDoG(int i) 
{ 
	return m_dogImgs[i]; 
} 
 
int CScaleSpace::getDoGCount() 
{ 
	return m_dogCount; 
} 
 
void CScaleSpace::findPeak(double r,double dogThresh,std::vector* vec) 
{ 
	// dogThresh is in range [0,1] 
	dogThresh *= 255;  
	// {x,y,scale offset} 
	int dir[27][3] =  
	{{-1,-1,0},{0,-1,0},{1,-1,0},{-1,0,0},{1,0,0},{-1,1,0},{0,1,0},{1,1,0}, 
	{-1,-1,-1},{0,-1,-1},{1,-1,-1},{-1,0,-1},{0,0,-1},{1,0,-1},{-1,1,-1},{0,1,-1},{1,1,-1}, 
	{-1,-1,1},{0,-1,1},{1,-1,1},{-1,0,1},{0,0,1},{1,0,1},{-1,1,1},{0,1,1},{1,1,1}}; 
	for (int i = 1; i < m_dogCount-1; i++) 
	{ 
		for (int y = 1; y < cvGetSize (m_dogImgs[i]).height-1; y++) 
			for (int x = 1; x < cvGetSize (m_dogImgs[i]).width-1; x++) 
			{ 
				double val = cvGet2D (m_dogImgs[i],y,x).val[0]; 
				bool maxima = true; 
				for (int k = 0; k < 27; k++) 
				{ 
					if (val < dogThresh || val < cvGet2D (m_dogImgs[i+dir[k][2]],y+dir[k][1],x+dir[k][0]).val[0]) 
					{ 
						maxima = false; 
						break; 
					} 
				} 
				if (maxima) 
				{ 
					// Eliminate edge response 
					double dxx = cvGet2D(m_dogImgs[i],y,x+1).val[0] + 
								 cvGet2D(m_dogImgs[i],y,x-1).val[0] - 
								 2 * cvGet2D(m_dogImgs[i],y,x).val[0]; 
					double dyy = cvGet2D(m_dogImgs[i],y+1,x).val[0] + 
								 cvGet2D(m_dogImgs[i],y-1,x).val[0] - 
								 2 * cvGet2D(m_dogImgs[i],y,x).val[0]; 
					double dxy = 0.25 * (cvGet2D(m_dogImgs[i],y+1,x+1).val[0] - 
										 cvGet2D(m_dogImgs[i],y-1,x+1).val[0] - 
										 cvGet2D(m_dogImgs[i],y+1,x-1).val[0] + 
										 cvGet2D(m_dogImgs[i],y-1,x-1).val[0]);					 
					double tr = dxx+dyy; // trace  
					double det = dxx*dyy - dxy*dxy; //det					 
					double rsq = r+1; 
					rsq *= rsq; 
					if (tr*tr/(det+1e-20) > (rsq)/r) 
						maxima = false; 
				} 
				if (maxima) 
				{ 
					CScalePoint p (x,y,i); 
					vec->push_back(p); 
				} 
				//else 
				//	cvSet2D (dogImg[i],y,x,cvScalar(0)); 
			}		 
	} 
} 
 
void CScaleSpace::filterAndLocalizePeaks(std::vector& peaks,std::vector* filtered,  
										 double dValueLoThresh, double scaleAdjustThresh, int relocationMaximum) 
{ 
	dValueLoThresh *= 255; 
	CvMat *processedMap = cvCreateMat (cvGetSize(m_dogImgs[0]).height,cvGetSize(m_dogImgs[0]).width,CV_8UC1); 
	cvSetZero (processedMap); 
 
	for (int i = 0; i < peaks.size(); i++)  
	{ 
		// Kuas : already reject edge like in findPeak 
		//if (IsTooEdgelike (spaces[peak.Level], peak.X, peak.Y, edgeRatio)) 
		//	continue; 
 
		// When the localization hits some problem, i.e. while moving the 
		// point a border is reached, then skip this point. 
		if (LocalizeIsWeak (peaks[i], relocationMaximum, processedMap)) 
			continue; 
		 
		if (abs (peaks[i].m_fineS) > scaleAdjustThresh) 
			continue; 
 
		// Additional local pixel information is now available, threshhold 
		// the D(^x) 
		// Console.WriteLine ("{0} {1} {2} # == DVALUE", peak.Y, peak.X, peak.Local.DValue); 
		if (abs (peaks[i].m_DValue) <= dValueLoThresh) 
			continue; 
	 
		// its edgy enough, add it 
		filtered->push_back (peaks[i]); 
	} 
 
	cvReleaseMat (&processedMap); 
	//return (filtered); 
 
} 
 
bool CScaleSpace::LocalizeIsWeak(CScalePoint& peak, int steps, CvMat* processed) 
{ 
	bool needToAdjust = true; 
	int adjusted = steps; 
	while (needToAdjust)  
	{ 
		int x = peak.m_x; 
		int y = peak.m_y; 
 
		// Points we cannot say anything about, as they lie on the border 
		// of the scale space 
		// Kuas : seem to be not happen because in FindPeaks not get these peaks 
		//if (point.Level <= 0 || point.Level >= (spaces.Length - 1)) 
		//	return (true); 
 
		IplImage* dog = m_dogImgs[peak.m_level]; 
		if (x <= 0 || x >= (cvGetSize(dog).width-1)) 
			return (true); 
		if (y <= 0 || y >= (cvGetSize(dog).height-1)) 
			return (true); 
 
		double dp; 
		CvMat *adj = cvCreateMat (3,1,CV_32F); 
		GetAdjustment (peak, peak.m_level, x, y, &dp, adj); 
 
		// Get adjustments and check if we require further adjustments due 
		// to pixel level moves. If so, turn the adjustments into real 
		// changes and continue the loop. Do not adjust the plane, as we 
		// are usually quite low on planes in thie space and could not do 
		// further adjustments from the top/bottom planes. 
		double adjS = cvGet2D (adj,0,0).val[0];//adj[0, 0]; 
		double adjY = cvGet2D (adj,1,0).val[0];//adj[1, 0]; 
		double adjX = cvGet2D (adj,2,0).val[0];//adj[2, 0]; 
		cvReleaseMat (&adj); 
 
		if (abs (adjX) > 0.5 || abs (adjY) > 0.5)  
		{ 
			// Already adjusted the last time, give up 
			if (adjusted == 0) { 
				//Console.WriteLine ("too many adjustments, returning"); 
				return (true); 
			} 
 
			adjusted -= 1; 
 
			// Check that just one pixel step is needed, otherwise discard 
			// the point 
			double distSq = adjX * adjX + adjY * adjY; 
			if (distSq > 2.0) 
				return (true); 
 
			peak.m_x = (int) (peak.m_x + adjX + 0.5); 
			peak.m_y = (int) (peak.m_y + adjY + 0.5); 
			//point.Level = (int) (point.Level + adjS + 0.5); 
 
			//TRACE ("moved point by (%f,%f: %f) to (%d,%d: %d)", 
			//	adjX, adjY, adjS, peak.m_x, peak.m_y, peak.m_level); 
			continue; 
		} 
 
		// Check if we already have a keypoint within this octave for this 
		// pixel position in order to avoid dupes. (Maybe we can move this 
		// check earlier after any adjustment, so we catch dupes earlier). 
		// If its not in there, mark it for later searches. 
		// 
		// FIXME: check why there does not seem to be a dupe at all 
		if (cvGet2D (processed,peak.m_y,peak.m_x).val[0] != 0) 
			return (true); 
 
		cvSet2D (processed,peak.m_y,peak.m_x,cvScalar(1)); 
 
		// Save final sub-pixel adjustments. 
		peak.m_fineS = adjS; 
		peak.m_fineX = adjX; 
		peak.m_fineY = adjY; 
		peak.m_DValue = cvGet2D (dog,peak.m_y,peak.m_x).val[0] + 0.5 * dp; 
 
		needToAdjust = false; 
	} 
	return false; 
} 
 
void CScaleSpace::GetAdjustment(CScalePoint& point, int level, int x, int y, double* dp,CvMat* adj) 
{ 
	*dp = 0.0; 
	//if (point.Level <= 0 || point.Level >= (spaces.Length - 1)) 
	//	throw (new ArgumentException ("point.Level is not within [bottom-1;top-1] range")); 
 
	IplImage* below = m_dogImgs[level - 1]; 
	IplImage* current = m_dogImgs[level]; 
	IplImage* above = m_dogImgs[level + 1]; 
 
	CvMat* H = cvCreateMat( 3, 3, CV_32F); 
	//H[0, 0] = below[x, y] - 2 * current[x, y] + above[x, y]; 
	cvSet2D (H,0,0,cvScalar (cvGet2D (below,y,x).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(above,y,x).val[0]) ); 
	cvSet2D (H,0,1,cvScalar(0.25 * (cvGet2D(above,y+1,x).val[0] - cvGet2D(above,y-1,x).val[0] - (cvGet2D(below,y+1,x).val[0] - cvGet2D(below,y-1,x).val[0])))); 
	cvSet2D (H,1,0,cvGet2D (H,0,1)); 
	cvSet2D (H,0,2,cvScalar(0.25 * (cvGet2D(above,y,x+1).val[0] - cvGet2D(above,y,x-1).val[0] - (cvGet2D(below,y,x+1).val[0] - cvGet2D(below,y,x-1).val[0])))); 
	cvSet2D (H,2,0,cvGet2D(H,0,2));  
	cvSet2D (H,1,1,cvScalar(cvGet2D(current,y-1,x).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(current,y+1,x).val[0])); 
	cvSet2D (H,1,2,cvScalar(0.25 * (cvGet2D(current,y+1,x+1).val[0] - cvGet2D(current,y+1,x-1).val[0] -(cvGet2D(current,y-1,x+1).val[0] - cvGet2D(current,y-1,x-1).val[0])))); 
	cvSet2D (H,2,1,cvGet2D(H,1,2)); 
	cvSet2D (H,2,2,cvScalar(cvGet2D(current,y,x-1).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(current,y,x+1).val[0])); 
 
	CvMat* d = cvCreateMat(3, 1, CV_32F); 
	cvSet2D (d,0,0,cvScalar(0.5 * (cvGet2D(above,y,x).val[0] - cvGet2D(below,y,x).val[0]))); 
	cvSet2D (d,1,0,cvScalar(0.5 * (cvGet2D(current,y+1,x).val[0] - cvGet2D(current,y-1,x).val[0]))); 
	cvSet2D (d,2,0,cvScalar(0.5 * (cvGet2D(current,y,x+1).val[0] - cvGet2D(current,y,x-1).val[0]))); 
 
	CvMat* b = cvCloneMat(d); 
	//b.Negate (); 
	cvSet2D (b,0,0,cvScalar(-cvGet2D(b,0,0).val[0])); 
	cvSet2D (b,1,0,cvScalar(-cvGet2D(b,1,0).val[0])); 
	cvSet2D (b,2,0,cvScalar(-cvGet2D(b,2,0).val[0])); 
 
	// Solve: A x = b 
	//H.SolveLinear (b); 
	// adj ::= x 
	cvSolve (H,b,adj); 
 
	*dp = cvDotProduct(adj,d); 
	 
	cvReleaseMat (&H); 
	cvReleaseMat (&d); 
	cvReleaseMat (&b); 
} 
 
#define sqr(x) (x*x) 
 
void CScaleSpace::GenMagnitudeAndDirectionMaps() 
{ 
	// We leave the first entry to null, and ommit the last. This way, the 
	// magnitudes and directions maps have the same index as the 
	// imgScaled maps they belong to. 
	m_magnitudes = new IplImage*[m_dogCount - 1]; 
	m_directions = new IplImage*[m_dogCount - 1]; 
 
	// Build the maps, omitting the border pixels, as we cannot build 
	// gradient information there. 
	for (int s = 1 ; s < (m_dogCount - 1); s++)  
	{ 
		m_magnitudes[s] = cvCreateImage (cvGetSize(m_dogImgs[0]),IPL_DEPTH_32F,1); 
		m_directions[s] = cvCreateImage (cvGetSize(m_dogImgs[0]),IPL_DEPTH_32F,1); 
 
		for (int y = 1 ; y < cvGetSize(m_gaussImgs[s]).height - 1 ; y++)  
		{ 
			for (int x = 1 ; x < cvGetSize(m_gaussImgs[s]).width - 1 ; x++)  
			{ 
				// gradient magnitude m 
				double dx = cvGet2D(m_gaussImgs[s],y,x+1).val[0] -  
							cvGet2D(m_gaussImgs[s],y,x-1).val[0]; 
				double dy = cvGet2D(m_gaussImgs[s],y+1,x).val[0] -  
							cvGet2D(m_gaussImgs[s],y-1,x).val[0]; 
				cvSet2D (m_magnitudes[s],y,x,cvScalar( sqrt (dx*dx + dy*dy) ) ); 
 
				// gradient direction theta 
				cvSet2D (m_directions[s],y,x,cvScalar(atan2(dy,dx) )); 
			} 
		} 
	}	 
} 
 
void CScaleSpace::ClearMagnitudeAndDirectionMaps() 
{ 
	for (int i = 1; i < m_dogCount-1; i++) 
	{ 
		cvReleaseImage (&m_magnitudes[i]); 
		cvReleaseImage (&m_directions[i]); 
	} 
	delete m_magnitudes; 
	delete m_directions; 
} 
 
void CScaleSpace::GenerateKeypoints (std::vector& localizedPeaks, std::vector* keypoints,int scaleCount, double octaveSigma) 
{ 
	for (int i = 0; i < localizedPeaks.size(); i++)  
	{ 
		std::vector thisKeyPoints; 
		// Generate zero or more keypoints from the scale point locations. 
		// TODO: make the values configurable 
		// Songkran : make 36,0.8 be constant 
		GenerateKeypointSingle (m_basePixScale, localizedPeaks[i], 36, 0.8, scaleCount, octaveSigma, &thisKeyPoints); 
 
 
		 
		// Generate the feature descriptor. 
		CreateDescriptors (&thisKeyPoints,m_magnitudes[localizedPeaks[i].m_level], m_directions[localizedPeaks[i].m_level], 2.0, 4, 8, 0.2); 
 
		 
		// Only copy over those keypoints that have been successfully 
		// assigned a descriptor (feature vector). 
		// Songkran : all key must be already assign feature vector i think 
		for (int j = 0; j < thisKeyPoints.size(); j++)  
		{ 
			//if (kp.HasFV == false) 
			//	throw (new Exception ("should not happen")); 
 
			// Transform the this level image relative coordinate system 
			// to the original image coordinates by multiplying with the 
			// current img scale (which starts with either 0.5 or 1.0 and 
			// then always doubles: 2.0, 4.0, ..) 
			// Note that the kp coordinates are not used for processing by 
			// the detection methods and this has to be the last step. 
			// Also transform the relative-to-image scale to an 
			// absolute-to-original-image scale. 
 
			// Songkran :  
			// m_kpScale = octaveSigma * exp (((point.m_level + point.m_fineS) / scaleCount) * log(2)); 
			CKeyPoint* kp = thisKeyPoints[j]; 
			if (kp->m_featureVec == NULL) 
				TRACE ("Songkran : Something wrong with feature point descriptor\n"); 
			kp->m_x *= kp->m_imgScale; 
			kp->m_y *= kp->m_imgScale; 
			kp->m_kpScale *= kp->m_imgScale; 
			keypoints->push_back (kp); 
		} 
	} 
} 
 
 
// Average the content of the direction bins. 
void CScaleSpace::AverageWeakBins (double* bins, 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 < 4 ; ++sn)  
	{ 
		double firstE = bins[0]; 
		double last = bins[binCount - 1]; 
 
		for (int sw = 0 ; sw < binCount ; ++sw)  
		{ 
			double cur = bins[sw]; 
			double next = (sw == (binCount - 1)) ? firstE : bins[(sw + 1) % binCount]; 
			bins[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 CScaleSpace::InterpolateOrientation (double left, double middle, 
							 double right, double* degreeCorrection, double* peakValue) 
{ 
	// Kuas: 
	// if we set f(x) = 0 then 
	// a ( x^2 -2cx + c^2) + b= 0 
	// x1+x2 = 2c/a ,so we get 
	// a = 2c/(x1+x2) 
	// and so .. i don't under stand this code 
	double a = ((left + right) - 2.0 * middle) / 2.0; 
 
	// Not a parabola 
	if (a == 0.0) 
		return (false); 
 
	*peakValue = 0; //Double.NaN; 
	*degreeCorrection = 0; //Double.NaN; 
 
	double c = (((left - middle) / a) - 1.0) / 2.0; 
	double b = middle - c * c * a; 
 
	if (c < -0.5 || c > 0.5) 
	{ 
		TRACE ("KUAS : ERROR IN InterpolateOrientation\n"); 
	} 
 
	*degreeCorrection = c; 
	*peakValue = b; 
 
	return (true); 
} 
 
 
// Create the descriptor vector for a list of keypoints. 
// 
// keypoints: The list of keypoints to be processed. Everything but the 
//     descriptor must be filled in already. 
// magnitude/direction: The precomputed gradient magnitude and direction 
//     maps. 
// considerScaleFactor: The downscale factor, which describes the amount 
//     of pixels in the circular region relative to the keypoint scale. 
//     Low values means few pixels considered, large values extend the 
//     range. (Use values between 1.0 and 6.0) 
// descDim: The dimension size of the output descriptor. There will be 
//     descDim * descDim * directionCount elements in the feature vector. 
// directionCount: The dimensionality of the low level gradient vectors. 
// fvGradHicap: The feature vector gradient length hi-cap threshhold. 
//     (Should be: 0.2) 
// 
// Some parts modelled after Alexandre Jenny's Matlab implementation. 
// 
// Songkran : I think this is not implements according to Lowe's paper 
//			  but the idia behind is quite the same 
//			  in this implementation he use variable size of window (change by scale of DoG img) 
void CScaleSpace::CreateDescriptors (std::vector* keypoints, 
	IplImage* magnitude, IplImage* direction, 
	double considerScaleFactor, int descDim, int directionCount, 
	double fvGradHicap) 
//	2.0, 4, 8, 
//	0.2); 
{ 
	if (keypoints->size() <= 0) 
		return; 
 
	considerScaleFactor *= (*keypoints)[0]->m_kpScale; 
	double dDim05 = ((double) descDim) / 2.0; 
 
	// Now calculate the radius: We consider pixels in a square with 
	// dimension 'descDim' plus 0.5 in each direction. As the feature 
	// vector elements at the diagonal borders are most distant from the 
	// center pixel we have scale up with sqrt(2). 
	// Songkran : I think in Lowe paper he suggest to use radius = (descDim*4)/2 * sqrt(2) 
	int radius = (int) (((descDim + 1.0) / 2) * 
		sqrt (2.0) * considerScaleFactor + 0.5); 
 
	// Precompute the sigma for the "center-most, border-less" gaussian 
	// weighting. 
	// (We are operating to dDim05, CV book tells us G(x), x > 3 \sigma 
	//  negligible, but this range seems much shorter!?) 
	// 
	// In Lowe03, page 15 it says "A Gaussian weighting function with 
	// \sigma equal to one half the width of the descriptor window is 
	// used", so we just use his advice. 
	// Songkran : should the line below recorrect to 2.0 * (r/2) * (r/2) 
	double sigma2Sq = 2.0 * dDim05 * dDim05; 
 
	for (int i = 0; i < keypoints->size(); i++) 
	{ 
		CKeyPoint* kp = (*keypoints)[i]; 
		// The angle to rotate with: negate the orientation. 
		double angle = -kp->m_orientation; 
 
		kp->CreateVector (descDim, descDim, directionCount); 
		//Console.WriteLine ("  FV allocated"); 
 
		for (int y = -radius ; y < radius ; ++y)  
		{ 
			for (int x = -radius ; x < radius ; ++x)  
			{ 
				// Rotate and scale 
				double yR = sin (angle) * x + cos (angle) * y; 
				double xR = cos (angle) * x - sin (angle) * y; 
 
				yR /= considerScaleFactor; 
				xR /= considerScaleFactor; 
 
				// Now consider all (xR, yR) that are anchored within 
				// (- descDim/2 - 0.5 ; -descDim/2 - 0.5) to 
				//    (descDim/2 + 0.5 ; descDim/2 + 0.5), 
				// as only those can influence the FV. 
				if (yR >= (dDim05 + 0.5) || xR >= (dDim05 + 0.5) || 
					xR <= -(dDim05 + 0.5) || yR <= -(dDim05 + 0.5)) 
					continue; 
 
				int currentX = (int) (x + kp->m_x + 0.5); 
				int currentY = (int) (y + kp->m_y + 0.5); 
				if (currentX < 1 || currentX >= (cvGetSize(magnitude).width - 1) || 
					currentY < 1 || currentY >= (cvGetSize(magnitude).height - 1)) 
					continue; 
 
				// Weight the magnitude relative to the center of the 
				// whole FV. We do not need a normalizing factor now, as 
				// we normalize the whole FV later anyway (see below). 
				// xR, yR are each in -(dDim05 + 0.5) to (dDim05 + 0.5) 
				// range 
				double magW = exp (-(xR * xR + yR * yR) / sigma2Sq) *  
					cvGet2D (magnitude,currentY,currentX).val[0]; 
 
				// Anchor to (-1.0, -1.0)-(dDim + 1.0, dDim + 1.0), where 
				// the FV points are located at (x, y) 
				// Songkran : i think this should be yR += dDim05; 
				// Songkran : and range is from (-0.5,-0.5) to (dDim+0.5,dDim+0.5) 
				//yR += dDim05 - 0.5; 
				//xR += dDim05 - 0.5; 
				yR += dDim05; 
				xR += dDim05; 
 
				// Build linear interpolation weights: 
				// A B 
				// C D 
				// 
				// The keypoint is located between A, B, C and D. 
				// Songkran : this is trilinear interpolation (3 variable x,y,dir) 
				int xIdx[2]; 
				int yIdx[2]; 
				int dirIdx[2]; 
				double xWeight[2]; 
				double yWeight[2]; 
				double dirWeight[2]; 
				bool flagx[2],flagy[2],flagdir[2]; 
				memset (flagx,false,2*sizeof(bool)); 
				memset (flagy,false,2*sizeof(bool)); 
				memset (flagdir,false,2*sizeof(bool)); 
 
				// Songkran : have to check xR < descDim also ?? 
				if (xR >= 0 && xR < descDim)  
				{ 
					xIdx[0] = (int) xR; 
					xWeight[0] = (1.0 - (xR - xIdx[0])); 
					flagx[0] = true; 
				} 
				if (yR >= 0 && yR < descDim)  
				{ 
					yIdx[0] = (int) yR; 
					yWeight[0] = (1.0 - (yR - yIdx[0])); 
					flagy[0] = true; 
				} 
 
				if (xR+1 < descDim)  
				{ 
					xIdx[1] = (int) (xR + 1.0); 
					//	xWeight[0] + xWeight[1] = 1 
					//  1 - xR + xIdx[0] + xR - xIdx[0] - 1 + 1 
					xWeight[1] = xR - xIdx[1] + 1.0; 
					flagx[1] = true; 
				} 
				if (yR+1 < descDim)  
				{ 
					//	yWeight[0] + yWeight[1] = 1 
					//  1 - yR + yIdy[0] + yR - yIdy[0] - 1 + 1 
					yIdx[1] = (int) (yR + 1.0); 
					yWeight[1] = yR - yIdx[1] + 1.0; 
					flagy[1] = true; 
				} 
 
				// Rotate the gradient direction by the keypoint 
				// orientation, then normalize to [-pi ; pi] range. 
				// Songkran : should correct to dir += 2*Math.PI ? 
				double dir = cvGet2D(direction,currentY,currentX).val[0] - kp->m_orientation; 
				if (dir <= -PI) 
					//dir += Math.PI; 
					dir += 2*PI; 
				if (dir > PI) 
					//dir -= Math.PI; 
					dir -= 2*PI; 
 
				double idxDir = (dir * directionCount) / (2.0 * PI); 
				if (idxDir < 0.0) 
					idxDir += directionCount; 
 
				dirIdx[0] = (int) idxDir; 
				dirIdx[1] = (dirIdx[0] + 1) % directionCount; 
				dirWeight[0] = 1.0 - (idxDir - dirIdx[0]); 
				dirWeight[1] = idxDir - dirIdx[0]; 
 
				for (int iy = 0 ; iy < 2 ; ++iy)  
					for (int ix = 0 ; ix < 2 ; ++ix)  
						for (int id = 0 ; id < 2 ; ++id)  
							if (flagx[ix] && flagy[iy]) 
								kp->FVSet (xIdx[ix], yIdx[iy], dirIdx[id], 
								kp->FVGet (xIdx[ix], yIdx[iy], dirIdx[id]) + 
								xWeight[ix] * yWeight[iy] * dirWeight[id] * magW); 
			} 
		} 
 
		// Normalize and hicap the feature vector, as recommended on page 
		// 16 in Lowe03. 
		kp->CapAndNormalizeFV (fvGradHicap); 
	} 
 
} 
 
 
// Assign each feature point one or more standardized orientations. 
// (section 5 in Lowe's paper) 
// 
// We use an orientation histogram with 36 bins, 10 degrees each. For 
// this, every pixel (x,y) lieing in a circle of 'squareDim' diameter 
// within in a 'squareDim' sized field within the image L ('gaussImg') is 
// examined and two measures calculated: 
// 
//    m = \sqrt{ (L_{x+1,y} - L_{x-1,y})^2 + (L_{x,y+1} - L_{x,y-1})^2 } 
//    theta = tan^{-1} ( \frac{ L_{x,y+1} - L_{x,y-1} } 
//                { L_{x+1,y} - L_{x-1,y} } ) 
// 
// Where m is the gradient magnitude around the pixel and theta is the 
// gradient orientation. The 'imgScale' value is the octave scale, 
// starting with 1.0 at the finest-detail octave, and doubling every 
// octave. The gradient orientations are discreetized to 'binCount' 
// directions (should be: 36). For every peak orientation that lies within 
// 'peakRelThresh' of the maximum peak value, a keypoint location is 
// added (should be: 0.8). 
// 
// Note that 'space' is the gaussian smoothed original image, not the 
// difference-of-gaussian one used for peak-search. 
 
// Kuas : imgScale is basePixScale (start scale (relative dimension with input img) of that octave) 
void CScaleSpace::GenerateKeypointSingle (double imgScale, CScalePoint point, 
	int binCount, double peakRelThresh, int scaleCount, 
	double octaveSigma, std::vector*vec) 
{ 
	// The relative estimated keypoint scale. The actual absolute keypoint 
	// scale to the original image is yielded by the product of imgScale. 
	// But as we operate in the current octave, the size relative to the 
	// anchoring images is missing the imgScale factor. 
	//double kpScale = octaveSigma * exp (((point.m_level + point.m_fineS) / scaleCount) * log(2)); 
double kpScale = octaveSigma * (exp (((point.m_level) / scaleCount) * log(2))+ point.m_fineS); 
	// Lowe03, "A gaussian-weighted circular window with a \sigma three 
	// times that of the scale of the keypoint". 
	// 
	// With \sigma = 3.0 * kpScale, the square dimension we have to 
	// consider is (3 * \sigma) (until the weight becomes very small). 
	// Kuas : Lowe04 say \sigma = 1.5 * kpScale 
	double sigma = 1.5 * kpScale; 
	int radius = (int) (3.0 * sigma / 2.0 + 0.5); 
	int radiusSq = radius * radius; 
 
	IplImage* magnitude = m_magnitudes[point.m_level]; 
	IplImage* direction = m_directions[point.m_level]; 
 
	// As the point may lie near the border, build the rectangle 
	// coordinates we can still reach, minus the border pixels, for which 
	// we do not have gradient information available. 
	int xMin = point.m_x - radius > 1 ? point.m_x - radius : 1; 
	int xMax = point.m_x + radius < cvGetSize(magnitude).width-1 ? point.m_x + radius : cvGetSize(magnitude).width-1; 
	int yMin = point.m_y - radius > 1 ? point.m_y - radius : 1; 
	int yMax = point.m_y + radius < cvGetSize(magnitude).height-1 ? point.m_y + radius : cvGetSize(magnitude).height-1; 
 
	// Precompute 1D gaussian divisor (2 \sigma^2) in: 
	// G(r) = e^{-\frac{r^2}{2 \sigma^2}} 
	double gaussianSigmaFactor = 2.0 * sigma * sigma; 
 
	double *bins = new double[binCount]; 
	memset (bins,0,sizeof(double)*binCount); 
	// Build the direction histogram 
	for (int y = yMin ; y < yMax ; ++y) { 
		for (int x = xMin ; x < xMax ; ++x) { 
			// Only consider pixels in the circle, else we might skew the 
			// orientation histogram by considering more pixels into the 
			// corner directions 
			int relX = x - point.m_x; 
			int relY = y - point.m_y; 
			if (sqr(relX) + sqr(relY) > radiusSq) 
				continue; 
 
			// The gaussian weight factor. 
			double gaussianWeight = exp (- ((relX * relX + relY * relY) / gaussianSigmaFactor)); 
 
			// find the closest bin and add the direction 
			//int binIdx = FindClosestRotationBin (binCount, direction[x, y]); 
			double angle = cvGet2D (direction,y,x).val[0]; 
			angle += PI; 
			angle /= 2.0 * PI; 
 
			// calculate the aligned bin 
			angle *= binCount; 
 
			int binIdx = (int) angle; 
			if (binIdx == binCount) 
				binIdx = 0; 
 
			bins[binIdx] += cvGet2D(magnitude,y,x).val[0] * gaussianWeight; 
		} 
	} 
 
	// As there may be succeeding histogram entries like this: 
	// ( ..., 0.4, 0.3, 0.4, ... ) where the real peak is located at the 
	// middle of this three entries, we can improve the distinctiveness of 
	// the bins by applying an averaging pass. 
	// 
	// TODO: is this really the best method? (we also loose a bit of 
	// information. Maybe there is a one-step method that conserves more) 
	AverageWeakBins (bins, binCount); 
 
	// find the maximum peak in gradient orientation 
	double maxGrad = 0.0; 
	int maxBin = 0; 
	for (int b = 0 ; b < binCount ; ++b) { 
		if (bins[b] > maxGrad) { 
			maxGrad = bins[b]; 
			maxBin = b; 
		} 
	} 
 
	// First determine the real interpolated peak high at the maximum bin 
	// position, which is guaranteed to be an absolute peak. 
	// 
	// XXX: should we use the estimated peak value as reference for the 
	//   0.8 check or the original bin-value? 
	double maxPeakValue, maxDegreeCorrection; 
	InterpolateOrientation (bins[maxBin == 0 ? (binCount - 1) : (maxBin - 1)], 
		bins[maxBin], bins[(maxBin + 1) % binCount], 
		&maxDegreeCorrection, &maxPeakValue); 
 
	// 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 = new bool[binCount]; 
	for (b = 0 ; b < binCount ; ++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 (bins[b] < (peakRelThresh * maxPeakValue)) 
			continue; 
 
		int leftI = (b == 0) ? (binCount - 1) : (b - 1); 
		int rightI = (b + 1) % binCount; 
		if (bins[b] <= bins[leftI] || bins[b] <= bins[rightI]) 
			continue;	// not local peak 
 
		binIsKeypoint[b] = true; 
	} 
 
	// All the valid keypoint bins are now marked in binIsKeypoint, now 
	// build them. 
 
	// find other possible locations 
	double oneBinRad = (2.0 * PI) / binCount; 
 
	for (b = 0 ; b < binCount ; ++b)  
	{ 
		if (binIsKeypoint[b] == false) 
			continue; 
 
		int bLeft = (b == 0) ? (binCount - 1) : (b - 1); 
		int bRight = (b + 1) % binCount; 
 
		// Get an interpolated peak direction and value guess. 
		double peakValue; 
		double degreeCorrection; 
 
		if (InterpolateOrientation (bins[bLeft], bins[b], bins[bRight], 
			°reeCorrection, &peakValue) == false) 
		{ 
			//throw (new InvalidOperationException ("BUG: Parabola fitting broken")); 
			TRACE ("BUG: Parabola fitting broken\n"); 
		} 
 
		// [-1.0 ; 1.0] -> [0 ; binrange], and add the fixed absolute bin 
		// position. 
		// We subtract PI because bin 0 refers to 0, binCount-1 bin refers 
		// to a bin just below 2PI, so -> [-PI ; PI]. Note that at this 
		// point we determine the canonical descriptor anchor angle. It 
		// does not matter where we set it relative to the peak degree, 
		// but it has to be constant. Also, if the output of this 
		// implementation is to be matched with other implementations it 
		// must be the same constant angle (here: -PI). 
		double degree = (b + degreeCorrection) * oneBinRad - PI; 
 
		if (degree < -PI) 
			degree += 2.0 * PI; 
		else if (degree > PI) 
			degree -= 2.0 * PI; 
 
		CKeyPoint* kp = new CKeyPoint (m_gaussImgs[point.m_level], 
			point.m_x + point.m_fineX, 
			point.m_y + point.m_fineY, 
			imgScale, kpScale, degree); 
		vec->push_back (kp); 
	} 
 
	delete binIsKeypoint; 
	delete bins; 
}