// file: sift-driver.cpp // author: Andrea Vedaldi // description: SIFT command line utility implementation // AUTORIGTHS #include #include #include #include #include #include #include #include #include #include extern "C" { #include #if defined(__clang__) || defined(__llvm__) #include #else #include #endif #include } using namespace std ; typedef unsigned char uint8_t; typedef unsigned int uint32_t; size_t const not_found = numeric_limits::max() - 1 ; /** @brief Case insensitive character comparison ** ** This predicate returns @c true if @a a and @a b are equal up to ** case. ** ** @return predicate value. **/ inline bool ciIsEqual(char a, char b) { return tolower((char unsigned)a) == tolower((char unsigned)b) ; } /** @brief Case insensitive extension removal ** ** The function returns @a name with the suffix $a ext removed. The ** suffix is matched case-insensitve. ** ** @return @a name without @a ext. **/ string // refactor-nice.pl: check this substitution // old: removeExtension(string name, string ext) removeExtension(string name, std::string ext) { string::iterator pos = find_end(name.begin(),name.end(),ext.begin(),ext.end(),ciIsEqual) ; // make sure the occurence is at the end if(pos+ext.size() == name.end()) { return name.substr(0, pos-name.begin()) ; } else { return name ; } } /** @brief Insert descriptor into stream ** ** The function writes a descriptor in ASCII/binary format ** and in integer/floating point format into the stream. ** ** @param os output stream. ** @param descr_pt descriptor (floating point) ** @param binary write binary descriptor? ** @param fp write floating point data? **/ std::ostream& insertDescriptor(std::ostream& os, VL::float_t const * descr_pt, bool binary, bool fp ) { #define RAW_CONST_PT(x) reinterpret_cast(x) #define RAW_PT(x) reinterpret_cast(x) if( fp ) { /* convert to 32 bits floats (single precision) */ VL::float32_t fdescr_pt [128] ; for(int i = 0 ; i < 128 ; ++i) fdescr_pt[i] = VL::float32_t( descr_pt[i]) ; if( binary ) { /* Test for endianess. Recall: big_endian = the most significant byte at lower memory address. */ short int const word = 0x0001 ; bool little_endian = RAW_CONST_PT(&word)[0] ; /* We save in big-endian (network) order. So if this machine is little endiand do the appropriate conversion. */ if( little_endian ) { for(int i = 0 ; i < 128 ; ++i) { VL::float32_t tmp = fdescr_pt[ i ] ; char* pt = RAW_PT(fdescr_pt + i) ; char* spt = RAW_PT(&tmp) ; pt[0] = spt[3] ; pt[1] = spt[2] ; pt[2] = spt[1] ; pt[3] = spt[0] ; } } os.write( RAW_PT(fdescr_pt), 128 * sizeof(VL::float32_t) ) ; } else { for(int i = 0 ; i < 128 ; ++i) os << ' ' << fdescr_pt[i] ; } } else { VL::uint8_t idescr_pt [128] ; for(int i = 0 ; i < 128 ; ++i) idescr_pt[i] = uint8_t(float_t(512) * descr_pt[i]) ; if( binary ) { os.write( RAW_PT(idescr_pt), 128) ; } else { for(int i = 0 ; i < 128 ; ++i) os << ' ' << uint32_t( idescr_pt[i] ) ; } } return os ; } /* keypoint list */ typedef vector > Keypoints ; /* predicate used to order keypoints by increasing scale */ bool cmpKeypoints (Keypoints::value_type const&a, Keypoints::value_type const&b) { return a.first.sigma < b.first.sigma ; } // ------------------------------------------------------------------- // main // ------------------------------------------------------------------- int main(int argc, char** argv) { int first = -1 ; int octaves = -1 ; int levels = 3 ; float threshold = 0.04f / levels / 2.0f ; float edgeThreshold = 10.0f; float magnif = 1.5 ; int nodescr = 0 ; int noorient = 0 ; int stableorder = 0 ; int savegss = 0 ; int verbose = 0 ; int binary = 0 ; int haveKeypoints = 0 ; int unnormalized = 0 ; int fp = 0 ; // refactor-nice.pl: check this substitution // old: string outputFilenamePrefix ; std::string outputFilenamePrefix ; // refactor-nice.pl: check this substitution // old: string outputFilename ; std::string outputFilename ; // refactor-nice.pl: check this substitution // old: string descriptorsFilename ; std::string descriptorsFilename ; // refactor-nice.pl: check this substitution // old: string keypointsFilename ; std::string keypointsFilename ; static struct option longopts[] = { { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { "output", required_argument, NULL, 'o' }, { "prefix", required_argument, NULL, 'p' }, { "first-octave", required_argument, NULL, 'f' }, { "keypoints", required_argument, NULL, 'k' }, { "octaves", required_argument, NULL, 'O' }, { "levels", required_argument, NULL, 'S' }, { "threshold", required_argument, NULL, 't' }, { "edge-threshold", required_argument, NULL, 'e' }, { "magnif", required_argument, NULL, 'm' }, { "binary", no_argument, NULL, 'b' }, { "no-descriptors", no_argument, &nodescr, 1 }, { "no-orientations", no_argument, &noorient, 1 }, { "stable-order", no_argument, &stableorder, 1 }, { "save-gss", no_argument, &savegss, 1 }, { "unnormalized", no_argument, &unnormalized, 1 }, { "floating-point", no_argument, &fp, 1 }, { NULL, 0, NULL, 0 } }; int ch ; try { while ( (ch = getopt_long(argc, argv, "vho:p:f:k:O:S:t:e:b", longopts, NULL)) != -1) { switch (ch) { case '?' : VL_THROW("Invalid option '"<> first ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; } break ; case 'O' : // octaves { std::istringstream iss(optarg) ; iss >> octaves ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; if( octaves < 1 ) { VL_THROW("The number of octaves cannot be smaller than one."); } } break ; case 'S' : // levels { std::istringstream iss(optarg) ; iss >> levels ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; if( levels < 1 ) { VL_THROW("The number of levels cannot be smaller than one.") ; } } break ; case 't' : // threshold { std::istringstream iss(optarg) ; iss >> threshold ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; } break ; case 'e' : // edge-threshold { std::istringstream iss(optarg) ; iss >> edgeThreshold ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; } break ; case 'm' : // magnification { std::istringstream iss(optarg) ; iss >> magnif ; if( iss.fail() ) VL_THROW("Invalid argument '" << optarg << "'.") ; } break ; case 'o' : // output filename { outputFilename = std::string(optarg) ; break ; } case 'p' : // output prefix { outputFilenamePrefix = std::string(optarg) ; break ; } case 'k' : // keypoint file { keypointsFilename = std::string(optarg) ; haveKeypoints = 1 ; break ; } case 'b' : // write descriptors to a binary file { binary = 1 ; break ; } case 0 : // all other options break ; default: assert(false) ; } } argc -= optind; argv += optind; // check for argument consistency if(argc == 0) VL_THROW("No input image specfied.") ; if(outputFilename.size() != 0 && ((argc > 1) | binary)) { VL_THROW("--output cannot be used with multiple images or --binary.") ; } if(outputFilename.size() !=0 && outputFilenamePrefix.size() !=0) { VL_THROW("--output cannot be used in combination with --prefix.") ; } /* end option try-catch block */ } catch( VL::Exception const & e ) { cerr << "siftpp: error: " << e.msg << endl ; exit(1) ; } // ----------------------------------------------------------------- // Loop over input images // ----------------------------------------------------------------- while( argc > 0 ) { // refactor-nice.pl: check this substitution // old: string name(argv[0]) ; std::string name(argv[0]) ; try { VL::PgmBuffer buffer ; // compute the output filenames: // // 1) if --output is specified, then we just use the one provided // by the user // // 2) if --output is not specified, we derive the output filename // from the input filename by // - removing the extension part from the output filename // - and if outputFilenamePrefix is non void, removing // the directory part and prefixing outputFilenamePrefix. // // 3) in any case we derive the binary descriptor filename by // removing from the output filename the .key extension (if any) // and adding a .desc extension. if(outputFilename.size() == 0) { // case 2) above outputFilename = name ; // if we specify an output directory, then extract // the basename if(outputFilenamePrefix.size() != 0) { outputFilename = outputFilenamePrefix + std::string(basename((char*)outputFilename.c_str())) ; } // remove .pgm extension, add .key outputFilename = removeExtension(outputFilename, ".pgm") ; outputFilename += ".key" ; } // remove .key extension, add .desc descriptorsFilename = removeExtension(outputFilename, ".key") ; descriptorsFilename += ".desc" ; // --------------------------------------------------------------- // Load PGM image // --------------------------------------------------------------- verbose && cout << "siftpp: lodaing PGM image '" << name << "' ..." << flush; try { ifstream in(name.c_str(), ios::binary) ; if(! in.good()) VL_THROW("Could not open '"< keypointsIn_pt ; if( haveKeypoints ) { keypointsIn_pt = auto_ptr (new ifstream(keypointsFilename.c_str(), ios::binary)) ; if( ! keypointsIn_pt->good() ) VL_THROW("Could not open keypoints file '" << keypointsFilename << "'.") ; verbose && cout << "siftpp: read keypoints from : '" << keypointsFilename << "'" << endl ; } /* If the descriptors are redirected to a binary file, then open it now */ auto_ptr descriptorsOut_pt ; if( binary ) { descriptorsOut_pt = auto_ptr (new ofstream(descriptorsFilename.c_str(), ios::binary)) ; if( ! descriptorsOut_pt->good() ) VL_THROW("Could not open descriptors file '" << descriptorsFilename << "'.") ; verbose && cout << "siftpp: write descriptors to : '" << descriptorsFilename << "'" << endl ; } if( haveKeypoints ) { // ------------------------------------------------------------- // Reads keypoint from file, compute descriptors // ------------------------------------------------------------- Keypoints keypoints ; while( !keypointsIn_pt->eof() ) { VL::float_t x,y,sigma,th ; /* read x, y, sigma and th from the beginning of the line */ (*keypointsIn_pt) >> x >> y >> sigma >> th ; /* skip the rest of the line */ (*keypointsIn_pt).ignore(numeric_limits::max(),'\n') ; /* break the loop if end of file reached */ if( keypointsIn_pt->eof() ) break ; /* trhow an error if something wrong */ if( ! keypointsIn_pt->good() ) VL_THROW("Error reading keypoints file.") ; /* compute integer components */ VL::Sift::Keypoint key = sift.getKeypoint(x,y,sigma) ; Keypoints::value_type entry ; entry.first = key ; entry.second = th ; keypoints.push_back(entry) ; } /* sort keypoints by scale if not required otherwise */ if(! stableorder) sort(keypoints.begin(), keypoints.end(), cmpKeypoints) ; // process in batch for(Keypoints::const_iterator iter = keypoints.begin() ; iter != keypoints.end() ; ++iter) { VL::Sift::Keypoint const& key = iter->first ; VL::float_t th = iter->second ; /* write keypoint */ out << setprecision(2) << key.x << " " << setprecision(2) << key.y << " " << setprecision(2) << key.sigma << " " << setprecision(3) << th ; /* compute descriptor */ VL::float_t descr [128] ; sift.computeKeypointDescriptor(descr, key, th) ; /* save to appropriate file */ if( descriptorsOut_pt.get() ) { ostream& os = *descriptorsOut_pt.get() ; insertDescriptor(os, descr, true, fp) ; } else { insertDescriptor(out, descr, false, fp) ; } /* next keypoint */ out << endl ; } // next keypoint } else { // ------------------------------------------------------------- // Run detector, compute orientations and descriptors // ------------------------------------------------------------- for( VL::Sift::KeypointsConstIter iter = sift.keypointsBegin() ; iter != sift.keypointsEnd() ; ++iter ) { // detect orientations VL::float_t angles [4] ; int nangles ; if( ! noorient ) { nangles = sift.computeKeypointOrientations(angles, *iter) ; } else { nangles = 1; angles[0] = VL::float_t(0) ; } // compute descriptors for(int a = 0 ; a < nangles ; ++a) { out << setprecision(2) << iter->x << ' ' << setprecision(2) << iter->y << ' ' << setprecision(2) << iter->sigma << ' ' << setprecision(3) << angles[a] ; /* compute descriptor */ VL::float_t descr_pt [128] ; sift.computeKeypointDescriptor(descr_pt, *iter, angles[a]) ; /* save descriptor to to appropriate file */ if( ! nodescr ) { if( descriptorsOut_pt.get() ) { ostream& os = *descriptorsOut_pt.get() ; insertDescriptor(os, descr_pt, true, fp) ; } else { insertDescriptor(out, descr_pt, false, fp) ; } } /* next line */ out << endl ; } // next angle } // next keypoint } out.close() ; if(descriptorsOut_pt.get()) descriptorsOut_pt->close(); if(keypointsIn_pt.get()) keypointsIn_pt->close(); verbose && cout << "siftpp: job completed"<