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