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

Generated on 24 May 2017 for Hugintrunk by  doxygen 1.4.7