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]._needsremap?"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._needsremap)
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         aImgData._needsremap=(img.getHFOV()>=65 && img.getProjection() != HuginBase::SrcPanoImage::FISHEYE_STEREOGRAPHIC);
00805         // set image detection size
00806         if(aImgData._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         }
00811         else
00812         {
00813             _filesData[imgNr]._detectWidth = img.getSize().width();
00814             _filesData[imgNr]._detectHeight = img.getSize().height();
00815         };
00816 
00817         if (_downscale)
00818         {
00819             _filesData[imgNr]._detectWidth >>= 1;
00820             _filesData[imgNr]._detectHeight >>= 1;
00821         }
00822 
00823         // set image remapping options
00824         if(aImgData._needsremap)
00825         {
00826             aImgData._projOpts.setProjection(HuginBase::PanoramaOptions::STEREOGRAPHIC);
00827             aImgData._projOpts.setHFOV(250);
00828             aImgData._projOpts.setVFOV(250);
00829             aImgData._projOpts.setWidth(250);
00830             aImgData._projOpts.setHeight(250);
00831 
00832             // determine size of output image.
00833             // The old code did not work with images with images with a FOV
00834             // approaching 180 degrees
00835             vigra::Rect2D roi=estimateOutputROI(_panoramaInfoCopy,aImgData._projOpts,imgNr);
00836             double scalefactor = std::max((double)_filesData[imgNr]._detectWidth / roi.width(),
00837                                      (double)_filesData[imgNr]._detectHeight / roi.height() );
00838 
00839             // resize output canvas
00840             vigra::Size2D canvasSize((int)aImgData._projOpts.getWidth() * scalefactor,
00841                                      (int)aImgData._projOpts.getHeight() * scalefactor);
00842             aImgData._projOpts.setWidth(canvasSize.width(), false);
00843             aImgData._projOpts.setHeight(canvasSize.height());
00844 
00845             // set roi to cover the remapped input image
00846             roi *= scalefactor;
00847             _filesData[imgNr]._detectWidth = roi.width();
00848             _filesData[imgNr]._detectHeight = roi.height();
00849             aImgData._projOpts.setROI(roi);
00850         }
00851 
00852         // Specify if the image has an associated keypoint file
00853 
00854         aImgData._keyfilename = getKeyfilenameFor(_keypath,aImgData._name);
00855         aImgData._hasakeyfile = hugin_utils::FileExists(aImgData._keyfilename);
00856         if(aImgData._hasakeyfile)
00857         {
00858             imgWithKeyfile++;
00859         };
00860     }
00861     //update masks, convert positive masks into negative masks
00862     //because positive masks works only if the images are on the final positions
00863     _panoramaInfoCopy.updateMasks(true);
00864 
00865     //if all images has keyfile, we don't need to load celeste model file
00866     if(nImg==imgWithKeyfile)
00867     {
00868         _celeste=false;
00869     };
00870     return true;
00871 }
00872 
00873 bool PanoDetector::checkLoadSuccess()
00874 {
00875     if(!_keyPointsIdx.empty())
00876     {
00877         for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
00878         {
00879             if(_filesData[_keyPointsIdx[i]]._loadFail)
00880             {
00881                 return false;
00882             };
00883         };
00884     }
00885     else
00886     {
00887         for (unsigned int aFileN = 0; aFileN < _filesData.size(); ++aFileN)
00888         {
00889             if(_filesData[aFileN]._loadFail)
00890             {
00891                 return false;
00892             };
00893         };
00894     };
00895     return true;
00896 }
00897 
00898 void PanoDetector::CleanupKeyfiles()
00899 {
00900     for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
00901     {
00902         if (aB->second._hasakeyfile)
00903         {
00904             remove(aB->second._keyfilename.c_str());
00905         };
00906     };
00907 };
00908 
00909 void PanoDetector::buildMultiRowImageSets()
00910 {
00911     std::vector<HuginBase::UIntVector> stacks = HuginBase::getSortedStacks(_panoramaInfo);
00912     //get image with median exposure for search with cp generator
00913     for (size_t i = 0; i < stacks.size(); ++i)
00914     {
00915         size_t index = 0;
00916         if (_panoramaInfo->getImage(*(stacks[i].begin())).getExposureValue() != _panoramaInfo->getImage(*(stacks[i].rbegin())).getExposureValue())
00917         {
00918             index = stacks[i].size() / 2;
00919         };
00920         _image_layer.insert(stacks[i][index]);
00921         if (stacks[i].size()>1)
00922         {
00923             //build list for stacks, consider only unlinked stacks
00924             if (!_panoramaInfo->getImage(*(stacks[i].begin())).YawisLinked())
00925             {
00926                 _image_stacks.push_back(stacks[i]);
00927             };
00928         };
00929     };
00930 };
00931 
00932 bool PanoDetector::matchMultiRow()
00933 {
00934     RunnableVector queue;
00935     MatchData_t matchesData;
00936     //step 1
00937     std::vector<HuginBase::UIntSet> checkedImagePairs(_panoramaInfo->getNrOfImages());
00938     for (size_t i = 0; i < _image_stacks.size();i++)
00939     {
00940         //build match list for stacks
00941         for(unsigned int j=0; j<_image_stacks[i].size()-1; j++)
00942         {
00943             const size_t img1 = _image_stacks[i][j];
00944             const size_t img2 = _image_stacks[i][j + 1];
00945             matchesData.push_back(MatchData());
00946             MatchData& aM=matchesData.back();
00947             if (img1 < img2)
00948             {
00949                 aM._i1 = &(_filesData[img1]);
00950                 aM._i2 = &(_filesData[img2]);
00951             }
00952             else
00953             {
00954                 aM._i1 = &(_filesData[img2]);
00955                 aM._i2 = &(_filesData[img1]);
00956             };
00957             checkedImagePairs[img1].insert(img2);
00958             checkedImagePairs[img2].insert(img1);
00959         };
00960     };
00961     //build match data list for image pairs
00962     if(_image_layer.size()>1)
00963     {
00964         for (HuginBase::UIntSet::const_iterator it = _image_layer.begin(); it != _image_layer.end(); ++it)
00965         {
00966             const size_t img1 = *it;
00967             HuginBase::UIntSet::const_iterator it2 = it;
00968             ++it2;
00969             if (it2 != _image_layer.end())
00970             {
00971                 const size_t img2 = *it2;
00972                 matchesData.push_back(MatchData());
00973                 MatchData& aM = matchesData.back();
00974                 if (img1 < img2)
00975                 {
00976                     aM._i1 = &(_filesData[img1]);
00977                     aM._i2 = &(_filesData[img2]);
00978                 }
00979                 else
00980                 {
00981                     aM._i1 = &(_filesData[img2]);
00982                     aM._i2 = &(_filesData[img1]);
00983                 };
00984                 checkedImagePairs[img1].insert(img2);
00985                 checkedImagePairs[img2].insert(img1);
00986             };
00987         };
00988     };
00989     TRACE_INFO(std::endl<< "--- Find matches ---" << std::endl);
00990     for (size_t i = 0; i < matchesData.size(); ++i)
00991     {
00992         queue.push_back(new MatchDataRunnable(matchesData[i], *this));
00993     };
00994     RunQueue(queue);
00995 
00996     // Add detected matches to _panoramaInfo
00997     for (size_t i = 0; i < matchesData.size(); ++i)
00998     {
00999         const MatchData& aM = matchesData[i];
01000         for (size_t j = 0; j < aM._matches.size(); ++j)
01001         {
01002             const lfeat::PointMatchPtr& aPM = aM._matches[j];
01003             _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
01004                 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
01005         };
01006     };
01007 
01008     // step 2: connect all image groups
01009     queue.clear();
01010     matchesData.clear();
01011     HuginBase::Panorama mediumPano = _panoramaInfo->getSubset(_image_layer);
01012     HuginGraph::ImageGraph graph(mediumPano);
01013     const HuginGraph::ImageGraph::Components comps = graph.GetComponents();
01014     const size_t n = comps.size();
01015     if(n>1)
01016     {
01017         std::vector<unsigned int> ImagesGroups;
01018         for(size_t i=0; i<n; i++)
01019         {
01020             HuginBase::UIntSet::iterator imgIt = _image_layer.begin();
01021             std::advance(imgIt, *(comps[i].begin()));
01022             ImagesGroups.push_back(*imgIt);
01023             if(comps[i].size()>1)
01024             {
01025                 imgIt = _image_layer.begin();
01026                 std::advance(imgIt, *(comps[i].rbegin()));
01027                 ImagesGroups.push_back(*imgIt);
01028             }
01029         };
01030         for(unsigned int i=0; i<ImagesGroups.size()-1; i++)
01031         {
01032             for(unsigned int j=i+1; j<ImagesGroups.size(); j++)
01033             {
01034                 size_t img1=ImagesGroups[i];
01035                 size_t img2=ImagesGroups[j];
01036                 //skip already checked image pairs
01037                 if(set_contains(checkedImagePairs[img1],img2))
01038                 {
01039                     continue;
01040                 };
01041                 matchesData.push_back(MatchData());
01042                 MatchData& aM = matchesData.back();
01043                 if (img1 < img2)
01044                 {
01045                     aM._i1 = &(_filesData[img1]);
01046                     aM._i2 = &(_filesData[img2]);
01047                 }
01048                 else
01049                 {
01050                     aM._i1 = &(_filesData[img2]);
01051                     aM._i2 = &(_filesData[img1]);
01052                 };
01053                 checkedImagePairs[img1].insert(img2);
01054                 checkedImagePairs[img2].insert(img1);
01055             };
01056         };
01057         TRACE_INFO(std::endl<< "--- Find matches in images groups ---" << std::endl);
01058         for (size_t i = 0; i < matchesData.size(); ++i)
01059         {
01060             queue.push_back(new MatchDataRunnable(matchesData[i], *this));
01061         };
01062         RunQueue(queue);
01063 
01064         for (size_t i = 0; i < matchesData.size(); ++i)
01065         {
01066             const MatchData& aM = matchesData[i];
01067             for (size_t j = 0; j < aM._matches.size(); ++j)
01068             {
01069                 const lfeat::PointMatchPtr& aPM = aM._matches[j];
01070                 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
01071                     aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
01072             };
01073         };
01074     };
01075     // step 3: now connect all overlapping images
01076     queue.clear();
01077     matchesData.clear();
01078     HuginBase::Panorama optPano=_panoramaInfo->getSubset(_image_layer);
01079     HuginGraph::ImageGraph graph2(optPano);
01080     if(graph2.IsConnected())
01081     {
01082         if(_image_layer.size()>2)
01083         {
01084             //reset translation parameters
01085             HuginBase::VariableMapVector varMapVec = optPano.getVariables();
01086             for(size_t i=0; i<varMapVec.size(); i++)
01087             {
01088                 map_get(varMapVec[i], "TrX").setValue(0);
01089                 map_get(varMapVec[i], "TrY").setValue(0);
01090                 map_get(varMapVec[i], "TrZ").setValue(0);
01091             };
01092             optPano.updateVariables(varMapVec);
01093             //next steps happens only when all images are connected;
01094             //now optimize panorama
01095             HuginBase::PanoramaOptions opts = optPano.getOptions();
01096             opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
01097             opts.optimizeReferenceImage=0;
01098             // calculate proper scaling, 1:1 resolution.
01099             // Otherwise optimizer distances are meaningless.
01100             opts.setWidth(30000, false);
01101             opts.setHeight(15000);
01102 
01103             optPano.setOptions(opts);
01104             int w = hugin_utils::roundi(HuginBase::CalculateOptimalScale::calcOptimalScale(optPano) * opts.getWidth());
01105             opts.setWidth(w);
01106             opts.setHeight(w/2);
01107             optPano.setOptions(opts);
01108 
01109             //generate optimize vector, optimize only yaw and pitch
01110             HuginBase::OptimizeVector optvars;
01111             for (unsigned i=0; i < optPano.getNrOfImages(); i++)
01112             {
01113                 std::set<std::string> imgopt;
01114                 if(i==opts.optimizeReferenceImage)
01115                 {
01116                     //optimize only anchors pitch, not yaw
01117                     imgopt.insert("p");
01118                 }
01119                 else
01120                 {
01121                     imgopt.insert("p");
01122                     imgopt.insert("y");
01123                 };
01124                 optvars.push_back(imgopt);
01125             }
01126             optPano.setOptimizeVector(optvars);
01127 
01128             // remove vertical and horizontal control points
01129             HuginBase::CPVector cps = optPano.getCtrlPoints();
01130             HuginBase::CPVector newCP;
01131             for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
01132             {
01133                 if (it->mode == HuginBase::ControlPoint::X_Y)
01134                 {
01135                     newCP.push_back(*it);
01136                 }
01137             }
01138             optPano.setCtrlPoints(newCP);
01139 
01140             if (getVerbose() < 2)
01141             {
01142                 PT_setProgressFcn(ptProgress);
01143                 PT_setInfoDlgFcn(ptinfoDlg);
01144             };
01145             //in a first step do a pairwise optimisation
01146             HuginBase::AutoOptimise::autoOptimise(optPano, false);
01147             //now the final optimisation
01148             HuginBase::PTools::optimize(optPano);
01149             if (getVerbose() < 2)
01150             {
01151                 PT_setProgressFcn(NULL);
01152                 PT_setInfoDlgFcn(NULL);
01153             };
01154 
01155             //now match overlapping images
01156             std::vector<size_t> imgMap(_image_layer.begin(), _image_layer.end());
01157             if(!matchPrealigned(&optPano, checkedImagePairs, imgMap, false))
01158             {
01159                 return false;
01160             };
01161         };
01162     }
01163     else
01164     {
01165         if(!match(checkedImagePairs))
01166         {
01167             return false;
01168         };
01169     };
01170     return true;
01171 };
01172 
01173 bool PanoDetector::matchPrealigned(HuginBase::Panorama* pano, std::vector<HuginBase::UIntSet> &connectedImages, std::vector<size_t> imgMap, bool exactOverlap)
01174 {
01175     RunnableVector queue;
01176     MatchData_t matchesData;
01177     HuginBase::Panorama tempPano = pano->duplicate();
01178     if(!exactOverlap)
01179     {
01180         // increase hfov by 25 % to handle narrow overlaps (or even no overlap) better
01181         HuginBase::VariableMapVector varMapVec = tempPano.getVariables();
01182         for(size_t i=0; i<tempPano.getNrOfImages(); i++)
01183         {
01184             HuginBase::Variable& hfovVar = map_get(varMapVec[i], "v");
01185             hfovVar.setValue(std::min(360.0, 1.25 * hfovVar.getValue()));
01186         };
01187         tempPano.updateVariables(varMapVec);
01188     };
01189     HuginBase::CalculateImageOverlap overlap(&tempPano);
01190     overlap.calculate(10);
01191     for(size_t i=0; i<tempPano.getNrOfImages()-1; i++)
01192     {
01193         for(size_t j=i+1; j<tempPano.getNrOfImages(); j++)
01194         {
01195             if(set_contains(connectedImages[imgMap[i]],imgMap[j]))
01196             {
01197                 continue;
01198             };
01199             if(overlap.getOverlap(i,j)>0)
01200             {
01201                 matchesData.push_back(MatchData());
01202                 MatchData& aM = matchesData.back();
01203                 aM._i1 = &(_filesData[imgMap[i]]);
01204                 aM._i2 = &(_filesData[imgMap[j]]);
01205                 connectedImages[imgMap[i]].insert(imgMap[j]);
01206                 connectedImages[imgMap[j]].insert(imgMap[i]);
01207             };
01208         };
01209     };
01210 
01211     TRACE_INFO(std::endl<< "--- Find matches for overlapping images ---" << std::endl);
01212     for (size_t i = 0; i < matchesData.size(); ++i)
01213     {
01214         queue.push_back(new MatchDataRunnable(matchesData[i], *this));
01215     };
01216     RunQueue(queue);
01217 
01218     // Add detected matches to _panoramaInfo
01219     for (size_t i = 0; i < matchesData.size(); ++i)
01220     {
01221         const MatchData& aM = matchesData[i];
01222         for (size_t j = 0; j < aM._matches.size(); ++j)
01223         {
01224             const lfeat::PointMatchPtr& aPM = aM._matches[j];
01225             _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
01226                 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
01227         };
01228     };
01229 
01230     return true;
01231 };

Generated on 25 Aug 2016 for Hugintrunk by  doxygen 1.4.7