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 #include "hugin_base/hugin_utils/platform.h"
00035 
00036 #include <algorithms/nona/ComputeImageROI.h>
00037 #include <nona/RemappedPanoImage.h>
00038 #include <nona/ImageRemapper.h>
00039 
00040 //for multi row strategy
00041 #include <panodata/StandardImageVariableGroups.h>
00042 #include <PT/Panorama.h>
00043 #include <PT/ImageGraph.h>
00044 #include <algorithms/optimizer/PTOptimizer.h>
00045 #include <algorithms/basic/CalculateOverlap.h>
00046 
00047 #include "ImageImport.h"
00048 
00049 #ifdef _WINDOWS
00050 #include <windows.h>
00051 #include <direct.h>
00052 #else
00053 #include <unistd.h>
00054 #endif
00055 #ifdef __APPLE__
00056 #include <hugin_config.h>
00057 #include <mach-o/dyld.h>        /* _NSGetExecutablePath */
00058 #include <limits.h>             /* PATH_MAX */
00059 #include <libgen.h>             /* dirname */
00060 #endif
00061 
00062 #ifndef srandom
00063 #define srandom srand
00064 #endif
00065 
00066 using namespace std;
00067 using namespace ZThread;
00068 using namespace HuginBase;
00069 using namespace AppBase;
00070 using namespace HuginBase::Nona;
00071 using namespace hugin_utils;
00072 
00073 
00074 #define TRACE_IMG(X) {if (_panoDetector.getVerbose() == 1) {TRACE_INFO("i" << _imgData._number << " : " << X << endl);}}
00075 #define TRACE_PAIR(X) {if (_panoDetector.getVerbose() == 1){ TRACE_INFO("i" << _matchData._i1->_number << " <> " \
00076                 "i" << _matchData._i2->_number << " : " << X << endl);}}
00077 
00078 std::string includeTrailingPathSep(std::string path)
00079 {
00080     std::string pathWithSep(path);
00081 #ifdef _WINDOWS
00082     if(pathWithSep[pathWithSep.length()-1]!='\\' || pathWithSep[pathWithSep.length()-1]!='/')
00083     {
00084         pathWithSep.append("\\");
00085     }
00086 #else
00087     if(pathWithSep[pathWithSep.length()-1]!='/')
00088     {
00089         pathWithSep.append("/");
00090     }
00091 #endif
00092     return pathWithSep;
00093 };
00094 
00095 std::string getKeyfilenameFor(std::string keyfilesPath, std::string filename)
00096 {
00097     std::string newfilename;
00098     if(keyfilesPath.empty())
00099     {
00100         //if no path for keyfiles is given we are saving into the same directory as image file
00101         newfilename=stripExtension(filename);
00102     }
00103     else
00104     {
00105         newfilename=includeTrailingPathSep(keyfilesPath);
00106         newfilename.append(stripPath(stripExtension(filename)));
00107     };
00108     newfilename.append(".key");
00109     return newfilename;
00110 };
00111 
00112 PanoDetector::PanoDetector() :
00113     _writeAllKeyPoints(false), _verbose(1),
00114     _sieve1Width(10), _sieve1Height(10), _sieve1Size(100),
00115     _kdTreeSearchSteps(200), _kdTreeSecondDistance(0.25), _ransacIters(1000), _ransacDistanceThres(50),
00116     _sieve2Width(5), _sieve2Height(5),_sieve2Size(1), _test(false), _cores(hugin_utils::getCPUCount()),
00117     _minimumMatches(6), _linearMatchLen(1), _downscale(true), _cache(false), _cleanup(false),
00118     _keypath(""), _outputFile("default.pto"), _outputGiven(false), _celeste(false), _celesteThreshold(0.5), _celesteRadius(20), 
00119     _matchingStrategy(ALLPAIRS)
00120 {
00121     _panoramaInfo = new Panorama();
00122 }
00123 
00124 
00125 bool PanoDetector::checkData()
00126 {
00127     // test linear match data
00128     if (_linearMatchLen < 1)
00129     {
00130         std::cout << "Linear match length must be at least 1." << std::endl;
00131         return false;
00132     }
00133 
00134     // check the test mode
00135     if (_test)
00136     {
00137         if (_filesData.size() != 2)
00138         {
00139             std::cout << "In test mode you must provide exactly 2 images." << std::endl;
00140             return false;
00141         }
00142     }
00143 
00144     return true;
00145 }
00146 
00147 void PanoDetector::printDetails()
00148 {
00149     cout << "Input file           : " << _inputFile << endl;
00150     if (_keyPointsIdx.size() != 0)
00151     {
00152         cout << "Output file(s)       : keyfile(s) for images";
00153         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00154         {
00155             cout << " " << _keyPointsIdx[i] << endl;
00156         }
00157     }
00158     else
00159     {
00160         if(_writeAllKeyPoints)
00161         {
00162             cout << "Output file(s)       : keyfiles for all images in project" << endl;
00163         }
00164         else
00165         {
00166             cout << "Output file          : " << _outputFile << endl;
00167         };
00168     }
00169     if(_keypath.size()>0)
00170     {
00171         cout <<     "Path to keyfiles     : " << _keypath << endl;
00172     };
00173     if(_cleanup)
00174     {
00175         cout << "Cleanup temporary files." << endl;
00176     };
00177     if(_cache)
00178     {
00179         cout << "Automatically cache keypoints files to disc." << endl;
00180     };
00181     cout << "Number of CPU        : " << _cores << endl << endl;
00182     cout << "Input image options" << endl;
00183     cout << "  Downscale to half-size : " << (_downscale?"yes":"no") << endl;
00184     if(_celeste)
00185     {
00186         cout << "Celeste options" << endl;
00187         cout << "  Threshold : " << _celesteThreshold << endl;
00188         cout << "  Radius : " << _celesteRadius << endl;
00189     }
00190     cout << "Sieve 1 Options" << endl;
00191     cout << "  Width : " << _sieve1Width << endl;
00192     cout << "  Height : " << _sieve1Height << endl;
00193     cout << "  Size : " << _sieve1Size << endl;
00194     cout << "  ==> Maximum keypoints per image : " << _sieve1Size* _sieve1Height* _sieve1Width << endl;
00195     cout << "KDTree Options" << endl;
00196     cout << "  Search steps : " << _kdTreeSearchSteps << endl;
00197     cout << "  Second match distance : " << _kdTreeSecondDistance << endl;
00198     cout << "Matching Options" << endl;
00199     switch(_matchingStrategy)
00200     {
00201         case ALLPAIRS:
00202             cout << "  Mode : All pairs" << endl;
00203             break;
00204         case LINEAR:
00205             cout << "  Mode : Linear match with length of " << _linearMatchLen << " image" << endl;
00206             break;
00207         case MULTIROW:
00208             cout << "  Mode : Multi row" << endl;
00209             break;
00210         case PREALIGNED:
00211             cout << "  Mode : Prealigned positions" << endl;
00212             break;
00213     };
00214     cout << "  Distance threshold : " << _ransacDistanceThres << endl;
00215     cout << "RANSAC Options" << endl;
00216     cout << "  Mode : ";
00217     switch (_ransacMode)
00218     {
00219         case RANSACOptimizer::AUTO:
00220             cout << "auto" << endl;
00221             break;
00222         case RANSACOptimizer::HOMOGRAPHY:
00223             cout << "homography" << endl;
00224             break;
00225         case RANSACOptimizer::RPY:
00226             cout << "roll, pitch, yaw" << endl;
00227             break;
00228         case RANSACOptimizer::RPYV:
00229             cout << "roll, pitch, yaw, fov" << endl;
00230             break;
00231         case RANSACOptimizer::RPYVB:
00232             cout << "roll, pitch, yaw, fov, distortion" << endl;
00233             break;
00234     }
00235     cout << "  Iterations : " << _ransacIters << endl;
00236     cout << "  Distance threshold : " << _ransacDistanceThres << endl;
00237     cout << "Minimum matches per image pair: " << _minimumMatches << endl;
00238     cout << "Sieve 2 Options" << endl;
00239     cout << "  Width : " << _sieve2Width << endl;
00240     cout << "  Height : " << _sieve2Height << endl;
00241     cout << "  Size : " << _sieve2Size << endl;
00242     cout << "  ==> Maximum matches per image pair : " << _sieve2Size* _sieve2Height* _sieve2Width << endl;
00243 }
00244 
00245 void PanoDetector::printFilenames()
00246 {
00247     cout << endl << "Project contains the following images:" << endl;
00248     for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
00249     {
00250         std::string name(_panoramaInfo->getImage(i).getFilename());
00251         if(name.compare(0,_prefix.length(),_prefix)==0)
00252         {
00253             name=name.substr(_prefix.length(),name.length()-_prefix.length());
00254         }
00255         cout << "Image " << i << endl << "  Imagefile: " << name << endl;
00256         bool writeKeyfileForImage=false;
00257         if(_keyPointsIdx.size()>0)
00258         {
00259             for(unsigned j=0; j<_keyPointsIdx.size() && !writeKeyfileForImage; j++)
00260             {
00261                 writeKeyfileForImage=_keyPointsIdx[j]==i;
00262             };
00263         };
00264         if(_cache || _filesData[i]._hasakeyfile || writeKeyfileForImage)
00265         {
00266             name=_filesData[i]._keyfilename;
00267             if(name.compare(0,_prefix.length(),_prefix)==0)
00268             {
00269                 name=name.substr(_prefix.length(),name.length()-_prefix.length());
00270             }
00271             cout << "  Keyfile  : " << name;
00272             if(writeKeyfileForImage)
00273             {
00274                 cout << " (will be generated)" << endl;
00275             }
00276             else
00277             {
00278                 cout << (_filesData[i]._hasakeyfile?" (will be loaded)":" (will be generated)") << endl;
00279             };
00280         };
00281         cout << "  Remapped : " << (_filesData[i]._needsremap?"yes":"no") << endl;
00282     };
00283 };
00284 
00285 // definition of a runnable class for image data
00286 class ImgDataRunnable : public Runnable
00287 {
00288 public:
00289     ImgDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00290         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00291 
00292     void run()
00293     {
00294         TRACE_IMG("Analyzing image...");
00295         if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
00296         {
00297             return;
00298         }
00299         PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
00300         PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
00301         PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
00302         PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
00303         PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
00304         PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
00305     }
00306 private:
00307     const PanoDetector&                 _panoDetector;
00308     PanoDetector::ImgData&              _imgData;
00309 };
00310 
00311 // definition of a runnable class for writeKeyPoints
00312 class WriteKeyPointsRunnable : public Runnable
00313 {
00314 public:
00315     WriteKeyPointsRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00316         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00317 
00318     void run()
00319     {
00320         TRACE_IMG("Analyzing image...");
00321         if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
00322         {
00323             return;
00324         }
00325         PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
00326         PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
00327         PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
00328         PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
00329         PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
00330     }
00331 private:
00332     const PanoDetector&                 _panoDetector;
00333     PanoDetector::ImgData&              _imgData;
00334 };
00335 
00336 // definition of a runnable class for keypoints data
00337 class LoadKeypointsDataRunnable : public Runnable
00338 {
00339 public:
00340     LoadKeypointsDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
00341         _imgData(iImageData), _panoDetector(iPanoDetector) {};
00342 
00343     void run()
00344     {
00345         TRACE_IMG("Loading keypoints...");
00346         PanoDetector::LoadKeypoints(_imgData, _panoDetector);
00347         PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
00348     }
00349 
00350 private:
00351     const PanoDetector&                 _panoDetector;
00352     PanoDetector::ImgData&              _imgData;
00353 };
00354 
00355 // definition of a runnable class for MatchData
00356 class MatchDataRunnable : public Runnable
00357 {
00358 public:
00359     MatchDataRunnable(PanoDetector::MatchData& iMatchData, const PanoDetector& iPanoDetector) :
00360         _matchData(iMatchData), _panoDetector(iPanoDetector) {};
00361 
00362     void run()
00363     {
00364         //TRACE_PAIR("Matching...");
00365         if(_matchData._i1->_kp.size()>0 && _matchData._i2->_kp.size()>0)
00366         {
00367             PanoDetector::FindMatchesInPair(_matchData, _panoDetector);
00368             PanoDetector::RansacMatchesInPair(_matchData, _panoDetector);
00369             PanoDetector::FilterMatchesInPair(_matchData, _panoDetector);
00370             TRACE_PAIR("Found " << _matchData._matches.size() << " matches");
00371         };
00372     }
00373 private:
00374     const PanoDetector&                 _panoDetector;
00375     PanoDetector::MatchData&    _matchData;
00376 };
00377 
00378 bool PanoDetector::LoadSVMModel()
00379 {
00380     string model_file = ("celeste.model");
00381     ifstream test(model_file.c_str());
00382     if (!test.good())
00383     {
00384 #if _WINDOWS
00385         char buffer[MAX_PATH];//always use MAX_PATH for filepaths
00386         GetModuleFileNameA(NULL,buffer,sizeof(buffer));
00387         string working_path=(buffer);
00388         string install_path_model="";
00389         //remove filename
00390         std::string::size_type pos=working_path.rfind("\\");
00391         if(pos!=std::string::npos)
00392         {
00393             working_path.erase(pos);
00394             //remove last dir: should be bin
00395             pos=working_path.rfind("\\");
00396             if(pos!=std::string::npos)
00397             {
00398                 working_path.erase(pos);
00399                 //append path delimiter and path
00400                 working_path.append("\\share\\hugin\\data\\");
00401                 install_path_model=working_path;
00402             }
00403         }
00404 #elif defined MAC_SELF_CONTAINED_BUNDLE
00405         //string install_path_model = ("./xrc/");
00406         char path[PATH_MAX + 1];
00407         uint32_t size = sizeof(path);
00408         string install_path_model("");
00409         if (_NSGetExecutablePath(path, &size) == 0)
00410         {
00411             //install_path_model=path;
00412             install_path_model=dirname(path);
00413             install_path_model.append("/xrc/");
00414             cout << "Detected path " << install_path_model << endl << endl;
00415         }
00416 #else
00417         string install_path_model = (INSTALL_DATA_DIR);
00418 #endif
00419         install_path_model.append(model_file);
00420         ifstream test2(install_path_model.c_str());
00421         if (!test2.good())
00422         {
00423             cout << endl << "Couldn't open SVM model file " << model_file << endl;
00424             cout << "Also tried " << install_path_model << endl << endl;
00425             return false;
00426         };
00427         model_file = install_path_model;
00428     }
00429     if(!celeste::loadSVMmodel(svmModel,model_file))
00430     {
00431         svmModel=NULL;
00432         return false;
00433     };
00434     return true;
00435 };
00436 
00437 void PanoDetector::run()
00438 {
00439     // init the random time generator
00440     srandom((unsigned int)time(NULL));
00441 
00442     // Load the input project file
00443     if(!loadProject())
00444     {
00445         return;
00446     };
00447     if(_writeAllKeyPoints)
00448     {
00449         for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
00450         {
00451             _keyPointsIdx.push_back(i);
00452         };
00453     };
00454 
00455     if(_cleanup)
00456     {
00457         CleanupKeyfiles();
00458         return;
00459     };
00460 
00461     //checking, if memory allows running desired number of threads
00462     unsigned long maxImageSize=0;
00463     bool withRemap=false;
00464     for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00465     {
00466         if(!aB->second._hasakeyfile)
00467         {
00468             maxImageSize=std::max<unsigned long>(aB->second._detectWidth*aB->second._detectHeight,maxImageSize);
00469             if(aB->second._needsremap)
00470             {
00471                 withRemap=true;
00472             };
00473         };
00474     };
00475     if(maxImageSize!=0)
00476     {
00477         unsigned long long maxCores;
00478         //determinded factors by testing of some projects
00479         //the memory usage seems to be very high
00480         //if the memory usage could be decreased these numbers can be decreased
00481         if(withRemap)
00482         {
00483             maxCores=utils::getTotalMemory()/(maxImageSize*75);
00484         }
00485         else
00486         {
00487             maxCores=utils::getTotalMemory()/(maxImageSize*50);
00488         };
00489         if(maxCores<1)
00490         {
00491             maxCores=1;
00492         }
00493         if(maxCores<_cores)
00494         {
00495             if(getVerbose()>0)
00496             {
00497                 std::cout << "\nThe available memory does not allow running " << _cores << " threads parallel.\n"
00498                             << "Running cpfind with " << maxCores << " threads.\n";
00499             };
00500             setCores(maxCores);
00501         };
00502     };
00503     PoolExecutor aExecutor(_cores);
00504     svmModel=NULL;
00505     if(_celeste)
00506     {
00507         TRACE_INFO("\nLoading Celeste model file...\n");
00508         if(!LoadSVMModel())
00509         {
00510             setCeleste(false);
00511         };
00512     };
00513 
00514     //print some more information about the images
00515     if (_verbose > 0)
00516     {
00517         printFilenames();
00518     }
00519 
00520     // 2. run analysis of images or keypoints
00521 #if _WINDOWS
00522     //multi threading of image loading results sometime in a race condition
00523     //try to prevent this by initialisation of codecManager before
00524     //running multi threading part
00525     std::string s=vigra::impexListExtensions();
00526 #endif
00527     try
00528     {
00529         if (_keyPointsIdx.size() != 0)
00530         {
00531             if (_verbose > 0)
00532             {
00533                 TRACE_INFO(endl<< "--- Analyze Images ---" << endl);
00534             }
00535             for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00536             {
00537                 aExecutor.execute(new WriteKeyPointsRunnable(_filesData[_keyPointsIdx[i]], *this));
00538             };
00539         }
00540         else
00541         {
00542             TRACE_INFO(endl<< "--- Analyze Images ---" << endl);
00543             for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00544             {
00545                 if (aB->second._hasakeyfile)
00546                 {
00547                     aExecutor.execute(new LoadKeypointsDataRunnable(aB->second, *this));
00548                 }
00549                 else
00550                 {
00551                     aExecutor.execute(new ImgDataRunnable(aB->second, *this));
00552                 }
00553             }
00554         }
00555         aExecutor.wait();
00556     }
00557     catch(Synchronization_Exception& e)
00558     {
00559         TRACE_ERROR(e.what() << endl);
00560         if(svmModel!=NULL)
00561         {
00562             celeste::destroySVMmodel(svmModel);
00563         };
00564         return;
00565     }
00566 
00567     if(svmModel!=NULL)
00568     {
00569         celeste::destroySVMmodel(svmModel);
00570     };
00571 
00572     // check if the load of images succeed.
00573     if (!checkLoadSuccess())
00574     {
00575         TRACE_INFO("One or more images failed to load. Exiting.");
00576         return;
00577     }
00578 
00579     if(_cache)
00580     {
00581         TRACE_INFO(endl << "--- Cache keyfiles to disc ---" << endl);
00582         for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00583         {
00584             if (!aB->second._hasakeyfile)
00585             {
00586                 TRACE_INFO("i" << aB->second._number << " : Caching keypoints..." << endl);
00587                 writeKeyfile(aB->second);
00588             };
00589         };
00590     };
00591 
00592     // Detect matches if writeKeyPoints wasn't set
00593     if(_keyPointsIdx.size() == 0)
00594     {
00595         switch (getMatchingStrategy())
00596         {
00597             case ALLPAIRS:
00598             case LINEAR:
00599                 {
00600                     std::vector<HuginBase::UIntSet> imgPairs(_panoramaInfo->getNrOfImages());
00601                     if(!match(aExecutor, imgPairs))
00602                     {
00603                         return;
00604                     };
00605                 };
00606                 break;
00607             case MULTIROW:
00608                 if(!matchMultiRow(aExecutor))
00609                 {
00610                     return;
00611                 };
00612                 break;
00613             case PREALIGNED:
00614                 {
00615                     //check, which image pairs are already connected by control points
00616                     std::vector<HuginBase::UIntSet> connectedImages(_panoramaInfo->getNrOfImages());
00617                     HuginBase::CPVector cps=_panoramaInfo->getCtrlPoints();
00618                     for(HuginBase::CPVector::const_iterator it=cps.begin();it!=cps.end(); it++)
00619                     {
00620                         if((*it).mode==HuginBase::ControlPoint::X_Y)
00621                         {
00622                             connectedImages[(*it).image1Nr].insert((*it).image2Nr);
00623                             connectedImages[(*it).image2Nr].insert((*it).image1Nr);
00624                         };
00625                     };
00626                     //build dummy map
00627                     std::vector<size_t> imgMap(_panoramaInfo->getNrOfImages());
00628                     for(size_t i=0; i<_panoramaInfo->getNrOfImages(); i++)
00629                     {
00630                         imgMap[i]=i;
00631                     };
00632                     //and the final matching step
00633                     if(!matchPrealigned(aExecutor,_panoramaInfo, connectedImages, imgMap))
00634                     {
00635                         return;
00636                     };
00637                 }
00638                 break;
00639         };
00640     }
00641 
00642     // 5. write output
00643     if (_keyPointsIdx.size() != 0)
00644     {
00645         //Write all keyfiles
00646         TRACE_INFO(endl<< "--- Write Keyfiles output ---" << endl << endl);
00647         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00648         {
00649             writeKeyfile(_filesData[_keyPointsIdx[i]]);
00650         };
00651         if(_outputGiven)
00652         {
00653             cout << endl << "Warning: You have given the --output switch." << endl
00654                  << "This switch is not compatible with the --writekeyfile or --kall switch." << endl
00655                  << "If you want to generate the keyfiles and" << endl
00656                  << "do the matching in the same run use the --cache switch instead." << endl << endl;
00657         };
00658     }
00659     else
00660     {
00662         TRACE_INFO(endl<< "--- Write Project output ---" << endl);
00663         writeOutput();
00664         TRACE_INFO("Written output to " << _outputFile << endl << endl);
00665     };
00666 }
00667 
00668 bool PanoDetector::match(PoolExecutor& aExecutor, std::vector<HuginBase::UIntSet> &checkedPairs)
00669 {
00670     // 3. prepare matches
00671     _matchesData.clear();
00672     unsigned int aLen = _filesData.size();
00673     if (getMatchingStrategy()==LINEAR)
00674     {
00675         aLen = _linearMatchLen;
00676     }
00677 
00678     if (aLen >= _filesData.size())
00679     {
00680         aLen = _filesData.size() - 1;
00681     }
00682 
00683     for (unsigned int i1 = 0; i1 < _filesData.size(); ++i1)
00684     {
00685         unsigned int aEnd = i1 + 1 + aLen;
00686         if (_filesData.size() < aEnd)
00687         {
00688             aEnd = _filesData.size();
00689         }
00690 
00691         for (unsigned int i2 = (i1+1); i2 < aEnd; ++i2)
00692         {
00693             if(set_contains(checkedPairs[i1], i2))
00694             {
00695                 continue;
00696             };
00697             // create a new entry in the matches map
00698             _matchesData.push_back(MatchData());
00699 
00700             MatchData& aM = _matchesData.back();
00701             aM._i1 = &(_filesData[i1]);
00702             aM._i2 = &(_filesData[i2]);
00703 
00704             checkedPairs[i1].insert(i2);
00705             checkedPairs[i2].insert(i1);
00706         }
00707     }
00708     // 4. find matches
00709     TRACE_INFO(endl<< "--- Find pair-wise matches ---" << endl);
00710     try
00711     {
00712         BOOST_FOREACH(MatchData& aMD, _matchesData)
00713         aExecutor.execute(new MatchDataRunnable(aMD, *this));
00714         aExecutor.wait();
00715     }
00716     catch(Synchronization_Exception& e)
00717     {
00718         TRACE_ERROR(e.what() << endl);
00719         return false;
00720     }
00721 
00722     // Add detected matches to _panoramaInfo
00723     BOOST_FOREACH(MatchData& aM, _matchesData)
00724     BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches)
00725     _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
00726                                 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
00727     return true;
00728 };
00729 
00730 bool PanoDetector::loadProject()
00731 {
00732     ifstream ptoFile(_inputFile.c_str());
00733     if (ptoFile.bad())
00734     {
00735         cerr << "ERROR: could not open file: '" << _inputFile << "'!" << endl;
00736         return false;
00737     }
00738     _prefix=hugin_utils::getPathPrefix(_inputFile);
00739     if(_prefix.empty())
00740     {
00741         // Get the current working directory:
00742         char* buffer;
00743 #ifdef _WINDOWS
00744 #define getcwd _getcwd
00745 #endif
00746         if((buffer=getcwd(NULL,0))!=NULL)
00747         {
00748             _prefix.append(buffer);
00749             free(buffer);
00750             _prefix=includeTrailingPathSep(_prefix);
00751         }
00752     };
00753     _panoramaInfo->setFilePrefix(_prefix);
00754     AppBase::DocumentData::ReadWriteError err = _panoramaInfo->readData(ptoFile);
00755     if (err != AppBase::DocumentData::SUCCESSFUL)
00756     {
00757         cerr << "ERROR: couldn't parse panos tool script: '" << _inputFile << "'!" << endl;
00758         return false;
00759     }
00760 
00761     // Create a copy of panoramaInfo that will be used to define
00762     // image options
00763     _panoramaInfoCopy=_panoramaInfo->duplicate();
00764 
00765     // Add images found in the project file to _filesData
00766     unsigned int nImg = _panoramaInfo->getNrOfImages();
00767     unsigned int imgWithKeyfile=0;
00768     for (unsigned int imgNr = 0; imgNr < nImg; ++imgNr)
00769     {
00770         // insert the image in the map
00771         _filesData.insert(make_pair(imgNr, ImgData()));
00772 
00773         // get the data
00774         ImgData& aImgData = _filesData[imgNr];
00775 
00776         // get a copy of image info
00777         SrcPanoImage img = _panoramaInfoCopy.getSrcImage(imgNr);
00778 
00779         // set the name
00780         aImgData._name = img.getFilename();
00781 
00782         // modify image position in the copy
00783         img.setYaw(0);
00784         img.setRoll(0);
00785         img.setPitch(0);
00786         img.setX(0);
00787         img.setY(0);
00788         img.setZ(0);
00789         img.setActive(true);
00790         img.setResponseType(SrcPanoImage::RESPONSE_LINEAR);
00791         img.setExposureValue(0);
00792         _panoramaInfoCopy.setImage(imgNr,img);
00793 
00794         // Number pointing to image info in _panoramaInfo
00795         aImgData._number = imgNr;
00796 
00797         aImgData._needsremap=(img.getHFOV()>=65 && img.getProjection() != SrcPanoImage::FISHEYE_STEREOGRAPHIC);
00798         // set image detection size
00799         if(aImgData._needsremap)
00800         {
00801             _filesData[imgNr]._detectWidth = max(img.getSize().width(),img.getSize().height());
00802             _filesData[imgNr]._detectHeight = max(img.getSize().width(),img.getSize().height());
00803         }
00804         else
00805         {
00806             _filesData[imgNr]._detectWidth = img.getSize().width();
00807             _filesData[imgNr]._detectHeight = img.getSize().height();
00808         };
00809 
00810         if (_downscale)
00811         {
00812             _filesData[imgNr]._detectWidth >>= 1;
00813             _filesData[imgNr]._detectHeight >>= 1;
00814         }
00815 
00816         // set image remapping options
00817         if(aImgData._needsremap)
00818         {
00819             aImgData._projOpts.setProjection(PanoramaOptions::STEREOGRAPHIC);
00820             aImgData._projOpts.setHFOV(250);
00821             aImgData._projOpts.setVFOV(250);
00822             aImgData._projOpts.setWidth(250);
00823             aImgData._projOpts.setHeight(250);
00824 
00825             // determine size of output image.
00826             // The old code did not work with images with images with a FOV
00827             // approaching 180 degrees
00828             vigra::Rect2D roi=estimateOutputROI(_panoramaInfoCopy,aImgData._projOpts,imgNr);
00829             double scalefactor = max((double)_filesData[imgNr]._detectWidth / roi.width(),
00830                                      (double)_filesData[imgNr]._detectHeight / roi.height() );
00831 
00832             // resize output canvas
00833             vigra::Size2D canvasSize((int)aImgData._projOpts.getWidth() * scalefactor,
00834                                      (int)aImgData._projOpts.getHeight() * scalefactor);
00835             aImgData._projOpts.setWidth(canvasSize.width(), false);
00836             aImgData._projOpts.setHeight(canvasSize.height());
00837 
00838             // set roi to cover the remapped input image
00839             roi = roi * scalefactor;
00840             _filesData[imgNr]._detectWidth = roi.width();
00841             _filesData[imgNr]._detectHeight = roi.height();
00842             aImgData._projOpts.setROI(roi);
00843         }
00844 
00845         // Specify if the image has an associated keypoint file
00846 
00847         aImgData._keyfilename = getKeyfilenameFor(_keypath,aImgData._name);
00848         aImgData._hasakeyfile = hugin_utils::FileExists(aImgData._keyfilename);
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 Tue Jul 29 01:25:44 2014 for Hugintrunk by  doxygen 1.3.9.1