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

Generated on 10 Feb 2016 for Hugintrunk by  doxygen 1.4.7