// SiftDetect.cpp: implementation of the CSiftDetect class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
//#include "ImageSeek.h"
#include "SiftDetect.h"
#include <math.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
/*
Compares features for a decreasing-scale ordering. Intended for use with
CvSeqSort
@param feat1 first feature
@param feat2 second feature
@param param unused
@return Returns 1 if feat1's scale is greater than feat2's, -1 if vice versa,
and 0 if their scales are equal
*/
int feature_cmp( void* feat1, void* feat2, void* param )
{
struct feature* f1 = (struct feature*) feat1;
struct feature* f2 = (struct feature*) feat2;
if( f1->scl < f2->scl )
return 1;
if( f1->scl > f2->scl )
return -1;
return 0;
}
CSiftDetect::CSiftDetect()
{
}
CSiftDetect::~CSiftDetect()
{
}
int CSiftDetect::sift_features( IplImage* img, struct feature** feat )
{
return _sift_features( img, feat, SIFT_INTVLS, SIFT_SIGMA, SIFT_CONTR_THR,
SIFT_CURV_THR, SIFT_IMG_DBL, SIFT_DESCR_WIDTH,
SIFT_DESCR_HIST_BINS );
}
int CSiftDetect::_sift_features( IplImage* img, struct feature** feat, int intvls,
double sigma, double contr_thr, int curv_thr,
int img_dbl, int descr_width, int descr_hist_bins )
{
IplImage* init_img;
IplImage*** gauss_pyr, *** dog_pyr;
CvMemStorage* storage;
CvSeq* features;
int octvs, i, n = 0;
/* check arguments */
if( ! img )
TRACE( "NULL pointer error, %s, line %d", __FILE__, __LINE__ );
if( ! feat )
TRACE( "NULL pointer error, %s, line %d", __FILE__, __LINE__ );
/* build scale space pyramid; smallest dimension of top level is ~4 pixels */
init_img = create_init_img( img, img_dbl, sigma );
octvs = (log( (double)MIN( init_img->width, init_img->height ) ) / log(2.0) - 2);
gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );
dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls );
storage = cvCreateMemStorage( 0 );
features = scale_space_extrema( dog_pyr, octvs, intvls, contr_thr,
curv_thr, storage );
calc_feature_scales( features, sigma, intvls );
if( img_dbl )
adjust_for_img_dbl( features );
calc_feature_oris( features, gauss_pyr );
compute_descriptors( features, gauss_pyr, descr_width, descr_hist_bins );
cvSeqSort(features, (CvCmpFunc)feature_cmp, NULL );
n = features->total;
*feat =(struct feature *) calloc( n, sizeof(struct feature) );
*feat = (struct feature *)cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ );
/**/ for( i = 0; i < n; i++ )
{
free( (*feat)[i].feature_data );
(*feat)[i].feature_data = NULL;
}
cvReleaseMemStorage( &storage );
cvReleaseImage( &init_img );
release_pyr( &gauss_pyr, octvs, intvls + 3 );
release_pyr( &dog_pyr, octvs, intvls + 2 );
return n;
}
/************************ Functions prototyped here **************************/
/*
Converts an image to 8-bit grayscale and Gaussian-smooths it. The image is
optionally doubled in size prior to smoothing.
@param img input image
@param img_dbl if true, image is doubled in size prior to smoothing
@param sigma total std of Gaussian smoothing
*/
IplImage* CSiftDetect::create_init_img( IplImage* img, int img_dbl, double sigma )
{
IplImage* gray, * dbl;
double sig_diff;
gray = convert_to_gray32( img );
if( img_dbl )
{
sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 );
dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ),
IPL_DEPTH_32F, 1 );
cvResize( gray, dbl, CV_INTER_CUBIC );
cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );
cvReleaseImage( &gray );
return dbl;
}
else
{
sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA );
cvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );
return gray;
}
}
/*
Converts an image to 32-bit grayscale
@param img a 3-channel 8-bit color (BGR) or 8-bit gray image
@return Returns a 32-bit grayscale image
*/
IplImage* CSiftDetect::convert_to_gray32( IplImage* img )
{
IplImage* gray8, * gray32;
gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );
gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 );
if( img->nChannels == 1 )
gray8 =( IplImage*)cvClone( img );
else
cvCvtColor( img, gray8, CV_RGB2GRAY );
cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 );
cvReleaseImage( &gray8 );
return gray32;
}
/*
Builds Gaussian scale space pyramid from an image
@param base base image of the pyramid
@param octvs number of octaves of scale space
@param intvls number of intervals per octave
@param sigma amount of Gaussian smoothing per octave
@return Returns a Gaussian scale space pyramid as an octvs x (intvls + 3) array
*/
IplImage*** CSiftDetect::build_gauss_pyr( IplImage* base, int octvs,
int intvls, double sigma )
{
IplImage*** gauss_pyr;
double* sig =(double*) calloc( intvls + 3, sizeof(double));
double sig_total, sig_prev, k;
int i, o;
gauss_pyr = (IplImage***) calloc( octvs, sizeof( IplImage** ) );
for( i = 0; i < octvs; i++ )
gauss_pyr[i] =(IplImage**) calloc( intvls + 3, sizeof( IplImage* ) );
/*
precompute Gaussian sigmas using the following formula:
\sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2
*/
sig[0] = sigma;
k = pow( 2.0, 1.0 / intvls );
for( i = 1; i < intvls + 3; i++ )
{
sig_prev = pow( k, i - 1 ) * sigma;
sig_total = sig_prev * k;
sig[i] = sqrt( sig_total * sig_total - sig_prev * sig_prev );
}
for( o = 0; o < octvs; o++ )
for( i = 0; i < intvls + 3; i++ )
{
if( o == 0 && i == 0 )
gauss_pyr[o][i] = cvCloneImage(base);
/* base of new octvave is halved image from end of previous octave */
else if( i == 0 )
gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] );
/* blur the current octave's last image to create the next one */
else
{
gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]),
IPL_DEPTH_32F, 1 );
cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i],
CV_GAUSSIAN, 0, 0, sig[i], sig[i] );
}
}
free( sig );
return gauss_pyr;
}
/*
Downsamples an image to a quarter of its size (half in each dimension)
using nearest-neighbor interpolation
@param img an image
@return Returns an image whose dimensions are half those of img
*/
IplImage* CSiftDetect::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;
}
/*
Builds a difference of Gaussians scale space pyramid by subtracting adjacent
intervals of a Gaussian pyramid
@param gauss_pyr Gaussian scale-space pyramid
@param octvs number of octaves of scale space
@param intvls number of intervals per octave
@return Returns a difference of Gaussians scale space pyramid as an
octvs x (intvls + 2) array
*/
IplImage*** CSiftDetect::build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls )
{
IplImage*** dog_pyr;
int i, o;
dog_pyr = (IplImage***)calloc( octvs, sizeof( IplImage** ) );
for( i = 0; i < octvs; i++ )
dog_pyr[i] =(IplImage**) calloc( intvls + 2, sizeof(IplImage*) );
for( o = 0; o < octvs; o++ )
for( i = 0; i < intvls + 2; i++ )
{
dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]),
IPL_DEPTH_32F, 1 );
cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL );
}
return dog_pyr;
}
/*
Detects features at extrema in DoG scale space. Bad features are discarded
based on contrast