PanoDetector.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 ; tab-width: 4 -*-
00002 /*
00003 * Copyright (C) 2007-2008 Anael Orlinski
00004 *
00005 * This file is part of Panomatic.
00006 *
00007 * Panomatic is free software; you can redistribute it and/or modify
00008 * it under the terms of the GNU General Public License as published by
00009 * the Free Software Foundation; either version 2 of the License, or
00010 * (at your option) any later version.
00011 *
00012 * Panomatic is distributed in the hope that it will be useful,
00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 * GNU General Public License for more details.
00016 *
00017 * You should have received a copy of the GNU General Public License
00018 * along with Panomatic; if not, write to the Free Software
00019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 */
00021 
00022 #include "PanoDetector.h"
00023 #include <iostream>
00024 #include <fstream>
00025 #include <sstream>
00026 #include <boost/foreach.hpp>
00027 
00028 #include <time.h>
00029 
00030 #include "zthread/Runnable.h"
00031 #include "zthread/PoolExecutor.h"
00032 #include "Utils.h"
00033 #include "Tracer.h"
00034 
00035 #include <algorithms/nona/ComputeImageROI.h>
00036 #include <nona/RemappedPanoImage.h>
00037 #include <nona/ImageRemapper.h>
00038 
00039 //for multi row strategy
00040 #include <panodata/StandardImageVariableGroups.h>
00041 #include <PT/Panorama.h>
00042 #include <PT/ImageGraph.h>
00043 #include <algorithms/optimizer/PTOptimizer.h>
00044 #include <algorithms/basic/CalculateOverlap.h>
00045 
00046 #include "ImageImport.h"
00047 
00048 #ifdef _WINDOWS
00049 #include <windows.h>
00050 #include <direct.h>
00051 #else
00052 #include <unistd.h>
00053 #endif
00054 #ifdef __APPLE__
00055 #include <hugin_config.h>
00056 #include <mach-o/dyld.h>        /* _NSGetExecutablePath */
00057 #include <limits.h>             /* PATH_MAX */
00058 #include <libgen.h>             /* dirname */
00059 #endif
00060 
00061 #ifndef srandom
00062 #define srandom srand
00063 #endif
00064 
00065 using namespace std;
00066 using namespace ZThread;
00067 using namespace HuginBase;
00068 using namespace AppBase;
00069 using namespace HuginBase::Nona;
00070 using namespace hugin_utils;
00071 
00072 
00073 #define TRACE_IMG(X) {if (_panoDetector.getVerbose() == 1) {TRACE_INFO("i" << _imgData._number << " : " << X << endl);}}
00074 #define TRACE_PAIR(X) {if (_panoDetector.getVerbose() == 1){ TRACE_INFO("i" << _matchData._i1->_number << " <> " \
00075                 "i" << _matchData._i2->_number << " : " << X << endl);}}
00076 
00077 std::string includeTrailingPathSep(std::string path)
00078 {
00079     std::string pathWithSep(path);
00080 #ifdef _WINDOWS
00081     if(pathWithSep[pathWithSep.length()-1]!='\\' || pathWithSep[pathWithSep.length()-1]!='/')
00082     {
00083         pathWithSep.append("\\");
00084     }
00085 #else
00086     if(pathWithSep[pathWithSep.length()-1]!='/')
00087     {
00088         pathWithSep.append("/");
00089     }
00090 #endif
00091     return pathWithSep;
00092 };
00093 
00094 std::string getKeyfilenameFor(std::string keyfilesPath, std::string filename)
00095 {
00096     std::string newfilename;
00097     if(keyfilesPath.empty())
00098     {
00099         //if no path for keyfiles is given we are saving into the same directory as image file
00100         newfilename=stripExtension(filename);
00101     }
00102     else
00103     {
00104         newfilename=includeTrailingPathSep(keyfilesPath);
00105         newfilename.append(stripPath(stripExtension(filename)));
00106     };
00107     newfilename.append(".key");
00108     return newfilename;
00109 };
00110 
00111 PanoDetector::PanoDetector() :
00112     _writeAllKeyPoints(false), _verbose(1),
00113     _sieve1Width(10), _sieve1Height(10), _sieve1Size(100),
00114     _kdTreeSearchSteps(200), _kdTreeSecondDistance(0.25), _ransacIters(1000), _ransacDistanceThres(50),
00115     _sieve2Width(5), _sieve2Height(5),_sieve2Size(1), _test(false), _cores(utils::getCPUCount()),
00116     _minimumMatches(6), _linearMatchLen(1), _downscale(true), _cache(false), _cleanup(false),
00117     _keypath(""), _outputFile("default.pto"), _outputGiven(false), _celeste(false), _celesteThreshold(0.5), _celesteRadius(20), 
00118     _matchingStrategy(ALLPAIRS)
00119 {
00120     _panoramaInfo = new Panorama();
00121 }
00122 
00123 
00124 bool PanoDetector::checkData()
00125 {
00126     // test linear match data
00127     if (_linearMatchLen < 1)
00128     {
00129         std::cout << "Linear match length must be at least 1." << std::endl;
00130         return false;
00131     }
00132 
00133     // check the test mode
00134     if (_test)
00135     {
00136         if (_filesData.size() != 2)
00137         {
00138             std::cout << "In test mode you must provide exactly 2 images." << std::endl;
00139             return false;
00140         }
00141     }
00142 
00143     return true;
00144 }
00145 
00146 void PanoDetector::printDetails()
00147 {
00148     cout << "Input file           : " << _inputFile << endl;
00149     if (_keyPointsIdx.size() != 0)
00150     {
00151         cout << "Output file(s)       : keyfile(s) for images";
00152         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00153         {
00154             cout << " " << _keyPointsIdx[i] << endl;
00155         }
00156     }
00157     else
00158     {
00159         if(_writeAllKeyPoints)
00160         {
00161             cout << "Output file(s)       : keyfiles for all images in project" << endl;
00162         }
00163         else
00164         {
00165             cout << "Output file          : " << _outputFile << endl;
00166         };
00167     }
00168     if(_keypath.size()>0)
00169     {
00170         cout <<     "Path to keyfiles     : " << _keypath << endl;
00171     };
00172     if(_cleanup)
00173     {
00174         cout << "Cleanup temporary files." << endl;
00175     };
00176     if(_cache)
00177     {
00178         cout << "Automatically cache keypoints files to disc." << endl;
00179     };
00180     cout << "Number of CPU        : " << _cores << endl << endl;
00181     cout << "Input image options" << endl;
00182     cout << "  Downscale to half-size : " << (_downscale?"yes":"no") << endl;
00183     if(_celeste)
00184     {
00185         cout << "Celeste options" << endl;
00186         cout << "  Threshold : " << _celesteThreshold << endl;
00187         cout << "  Radius : " << _celesteRadius << endl;
00188     }
00189     cout << "Sieve 1 Options" << endl;
00190     cout << "  Width : " << _sieve1Width << endl;
00191     cout << "  Height : " << _sieve1Height << endl;
00192     cout << "  Size : " << _sieve1Size << endl;
00193     cout << "  ==> Maximum keypoints per image : " << _sieve1Size* _sieve1Height* _sieve1Width << endl;
00194     cout << "KDTree Options" << endl;
00195     cout << "  Search steps : " << _kdTreeSearchSteps << endl;
00196     cout << "  Second match distance : " << _kdTreeSecondDistance << endl;
00197     cout << "Matching Options" << endl;
00198     switch(_matchingStrategy)
00199     {
00200         case ALLPAIRS:
00201             cout << "  Mode : All pairs" << endl;
00202             break;
00203         case LINEAR:
00204             cout << "  Mode : Linear match with length of " << _linearMatchLen << " image" << endl;
00205             break;
00206         case MULTIROW:
00207             cout << "  Mode : Multi row" << endl;
00208             break;
00209         case PREALIGNED:
00210             cout << "  Mode : Prealigned positions" << endl;
00211             break;
00212     };
00213     cout << "  Distance threshold : " << _ransacDistanceThres << endl;
00214     cout << "RANSAC Options" << endl;
00215     cout << "  Mode : ";
00216     switch (_ransacMode)
00217     {
00218         case RANSACOptimizer::AUTO:
00219             cout << "auto" << endl;
00220             break;
00221         case RANSACOptimizer::HOMOGRAPHY:
00222             cout << "homography" << endl;
00223             break;
00224         case RANSACOptimizer::RPY:
00225             cout << "roll, pitch, yaw" << endl;
00226             break;
00227         case RANSACOptimizer::RPYV:
00228             cout << "roll, pitch, yaw, fov" << endl;
00229             break;
00230         case RANSACOptimizer::RPYVB:
00231             cout << "roll, pitch, yaw, fov, distortion" << endl;
00232             break;
00233     }
00234     cout << "  Iterations : " << _ransacIters << endl;
00235     cout << "  Distance threshold : " << _ransacDistanceThres << endl;
00236     cout << "Minimum matches per image pair: " << _minimumMatches << endl;
00237     cout << "Sieve 2 Options" << endl;
00238     cout << "  Width : " << _sieve2Width << endl;
00239     cout << "  Height : " << _sieve2Height << endl;
00240     cout << "  Size : " << _sieve2Size << endl;
00241     cout << "  ==> Maximum matches per image pair : " << _sieve2Size* _sieve2Height* _sieve2Width << endl;
00242 }
00243 
00244 void PanoDetector::printFilenames()
00245 {
00246     cout << endl << "Project contains the following images:" << endl;
00247     for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
00248     {
00249         std::string name(_panoramaInfo->getImage(i).getFilename());
00250         if(name.compare(0,_prefix.length(),_prefix)==0)
00251         {
00252             name=name.substr(_prefix.length(),name.length()-_prefix.length());
00253         }
00254         cout << "Image " << i << endl << "  Imagefile: " << name << endl;
00255         bool writeKeyfileForImage=false;
00256         if(_keyPointsIdx.size()>0)
00257         {
00258             for(unsigned j=0; j<_keyPointsIdx.size() && !writeKeyfileForImage; j++)
00259             {
00260                 writeKeyfileForImage=_keyPointsIdx[j]==i;
00261             };
00262         };
00263         if(_cache || _filesData[i]._hasakeyfile || writeKeyfileForImage)
00264         {
00265             name=_filesData[i]._keyfilename;
00266             if(name.compare(0,_prefix.length(),_prefix)==0)
00267             {
00268                 name=name.substr(_prefix.length(),name.length()-_prefix.length());
00269             }
00270             cout << "  Keyfile  : " << name;
00271             if(writeKeyfileForImage)
00272             {
00273                 cout << " (will be generated)" << endl;
00274             }
00275             else
00276             {
00277                 cout << (_filesData[i]._hasakeyfile?" (will be loaded)":" (will be generated)") << endl;
00278             };
00279         };
00280         cout << "  Remapped : " << (_filesData[i]._needsremap?"yes":"no") << endl;
00281     };
00282 };
00283 
00284 // definition of a runnable class for image data
00285 class ImgDataRunnable : public Runnable
00286 {
00287 public:
00288     ImgDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00289         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00290 
00291     void run()
00292     {
00293         TRACE_IMG("Analyzing image...");
00294         if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
00295         {
00296             return;
00297         }
00298         PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
00299         PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
00300         PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
00301         PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
00302         PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
00303         PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
00304     }
00305 private:
00306     const PanoDetector&                 _panoDetector;
00307     PanoDetector::ImgData&              _imgData;
00308 };
00309 
00310 // definition of a runnable class for writeKeyPoints
00311 class WriteKeyPointsRunnable : public Runnable
00312 {
00313 public:
00314     WriteKeyPointsRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00315         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00316 
00317     void run()
00318     {
00319         TRACE_IMG("Analyzing image...");
00320         if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
00321         {
00322             return;
00323         }
00324         PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
00325         PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
00326         PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
00327         PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
00328         PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
00329     }
00330 private:
00331     const PanoDetector&                 _panoDetector;
00332     PanoDetector::ImgData&              _imgData;
00333 };
00334 
00335 // definition of a runnable class for keypoints data
00336 class LoadKeypointsDataRunnable : public Runnable
00337 {
00338 public:
00339     LoadKeypointsDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00340         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00341 
00342     void run()
00343     {
00344         TRACE_IMG("Loading keypoints...");
00345         PanoDetector::LoadKeypoints(_imgData, _panoDetector);
00346         PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
00347     }
00348 
00349 private:
00350     const PanoDetector&                 _panoDetector;
00351     PanoDetector::ImgData&              _imgData;
00352 };
00353 
00354 // definition of a runnable class for MatchData
00355 class MatchDataRunnable : public Runnable
00356 {
00357 public:
00358     MatchDataRunnable(PanoDetector::MatchData& iMatchData, const PanoDetector& iPanoDetector) :
00359         _matchData(iMatchData), _panoDetector(iPanoDetector) {};
00360 
00361     void run()
00362     {
00363         //TRACE_PAIR("Matching...");
00364         if(_matchData._i1->_kp.size()>0 && _matchData._i2->_kp.size()>0)
00365         {
00366             PanoDetector::FindMatchesInPair(_matchData, _panoDetector);
00367             PanoDetector::RansacMatchesInPair(_matchData, _panoDetector);
00368             PanoDetector::FilterMatchesInPair(_matchData, _panoDetector);
00369             TRACE_PAIR("Found " << _matchData._matches.size() << " matches");
00370         };
00371     }
00372 private:
00373     const PanoDetector&                 _panoDetector;
00374     PanoDetector::MatchData&    _matchData;
00375 };
00376 
00377 bool PanoDetector::LoadSVMModel()
00378 {
00379     string model_file = ("celeste.model");
00380     ifstream test(model_file.c_str());
00381     if (!test.good())
00382     {
00383 #if _WINDOWS
00384         char buffer[MAX_PATH];//always use MAX_PATH for filepaths
00385         GetModuleFileNameA(NULL,buffer,sizeof(buffer));
00386         string working_path=(buffer);
00387         string install_path_model="";
00388         //remove filename
00389         std::string::size_type pos=working_path.rfind("\\");
00390         if(pos!=std::string::npos)
00391         {
00392             working_path.erase(pos);
00393             //remove last dir: should be bin
00394             pos=working_path.rfind("\\");
00395             if(pos!=std::string::npos)
00396             {
00397                 working_path.erase(pos);
00398                 //append path delimiter and path
00399                 working_path.append("\\share\\hugin\\data\\");
00400                 install_path_model=working_path;
00401             }
00402         }
00403 #elif defined MAC_SELF_CONTAINED_BUNDLE
00404         //string install_path_model = ("./xrc/");
00405         char path[PATH_MAX + 1];
00406         uint32_t size = sizeof(path);
00407         string install_path_model("");
00408         if (_NSGetExecutablePath(path, &size) == 0)
00409         {
00410             //install_path_model=path;
00411             install_path_model=dirname(path);
00412             install_path_model.append("/xrc/");
00413             cout << "Detected path " << install_path_model << endl << endl;
00414         }
00415 #else
00416         string install_path_model = (INSTALL_DATA_DIR);
00417 #endif
00418         install_path_model.append(model_file);
00419         ifstream test2(install_path_model.c_str());
00420         if (!test2.good())
00421         {
00422             cout << endl << "Couldn't open SVM model file " << model_file << endl;
00423             cout << "Also tried " << install_path_model << endl << endl;
00424             return false;
00425         };
00426         model_file = install_path_model;
00427     }
00428     if(!celeste::loadSVMmodel(svmModel,model_file))
00429     {
00430         svmModel=NULL;
00431         return false;
00432     };
00433     return true;
00434 };
00435 
00436 void PanoDetector::run()
00437 {
00438     // init the random time generator
00439     srandom((unsigned int)time(NULL));
00440 
00441     // Load the input project file
00442     if(!loadProject())
00443     {
00444         return;
00445     };
00446     if(_writeAllKeyPoints)
00447     {
00448         for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
00449         {
00450             _keyPointsIdx.push_back(i);
00451         };
00452     };
00453 
00454     if(_cleanup)
00455     {
00456         CleanupKeyfiles();
00457         return;
00458     };
00459 
00460     //checking, if memory allows running desired number of threads
00461     unsigned long maxImageSize=0;
00462     bool withRemap=false;
00463     for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00464     {
00465         if(!aB->second._hasakeyfile)
00466         {
00467             maxImageSize=std::max<unsigned long>(aB->second._detectWidth*aB->second._detectHeight,maxImageSize);
00468             if(aB->second._needsremap)
00469             {
00470                 withRemap=true;
00471             };
00472         };
00473     };
00474     if(maxImageSize!=0)
00475     {
00476         unsigned long long maxCores;
00477         //determinded factors by testing of some projects
00478         //the memory usage seems to be very high
00479         //if the memory usage could be decreased these numbers can be decreased
00480         if(withRemap)
00481         {
00482             maxCores=utils::getTotalMemory()/(maxImageSize*75);
00483         }
00484         else
00485         {
00486             maxCores=utils::getTotalMemory()/(maxImageSize*50);
00487         };
00488         if(maxCores<1)
00489         {
00490             maxCores=1;
00491         }
00492         if(maxCores<_cores)
00493         {
00494             if(getVerbose()>0)
00495             {
00496                 std::cout << "\nThe available memory does not allow running " << _cores << " threads parallel.\n"
00497                             << "Running cpfind with " << maxCores << " threads.\n";
00498             };
00499             setCores(maxCores);
00500         };
00501     };
00502     PoolExecutor aExecutor(_cores);
00503     svmModel=NULL;
00504     if(_celeste)
00505     {
00506         TRACE_INFO("\nLoading Celeste model file...\n");
00507         if(!LoadSVMModel())
00508         {
00509             setCeleste(false);
00510         };
00511     };
00512 
00513     //print some more information about the images
00514     if (_verbose > 0)
00515     {
00516         printFilenames();
00517     }
00518 
00519     // 2. run analysis of images or keypoints
00520 #if _WINDOWS
00521     //multi threading of image loading results sometime in a race condition
00522     //try to prevent this by initialisation of codecManager before
00523     //running multi threading part
00524     std::string s=vigra::impexListExtensions();
00525 #endif
00526     try
00527     {
00528         if (_keyPointsIdx.size() != 0)
00529         {
00530             if (_verbose > 0)
00531             {
00532                 TRACE_INFO(endl<< "--- Analyze Images ---" << endl);
00533             }
00534             for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00535             {
00536                 aExecutor.execute(new WriteKeyPointsRunnable(_filesData[_keyPointsIdx[i]], *this));
00537             };
00538         }
00539         else
00540         {
00541             TRACE_INFO(endl<< "--- Analyze Images ---" << endl);
00542             for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00543             {
00544                 if (aB->second._hasakeyfile)
00545                 {
00546                     aExecutor.execute(new LoadKeypointsDataRunnable(aB->second, *this));
00547                 }
00548                 else
00549                 {
00550                     aExecutor.execute(new ImgDataRunnable(aB->second, *this));
00551                 }
00552             }
00553         }
00554         aExecutor.wait();
00555     }
00556     catch(Synchronization_Exception& e)
00557     {
00558         TRACE_ERROR(e.what() << endl);
00559         if(svmModel!=NULL)
00560         {
00561             celeste::destroySVMmodel(svmModel);
00562         };
00563         return;
00564     }
00565 
00566     if(svmModel!=NULL)
00567     {
00568         celeste::destroySVMmodel(svmModel);
00569     };
00570 
00571     // check if the load of images succeed.
00572     if (!checkLoadSuccess())
00573     {
00574         TRACE_INFO("One or more images failed to load. Exiting.");
00575         return;
00576     }
00577 
00578     if(_cache)
00579     {
00580         TRACE_INFO(endl << "--- Cache keyfiles to disc ---" << endl);
00581         for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00582         {
00583             if (!aB->second._hasakeyfile)
00584             {
00585                 TRACE_INFO("i" << aB->second._number << " : Caching keypoints..." << endl);
00586                 writeKeyfile(aB->second);
00587             };
00588         };
00589     };
00590 
00591     // Detect matches if writeKeyPoints wasn't set
00592     if(_keyPointsIdx.size() == 0)
00593     {
00594         switch (getMatchingStrategy())
00595         {
00596             case ALLPAIRS:
00597             case LINEAR:
00598                 {
00599                     std::vector<HuginBase::UIntSet> imgPairs(_panoramaInfo->getNrOfImages());
00600                     if(!match(aExecutor, imgPairs))
00601                     {
00602                         return;
00603                     };
00604                 };
00605                 break;
00606             case MULTIROW:
00607                 if(!matchMultiRow(aExecutor))
00608                 {
00609                     return;
00610                 };
00611                 break;
00612             case PREALIGNED:
00613                 {
00614                     //check, which image pairs are already connected by control points
00615                     std::vector<HuginBase::UIntSet> connectedImages(_panoramaInfo->getNrOfImages());
00616                     HuginBase::CPVector cps=_panoramaInfo->getCtrlPoints();
00617                     for(HuginBase::CPVector::const_iterator it=cps.begin();it!=cps.end(); it++)
00618                     {
00619                         if((*it).mode==HuginBase::ControlPoint::X_Y)
00620                         {
00621                             connectedImages[(*it).image1Nr].insert((*it).image2Nr);
00622                             connectedImages[(*it).image2Nr].insert((*it).image1Nr);
00623                         };
00624                     };
00625                     //build dummy map
00626                     std::vector<size_t> imgMap(_panoramaInfo->getNrOfImages());
00627                     for(size_t i=0; i<_panoramaInfo->getNrOfImages(); i++)
00628                     {
00629                         imgMap[i]=i;
00630                     };
00631                     //and the final matching step
00632                     if(!matchPrealigned(aExecutor,_panoramaInfo, connectedImages, imgMap))
00633                     {
00634                         return;
00635                     };
00636                 }
00637                 break;
00638         };
00639     }
00640 
00641     // 5. write output
00642     if (_keyPointsIdx.size() != 0)
00643     {
00644         //Write all keyfiles
00645         TRACE_INFO(endl<< "--- Write Keyfiles output ---" << endl << endl);
00646         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00647         {
00648             writeKeyfile(_filesData[_keyPointsIdx[i]]);
00649         };
00650         if(_outputGiven)
00651         {
00652             cout << endl << "Warning: You have given the --output switch." << endl
00653                  << "This switch is not compatible with the --writekeyfile or --kall switch." << endl
00654                  << "If you want to generate the keyfiles and" << endl
00655                  << "do the matching in the same run use the --cache switch instead." << endl << endl;
00656         };
00657     }
00658     else
00659     {
00661         TRACE_INFO(endl<< "--- Write Project output ---" << endl);
00662         writeOutput();
00663         TRACE_INFO("Written output to " << _outputFile << endl << endl);
00664     };
00665 }
00666 
00667 bool PanoDetector::match(PoolExecutor& aExecutor, std::vector<HuginBase::UIntSet> &checkedPairs)
00668 {
00669     // 3. prepare matches
00670     _matchesData.clear();
00671     unsigned int aLen = _filesData.size();
00672     if (getMatchingStrategy()==LINEAR)
00673     {
00674         aLen = _linearMatchLen;
00675     }
00676 
00677     if (aLen >= _filesData.size())
00678     {
00679         aLen = _filesData.size() - 1;
00680     }
00681 
00682     for (unsigned int i1 = 0; i1 < _filesData.size(); ++i1)
00683     {
00684         unsigned int aEnd = i1 + 1 + aLen;
00685         if (_filesData.size() < aEnd)
00686         {
00687             aEnd = _filesData.size();
00688         }
00689 
00690         for (unsigned int i2 = (i1+1); i2 < aEnd; ++i2)
00691         {
00692             if(set_contains(checkedPairs[i1], i2))
00693             {
00694                 continue;
00695             };
00696             // create a new entry in the matches map
00697             _matchesData.push_back(MatchData());
00698 
00699             MatchData& aM = _matchesData.back();
00700             aM._i1 = &(_filesData[i1]);
00701             aM._i2 = &(_filesData[i2]);
00702 
00703             checkedPairs[i1].insert(i2);
00704             checkedPairs[i2].insert(i1);
00705         }
00706     }
00707     // 4. find matches
00708     TRACE_INFO(endl<< "--- Find pair-wise matches ---" << endl);
00709     try
00710     {
00711         BOOST_FOREACH(MatchData& aMD, _matchesData)
00712         aExecutor.execute(new MatchDataRunnable(aMD, *this));
00713         aExecutor.wait();
00714     }
00715     catch(Synchronization_Exception& e)
00716     {
00717         TRACE_ERROR(e.what() << endl);
00718         return false;
00719     }
00720 
00721     // Add detected matches to _panoramaInfo
00722     BOOST_FOREACH(MatchData& aM, _matchesData)
00723     BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches)
00724     _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
00725                                 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
00726     return true;
00727 };
00728 
00729 bool PanoDetector::loadProject()
00730 {
00731     ifstream ptoFile(_inputFile.c_str());
00732     if (ptoFile.bad())
00733     {
00734         cerr << "ERROR: could not open file: '" << _inputFile << "'!" << endl;
00735         return false;
00736     }
00737     _prefix=hugin_utils::getPathPrefix(_inputFile);
00738     if(_prefix.empty())
00739     {
00740         // Get the current working directory:
00741         char* buffer;
00742 #ifdef _WINDOWS
00743 #define getcwd _getcwd
00744 #endif
00745         if((buffer=getcwd(NULL,0))!=NULL)
00746         {
00747             _prefix.append(buffer);
00748             free(buffer);
00749             _prefix=includeTrailingPathSep(_prefix);
00750         }
00751     };
00752     _panoramaInfo->setFilePrefix(_prefix);
00753     AppBase::DocumentData::ReadWriteError err = _panoramaInfo->readData(ptoFile);
00754     if (err != AppBase::DocumentData::SUCCESSFUL)
00755     {
00756         cerr << "ERROR: couldn't parse panos tool script: '" << _inputFile << "'!" << endl;
00757         return false;
00758     }
00759 
00760     // Create a copy of panoramaInfo that will be used to define
00761     // image options
00762     _panoramaInfoCopy=_panoramaInfo->duplicate();
00763 
00764     // Add images found in the project file to _filesData
00765     unsigned int nImg = _panoramaInfo->getNrOfImages();
00766     unsigned int imgWithKeyfile=0;
00767     for (unsigned int imgNr = 0; imgNr < nImg; ++imgNr)
00768     {
00769         // insert the image in the map
00770         _filesData.insert(make_pair(imgNr, ImgData()));
00771 
00772         // get the data
00773         ImgData& aImgData = _filesData[imgNr];
00774 
00775         // get a copy of image info
00776         SrcPanoImage img = _panoramaInfoCopy.getSrcImage(imgNr);
00777 
00778         // set the name
00779         aImgData._name = img.getFilename();
00780 
00781         // modify image position in the copy
00782         img.setYaw(0);
00783         img.setRoll(0);
00784         img.setPitch(0);
00785         img.setX(0);
00786         img.setY(0);
00787         img.setZ(0);
00788         img.setActive(true);
00789         img.setResponseType(SrcPanoImage::RESPONSE_LINEAR);
00790         img.setExposureValue(0);
00791         _panoramaInfoCopy.setImage(imgNr,img);
00792 
00793         // Number pointing to image info in _panoramaInfo
00794         aImgData._number = imgNr;
00795 
00796         aImgData._needsremap=(img.getHFOV()>=65 && img.getProjection() != SrcPanoImage::FISHEYE_STEREOGRAPHIC);
00797         // set image detection size
00798         if(aImgData._needsremap)
00799         {
00800             _filesData[imgNr]._detectWidth = max(img.getSize().width(),img.getSize().height());
00801             _filesData[imgNr]._detectHeight = max(img.getSize().width(),img.getSize().height());
00802         }
00803         else
00804         {
00805             _filesData[imgNr]._detectWidth = img.getSize().width();
00806             _filesData[imgNr]._detectHeight = img.getSize().height();
00807         };
00808 
00809         if (_downscale)
00810         {
00811             _filesData[imgNr]._detectWidth >>= 1;
00812             _filesData[imgNr]._detectHeight >>= 1;
00813         }
00814 
00815         // set image remapping options
00816         if(aImgData._needsremap)
00817         {
00818             aImgData._projOpts.setProjection(PanoramaOptions::STEREOGRAPHIC);
00819             aImgData._projOpts.setHFOV(250);
00820             aImgData._projOpts.setVFOV(250);
00821             aImgData._projOpts.setWidth(250);
00822             aImgData._projOpts.setHeight(250);
00823 
00824             // determine size of output image.
00825             // The old code did not work with images with images with a FOV
00826             // approaching 180 degrees
00827             vigra::Rect2D roi=estimateOutputROI(_panoramaInfoCopy,aImgData._projOpts,imgNr);
00828             double scalefactor = max((double)_filesData[imgNr]._detectWidth / roi.width(),
00829                                      (double)_filesData[imgNr]._detectHeight / roi.height() );
00830 
00831             // resize output canvas
00832             vigra::Size2D canvasSize((int)aImgData._projOpts.getWidth() * scalefactor,
00833                                      (int)aImgData._projOpts.getHeight() * scalefactor);
00834             aImgData._projOpts.setWidth(canvasSize.width(), false);
00835             aImgData._projOpts.setHeight(canvasSize.height());
00836 
00837             // set roi to cover the remapped input image
00838             roi = roi * scalefactor;
00839             _filesData[imgNr]._detectWidth = roi.width();
00840             _filesData[imgNr]._detectHeight = roi.height();
00841             aImgData._projOpts.setROI(roi);
00842         }
00843 
00844         // Specify if the image has an associated keypoint file
00845 
00846         aImgData._keyfilename = getKeyfilenameFor(_keypath,aImgData._name);
00847         ifstream keyfile(aImgData._keyfilename.c_str());
00848         aImgData._hasakeyfile = keyfile.good();
00849         if(aImgData._hasakeyfile)
00850         {
00851             imgWithKeyfile++;
00852         };
00853     }
00854     //update masks, convert positive masks into negative masks
00855     //because positive masks works only if the images are on the final positions
00856     _panoramaInfoCopy.updateMasks(true);
00857 
00858     //if all images has keyfile, we don't need to load celeste model file
00859     if(nImg==imgWithKeyfile)
00860     {
00861         _celeste=false;
00862     };
00863     return true;
00864 }
00865 
00866 bool PanoDetector::checkLoadSuccess()
00867 {
00868     if(_keyPointsIdx.size()!=0)
00869     {
00870         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00871         {
00872             if(_filesData[_keyPointsIdx[i]]._loadFail)
00873             {
00874                 return false;
00875             };
00876         };
00877     }
00878     else
00879     {
00880         for (unsigned int aFileN = 0; aFileN < _filesData.size(); ++aFileN)
00881         {
00882             if(_filesData[aFileN]._loadFail)
00883             {
00884                 return false;
00885             };
00886         };
00887     };
00888     return true;
00889 }
00890 
00891 void PanoDetector::CleanupKeyfiles()
00892 {
00893     for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00894     {
00895         if (aB->second._hasakeyfile)
00896         {
00897             remove(aB->second._keyfilename.c_str());
00898         };
00899     };
00900 };
00901 
00902 struct img_ev
00903 {
00904     unsigned int img_nr;
00905     double ev;
00906 };
00907 struct stack_img
00908 {
00909     unsigned int layer_nr;
00910     std::vector<img_ev> images;
00911 };
00912 bool sort_img_ev (img_ev i1, img_ev i2)
00913 {
00914     return (i1.ev<i2.ev);
00915 };
00916 
00917 bool PanoDetector::matchMultiRow(PoolExecutor& aExecutor)
00918 {
00919     //step 1
00920     std::vector<HuginBase::UIntSet> checkedImagePairs(_panoramaInfo->getNrOfImages());
00921     std::vector<stack_img> stack_images;
00922     HuginBase::StandardImageVariableGroups* variable_groups = new HuginBase::StandardImageVariableGroups(*_panoramaInfo);
00923     for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
00924     {
00925         unsigned int stack_nr=variable_groups->getStacks().getPartNumber(i);
00926         //check, if this stack is already in list
00927         bool found=false;
00928         unsigned int index=0;
00929         for(index=0; index<stack_images.size(); index++)
00930         {
00931             found=(stack_images[index].layer_nr==stack_nr);
00932             if(found)
00933             {
00934                 break;
00935             };
00936         };
00937         if(!found)
00938         {
00939             //new stack
00940             stack_images.resize(stack_images.size()+1);
00941             index=stack_images.size()-1;
00942             //add new stack
00943             stack_images[index].layer_nr=stack_nr;
00944         };
00945         //add new image
00946         unsigned int new_image_index=stack_images[index].images.size();
00947         stack_images[index].images.resize(new_image_index+1);
00948         stack_images[index].images[new_image_index].img_nr=i;
00949         stack_images[index].images[new_image_index].ev=_panoramaInfo->getImage(i).getExposure();
00950     };
00951     delete variable_groups;
00952     //get image with median exposure for search with cp generator
00953     vector<size_t> images_layer;
00954     UIntSet images_layer_set;
00955     for(unsigned int i=0; i<stack_images.size(); i++)
00956     {
00957         std::sort(stack_images[i].images.begin(),stack_images[i].images.end(),sort_img_ev);
00958         unsigned int index=0;
00959         if(stack_images[i].images[0].ev!=stack_images[i].images[stack_images[i].images.size()-1].ev)
00960         {
00961             index=stack_images[i].images.size() / 2;
00962         };
00963         images_layer.push_back(stack_images[i].images[index].img_nr);
00964         images_layer_set.insert(stack_images[i].images[index].img_nr);
00965         if(stack_images[i].images.size()>1)
00966         {
00967             //build match list for stacks
00968             for(unsigned int j=0; j<stack_images[i].images.size()-1; j++)
00969             {
00970                 size_t img1=stack_images[i].images[j].img_nr;
00971                 size_t img2=stack_images[i].images[j+1].img_nr;
00972                 _matchesData.push_back(MatchData());
00973                 MatchData& aM=_matchesData.back();
00974                 aM._i1=&(_filesData[img1]);
00975                 aM._i2=&(_filesData[img2]);
00976                 checkedImagePairs[img1].insert(img2);
00977                 checkedImagePairs[img2].insert(img1);
00978             };
00979         };
00980     };
00981     //build match data list for image pairs
00982     if(images_layer.size()>1)
00983     {
00984         std::sort(images_layer.begin(), images_layer.end());
00985         for(unsigned int i=0; i<images_layer.size()-1; i++)
00986         {
00987             size_t img1=images_layer[i];
00988             size_t img2=images_layer[i+1];
00989             _matchesData.push_back(MatchData());
00990             MatchData& aM = _matchesData.back();
00991             aM._i1 = &(_filesData[img1]);
00992             aM._i2 = &(_filesData[img2]);
00993             checkedImagePairs[img1].insert(img2);
00994             checkedImagePairs[img2].insert(img1);
00995         };
00996     };
00997     TRACE_INFO(endl<< "--- Find matches ---" << endl);
00998     try
00999     {
01000         BOOST_FOREACH(MatchData& aMD, _matchesData)
01001         aExecutor.execute(new MatchDataRunnable(aMD, *this));
01002         aExecutor.wait();
01003     }
01004     catch(Synchronization_Exception& e)
01005     {
01006         TRACE_ERROR(e.what() << endl);
01007         return false;
01008     }
01009 
01010     // Add detected matches to _panoramaInfo
01011     BOOST_FOREACH(MatchData& aM, _matchesData)
01012     BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches)
01013     _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
01014                                 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
01015 
01016     // step 2: connect all image groups
01017     _matchesData.clear();
01018     Panorama mediumPano=_panoramaInfo->getSubset(images_layer_set);
01019     CPGraph graph;
01020     createCPGraph(mediumPano, graph);
01021     CPComponents comps;
01022     unsigned int n = findCPComponents(graph, comps);
01023     if(n>1)
01024     {
01025         vector<unsigned int> ImagesGroups;
01026         for(unsigned int i=0; i<n; i++)
01027         {
01028             ImagesGroups.push_back(images_layer[*(comps[i].begin())]);
01029             if(comps[i].size()>1)
01030             {
01031                 ImagesGroups.push_back(images_layer[*(comps[i].rbegin())]);
01032             }
01033         };
01034         for(unsigned int i=0; i<ImagesGroups.size()-1; i++)
01035         {
01036             for(unsigned int j=i+1; j<ImagesGroups.size(); j++)
01037             {
01038                 size_t img1=ImagesGroups[i];
01039                 size_t img2=ImagesGroups[j];
01040                 //skip already checked image pairs
01041                 if(set_contains(checkedImagePairs[img1],img2))
01042                 {
01043                     continue;
01044                 };
01045                 _matchesData.push_back(MatchData());
01046                 MatchData& aM = _matchesData.back();
01047                 aM._i1 = &(_filesData[img1]);
01048                 aM._i2 = &(_filesData[img2]);
01049                 checkedImagePairs[img1].insert(img2);
01050                 checkedImagePairs[img2].insert(img1);
01051             };
01052         };
01053         TRACE_INFO(endl<< "--- Find matches in images groups ---" << endl);
01054         try
01055         {
01056             BOOST_FOREACH(MatchData& aMD, _matchesData)
01057             aExecutor.execute(new MatchDataRunnable(aMD, *this));
01058             aExecutor.wait();
01059         }
01060         catch(Synchronization_Exception& e)
01061         {
01062             TRACE_ERROR(e.what() << endl);
01063             return false;
01064         }
01065         BOOST_FOREACH(MatchData& aM, _matchesData)
01066         BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches)
01067         _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
01068                                     aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
01069     };
01070     // step 3: now connect all overlapping images
01071     _matchesData.clear();
01072     PT::Panorama optPano=_panoramaInfo->getSubset(images_layer_set);
01073     createCPGraph(optPano, graph);
01074     if(findCPComponents(graph, comps)==1)
01075     {
01076         if(images_layer.size()>2)
01077         {
01078             //reset translation parameters
01079             VariableMapVector varMapVec=optPano.getVariables();
01080             for(size_t i=0; i<varMapVec.size(); i++)
01081             {
01082                 map_get(varMapVec[i], "TrX").setValue(0);
01083                 map_get(varMapVec[i], "TrY").setValue(0);
01084                 map_get(varMapVec[i], "TrZ").setValue(0);
01085             };
01086             optPano.updateVariables(varMapVec);
01087             //next steps happens only when all images are connected;
01088             //now optimize panorama
01089             PanoramaOptions opts = optPano.getOptions();
01090             opts.setProjection(PanoramaOptions::EQUIRECTANGULAR);
01091             opts.optimizeReferenceImage=0;
01092             // calculate proper scaling, 1:1 resolution.
01093             // Otherwise optimizer distances are meaningless.
01094             opts.setWidth(30000, false);
01095             opts.setHeight(15000);
01096 
01097             optPano.setOptions(opts);
01098             int w = optPano.calcOptimalWidth();
01099             opts.setWidth(w);
01100             opts.setHeight(w/2);
01101             optPano.setOptions(opts);
01102 
01103             //generate optimize vector, optimize only yaw and pitch
01104             OptimizeVector optvars;
01105             const SrcPanoImage& anchorImage = optPano.getImage(opts.optimizeReferenceImage);
01106             for (unsigned i=0; i < optPano.getNrOfImages(); i++)
01107             {
01108                 std::set<std::string> imgopt;
01109                 if(i==opts.optimizeReferenceImage)
01110                 {
01111                     //optimize only anchors pitch, not yaw
01112                     imgopt.insert("p");
01113                 }
01114                 else
01115                 {
01116                     imgopt.insert("p");
01117                     imgopt.insert("y");
01118                 };
01119                 optvars.push_back(imgopt);
01120             }
01121             optPano.setOptimizeVector(optvars);
01122 
01123             // remove vertical and horizontal control points
01124             CPVector cps = optPano.getCtrlPoints();
01125             CPVector newCP;
01126             for (CPVector::const_iterator it = cps.begin(); it != cps.end(); it++)
01127             {
01128                 if (it->mode == ControlPoint::X_Y)
01129                 {
01130                     newCP.push_back(*it);
01131                 }
01132             }
01133             optPano.setCtrlPoints(newCP);
01134 
01135             if (getVerbose() < 2)
01136             {
01137                 PT_setProgressFcn(ptProgress);
01138                 PT_setInfoDlgFcn(ptinfoDlg);
01139             };
01140             //in a first step do a pairwise optimisation
01141             HuginBase::AutoOptimise::autoOptimise(optPano, false);
01142             //now the final optimisation
01143             HuginBase::PTools::optimize(optPano);
01144             if (getVerbose() < 2)
01145             {
01146                 PT_setProgressFcn(NULL);
01147                 PT_setInfoDlgFcn(NULL);
01148             };
01149 
01150             //now match overlapping images
01151             if(!matchPrealigned(aExecutor, &optPano, checkedImagePairs, images_layer, false))
01152             {
01153                 return false;
01154             };
01155         };
01156     }
01157     else
01158     {
01159         if(!match(aExecutor, checkedImagePairs))
01160         {
01161             return false;
01162         };
01163     };
01164     return true;
01165 };
01166 
01167 bool PanoDetector::matchPrealigned(PoolExecutor& aExecutor, Panorama* pano, std::vector<HuginBase::UIntSet> &connectedImages, std::vector<size_t> imgMap, bool exactOverlap)
01168 {
01169     Panorama tempPano=pano->duplicate();
01170     if(!exactOverlap)
01171     {
01172         // increase hfov by 25 % to handle narrow overlaps (or even no overlap) better
01173         VariableMapVector varMapVec=tempPano.getVariables();
01174         for(size_t i=0; i<tempPano.getNrOfImages(); i++)
01175         {
01176             Variable hfovVar=map_get(varMapVec[i], "v");
01177             hfovVar.setValue(std::min(360.0, 1.25 * hfovVar.getValue()));
01178         };
01179         tempPano.updateVariables(varMapVec);
01180     };
01181     HuginBase::CalculateImageOverlap overlap(&tempPano);
01182     overlap.calculate(10);
01183     for(size_t i=0; i<tempPano.getNrOfImages()-1; i++)
01184     {
01185         for(size_t j=i+1; j<tempPano.getNrOfImages(); j++)
01186         {
01187             if(set_contains(connectedImages[imgMap[i]],imgMap[j]))
01188             {
01189                 continue;
01190             };
01191             if(overlap.getOverlap(i,j)>0)
01192             {
01193                 _matchesData.push_back(MatchData());
01194                 MatchData& aM = _matchesData.back();
01195                 aM._i1 = &(_filesData[imgMap[i]]);
01196                 aM._i2 = &(_filesData[imgMap[j]]);
01197                 connectedImages[imgMap[i]].insert(imgMap[j]);
01198                 connectedImages[imgMap[j]].insert(imgMap[i]);
01199             };
01200         };
01201     };
01202 
01203     TRACE_INFO(endl<< "--- Find matches for overlapping images ---" << endl);
01204     try
01205     {
01206         BOOST_FOREACH(MatchData& aMD, _matchesData)
01207         aExecutor.execute(new MatchDataRunnable(aMD, *this));
01208         aExecutor.wait();
01209     }
01210     catch(Synchronization_Exception& e)
01211     {
01212         TRACE_ERROR(e.what() << endl);
01213         return false;
01214     }
01215 
01216     // Add detected matches to _panoramaInfo
01217     BOOST_FOREACH(MatchData& aM, _matchesData)
01218     BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches)
01219     _panoramaInfo->addCtrlPoint(ControlPoint(imgMap[aM._i1->_number], aPM->_img1_x, aPM->_img1_y,
01220                                 imgMap[aM._i2->_number], aPM->_img2_x, aPM->_img2_y));
01221 
01222     return true;
01223 };

Generated on Thu May 23 01:25:42 2013 for Hugintrunk by  doxygen 1.3.9.1