00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00024
00025 #include "GL/glew.h"
00026 #include <iostream>
00027 #include <fstream>
00028 #include <string>
00029 #include <iomanip>
00030 #include <vector>
00031 #include <algorithm>
00032 #include <math.h>
00033
00034 #include <time.h>
00035 using namespace std;
00036
00037
00038 #include "GlobalUtil.h"
00039 #include "SiftGPU.h"
00040 #include "GLTexImage.h"
00041 #include "ShaderMan.h"
00042 #include "FrameBufferObject.h"
00043 #include "SiftPyramid.h"
00044 #include "PyramidGL.h"
00045
00046
00047 #if defined(CUDA_SIFTGPU_ENABLED)
00048 #include "PyramidCU.h"
00049 #endif
00050
00051 #if defined(CL_SIFTGPU_ENABLED)
00052 #include "PyramidCL.h"
00053 #endif
00054
00055
00057 #if defined(_WIN32)
00058 #include "direct.h"
00059 #pragma warning (disable : 4786)
00060 #pragma warning (disable : 4996)
00061 #else
00062
00063 #define _stricmp strcasecmp
00064 #include <stdlib.h>
00065 #include <string.h>
00066 #endif
00067
00068 #if !defined(_MAX_PATH)
00069 #if defined (PATH_MAX)
00070 #define _MAX_PATH PATH_MAX
00071 #else
00072 #define _MAX_PATH 512
00073 #endif
00074 #endif
00075
00078
00079
00080 class ImageList:public std::vector<std::string> {};
00081
00082 SiftGPU::SiftGPU(int np)
00083 {
00084 _texImage = new GLTexInput;
00085 _imgpath = new char[_MAX_PATH];
00086 _outpath = new char[_MAX_PATH];
00087 _imgpath[0] = _outpath[0] = 0;
00088 _initialized = 0;
00089 _image_loaded = 0;
00090 _current = 0;
00091 _list = new ImageList();
00092 _pyramid = NULL;
00093 }
00094
00095
00096
00097 SiftGPUEX::SiftGPUEX()
00098 {
00099 _view = _sub_view = 0;
00100 _view_debug = 0;
00101 GlobalUtil::_UseSiftGPUEX = 1;
00102 srand((unsigned int)time(NULL));
00103 RandomizeColor();
00104 }
00105
00106 void* SiftGPU::operator new (size_t size){
00107 void * p = malloc(size);
00108 if (p == 0)
00109 {
00110 const std::bad_alloc ba;
00111 throw ba;
00112 }
00113 return p;
00114 }
00115
00116
00117 void SiftGPUEX::RandomizeColor()
00118 {
00119 float hsv[3] = {0, 0.8f, 1.0f};
00120 for(int i = 0; i < COLOR_NUM*3; i+=3)
00121 {
00122 hsv[0] = (rand()%100)*0.01f;
00123 HSVtoRGB(hsv, _colors+i);
00124 }
00125 }
00126
00127 SiftGPU::~SiftGPU()
00128 {
00129 if(_pyramid) delete _pyramid;
00130 delete _texImage;
00131 delete _list;
00132 delete[] _imgpath;
00133 delete[] _outpath;
00134 }
00135
00136
00137 inline void SiftGPU::InitSiftGPU()
00138 {
00139 if(_initialized || GlobalUtil::_GoodOpenGL ==0) return;
00140
00141
00142 ParseSiftParam();
00143
00144 #if !defined(CUDA_SIFTGPU_ENABLED)
00145 if(GlobalUtil::_UseCUDA)
00146 {
00147 GlobalUtil::_UseCUDA = 0;
00148 std::cerr << "---------------------------------------------------------------------------\n"
00149 << "CUDA not supported in this binary! To enable it, please use SiftGPU_CUDA_Enable\n"
00150 << "solution for VS2005+ or set siftgpu_enable_cuda to 1 in makefile\n"
00151 << "----------------------------------------------------------------------------\n";
00152 }
00153 #else
00154 if(GlobalUtil::_UseCUDA == 0 && GlobalUtil::_UseOpenCL == 0)
00155 {
00156 GlobalUtil::InitGLParam(0);
00157 }
00158 if(GlobalUtil::_GoodOpenGL == 0)
00159 {
00160 GlobalUtil::_UseCUDA = 1;
00161 std::cerr << "Switch from OpenGL to CUDA\n";
00162 }
00163
00164 if(GlobalUtil::_UseCUDA && !PyramidCU::CheckCudaDevice(GlobalUtil::_DeviceIndex))
00165 {
00166 std::cerr << "Switch from CUDA to OpenGL\n";
00167 GlobalUtil::_UseCUDA = 0;
00168 }
00169 #endif
00170
00171 if(GlobalUtil::_verbose) std::cout <<"\n[SiftGPU Language]:\t"
00172 << (GlobalUtil::_UseCUDA? "CUDA" :
00173 (GlobalUtil::_UseOpenCL? "OpenCL" : "GLSL")) <<"\n";
00174
00175 #if defined(CUDA_SIFTGPU_ENABLED)
00176 if(GlobalUtil::_UseCUDA)
00177 _pyramid = new PyramidCU(*this);
00178 else
00179 #endif
00180 #if defined(CL_SIFTGPU_ENABLED)
00181 if(GlobalUtil::_UseOpenCL)
00182 _pyramid = new PyramidCL(*this);
00183 else
00184 #endif
00185
00186 if(GlobalUtil::_usePackedTex) {
00187 _pyramid = new PyramidPacked(*this);
00188 }
00189 else
00190 _pyramid = new PyramidNaive(*this);
00191
00192 if(GlobalUtil::_GoodOpenGL && GlobalUtil::_InitPyramidWidth > 0 && GlobalUtil::_InitPyramidHeight > 0)
00193 {
00194 GlobalUtil::StartTimer("Initialize Pyramids");
00195 _pyramid->InitPyramid(GlobalUtil::_InitPyramidWidth, GlobalUtil::_InitPyramidHeight, 0);
00196 GlobalUtil::StopTimer();
00197 }
00198
00199 ClockTimer::InitHighResolution();
00200 _initialized = 1;
00201 }
00202
00203 int SiftGPU::RunSIFT(int index)
00204 {
00205 if(_list->size()>0 )
00206 {
00207 index = index % _list->size();
00208 if(strcmp(_imgpath, _list->at(index).data()))
00209 {
00210 strcpy(_imgpath, _list->at(index).data());
00211 _image_loaded = 0;
00212 _current = index;
00213 }
00214 return RunSIFT();
00215 }else
00216 {
00217 return 0;
00218 }
00219
00220 }
00221
00222 int SiftGPU::RunSIFT( int width, int height, const void * data, unsigned int gl_format, unsigned int gl_type)
00223 {
00224
00225
00226
00227 if(GlobalUtil::_GoodOpenGL ==0 ) return 0;
00228 if(!_initialized) {
00229 InitSiftGPU();
00230 }
00231 else {
00232 GlobalUtil::SetGLParam();
00233 }
00234 if(GlobalUtil::_GoodOpenGL ==0 ) return 0;
00235 if(width > 0 && height >0 && data != NULL)
00236 {
00237 _imgpath[0] = 0;
00238
00239 GlobalUtil::StartTimer("Upload Image data");
00240 if(_texImage->SetImageData(width, height, data, gl_format, gl_type))
00241 {
00242 _image_loaded = 2;
00243 GlobalUtil::StopTimer();
00244 _timing[0] = GlobalUtil::GetElapsedTime();
00245
00246
00247 GlobalUtil::StartTimer("Initialize Pyramid");
00248 _pyramid->InitPyramid(width, height, _texImage->_down_sampled);
00249 GlobalUtil::StopTimer();
00250 _timing[1] = GlobalUtil::GetElapsedTime();
00251
00252 return RunSIFT();
00253 }else
00254 {
00255 return 0;
00256 }
00257 }else
00258 {
00259 return 0;
00260 }
00261
00262 }
00263
00264 int SiftGPU::RunSIFT(const char * imgpath)
00265 {
00266 if(imgpath && imgpath[0])
00267 {
00268
00269 strcpy(_imgpath, imgpath);
00270 _image_loaded = 0;
00271 return RunSIFT();
00272 }else
00273 {
00274 return 0;
00275 }
00276
00277
00278 }
00279
00280 int SiftGPU::RunSIFT(int num, const SiftKeypoint * keys, int keys_have_orientation)
00281 {
00282 if(num <=0) return 0;
00283 _pyramid->SetKeypointList(num, (const float*) keys, 1, keys_have_orientation);
00284 return RunSIFT();
00285 }
00286
00287 int SiftGPU::RunSIFT()
00288 {
00289
00290 if(_imgpath[0]==0 && _image_loaded == 0) return 0;
00291
00292
00293 if(GlobalUtil::_GoodOpenGL ==0 ) return 0;
00294
00295 ClockTimer timer;
00296
00297 if(!_initialized)
00298 {
00299
00300 InitSiftGPU();
00301 if(GlobalUtil::_GoodOpenGL ==0 ) return 0;
00302 }else
00303 {
00304
00305 GlobalUtil::SetGLParam();
00306 }
00307
00308 timer.StartTimer("RUN SIFT");
00309
00310 if( _image_loaded ==0)
00311 {
00312 int width, height;
00313
00314 GlobalUtil::StartTimer("Load Input Image");
00315 if(!_texImage->LoadImageFile(_imgpath, width, height)) return 0;
00316 _image_loaded = 1;
00317 GlobalUtil::StopTimer();
00318 _timing[0] = GlobalUtil::GetElapsedTime();
00319
00320
00321 GlobalUtil::StartTimer("Initialize Pyramid");
00322 _pyramid->InitPyramid(width, height, _texImage->_down_sampled);
00323 GlobalUtil::StopTimer();
00324 _timing[1] = GlobalUtil::GetElapsedTime();
00325
00326 }else
00327 {
00328
00329 if(!GlobalUtil::_UseCUDA && !GlobalUtil::_UseOpenCL)
00330 {
00331 GlobalUtil::FitViewPort(1,1);
00332 _texImage->FitTexViewPort();
00333 }
00334 if(_image_loaded == 1)
00335 {
00336 _timing[0] = _timing[1] = 0;
00337 }else
00338 {
00339 _image_loaded = 1;
00340 }
00341 }
00342
00343 if(_pyramid->_allocated ==0 ) return 0;
00344
00345
00346 #ifdef DEBUG_SIFTGPU
00347 _pyramid->BeginDEBUG(_imgpath);
00348 #endif
00349
00350
00351 _pyramid->RunSIFT(_texImage);
00352
00353
00354 _pyramid->GetPyramidTiming(_timing + 2);
00355
00356
00357 if(_outpath[0] ){ SaveSIFT(_outpath); _outpath[0] = 0;}
00358
00359
00360 if(GlobalUtil::_ExitAfterSIFT && GlobalUtil::_UseSiftGPUEX) exit(0);
00361
00362 timer.StopTimer();
00363 if(GlobalUtil::_verbose)std::cout<<endl;
00364
00365 return _pyramid->GetSucessStatus();
00366 }
00367
00368
00369 void SiftGPU::SetKeypointList(int num, const SiftKeypoint * keys, int keys_have_orientation)
00370 {
00371 _pyramid->SetKeypointList(num, (const float*)keys, 0, keys_have_orientation);
00372 }
00373
00374 void SiftGPUEX::DisplayInput()
00375 {
00376 if(_texImage==NULL) return;
00377 _texImage->VerifyTexture();
00378 _texImage->BindTex();
00379 _texImage->DrawImage();
00380 _texImage->UnbindTex();
00381
00382 }
00383
00384 void SiftGPU::SetVerbose(int verbose)
00385 {
00386 GlobalUtil::_timingO = verbose>2;
00387 GlobalUtil::_timingL = verbose>3;
00388 if(verbose == -1)
00389 {
00390
00391 if(GlobalUtil::_verbose)
00392 {
00393 GlobalUtil::_verbose = GlobalUtil::_timingS;
00394 GlobalUtil::_timingS = 0;
00395 if(GlobalUtil::_verbose ==0 && GlobalUtil::_UseSiftGPUEX)
00396 std::cout << "Console ouput disabled, press Q/V to enable\n\n";
00397 }else
00398 {
00399 GlobalUtil::_verbose = 1;
00400 GlobalUtil::_timingS = 1;
00401 }
00402 }else if(verbose == -2)
00403 {
00404
00405 GlobalUtil::_verbose = 0;
00406 GlobalUtil::_timingS = 1;
00407 }else
00408 {
00409 GlobalUtil::_verbose = verbose>0;
00410 GlobalUtil::_timingS = verbose>1;
00411 }
00412 }
00413
00414 SiftParam::SiftParam()
00415 {
00416
00417 _level_min = -1;
00418 _dog_level_num = 3;
00419 _level_max = 0;
00420 _sigma0 = 0;
00421 _sigman = 0;
00422 _edge_threshold = 0;
00423 _dog_threshold = 0;
00424
00425
00426 }
00427
00428 float SiftParam::GetInitialSmoothSigma(int octave_min)
00429 {
00430 float sa = _sigma0 * powf(2.0f, float(_level_min)/float(_dog_level_num)) ;
00431 float sb = _sigman / powf(2.0f, float(octave_min)) ;
00432 float sigma_skip0 = sa > sb + 0.001?sqrt(sa*sa - sb*sb): 0.0f;
00433 return sigma_skip0;
00434 }
00435
00436 void SiftParam::ParseSiftParam()
00437 {
00438
00439 if(_dog_level_num ==0) _dog_level_num = 3;
00440 if(_level_max ==0) _level_max = _dog_level_num + 1;
00441 if(_sigma0 ==0.0f) _sigma0 = 1.6f * powf(2.0f, 1.0f / _dog_level_num) ;
00442 if(_sigman == 0.0f) _sigman = 0.5f;
00443
00444
00445 _level_num = _level_max -_level_min + 1;
00446
00447 _level_ds = _level_min + _dog_level_num;
00448 if(_level_ds > _level_max ) _level_ds = _level_max ;
00449
00451 float _sigmak = powf(2.0f, 1.0f / _dog_level_num) ;
00452 float dsigma0 = _sigma0 * sqrt (1.0f - 1.0f / (_sigmak*_sigmak) ) ;
00453 float sa, sb;
00454
00455
00456 sa = _sigma0 * powf(_sigmak, (float)_level_min) ;
00457 sb = _sigman / powf(2.0f, (float)GlobalUtil::_octave_min_default) ;
00458
00459 _sigma_skip0 = sa>sb+ 0.001?sqrt(sa*sa - sb*sb): 0.0f;
00460
00461 sa = _sigma0 * powf(_sigmak, float(_level_min )) ;
00462 sb = _sigma0 * powf(_sigmak, float(_level_ds - _dog_level_num)) ;
00463
00464 _sigma_skip1 = sa>sb + 0.001? sqrt(sa*sa - sb*sb): 0.0f;
00465
00466 _sigma_num = _level_max - _level_min;
00467 _sigma = new float[_sigma_num];
00468
00469 for(int i = _level_min + 1; i <= _level_max; i++)
00470 {
00471 _sigma[i-_level_min -1] = dsigma0 * powf(_sigmak, float(i)) ;
00472 }
00473
00474 if(_dog_threshold ==0) _dog_threshold = 0.02f / _dog_level_num ;
00475 if(_edge_threshold==0) _edge_threshold = 10.0f;
00476 }
00477
00478
00479 void SiftGPUEX::DisplayOctave(void (*UseDisplayShader)(), int i)
00480 {
00481 if(_pyramid == NULL)return;
00482 const int grid_sz = (int)ceil(_level_num/2.0);
00483 double scale = 1.0/grid_sz ;
00484 int gx=0, gy=0, dx, dy;
00485
00486 if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min);
00487 else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min));
00488
00489
00490 i = i% _pyramid->_octave_num;
00491 if(i<0 ) i+= _pyramid->_octave_num;
00492
00493 scale *= ( 1<<(i));
00494
00495
00496
00497
00498 UseDisplayShader();
00499
00500 glPushMatrix();
00501 glScaled(scale, scale, scale);
00502 for(int level = _level_min; level<= _level_max; level++)
00503 {
00504 GLTexImage * tex = _pyramid->GetLevelTexture(i+_pyramid->_octave_min, level);
00505 dx = tex->GetImgWidth();
00506 dy = tex->GetImgHeight();
00507
00508 glPushMatrix();
00509
00510 glTranslated(dx*gx, dy*gy, 0);
00511
00512 tex->BindTex();
00513
00514 tex->DrawImage();
00515 tex->UnbindTex();
00516
00517 glPopMatrix();
00518
00519 gx++;
00520 if(gx>=grid_sz)
00521 {
00522 gx =0;
00523 gy++;
00524 }
00525
00526 }
00527
00528 glPopMatrix();
00529 ShaderMan::UnloadProgram();
00530 }
00531
00532 void SiftGPUEX::DisplayPyramid( void (*UseDisplayShader)(), int dataName, int nskip1, int nskip2)
00533 {
00534
00535 if(_pyramid == NULL)return;
00536 int grid_sz = (_level_num -nskip1 - nskip2);
00537 if(grid_sz > 4) grid_sz = (int)ceil(grid_sz*0.5);
00538 double scale = 1.0/grid_sz;
00539 int stepx = 0, stepy = 0, dx, dy=0, nstep;
00540
00541 if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min);
00542 else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min));
00543
00544
00545 glPushMatrix();
00546 glScaled(scale, scale, scale);
00547
00548 for(int i = _pyramid->_octave_min; i < _pyramid->_octave_min+_pyramid->_octave_num; i++)
00549 {
00550
00551 nstep = i==_pyramid->_octave_min? grid_sz: _level_num;
00552 dx = 0;
00553 UseDisplayShader();
00554 for(int j = _level_min + nskip1; j <= _level_max-nskip2; j++)
00555 {
00556 GLTexImage * tex = _pyramid->GetLevelTexture(i, j, dataName);
00557 if(tex->GetImgWidth() == 0 || tex->GetImgHeight() == 0) continue;
00558 stepx = tex->GetImgWidth();
00559 stepy = tex->GetImgHeight();
00561 if(j == _level_min + nskip1 + nstep)
00562 {
00563 dy += stepy;
00564 dx = 0;
00565 }
00566
00567 glPushMatrix();
00568 glTranslated(dx, dy, 0);
00569 tex->BindTex();
00570 tex->DrawImage();
00571 tex->UnbindTex();
00572 glPopMatrix();
00573
00574 dx += stepx;
00575
00576 }
00577
00578 ShaderMan::UnloadProgram();
00579
00580 dy+= stepy;
00581 }
00582
00583 glPopMatrix();
00584 }
00585
00586
00587 void SiftGPUEX::DisplayLevel(void (*UseDisplayShader)(), int i)
00588 {
00589 if(_pyramid == NULL)return;
00590
00591 i = i%(_level_num * _pyramid->_octave_num);
00592 if (i<0 ) i+= (_level_num * _pyramid->_octave_num);
00593 int octave = _pyramid->_octave_min + i/_level_num;
00594 int level = _level_min + i%_level_num;
00595 double scale = 1.0;
00596
00597 if(octave >0) scale *= (1<<octave);
00598 else if(octave < 0) scale /= (1<<(-octave));
00599
00600 GLTexImage * tex = _pyramid->GetLevelTexture(octave, level);
00601
00602 UseDisplayShader();
00603
00604 glPushMatrix();
00605 glScaled(scale, scale, scale);
00606 tex->BindTex();
00607 tex->DrawImage();
00608 tex->UnbindTex();
00609 glPopMatrix();
00610 ShaderMan::UnloadProgram();
00611 }
00612
00613 void SiftGPUEX::DisplaySIFT()
00614 {
00615 if(_pyramid == NULL) return;
00616 glEnable(GlobalUtil::_texTarget);
00617 switch(_view)
00618 {
00619 case 0:
00620 DisplayInput();
00621 DisplayFeatureBox(_sub_view);
00622 break;
00623 case 1:
00624 DisplayPyramid(ShaderMan::UseShaderDisplayGaussian, SiftPyramid::DATA_GAUSSIAN);
00625 break;
00626 case 2:
00627 DisplayOctave(ShaderMan::UseShaderDisplayGaussian, _sub_view);
00628 break;
00629 case 3:
00630 DisplayLevel(ShaderMan::UseShaderDisplayGaussian, _sub_view);
00631 break;
00632 case 4:
00633 DisplayPyramid(ShaderMan::UseShaderDisplayDOG, SiftPyramid::DATA_DOG, 1);
00634 break;
00635 case 5:
00636 DisplayPyramid(ShaderMan::UseShaderDisplayGrad, SiftPyramid::DATA_GRAD, 1);
00637 break;
00638 case 6:
00639 DisplayPyramid(ShaderMan::UseShaderDisplayDOG, SiftPyramid::DATA_DOG,2, 1);
00640 DisplayPyramid(ShaderMan::UseShaderDisplayKeypoints, SiftPyramid::DATA_KEYPOINT, 2,1);
00641 }
00642 }
00643
00644
00645 void SiftGPUEX::SetView(int view, int sub_view, char *title)
00646 {
00647 const char* view_titles[] =
00648 {
00649 "Original Image",
00650 "Gaussian Pyramid",
00651 "Octave Images",
00652 "Level Image",
00653 "Difference of Gaussian",
00654 "Gradient",
00655 "Keypoints"
00656 };
00657 const int view_num = 7;
00658 _view = view % view_num;
00659 if(_view <0) _view +=view_num;
00660 _sub_view = sub_view;
00661
00662 if(_view_debug)
00663 strcpy(title, "Debug...");
00664 else
00665 strcpy(title, view_titles[_view]);
00666
00667 }
00668
00669
00670 void SiftGPU::PrintUsage()
00671 {
00672 std::cout
00673 <<"SiftGPU Usage:\n"
00674 <<"-h -help : Parameter information\n"
00675 <<"-i <strings> : Filename(s) of the input image(s)\n"
00676 <<"-il <string> : Filename of an image list file\n"
00677 <<"-o <string> : Where to save SIFT features\n"
00678 <<"-f <float> : Filter width factor; Width will be 2*factor+1 (default : 4.0)\n"
00679 <<"-w <float> : Orientation sample window factor (default: 2.0)\n"
00680 <<"-dw <float> * : Descriptor grid size factor (default : 3.0)\n"
00681 <<"-fo <int> * : First octave to detect DOG keypoints(default : 0)\n"
00682 <<"-no <int> : Maximum number of Octaves (default : no limit)\n"
00683 <<"-d <int> : Number of DOG levels in an octave (default : 3)\n"
00684 <<"-t <float> : DOG threshold (default : 0.02/3)\n"
00685 <<"-e <float> : Edge Threshold (default : 10.0)\n"
00686 <<"-m <int=2> : Multi Feature Orientations (default : 1)\n"
00687 <<"-m2p : 2 Orientations packed as one float\n"
00688 <<"-s <int=1> : Sub-Pixel, Sub-Scale Localization, Multi-Refinement(num)\n"
00689 <<"-lcpu -lc <int> : CPU/GPU mixed Feature List Generation (defaut : 6)\n"
00690 <<" Use GPU first, and use CPU when reduction size <= pow(2,num)\n"
00691 <<" When <num> is missing or equals -1, no GPU will be used\n"
00692 <<"-noprep : Upload raw data to GPU (default: RGB->LUM and down-sample on CPU)\n"
00693 <<"-sd : Skip descriptor computation if specified\n"
00694 <<"-unn * : Write unnormalized descriptor if specified\n"
00695 <<"-b * : Write binary sift file if specified\n"
00696 <<"-fs <int> : Block Size for freature storage <default : 4>\n"
00697 <<"-cuda <int=0> : Use CUDA SiftGPU, and specifiy the device index\n"
00698 <<"-tight : Automatically resize pyramid to fit new images tightly\n"
00699 <<"-p <W>x<H> : Inititialize the pyramids to contain image of WxH (eg -p 1024x768)\n"
00700 <<"-lm <int> : Maximum feature count for a level (for pre-allocation)\n"
00701 <<"-lmp <float> : Maximum percent of pixels as features (for pre-allocaton)\n"
00702 <<"-tc[1|2|3] <int> *: Threshold for limiting the overall number of features (3 methods)\n"
00703 <<"-v <int> : Level of timing details. Same as calling Setverbose() function\n"
00704 <<"-loweo : (0, 0) at center of top-left pixel (defaut: corner)\n"
00705 <<"-maxd <int> * : Max working dimension (default : 2560 (unpacked) / 3200 (packed))\n"
00706 <<"-exit : Exit program after processing the input image\n"
00707 <<"-unpack : Use the old unpacked implementation\n"
00708 <<"-di : Use dynamic array indexing if available (defualt : no)\n"
00709 <<" It could make computation faster on cards like GTX 280\n"
00710 <<"-ofix * : use 0 as feature orientations.\n"
00711 <<"-ofix-not * : disable -ofix.\n"
00712 <<"-winpos <X>x<Y> * : Screen coordinate used in Win32 to select monitor/GPU.\n"
00713 <<"-display <string>*: Display name used in Linux/Mac to select monitor/GPU.\n"
00714 <<"\n"
00715 <<"NOTE: parameters marked with * can be changed after initialization\n"
00716 <<"\n";
00717 }
00718
00719 void SiftGPU::ParseParam(int argc, char **argv)
00720 {
00721 #define CHAR1_TO_INT(x) ((x >= 'A' && x <= 'Z') ? x + 32 : x)
00722 #define CHAR2_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR1_TO_INT(str[i+1]) << 8) : 0)
00723 #define CHAR3_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR2_TO_INT(str, i + 1) << 8) : 0)
00724 #define STRING_TO_INT(str) (CHAR1_TO_INT(str[0]) + (CHAR3_TO_INT(str, 1) << 8))
00725
00726 #ifdef _MSC_VER
00727
00728 #define MAKEINT1(a) (#@a )
00729 #else
00730 #define mychar0 '0'
00731 #define mychar1 '1'
00732 #define mychar2 '2'
00733 #define mychar3 '3'
00734 #define mychara 'a'
00735 #define mycharb 'b'
00736 #define mycharc 'c'
00737 #define mychard 'd'
00738 #define mychare 'e'
00739 #define mycharf 'f'
00740 #define mycharg 'g'
00741 #define mycharh 'h'
00742 #define mychari 'i'
00743 #define mycharj 'j'
00744 #define mychark 'k'
00745 #define mycharl 'l'
00746 #define mycharm 'm'
00747 #define mycharn 'n'
00748 #define mycharo 'o'
00749 #define mycharp 'p'
00750 #define mycharq 'q'
00751 #define mycharr 'r'
00752 #define mychars 's'
00753 #define mychart 't'
00754 #define mycharu 'u'
00755 #define mycharv 'v'
00756 #define mycharw 'w'
00757 #define mycharx 'x'
00758 #define mychary 'y'
00759 #define mycharz 'z'
00760 #define MAKEINT1(a) (mychar##a )
00761 #endif
00762 #define MAKEINT2(a, b) (MAKEINT1(a) + (MAKEINT1(b) << 8))
00763 #define MAKEINT3(a, b, c) (MAKEINT1(a) + (MAKEINT2(b, c) << 8))
00764 #define MAKEINT4(a, b, c, d) (MAKEINT1(a) + (MAKEINT3(b, c, d) << 8))
00765
00766
00767 char* arg, *param, * opt;
00768 int setMaxD = 0, opti;
00769 for(int i = 0; i< argc; i++)
00770 {
00771 arg = argv[i];
00772 if(arg == NULL || arg[0] != '-' || !arg[1])continue;
00773 opt = arg+1;
00774 opti = STRING_TO_INT(opt);
00775 param = argv[i+1];
00776
00778 switch(opti)
00779 {
00780 case MAKEINT1(h):
00781 case MAKEINT4(h, e, l, p):
00782 PrintUsage();
00783 break;
00784 case MAKEINT4(c, u, d, a):
00785 #if defined(CUDA_SIFTGPU_ENABLED)
00786
00787 if(!_initialized)
00788 {
00789 GlobalUtil::_UseCUDA = 1;
00790 int device = -1;
00791 if(i+1 <argc && sscanf(param, "%d", &device) && device >=0)
00792 {
00793 GlobalUtil::_DeviceIndex = device;
00794 i++;
00795 }
00796 }
00797 #else
00798 std::cerr << "---------------------------------------------------------------------------\n"
00799 << "CUDA not supported in this binary! To enable it, please use SiftGPU_CUDA_Enable\n"
00800 << "solution for VS2005+ or set siftgpu_enable_cuda to 1 in makefile\n"
00801 << "----------------------------------------------------------------------------\n";
00802 #endif
00803 break;
00804 case MAKEINT2(c, l):
00805 #if defined(CL_SIFTGPU_ENABLED)
00806 if(!_initialized) GlobalUtil::_UseOpenCL = 1;
00807 #else
00808 std::cerr << "---------------------------------------------------------------------------\n"
00809 << "OpenCL not supported in this binary! Define CL_CUDA_SIFTGPU_ENABLED to..\n"
00810 << "----------------------------------------------------------------------------\n";
00811 #endif
00812 break;
00813
00814 case MAKEINT4(p, a, c, k):
00815 if(!_initialized) GlobalUtil::_usePackedTex = 1;
00816 break;
00817 case MAKEINT4(u, n, p, a):
00818 if(!_initialized)
00819 {
00820 GlobalUtil::_usePackedTex = 0;
00821 if(!setMaxD) GlobalUtil::_texMaxDim = 2560;
00822 }
00823 break;
00824 case MAKEINT4(l, c, p, u):
00825 case MAKEINT2(l, c):
00826 if(!_initialized)
00827 {
00828 int gskip = -1;
00829 if(i+1 <argc) sscanf(param, "%d", &gskip);
00830 if(gskip >= 0)
00831 {
00832 GlobalUtil::_ListGenSkipGPU = gskip;
00833 }else
00834 {
00835 GlobalUtil::_ListGenGPU = 0;
00836 }
00837 }
00838 break;
00839 case MAKEINT4(p, r, e, p):
00840 GlobalUtil::_PreProcessOnCPU = 1;
00841 break;
00842 case MAKEINT4(n, o, p, r):
00843 GlobalUtil::_PreProcessOnCPU = 0;
00844 break;
00845 case MAKEINT4(f, b, o, 1):
00846 FrameBufferObject::UseSingleFBO =1;
00847 break;
00848 case MAKEINT4(f, b, o, s):
00849 FrameBufferObject::UseSingleFBO = 0;
00850 break;
00851 case MAKEINT2(s, d):
00852 if(!_initialized) GlobalUtil::_DescriptorPPT =0;
00853 break;
00854 case MAKEINT3(u, n, n):
00855 GlobalUtil::_NormalizedSIFT =0;
00856 break;
00857 case MAKEINT4(n, d, e, s):
00858 GlobalUtil::_NormalizedSIFT =1;
00859 break;
00860 case MAKEINT1(b):
00861 GlobalUtil::_BinarySIFT = 1;
00862 break;
00863 case MAKEINT4(t, i, g, h):
00864 GlobalUtil::_ForceTightPyramid = 1;
00865 break;
00866 case MAKEINT4(e, x, i, t):
00867 GlobalUtil::_ExitAfterSIFT = 1;
00868 break;
00869 case MAKEINT2(d, i):
00870 GlobalUtil::_UseDynamicIndexing = 1;
00871 break;
00872 case MAKEINT4(s, i, g, n):
00873 if(!_initialized || GlobalUtil::_UseCUDA) GlobalUtil::_KeepExtremumSign = 1;
00874 break;
00875 case MAKEINT1(m):
00876 case MAKEINT2(m, o):
00877 if(!_initialized)
00878 {
00879 int mo = 2;
00880 if(i+1 <argc) sscanf(param, "%d", &mo);
00881
00882 GlobalUtil::_MaxOrientation = min(max(1, mo), 4);
00883 }
00884 break;
00885 case MAKEINT3(m, 2, p):
00886 if(!_initialized)
00887 {
00888 GlobalUtil::_MaxOrientation = 2;
00889 GlobalUtil::_OrientationPack2 = 1;
00890 }
00891 break;
00892 case MAKEINT1(s):
00893 if(!_initialized)
00894 {
00895 int sp = 1;
00896 if(i+1 <argc) sscanf(param, "%d", &sp);
00897
00898 GlobalUtil::_SubpixelLocalization = min(max(0, sp),5);
00899 }
00900 break;
00901 case MAKEINT4(o, f, i, x):
00902 GlobalUtil::_FixedOrientation = (_stricmp(opt, "ofix")==0);
00903 break;
00904 case MAKEINT4(l, o, w, e):
00905 GlobalUtil::_LoweOrigin = 1;
00906 break;
00907 case MAKEINT4(n, a, r, r):
00908 GlobalUtil::_NarrowFeatureTex = 1;
00909 break;
00910 case MAKEINT4(d, e, b, u):
00911 GlobalUtil::_debug = 1;
00912 break;
00913 case MAKEINT2(k, 0):
00914 GlobalUtil::_KeyPointListForceLevel0 = 1;
00915 break;
00916 case MAKEINT2(k, x):
00917 GlobalUtil::_KeyPointListForceLevel0 = 0;
00918 break;
00919 default:
00920 if(i + 1 >= argc) break;
00921 switch(opti)
00922 {
00923 case MAKEINT1(i):
00924 strcpy(_imgpath, param);
00925 i++;
00926
00927 _list->push_back(param);
00928 while( i+1 < argc && argv[i+1][0] !='-')
00929 {
00930 _list->push_back(argv[++i]);
00931 }
00932 break;
00933 case MAKEINT2(i, l):
00934 LoadImageList(param);
00935 i++;
00936 break;
00937 case MAKEINT1(o):
00938 strcpy(_outpath, param);
00939 i++;
00940 break;
00941 case MAKEINT1(f):
00942 {
00943 float factor = 0.0f;
00944 if(sscanf(param, "%f", &factor) && factor > 0 )
00945 {
00946 GlobalUtil::_FilterWidthFactor = factor;
00947 i++;
00948 }
00949 }
00950 break;
00951 case MAKEINT2(o, t):
00952 {
00953 float factor = 0.0f;
00954 if(sscanf(param, "%f", &factor) && factor>0 )
00955 {
00956 GlobalUtil::_MulitiOrientationThreshold = factor;
00957 i++;
00958 }
00959 break;
00960 }
00961 case MAKEINT1(w):
00962 {
00963 float factor = 0.0f;
00964 if(sscanf(param, "%f", &factor) && factor>0 )
00965 {
00966 GlobalUtil::_OrientationWindowFactor = factor;
00967 i++;
00968 }
00969 break;
00970 }
00971 case MAKEINT2(d, w):
00972 {
00973 float factor = 0.0f;
00974 if(sscanf(param, "%f", &factor) && factor > 0 )
00975 {
00976 GlobalUtil::_DescriptorWindowFactor = factor;
00977 i++;
00978 }
00979 break;
00980 }
00981 case MAKEINT2(f, o):
00982 {
00983 int first_octave = -3;
00984 if(sscanf(param, "%d", &first_octave) && first_octave >=-2 )
00985 {
00986 GlobalUtil::_octave_min_default = first_octave;
00987 i++;
00988 }
00989 break;
00990 }
00991 case MAKEINT2(n, o):
00992 if(!_initialized)
00993 {
00994 int octave_num=-1;
00995 if(sscanf(param, "%d", &octave_num))
00996 {
00997 octave_num = max(-1, octave_num);
00998 if(octave_num ==-1 || octave_num >=1)
00999 {
01000 GlobalUtil::_octave_num_default = octave_num;
01001 i++;
01002 }
01003 }
01004 }
01005 break;
01006 case MAKEINT1(t):
01007 {
01008 float threshold = 0.0f;
01009 if(sscanf(param, "%f", &threshold) && threshold >0 && threshold < 0.5f)
01010 {
01011 SiftParam::_dog_threshold = threshold;
01012 i++;
01013 }
01014 break;
01015 }
01016 case MAKEINT1(e):
01017 {
01018 float threshold = 0.0f;
01019 if(sscanf(param, "%f", &threshold) && threshold >0 )
01020 {
01021 SiftParam::_edge_threshold = threshold;
01022 i++;
01023 }
01024 break;
01025 }
01026 case MAKEINT1(d):
01027 {
01028 int num = 0;
01029 if(sscanf(param, "%d", &num) && num >=1 && num <=10)
01030 {
01031 SiftParam::_dog_level_num = num;
01032 i++;
01033 }
01034 break;
01035 }
01036 case MAKEINT2(f, s):
01037 {
01038 int num = 0;
01039 if(sscanf(param, "%d", &num) && num >=1)
01040 {
01041 GlobalParam::_FeatureTexBlock = num;
01042 i++;
01043 }
01044 break;
01045 }
01046 case MAKEINT1(p):
01047 {
01048 int w =0, h=0;
01049 if(sscanf(param, "%dx%d", &w, &h) == 2 && w >0 && h>0)
01050 {
01051 GlobalParam::_InitPyramidWidth = w;
01052 GlobalParam::_InitPyramidHeight = h;
01053 i++;
01054 }
01055 break;
01056 }
01057 case MAKEINT4(w, i, n, p):
01058 {
01059 int x =0, y=0;
01060 if(sscanf(param, "%dx%d", &x, &y) == 2)
01061 {
01062 GlobalParam::_WindowInitX = x;
01063 GlobalParam::_WindowInitY = y;
01064 i++;
01065 }
01066 break;
01067 }
01068 case MAKEINT4(d, i, s, p):
01069 {
01070 GlobalParam::_WindowDisplay = param;
01071 i++;
01072 break;
01073 }
01074 case MAKEINT2(l, m):
01075 {
01076 int num = 0;
01077 if(sscanf(param, "%d", &num) && num >=1000)
01078 {
01079 GlobalParam::_MaxLevelFeatureNum = num;
01080 i++;
01081 }
01082 break;
01083 }
01084 case MAKEINT3(l, m, p):
01085 {
01086 float num = 0.0f;
01087 if(sscanf(param, "%f", &num) && num >=0.001)
01088 {
01089 GlobalParam::_MaxFeaturePercent = num;
01090 i++;
01091 }
01092 break;
01093 }
01094 case MAKEINT3(t, c, 2):
01095 case MAKEINT3(t, c, 3):
01096 case MAKEINT2(t, c):
01097 case MAKEINT3(t, c, 1):
01098 {
01099 switch (opti)
01100 {
01101 case MAKEINT3(t, c, 2): GlobalUtil::_TruncateMethod = 1; break;
01102 case MAKEINT3(t, c, 3): GlobalUtil::_TruncateMethod = 2; break;
01103 default: GlobalUtil::_TruncateMethod = 0; break;
01104 }
01105 int num = -1;
01106 if(sscanf(param, "%d", &num) && num > 0)
01107 {
01108 GlobalParam::_FeatureCountThreshold = num;
01109 i++;
01110 }
01111 break;
01112 }
01113 case MAKEINT1(v):
01114 {
01115 int num = 0;
01116 if(sscanf(param, "%d", &num) && num >=0 && num <= 4)
01117 {
01118 SetVerbose(num);
01119 }
01120 break;
01121 }
01122 case MAKEINT4(m, a, x, d):
01123 {
01124 int num = 0;
01125 if(sscanf(param, "%d", &num) && num > 0)
01126 {
01127 GlobalUtil::_texMaxDim = num;
01128 setMaxD = 1;
01129 }
01130 break;
01131 }
01132 case MAKEINT4(m, i, n, d):
01133 {
01134 int num = 0;
01135 if(sscanf(param, "%d", &num) && num >= 8)
01136 {
01137 GlobalUtil::_texMinDim = num;
01138 }
01139 break;
01140 }
01141 default:
01142 break;
01143 }
01144 break;
01145 }
01146 }
01147
01148 if(_outpath[0] && _list->size()>1) _outpath[0] = 0;
01149 }
01150
01151 void SiftGPU::SetImageList(int nimage, const char** filelist)
01152 {
01153 _list->resize(0);
01154 for(int i = 0; i < nimage; i++)
01155 {
01156 _list->push_back(filelist[i]);
01157 }
01158 _current = 0;
01159
01160 }
01161 void SiftGPU:: LoadImageList(char *imlist)
01162 {
01163 char filename[_MAX_PATH];
01164 ifstream in(imlist);
01165 while(in>>filename)
01166 {
01167 _list->push_back(filename);
01168 }
01169 in.close();
01170
01171
01172 if(_list->size()>0)
01173 {
01174 strcpy(_imgpath, _list->at(0).data());
01175 strcpy(filename, imlist);
01176 char * slash = strrchr(filename, '\\');
01177 if(slash == 0) slash = strrchr(filename, '/');
01178 if(slash )
01179 {
01180 slash[1] = 0;
01181 chdir(filename);
01182 }
01183 }
01184 _image_loaded = 0;
01185
01186
01187 }
01188 float SiftParam::GetLevelSigma( int lev)
01189 {
01190 return _sigma0 * powf( 2.0f, float(lev) / float(_dog_level_num ));
01191 }
01192
01193
01194
01195 void SiftGPUEX::DisplayFeatureBox(int view )
01196 {
01197 view = view%3;
01198 if(view<0)view+=3;
01199 if(view ==2) return;
01200 int idx = 0;
01201 const int *fnum = _pyramid->GetLevelFeatureNum();
01202 const GLuint *vbo = _pyramid->GetFeatureDipslayVBO();
01203 const GLuint *vbop = _pyramid->GetPointDisplayVBO();
01204 if(vbo == NULL || vbop == NULL) return;
01205
01206 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
01207 glEnableClientState(GL_VERTEX_ARRAY);
01208 glPushMatrix();
01209
01210 glPointSize(2.0f);
01211
01212 float scale = 1.0f;
01213 if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min);
01214 else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min));
01215 glScalef(scale, scale, 1.0f);
01216
01217
01218 for(int i = 0; i < _pyramid->_octave_num; i++)
01219 {
01220
01221 for(int j = 0; j < _dog_level_num; j++, idx++)
01222 {
01223 if(fnum[idx]>0)
01224 {
01225 if(view ==0)
01226 {
01227 glColor3f(0.2f, 1.0f, 0.2f);
01228 glBindBuffer(GL_ARRAY_BUFFER_ARB, vbop[idx]);
01229 glVertexPointer( 4, GL_FLOAT,4*sizeof(float), (char *) 0);
01230 glDrawArrays( GL_POINTS, 0, fnum[idx]);
01231 glFlush();
01232 }else
01233 {
01234
01235
01236 glColor3fv(_colors+ (idx%COLOR_NUM)*3);
01237 glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo[idx]);
01238 glVertexPointer( 4, GL_FLOAT,4*sizeof(float), (char *) 0);
01239 glDrawArrays( GL_LINES, 0, fnum[idx]*10 );
01240 glFlush();
01241 }
01242
01243 }
01244
01245 }
01246 glTranslatef(-.5f, -.5f, 0.0f);
01247 glScalef(2.0f, 2.0f, 1.0f);
01248
01249 }
01250 glPopMatrix();
01251 glDisableClientState(GL_VERTEX_ARRAY);
01252 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
01253 glPointSize(1.0f);
01254
01255 }
01256
01257 void SiftGPUEX::ToggleDisplayDebug()
01258 {
01259 _view_debug = !_view_debug;
01260 }
01261
01262 void SiftGPUEX::DisplayDebug()
01263 {
01264 glPointSize(1.0f);
01265 glColor3f(1.0f, 0.0f, 0.0f);
01266 ShaderMan::UseShaderDebug();
01267 glBegin(GL_POINTS);
01268 for(int i = 0; i < 100; i++)
01269 {
01270 glVertex2f(i*4.0f+0.5f, i*4.0f+0.5f);
01271 }
01272 glEnd();
01273 ShaderMan::UnloadProgram();
01274 }
01275
01276 int SiftGPU::CreateContextGL()
01277 {
01278 if(GlobalUtil::_UseOpenCL || GlobalUtil::_UseCUDA)
01279 {
01280
01281 }
01282 else if(!GlobalUtil::CreateWindowEZ())
01283 {
01284 #if CUDA_SIFTGPU_ENABLED
01285 GlobalUtil::_UseCUDA = 1;
01286 #else
01287 return 0;
01288 #endif
01289 }
01290
01291 return VerifyContextGL();
01292 }
01293
01294 int SiftGPU::VerifyContextGL()
01295 {
01296 InitSiftGPU();
01297 return (GlobalUtil::_GoodOpenGL > 0) + GlobalUtil::_FullSupported;
01298 }
01299
01300 int SiftGPU::IsFullSupported()
01301 {
01302 return GlobalUtil::_GoodOpenGL > 0 && GlobalUtil::_FullSupported;
01303 }
01304
01305 void SiftGPU::SaveSIFT(const char * szFileName)
01306 {
01307 _pyramid->SaveSIFT(szFileName);
01308 }
01309
01310 int SiftGPU::GetFeatureNum()
01311 {
01312 return _pyramid->GetFeatureNum();
01313 }
01314
01315 void SiftGPU::GetFeatureVector(SiftKeypoint * keys, float * descriptors)
01316 {
01317
01318 if(GlobalUtil::_DescriptorPPT)
01319 {
01320
01321 _pyramid->CopyFeatureVector((float*) (&keys[0]), &descriptors[0]);
01322 }else
01323 {
01324
01325 _pyramid->CopyFeatureVector((float*) (&keys[0]), NULL);
01326 }
01327 }
01328
01329 void SiftGPU::SetTightPyramid(int tight)
01330 {
01331 GlobalUtil::_ForceTightPyramid = tight;
01332 }
01333
01334 int SiftGPU::AllocatePyramid(int width, int height)
01335 {
01336 _pyramid->_down_sample_factor = 0;
01337 _pyramid->_octave_min = GlobalUtil::_octave_min_default;
01338 if(GlobalUtil::_octave_min_default>=0)
01339 {
01340 width >>= GlobalUtil::_octave_min_default;
01341 height >>= GlobalUtil::_octave_min_default;
01342 }else
01343 {
01344 width <<= (-GlobalUtil::_octave_min_default);
01345 height <<= (-GlobalUtil::_octave_min_default);
01346 }
01347 _pyramid->ResizePyramid(width, height);
01348 return _pyramid->_pyramid_height == height && width == _pyramid->_pyramid_width ;
01349 }
01350
01351 void SiftGPU::SetMaxDimension(int sz)
01352 {
01353 if(sz < GlobalUtil::_texMaxDimGL)
01354 {
01355 GlobalUtil::_texMaxDim = sz;
01356 }
01357 }
01358 int SiftGPU::GetImageCount()
01359 {
01360 return _list->size();
01361 }
01362
01363 void SiftGPUEX::HSVtoRGB(float hsv[3],float rgb[3] )
01364 {
01365
01366 int i;
01367 float q, t, p;
01368 float hh,f, v = hsv[2];
01369 if(hsv[1]==0.0f)
01370 {
01371 rgb[0]=rgb[1]=rgb[2]=v;
01372 }
01373 else
01374 {
01376 hh =hsv[0]*6.0f ;
01377 i =(int)hh ;
01378 f = hh- i;
01380 p= v * ( 1 - hsv[1] );
01381 q = v * ( 1 - hsv[1] * f );
01382 t = v * ( 1 - hsv[1] * ( 1 - f ) );
01383 switch( i ) {
01384 case 0:rgb[0] = v;rgb[1] = t;rgb[2] = p;break;
01385 case 1:rgb[0] = q;rgb[1] = v;rgb[2] = p;break;
01386 case 2:rgb[0] = p;rgb[1] = v;rgb[2] = t;break;
01387 case 3:rgb[0] = p;rgb[1] = q;rgb[2] = v;break;
01388 case 4:rgb[0] = t;rgb[1] = p;rgb[2] = v;break;
01389 case 5:rgb[0] = v;rgb[1] = p;rgb[2] = q;break;
01390 default:rgb[0]= 0;rgb[1] = 0;rgb[2] = 0;
01391 }
01392 }
01393 }
01394
01395 void SiftGPUEX::GetImageDimension( int &w, int &h)
01396 {
01397 w = _texImage->GetImgWidth();
01398 h = _texImage->GetImgHeight();
01399
01400 }
01401
01402 void SiftGPUEX::GetInitWindowPotition(int&x, int&y)
01403 {
01404 x = GlobalUtil::_WindowInitX;
01405 y = GlobalUtil::_WindowInitY;
01406 }
01407
01408 SiftGPU* CreateNewSiftGPU(int np)
01409 {
01410 return new SiftGPU(np);
01411 }
01412
01414 void* ComboSiftGPU::operator new (size_t size){
01415 void * p = malloc(size);
01416 if (p == 0)
01417 {
01418 const std::bad_alloc ba;
01419 throw ba;
01420 }
01421 return p;
01422 }
01423
01424 ComboSiftGPU* CreateComboSiftGPU()
01425 {
01426 return new ComboSiftGPU();
01427 }
01428