//////////////////////////////////////////////////////////////////////////// // File: SiftPyramid.cpp // Author: Changchang Wu // Description : Implementation of the SiftPyramid class. // // // // Copyright (c) 2007 University of North Carolina at Chapel Hill // All Rights Reserved // // Permission to use, copy, modify and distribute this software and its // documentation for educational, research and non-profit purposes, without // fee, and without a written agreement is hereby granted, provided that the // above copyright notice and the following paragraph appear in all copies. // // The University of North Carolina at Chapel Hill make no representations // about the suitability of this software for any purpose. It is provided // 'as is' without express or implied warranty. // // Please send BUG REPORTS to ccwu@cs.unc.edu // //////////////////////////////////////////////////////////////////////////// #include "GL/glew.h" #include #include #include #include #include #include #include using namespace std; #include "GlobalUtil.h" #include "SiftPyramid.h" #include "SiftGPU.h" #ifdef DEBUG_SIFTGPU #include "IL/il.h" #include "direct.h" #include "io.h" #include #endif void SiftPyramid::RunSIFT(GLTexInput*input) { CleanupBeforeSIFT(); if(_existing_keypoints & SIFT_SKIP_FILTERING) { }else { GlobalUtil::StartTimer("Build Pyramid"); BuildPyramid(input); GlobalUtil::StopTimer(); _timing[0] = GetElapsedTime(); } if(_existing_keypoints) { //existing keypoint list should at least have the locations and scale GlobalUtil::StartTimer("Upload Feature List"); if(!(_existing_keypoints & SIFT_SKIP_FILTERING)) ComputeGradient(); GenerateFeatureListTex(); GlobalUtil::StopTimer(); _timing[2] = GetElapsedTime(); }else { GlobalUtil::StartTimer("Detect Keypoints"); DetectKeypointsEX(); GlobalUtil::StopTimer(); _timing[1] = GetElapsedTime(); if(GlobalUtil::_ListGenGPU ==1) { GlobalUtil::StartTimer("Get Feature List"); GenerateFeatureList(); GlobalUtil::StopTimer(); }else { GlobalUtil::StartTimer("Transfer Feature List"); GenerateFeatureListCPU(); GlobalUtil::StopTimer(); } LimitFeatureCount(0); _timing[2] = GetElapsedTime(); } if(_existing_keypoints& SIFT_SKIP_ORIENTATION) { //use exisitng feature orientation or }else if(GlobalUtil::_MaxOrientation>0) { //some extra tricks are done to handle existing keypoint list GlobalUtil::StartTimer("Feature Orientations"); GetFeatureOrientations(); GlobalUtil::StopTimer(); _timing[3] = GetElapsedTime(); //for existing keypoint list, only the strongest orientation is kept. if(GlobalUtil::_MaxOrientation >1 && !_existing_keypoints && !GlobalUtil::_FixedOrientation) { GlobalUtil::StartTimer("MultiO Feature List"); ReshapeFeatureListCPU(); LimitFeatureCount(1); GlobalUtil::StopTimer(); _timing[4] = GetElapsedTime(); } }else { GlobalUtil::StartTimer("Feature Orientations"); GetSimplifiedOrientation(); GlobalUtil::StopTimer(); _timing[3] = GetElapsedTime(); } PrepareBuffer(); if(_existing_keypoints & SIFT_SKIP_ORIENTATION) { //no need to read back feature if all fields of keypoints are already specified }else { GlobalUtil::StartTimer("Download Keypoints"); #ifdef NO_DUPLICATE_DOWNLOAD if(GlobalUtil::_MaxOrientation < 2 || GlobalUtil::_FixedOrientation) #endif DownloadKeypoints(); GlobalUtil::StopTimer(); _timing[5] = GetElapsedTime(); } if(GlobalUtil::_DescriptorPPT) { //desciprotrs are downloaded in descriptor computation of each level GlobalUtil::StartTimer("Get Descriptor"); GetFeatureDescriptors(); GlobalUtil::StopTimer(); _timing[6] = GetElapsedTime(); } //reset the existing keypoints _existing_keypoints = 0; _keypoint_index.resize(0); if(GlobalUtil::_UseSiftGPUEX) { GlobalUtil::StartTimer("Gen. Display VBO"); GenerateFeatureDisplayVBO(); GlobalUtil::StopTimer(); _timing[7] = GlobalUtil::GetElapsedTime(); } //clean up CleanUpAfterSIFT(); } void SiftPyramid::LimitFeatureCount(int have_keylist) { if(GlobalUtil::_FeatureCountThreshold <= 0 || _existing_keypoints) return; /////////////////////////////////////////////////////////////// //skip the lowest levels to reduce number of features. if(GlobalUtil::_TruncateMethod == 2) { int i = 0, new_feature_num = 0, level_num = param._dog_level_num * _octave_num; for(; new_feature_num < _FeatureCountThreshold && i < level_num; ++i) new_feature_num += _levelFeatureNum[i]; for(; i < level_num; ++i) _levelFeatureNum[i] = 0; if(new_feature_num < _featureNum) { _featureNum = new_feature_num; if(GlobalUtil::_verbose ) { std::cout<<"#Features Reduced:\t"<<_featureNum< _FeatureCountThreshold) { num_to_erase += _levelFeatureNum[i]; _featureNum -= _levelFeatureNum[i]; _levelFeatureNum[i++] = 0; } if(num_to_erase > 0 && have_keylist) { _keypoint_buffer.erase(_keypoint_buffer.begin(), _keypoint_buffer.begin() + num_to_erase * 4); } if(GlobalUtil::_verbose && num_to_erase > 0) { std::cout<<"#Features Reduced:\t"<<_featureNum< i - 3... //768 in [2^9, 2^10) -> 6 -> smallest will be 768 / 32 = 24 int num = (int) floor (log ( inputsz * 2.0 / GlobalUtil::_texMinDim )/log(2.0)); return num <= 0 ? 1 : num; } void SiftPyramid::CopyFeatureVector(float*keys, float *descriptors) { if(keys) memcpy(keys, &_keypoint_buffer[0], 4*_featureNum*sizeof(float)); if(descriptors) memcpy(descriptors, &_descriptor_buffer[0], 128*_featureNum*sizeof(float)); } void SiftPyramid:: SetKeypointList(int num, const float * keys, int run_on_current, int skip_orientation) { //for each input keypoint //sort the key point list by size, and assign them to corresponding levels if(num <=0) return; _featureNum = num; ///copy the keypoints _keypoint_buffer.resize(num * 4); memcpy(&_keypoint_buffer[0], keys, 4 * num * sizeof(float)); //location and scale can be skipped _existing_keypoints = SIFT_SKIP_DETECTION; //filtering is skipped if it is running on the same image if(run_on_current) _existing_keypoints |= SIFT_SKIP_FILTERING; //orientation can be skipped if specified if(skip_orientation) _existing_keypoints |= SIFT_SKIP_ORIENTATION; //hacking parameter for using rectangle description mode if(skip_orientation == -1) _existing_keypoints |= SIFT_RECT_DESCRIPTION; } void SiftPyramid::SaveSIFT(const char * szFileName) { if (_featureNum <=0) return; float * pk = &_keypoint_buffer[0]; if(GlobalUtil::_BinarySIFT) { std::ofstream out(szFileName, ios::binary); out.write((char* )(&_featureNum), sizeof(int)); if(GlobalUtil::_DescriptorPPT) { int dim = 128; out.write((char* )(&dim), sizeof(int)); float * pd = &_descriptor_buffer[0] ; for(int i = 0; i < _featureNum; i++, pk+=4, pd +=128) { out.write((char* )(pk +1), sizeof(float)); out.write((char* )(pk), sizeof(float)); out.write((char* )(pk+2), 2 * sizeof(float)); out.write((char* )(pd), 128 * sizeof(float)); } }else { int dim = 0; out.write((char* )(&dim), sizeof(int)); for(int i = 0; i < _featureNum; i++, pk+=4) { out.write((char* )(pk +1), sizeof(float)); out.write((char* )(pk), sizeof(float)); out.write((char* )(pk+2), 2 * sizeof(float)); } } }else { std::ofstream out(szFileName); out.flags(ios::fixed); if(GlobalUtil::_DescriptorPPT) { float * pd = &_descriptor_buffer[0] ; out<<_featureNum<<" 128"<GetImgWidth(); int height = tex->GetImgHeight(); float* buffer1 = new float[ width * height * 4]; float* buffer2 = new float[ width * height * 4]; //read data back glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); tex->AttachToFBO(0); tex->FitTexViewPort(); glReadPixels(0, 0, width, height, GL_RGBA , GL_FLOAT, buffer1); //Tiffs saved with IL are flipped for(int i = 0; i < height; i++) { memcpy(buffer2 + i * width * 4, buffer1 + (height - i - 1) * width * 4, width * 4 * sizeof(float)); } //save data as floating point tiff file ilGenImages(1, &imID); ilBindImage(imID); ilEnable(IL_FILE_OVERWRITE); ilTexImage(width, height, 1, 4, IL_RGBA, IL_FLOAT, buffer2); ilSave(IL_TIF, name); ilDeleteImages(1, &imID); delete buffer1; delete buffer2; glReadBuffer(GL_NONE); } #endif