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

Generated on 28 Apr 2016 for Hugintrunk by  doxygen 1.4.7