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

Generated on 3 Aug 2015 for Hugintrunk by  doxygen 1.4.7