keypoints.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007-2008 Anael Orlinski
00003  *
00004  * This file is part of Panomatic.
00005  *
00006  * Panomatic is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * Panomatic is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with Panomatic; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 #include <iostream>
00022 #include <fstream>
00023 #include <vector>
00024 #include <string>
00025 #include <boost/foreach.hpp>
00026 #include <boost/shared_ptr.hpp>
00027 
00028 #include <tclap/CmdLine.h>
00029 
00030 #include <localfeatures/KeyPointDetector.h>
00031 #include <localfeatures/CircularKeyPointDescriptor.h>
00032 #include <localfeatures/Sieve.h>
00033 #include <localfeatures/KeyPointIO.h>
00034 
00035 #include <vigra/impex.hxx>
00036 #include <vigra/stdimage.hxx>
00037 #include <vigra/stdimagefunctions.hxx>
00038 #include <vigra/rgbvalue.hxx>
00039 
00040 using namespace std;
00041 using namespace TCLAP;
00042 using namespace lfeat;
00043 
00044 const char* kVersion="0.9.5";
00045 
00046 #define TRACE_IMG(A) cerr << A << std::endl
00047 #define TRACE_INFO(A) cerr << A << std::endl
00048 
00049 
00050 //typedef boost::shared_ptr<lfeat::KeyPoint>                    KeyPointPtr;
00051 //typedef std::vector<KeyPointPtr>                                              KeyPointVect_t;
00052 //typedef std::vector<KeyPointPtr>::iterator                            KeyPointVectIt_t;
00053 
00054 
00055 // define a Keypoint insertor
00056 class KeyPointVectInsertor : public lfeat::KeyPointInsertor
00057 {
00058 public:
00059     KeyPointVectInsertor ( KeyPointVect_t& iVect ) : _v ( iVect ) {};
00060     inline virtual void operator() ( const lfeat::KeyPoint& k )
00061     {
00062         _v.push_back ( KeyPointPtr ( new lfeat::KeyPoint ( k ) ) );
00063     }
00064 
00065 private:
00066     KeyPointVect_t& _v;
00067 };
00068 
00069 // define a sieve extractor
00070 class SieveExtractorKP : public lfeat::SieveExtractor<KeyPointPtr>
00071 {
00072 public:
00073     SieveExtractorKP ( KeyPointVect_t& iV ) : _v ( iV ) {};
00074     inline virtual void operator() ( const KeyPointPtr& k )
00075     {
00076         _v.push_back ( k );
00077     }
00078 private:
00079     KeyPointVect_t& _v;
00080 };
00081 
00082 bool DetectKeypoints ( const std::string& imgfile, bool downscale,
00083                        double surfScoreThreshold,
00084                        KeyPointPtr preKeypoint,
00085                        bool onlyInterestPoints, int sieveWidth,
00086                        int sieveHeight, int sieveSize, KeypointWriter& writer )
00087 {
00088     TRACE_IMG ( "Analyze image..." );
00089     try
00090 
00091     {
00092         vigra::ImageImportInfo aImageInfo ( imgfile.c_str() );
00093 
00094         int aNewImgWidth = aImageInfo.width();
00095         int aNewImgHeight = aImageInfo.height();
00096 
00097         int aOrigImgWidth = aNewImgWidth;
00098         int aOrigImgHeight = aNewImgHeight;
00099 
00100         int scale = 1;
00101         if ( downscale )
00102         {
00103             aNewImgWidth >>= 1;
00104             aNewImgHeight >>= 1;
00105             scale = 2;
00106         }
00107 
00108         vigra::DImage aImageDouble ( aNewImgWidth, aNewImgHeight );
00109 
00110         if ( aImageInfo.isGrayscale() )
00111         {
00112             if ( downscale )
00113             {
00114                 TRACE_IMG ( "Load greyscale..." );
00115                 vigra::DImage aImageG ( aImageInfo.width(), aImageInfo.height() );
00116                 importImage ( aImageInfo, destImage ( aImageG ) );
00117                 vigra::resizeImageNoInterpolation (
00118                     aImageG.upperLeft(),
00119                     aImageG.upperLeft() + vigra::Diff2D ( aNewImgWidth * 2, aNewImgHeight * 2 ),
00120                     vigra::DImage::Accessor(),
00121                     aImageDouble.upperLeft(),
00122                     aImageDouble.lowerRight(),
00123                     vigra::DImage::Accessor() );
00124             }
00125             else
00126             {
00127                 TRACE_IMG ( "Load greyscale..." );
00128                 importImage ( aImageInfo, destImage ( aImageDouble ) );
00129             }
00130         }
00131         else
00132         {
00133             TRACE_IMG ( "Load RGB..." );
00134             //open the image in RGB
00135             vigra::DRGBImage aImageRGB ( aImageInfo.width(), aImageInfo.height() );
00136 
00137             if ( aImageInfo.numExtraBands() == 1 )
00138 
00139             {
00140 
00141                 TRACE_INFO ( "Image with alpha channels are not supported" );
00142                 return false;
00143 
00144             }
00145 
00146             else if ( aImageInfo.numExtraBands() == 0 )
00147 
00148             {
00149 
00150                 vigra::importImage ( aImageInfo, destImage ( aImageRGB ) );
00151             }
00152             else
00153             {
00154                 TRACE_INFO ( "Image with multiple alpha channels are not supported" );
00155                 return false;
00156             }
00157 
00158             if ( downscale )
00159             {
00160                 TRACE_IMG ( "Resize to greyscale double..." );
00161                 vigra::resizeImageNoInterpolation (
00162                     aImageRGB.upperLeft(),
00163                     aImageRGB.upperLeft() + vigra::Diff2D ( aNewImgWidth * 2, aNewImgHeight * 2 ),
00164                     vigra::RGBToGrayAccessor<vigra::RGBValue<double> >(),
00165                     aImageDouble.upperLeft(),
00166                     aImageDouble.lowerRight(),
00167                     vigra::DImage::Accessor() );
00168 
00169             }
00170             else
00171             {
00172                 // convert to greyscale
00173                 TRACE_IMG ( "Convert to greyscale double..." );
00174                 vigra::copyImage (      aImageRGB.upperLeft(),
00175                                     aImageRGB.lowerRight(),
00176                                     vigra::RGBToGrayAccessor<vigra::RGBValue<double> >(),
00177                                     aImageDouble.upperLeft(),
00178                                     vigra::DImage::Accessor() );
00179             }
00180         }
00181 
00182 
00183         ImageInfo imginfo(imgfile, aOrigImgWidth, aOrigImgHeight);
00184 
00185         TRACE_IMG ( "Build integral image..." );
00186         // create integral image
00187         lfeat::Image img;
00188         img.init ( aImageDouble );
00189 
00190         KeyPointVect_t kp;
00191         KeyPointVectInsertor aInsertor = KeyPointVectInsertor ( kp );
00192         if ( ! preKeypoint)
00193         {
00194             // setup the detector
00195             lfeat::KeyPointDetector aKP;
00196             aKP.setScoreThreshold ( surfScoreThreshold );
00197 
00198             // detect the keypoints
00199             aKP.detectKeypoints ( img, aInsertor );
00200 
00201             TRACE_IMG ( "Found "<< kp.size() << " interest points." );
00202 
00203             TRACE_IMG ( "Filtering keypoints..." );
00204 
00205             lfeat::Sieve<lfeat::KeyPointPtr, lfeat::KeyPointPtrSort > aSieve ( sieveWidth, sieveHeight, sieveSize );
00206             // insert the points in the Sieve
00207             double aXF = ( double ) sieveWidth / ( double ) aImageDouble.width();
00208             double aYF = ( double ) sieveHeight / ( double ) aImageDouble.height();
00209             BOOST_FOREACH ( KeyPointPtr& aK, kp )
00210             aSieve.insert ( aK, ( int ) ( aK->_x * aXF ), ( int ) ( aK->_y * aYF ) );
00211 
00212             // pull remaining values from the sieve
00213             kp.clear();
00214 
00215             // make an extractor and pull the points
00216             SieveExtractorKP aSieveExt ( kp );
00217             aSieve.extract ( aSieveExt );
00218 
00219             TRACE_IMG ( "Kept " << kp.size() << " interest points." );
00220         }
00221         else
00222         {
00223             kp.push_back(boost::shared_ptr<KeyPoint>(preKeypoint));
00224         }
00225 
00226         lfeat::KeyPointDescriptor* aKPD;
00227         aKPD = new lfeat::CircularKeyPointDescriptor( img );
00228         TRACE_IMG ( "Generating descriptors and writing output..." );
00229 
00230 
00231         int dims = aKPD->getDescriptorLength();
00232         if ( onlyInterestPoints )
00233         {
00234             dims = 0;
00235         }
00236 
00237         // compute orientation
00238         // vector for keypoints with more than one orientation
00239         KeyPointVect_t kp_new_ori;
00240 
00241         BOOST_FOREACH ( KeyPointPtr& aK, kp )
00242         {
00243             if (!( preKeypoint  && preKeypoint->_ori > -10000))
00244             {
00245                 double angles[4];
00246                 int nAngles = aKPD->assignOrientation ( *aK, angles );
00247                 std::cerr << "Orientations:" << aK->_ori;
00248                 for (int i=0; i < nAngles; i++)
00249                 {
00250                     // duplicate Keypoint with additional angles
00251                     KeyPointPtr aKn = KeyPointPtr ( new lfeat::KeyPoint ( *aK ) );
00252                     aKn->_ori = angles[i];
00253                     std::cerr << " " << aKn->_ori;
00254                     kp_new_ori.push_back(aKn);
00255                 }
00256                 std::cerr << std::endl;
00257             }
00258         }
00259 
00260         // append new keypoints to kp
00261         kp.insert(kp.end(), kp_new_ori.begin(), kp_new_ori.end());
00262 
00263         writer.writeHeader ( imginfo, kp.size(), aKPD->getDescriptorLength() );
00264 
00265         BOOST_FOREACH ( KeyPointPtr& aK, kp )
00266         {
00267             if ( !onlyInterestPoints )
00268             {
00269                 aKPD->makeDescriptor ( *aK );
00270             }
00271             writer.writeKeypoint ( aK->_x * scale, aK->_y * scale, aK->_scale * scale, aK->_ori,
00272                                    aK->_score, dims, aK->_vec );
00273         }
00274         writer.writeFooter();
00275         delete aKPD;
00276     }
00277 
00278     catch ( std::exception& e )
00279 
00280     {
00281 
00282         TRACE_INFO ( "An error happened while computing keypoints : caught exception: " << e.what() << endl );
00283 
00284         return false;
00285 
00286     }
00287 
00288     return true;
00289 }
00290 
00291 
00292 class MyOutput : public StdOutput
00293 {
00294 public:
00295 
00296     virtual void failure ( CmdLineInterface& c, ArgException& e )
00297     {
00298         std::cerr << "Parse error: " << e.argId() << std::endl << "             " << e.error() << std::endl << std::endl << endl;
00299         usage ( c );
00300     }
00301 
00302     virtual void usage ( CmdLineInterface& c )
00303     {
00304         int iML = 30;
00305         cout << "Basic usage : " << endl;
00306         cout << "  "<< c.getProgramName() << " [options ] IMG" << endl;
00307 
00308         cout << endl <<"All options : " << endl;
00309         list<Arg*> args = c.getArgList();
00310         for ( ArgListIterator it = args.begin(); it != args.end(); it++ )
00311         {
00312             string aL = ( *it )->longID();
00313             string aD = ( *it )->getDescription();
00314             // replace tabs by n spaces.
00315             size_t p = aD.find_first_of ( "\t" );
00316             while ( p != string::npos )
00317             {
00318                 string aD1 = aD.substr ( 0, p );
00319                 string aD2 = aD.substr ( p+1, aD.size() - p + 1 );
00320 
00321                 aD = aD1 + "\n" + string ( iML, ' ' ) + aD2;
00322                 p = aD.find_first_of ( "\t" );
00323             }
00324 
00325 
00326             if ( (int)aL.size() > iML )
00327             {
00328                 cout << aL << endl << string ( iML, ' ' )  << aD << endl;
00329             }
00330             else
00331             {
00332                 cout << aL  << string ( iML - aL.size(), ' ' )   << aD << endl;
00333             }
00334         }
00335     }
00336 
00337     virtual void version ( CmdLineInterface& c )
00338     {
00339         cout << "my version message: 0.1" << endl;
00340     }
00341 };
00342 
00343 
00344 
00345 void parseOptions ( int argc, char** argv )
00346 {
00347     try
00348     {
00349 
00350         CmdLine cmd ( "keypoints", ' ', kVersion );
00351 
00352         MyOutput my;
00353         cmd.setOutput ( &my );
00354 
00355         SwitchArg aArgFullScale ( "","fullscale", "Uses full scale image to detect keypoints    (default:false)\n", false );
00356         // SURF has a better performance than the other descriptors, use it by default, if it is enabled
00357         ValueArg<int> aArgSurfScoreThreshold ( "","surfscore", "Detection score threshold    (default : 1000)\n", false, 1000, "int" );
00358         ValueArg<int> aArgSieve1Width ( "","sievewidth", "Interest point sieve: Number of buckets on width    (default : 10)", false, 10, "int" );
00359         ValueArg<int> aArgSieve1Height ( "","sieveheight",  "Interest point sieve : Number of buckets on height    (default : 10)", false, 10, "int" );
00360         ValueArg<int> aArgSieve1Size ( "","sievesize",  "Interest point sieve : Max points per bucket    (default : 10)\n", false, 10, "int" );
00361         ValueArg<std::string> aArgOutputFormat ( "","format", "Output format (text, autopano-xml, descperf), default text\n", false, "text", "string" );
00362         ValueArg<std::string> aArgOutputFile ( "o","output", "Output file. If not specified, print to standard out\n", false, "", "string" );
00363         SwitchArg aArgInterestPoints ( "","interestpoints", "output only the interest points and the scale (default:false)\n", false );
00364         ValueArg<std::string> aArgFixedInterestPoint ( "","ip", "Compute descriptor at x,y,scale,ori \n", false, "", "string" );
00365 
00366         cmd.add ( aArgSurfScoreThreshold );
00367         cmd.add ( aArgFullScale );
00368         cmd.add ( aArgSieve1Width );
00369         cmd.add ( aArgSieve1Height );
00370         cmd.add ( aArgSieve1Size );
00371         cmd.add ( aArgOutputFormat );
00372         cmd.add ( aArgOutputFile );
00373         cmd.add ( aArgInterestPoints );
00374         cmd.add ( aArgFixedInterestPoint );
00375 
00376         /*
00377                 SwitchArg aArgTest("t","test", "Enables test mode\n", false);
00378                 cmd.add( aArgTest );
00379         */
00380 
00381         UnlabeledMultiArg<string> aArgFiles ( "fileName", "Image files", true, "string" );
00382         cmd.add ( aArgFiles );
00383 
00384         cmd.parse ( argc,argv );
00385 
00386         //
00387         // Set variables
00388         //
00389         vector<string> aFiles = aArgFiles.getValue();
00390         if ( aFiles.size() != 1 )
00391         {
00392             exit ( 1 );
00393         }
00394 
00395         double surfScoreThreshold=1000;
00396         if ( aArgSurfScoreThreshold.isSet() )
00397         {
00398             surfScoreThreshold = ( aArgSurfScoreThreshold.getValue() );
00399         }
00400 
00401         bool downscale = true;
00402         if ( aArgFullScale.isSet() )
00403         {
00404             downscale = false;
00405         }
00406 
00407         int sieveWidth = 10;
00408         if ( aArgSieve1Width.isSet() )
00409         {
00410             sieveWidth = aArgSieve1Width.getValue();
00411         }
00412         int sieveHeight = 10;
00413         if ( aArgSieve1Height.isSet() )
00414         {
00415             sieveHeight = aArgSieve1Height.getValue();
00416         }
00417         int sieveSize = 10;
00418         if ( aArgSieve1Size.isSet() )
00419         {
00420             sieveSize = aArgSieve1Size.getValue();
00421         }
00422 
00423         bool onlyInterestPoints = false;
00424         if ( aArgInterestPoints.isSet() )
00425         {
00426             onlyInterestPoints = true;
00427         }
00428 
00429         std::ostream* outstream;
00430         if ( aArgOutputFile.isSet() )
00431         {
00432             outstream = new std::ofstream(aArgOutputFile.getValue().c_str());
00433         }
00434         else
00435         {
00436             outstream = & std::cout;
00437         }
00438 
00439         KeypointWriter* writer = 0;
00440         std::string outputformat = "text";
00441         if ( aArgOutputFormat.isSet() )
00442         {
00443             outputformat = aArgOutputFormat.getValue();
00444         }
00445         if (outputformat == "text")
00446         {
00447             writer = new SIFTFormatWriter(*outstream);
00448         }
00449         else if (outputformat == "autopano-sift-xml")
00450         {
00451             writer = new AutopanoSIFTWriter(*outstream);
00452         }
00453         else if (outputformat == "descperf")
00454         {
00455             writer = new DescPerfFormatWriter(*outstream);
00456         }
00457         else
00458         {
00459             std::cerr << "Unknown output format, valid values are text, autopano-sift-xml, descperf" << std::endl;
00460             exit(1);
00461         }
00462 
00463 
00464         KeyPointPtr preKPPtr;
00465         if ( aArgFixedInterestPoint.isSet() )
00466         {
00467             preKPPtr = KeyPointPtr(new KeyPoint());
00468             preKPPtr->_x = -10001;
00469             preKPPtr->_ori = -10001;
00470             int nf = sscanf(aArgFixedInterestPoint.getValue().c_str(), "%lf:%lf:%lf:%lf",
00471                             &(preKPPtr->_x), &(preKPPtr->_y), &(preKPPtr->_scale), &(preKPPtr->_ori));
00472             std::cerr << "passed orientation: " << preKPPtr->_ori << std::endl;
00473             if (nf < 3)
00474             {
00475                 std::cerr << "Invalid value for --ip option, expected --ip x:y:scale:ori" << std::endl;
00476                 exit(1);
00477             }
00478         }
00479 
00480         DetectKeypoints ( aFiles[0], downscale, surfScoreThreshold, preKPPtr, onlyInterestPoints, sieveWidth, sieveHeight, sieveSize, *writer );
00481 
00482         if ( aArgOutputFile.isSet() )
00483         {
00484             delete outstream;
00485         }
00486 
00487     }
00488     catch ( ArgException& e )
00489     {
00490         cout << "ERROR: " << e.error() << " " << e.argId() << endl;
00491     }
00492 }
00493 
00494 int main ( int argc, char** argv )
00495 {
00496     std::cerr << "keypoints " << kVersion << " by Anael Orlinski - naouel@naouel.org" << endl << endl;
00497 
00498     // create a panodetector object
00499     parseOptions ( argc, argv );
00500 
00501     return 0;
00502 
00503 }

Generated on 20 Oct 2014 for Hugintrunk by  doxygen 1.4.7