std::vector<cv::Mat> SiftMatcher::compute(Keypoints& keypoints)
{
  std::vector<cv::Mat> descriptors(keypoints.size());
  cv::SiftDescriptorExtractor extractor;

  for (size_t i = 0; i < keypoints.size(); ++i)
  {
    extractor.compute(m_images[i], keypoints[i], descriptors[i]);
  }

  return descriptors;
}
bool FeatureAlgorithm::extractFeatures(const cv::Mat& image, Keypoints& kp, Descriptors& desc) const
{
    assert(!image.empty());

    if (featureEngine)
    {
        (*featureEngine)(image, cv::noArray(), kp, desc);
    }
    else
    {
        detector->detect(image, kp);
    
        if (kp.empty())
            return false;
    
        extractor->compute(image, kp, desc);
    }
    
    
    return kp.size() > 0;
}
Beispiel #3
0
// -------------------------------------------------------------------
//                                                                main
// -------------------------------------------------------------------
int
fake_main(int argc, char** argv, float edgeThreshold)
{
  
  int    first          = -1 ;
  int    octaves        = -1 ;
  int    levels         = 3 ;
  float  threshold      = 0.04f / levels / 2.0f ;
  //float  edgeThreshold  = 10.0f;
  float  magnif         = 3.0 ;
  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 ;
  string outputFilenamePrefix ;
  string outputFilename ;
  string descriptorsFilename ;
  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 '"<<argv[optind-1]<<"'.") ;
        break;
        
      case ':' :
        VL_THROW("Missing argument of option '"<<argv[optind-1]<<"'.") ;
        break;
        
      case 'h' :
        std::cout
          << argv[0] << " [--verbose|=v] [--help|-h]" << endl
	  << "     [--output|-o NAME] [--prefix|-p PREFIX] [--binary|-b] [--save-gss] " << endl
          << "     [--no-descriptors] [--no-orientations] " << endl
          << "     [--levels|-S NUMBER] [--octaves|-O NUMBER] [--first-octave|-f NUMBER] " << endl
          << "     [--threshold|-t NUMBER] [--edge-threshold|-e NUMBER] " << endl
          << "     [--floating-point] [--unnormalized] " << endl
          << "     IMAGE [IMAGE2 ...]" << endl
          << endl
	  << "* Options *" << endl
          << " --verbose             Be verbose"<< endl
          << " --help                Print this message"<<endl
          << " --output=NAME         Write to this file"<<endl
	  << " --prefix=PREFIX       Derive output filename prefixing this string to the input file"<<endl
          << " --binary              Write descriptors to a separate file in binary format"<<endl
	  << " --keypoints=FILE      Reads keypoint frames from here; do not run SIFT detector" << endl
          << " --save-gss            Save Gaussian scale space on disk" << endl
          << " --octaves=O           Number of octaves" << endl
          << " --levels=S            Number of levels per octave" << endl
          << " --first-octave=MINO   Index of the first octave" << endl
          << " --threshold=THR       Keypoint strength threhsold" << endl
          << " --magnif=MAG          Keypoint magnification" << endl
          << " --edge-threshold=THR  On-edge threshold" << endl 
          << " --no-descriptors      Do not compute descriptors" << endl
          << " --no-orientations     Do not compute orientations" << endl
	  << " --stable-order        Do not reorder keypoints" << endl
          << " --unnormalzied        Do not normalize descriptors" << endl
          << " --floating-point      Save floating point descriptors" << endl
	  << endl
	  << " * Examples *" << endl
	  << argv[0] << " [OPTS...] image.pgm" << endl
	  << argv[0] << " [OPTS...] image.pgm --output=file.key" << endl
	  << argv[0] << " [OPTS...] image.pgm --keypoints=frames.key" << endl
	  << argv[0] << " [OPTS...] *.pgm --prefix=/tmp/" << endl
	  << argv[0] << " [OPTS...] *.pgm --prefix=/tmp/ --binary" << endl
	  << endl
	  << " * This build: " ;
#if defined VL_USEFASTMATH
	std::cout << "has fast approximate math" ;
#else
	std::cout << "has slow accurate math" ;
#endif
	std::cout << " (fp datatype is '"
		  << VL_EXPAND_AND_STRINGIFY(VL_FASTFLOAT)
		  << "') *"<<endl ;
        return 0 ;
	
      verbose = 1 ;
        
      case 'f': // first octave
        {
          std::istringstream iss(optarg) ;
          iss >> 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 0 : // all other options
        break ;
        
      default:
        assert(false) ;
      }
    }

    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
  // -----------------------------------------------------------------      

  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) {
	char * tmp = new char [outputFilename.length()+1] ;
	strcpy(tmp, outputFilename.c_str()) ;
	outputFilename = outputFilenamePrefix + 
	  std::string(basename(tmp)) ;
	delete [] tmp ;
      }
	
      // remove .pgm extension, add .key
      outputFilename = removeExtension(outputFilename, ".pgm") ;
      outputFilename += ".key" ;
      outputFilename = "/tmp/" + outputFilename;
    }
      
    // 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 '"<<name<<"'.") ;      
      extractPgm(in, buffer) ;
    }    
    catch(VL::Exception const& e) {
      throw VL::Exception("PGM read error: "+e.msg) ;	
    }
      
    verbose && cout 
      << " read "
      << buffer.width  <<" x "
      << buffer.height <<" pixels" 
      << endl ;
      
    // ---------------------------------------------------------------
    //                                            Gaussian scale space
    // ---------------------------------------------------------------    
    verbose && cout 
      << "siftpp: computing Gaussian scale space" 
      << endl ;
      
    int         O      = octaves ;    
    int const   S      = levels ;
    int const   omin   = first ;
    float const sigman = .5 ;
    float const sigma0 = 1.6 * powf(2.0f, 1.0f / S) ;
      
    // optionally autoselect the number number of octaves
    // we downsample up to 8x8 patches
    if(O < 1) {
      O = std::max
	(int
	 (std::floor
	  (log2
	   (std::min(buffer.width,buffer.height))) - omin -3), 1) ;
    }

    verbose && cout
      << "siftpp:   number of octaves     : " << O << endl 
      << "siftpp:   first octave          : " << omin << endl 
      << "siftpp:   levels per octave     : " << S 
      << endl ;
      
    // initialize scalespace
    VL::Sift sift(buffer.data, buffer.width, buffer.height, 
		  sigman, sigma0,
		  O, S,
		  omin, -1, S+1) ;
      
    verbose && cout 
      << "siftpp: Gaussian scale space completed"
      << endl ;
      
    // ---------------------------------------------------------------
    //                                       Save Gaussian scale space
    // ---------------------------------------------------------------    
      
    if(savegss) {
      verbose && cout<<"siftpp: saving Gaussian scale space"<<endl ;
	
      string imageBasename = removeExtension(outputFilename, ".key") ;
	
      for(int o = omin ; o < omin + O ; ++o) {
	for(int s = 0 ; s < S ; ++s) {
	    
	  ostringstream suffix ;
	  suffix<<'.'<<o<<'.'<<s<<".pgm" ;
	  string imageFilename = imageBasename + suffix.str() ;
	    
	  verbose && cout 
	    << "siftpp:   octave " << setw(3) << o
	    << " level " << setw(3) << s
	    << " to '" << imageFilename
	    << "' ..." << flush ;
	    
	  ofstream fout(imageFilename.c_str(), ios::binary) ;
	  if(!fout.good()) 
	    VL_THROW("Could not open '"<<imageFilename<<'\'') ;
	    
	  VL::insertPgm(fout,
			sift.getLevel(o,s),
			sift.getOctaveWidth(o),
			sift.getOctaveHeight(o)) ;
	  fout.close() ;
	    
	  verbose && cout
	    << " done." << endl ;
	}
      }
    }
      
    // -------------------------------------------------------------
    //                                             Run SIFT detector
    // -------------------------------------------------------------    
    if( ! haveKeypoints ) {

      verbose && cout 
	<< "siftpp: running detector  "<< endl
	<< "siftpp:   threshold             : " << threshold << endl
	<< "siftpp:   edge-threshold        : " << edgeThreshold
	<< endl ;
	
      sift.detectKeypoints(threshold, edgeThreshold) ;
	
      verbose && cout 
	<< "siftpp: detector completed with " 
	<< sift.keypointsEnd() - sift.keypointsBegin() 
	<< " keypoints" 
	<< endl ;
    }
      
    // -------------------------------------------------------------
    //                  Run SIFT orientation detector and descriptor
    // -------------------------------------------------------------    

    /* set descriptor options */
    sift.setNormalizeDescriptor( ! unnormalized ) ;
    sift.setMagnification( magnif ) ;

    if( verbose ) {
      cout << "siftpp: " ;
      if( ! noorient &   nodescr) cout << "computing keypoint orientations" ;
      if(   noorient & ! nodescr) cout << "computing keypoint descriptors" ;
      if( ! noorient & ! nodescr) cout << "computing orientations and descriptors" ;
      if(   noorient &   nodescr) cout << "finalizing" ; 
      cout << endl ;
    }
      
    {            
      // open output file
      ofstream out(outputFilename.c_str(), ios::binary) ;
        
      if( ! out.good() ) 
	VL_THROW("Could not open output file '"
		 << outputFilename
		 << "'.") ;
        
      verbose && cout
	<< "siftpp:   write keypoints to    : '" << outputFilename << "'"         << endl
	<< "siftpp:   floating point descr. : "  << (fp           ? "yes" : "no") << endl
	<< "siftpp:   binary descr.         : "  << (binary       ? "yes" : "no") << endl
	<< "siftpp:   unnormalized descr.   : "  << (unnormalized ? "yes" : "no") << endl
	<< "siftpp:   descr. magnif.        : "  << setprecision(3) << magnif
	<< endl ;
        
      out.flags(ios::fixed) ;
      
      /* If a keypoint file is provided, then open it now */
      auto_ptr<ifstream> keypointsIn_pt ;
        
      if( haveKeypoints ) {
	keypointsIn_pt = auto_ptr<ifstream>
	  (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<ofstream> descriptorsOut_pt ;
        
      if( binary ) {        
	descriptorsOut_pt = auto_ptr<ofstream>
	  (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<streamsize>::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"<<endl ;
    }
      
    argc-- ;
    argv++ ;
    outputFilename = string("") ;      
  }
bool performEstimation
(
    const FeatureAlgorithm& alg,
    const ImageTransformation& transformation,
    const cv::Mat& sourceImage,
    std::vector<FrameMatchingStatistics>& stat
)
{
  Keypoints   sourceKp;
  Descriptors sourceDesc;

  cv::Mat gray;
  if (sourceImage.channels() == 3)
      cv::cvtColor(sourceImage, gray, CV_BGR2GRAY);
  else if (sourceImage.channels() == 4)
      cv::cvtColor(sourceImage, gray, CV_BGRA2GRAY);
  else if(sourceImage.channels() == 1)
      gray = sourceImage;

  if (!alg.extractFeatures(gray, sourceKp, sourceDesc))
    return false;

  std::vector<float> x = transformation.getX();
  stat.resize(x.size());

  const int count = x.size();

  cv::Mat     transformedImage;
  Keypoints   resKpReal;
  Descriptors resDesc;
  Matches     matches;

  // To convert ticks to milliseconds
  const double toMsMul = 1000. / cv::getTickFrequency();

#pragma omp parallel for private(transformedImage, resKpReal, resDesc, matches)
  for (int i = 0; i < count; i++)
  {
    float       arg = x[i];
    FrameMatchingStatistics& s = stat[i];

    transformation.transform(arg, gray, transformedImage);

    int64 start = cv::getTickCount();

    alg.extractFeatures(transformedImage, resKpReal, resDesc);

    // Initialize required fields
    s.isValid        = resKpReal.size() > 0;
    s.argumentValue  = arg;

    if (!s.isValid)
        continue;

    if (alg.knMatchSupported)
    {
      std::vector<Matches> knMatches;
      alg.matchFeatures(sourceDesc, resDesc, 2, knMatches);
      ratioTest(knMatches, 0.75, matches);

      // Compute percent of false matches that were rejected by ratio test
      s.ratioTestFalseLevel = (float)(knMatches.size() - matches.size()) / (float) knMatches.size();
    }
    else
    {
      alg.matchFeatures(sourceDesc, resDesc, matches);
    }

    int64 end = cv::getTickCount();

    Matches correctMatches;
    cv::Mat homography;
    bool homographyFound = ImageTransformation::findHomography(sourceKp, resKpReal, matches, correctMatches, homography);

    // Some simple stat:
    s.isValid        = homographyFound;
    s.totalKeypoints = resKpReal.size();
    s.consumedTimeMs = (end - start) * toMsMul;

    // Compute overall percent of matched keypoints
    s.percentOfMatches      = (float) matches.size() / (float)(std::min(sourceKp.size(), resKpReal.size()));
    s.correctMatchesPercent = (float) correctMatches.size() / (float)matches.size();

    // Compute matching statistics
    computeMatchesDistanceStatistics(correctMatches, s.meanDistance, s.stdDevDistance);
  }

  return true;
}
void PointsToKeyPoints(const Points2f& ps, Keypoints& kps) {
    kps.clear();
    for (const auto& p : ps) {
        kps.push_back(KeyPoint(p, 1.0f));
    }
}