1 // -*- c-basic-offset: 4 ; tab-width: 4 -*-
2 /*
3 * Copyright (C) 2007-2008 Anael Orlinski
4 *
5 * This file is part of Panomatic.
6 *
7 * Panomatic is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * Panomatic is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Panomatic; if not, write to the Free Software
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include "PanoDetector.h"
23 #include <iostream>
24 #include <fstream>
25 #include <sstream>
26
27 #include <time.h>
28
29 #include "Utils.h"
30 #include "Tracer.h"
31 #include "hugin_base/hugin_utils/platform.h"
32
33 #include <algorithms/nona/ComputeImageROI.h>
34 #include <algorithms/basic/CalculateOptimalScale.h>
35 #include <algorithms/basic/LayerStacks.h>
36 #include <nona/RemappedPanoImage.h>
37 #include <nona/ImageRemapper.h>
38
39 //for multi row strategy
40 #include <panodata/StandardImageVariableGroups.h>
41 #include <panodata/Panorama.h>
42 #include <algorithms/optimizer/ImageGraph.h>
43 #include <algorithms/optimizer/PTOptimizer.h>
44 #include <algorithms/basic/CalculateOverlap.h>
45
46 #include "ImageImport.h"
47
48 #ifdef _WIN32
49 #include <direct.h>
50 #else
51 #include <unistd.h>
52 #endif
53 #include <hugin_config.h>
54
55 #ifndef srandom
56 #define srandom srand
57 #endif
58
59 #ifdef HAVE_OPENMP
60 #include <omp.h>
61 #endif
62
63 #define TRACE_IMG(X) {if (_panoDetector.getVerbose() == 1) {TRACE_INFO("i" << _imgData._number << " : " << X << std::endl);}}
64 #define TRACE_PAIR(X) {if (_panoDetector.getVerbose() == 1){ TRACE_INFO("i" << _matchData._i1->_number << " <> " \
65 "i" << _matchData._i2->_number << " : " << X << std::endl);}}
66
includeTrailingPathSep(std::string path)67 std::string includeTrailingPathSep(std::string path)
68 {
69 std::string pathWithSep(path);
70 #ifdef _WIN32
71 if(pathWithSep[pathWithSep.length()-1]!='\\' || pathWithSep[pathWithSep.length()-1]!='/')
72 {
73 pathWithSep.append("\\");
74 }
75 #else
76 if(pathWithSep[pathWithSep.length()-1]!='/')
77 {
78 pathWithSep.append("/");
79 }
80 #endif
81 return pathWithSep;
82 };
83
getKeyfilenameFor(std::string keyfilesPath,std::string filename)84 std::string getKeyfilenameFor(std::string keyfilesPath, std::string filename)
85 {
86 std::string newfilename;
87 if(keyfilesPath.empty())
88 {
89 //if no path for keyfiles is given we are saving into the same directory as image file
90 newfilename=hugin_utils::stripExtension(filename);
91 }
92 else
93 {
94 newfilename = includeTrailingPathSep(keyfilesPath);
95 newfilename.append(hugin_utils::stripPath(hugin_utils::stripExtension(filename)));
96 };
97 newfilename.append(".key");
98 return newfilename;
99 };
100
PanoDetector()101 PanoDetector::PanoDetector() :
102 _writeAllKeyPoints(false), _verbose(1),
103 _sieve1Width(10), _sieve1Height(10), _sieve1Size(100),
104 _kdTreeSearchSteps(200), _kdTreeSecondDistance(0.25),
105 _minimumMatches(6), _ransacMode(HuginBase::RANSACOptimizer::AUTO), _ransacIters(1000), _ransacDistanceThres(50),
106 _sieve2Width(5), _sieve2Height(5), _sieve2Size(1),
107 _matchingStrategy(ALLPAIRS), _linearMatchLen(1),
108 _test(false), _cores(0), _downscale(true), _cache(false), _cleanup(false),
109 _celeste(false), _celesteThreshold(0.5), _celesteRadius(20),
110 _keypath(""), _outputFile("default.pto"), _outputGiven(false), svmModel(NULL)
111 {
112 _panoramaInfo = new HuginBase::Panorama();
113 }
114
~PanoDetector()115 PanoDetector::~PanoDetector()
116 {
117 delete _panoramaInfo;
118 }
119
checkData()120 bool PanoDetector::checkData()
121 {
122 // test linear match data
123 if (_linearMatchLen < 1)
124 {
125 std::cout << "Linear match length must be at least 1." << std::endl;
126 return false;
127 }
128
129 // check the test mode
130 if (_test)
131 {
132 if (_filesData.size() != 2)
133 {
134 std::cout << "In test mode you must provide exactly 2 images." << std::endl;
135 return false;
136 }
137 }
138
139 return true;
140 }
141
printDetails()142 void PanoDetector::printDetails()
143 {
144 std::cout << "Input file : " << _inputFile << std::endl;
145 if (_keyPointsIdx.size() != 0)
146 {
147 std::cout << "Output file(s) : keyfile(s) for images";
148 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
149 {
150 std::cout << " " << _keyPointsIdx[i] << std::endl;
151 }
152 }
153 else
154 {
155 if(_writeAllKeyPoints)
156 {
157 std::cout << "Output file(s) : keyfiles for all images in project" << std::endl;
158 }
159 else
160 {
161 std::cout << "Output file : " << _outputFile << std::endl;
162 };
163 }
164 if(!_keypath.empty())
165 {
166 std::cout << "Path to keyfiles : " << _keypath << std::endl;
167 };
168 if(_cleanup)
169 {
170 std::cout << "Cleanup temporary files." << std::endl;
171 };
172 if(_cache)
173 {
174 std::cout << "Automatically cache keypoints files to disc." << std::endl;
175 };
176 #ifdef HAVE_OPENMP
177 std::cout << "Number of threads : " << (_cores>0 ? _cores : omp_get_max_threads()) << std::endl << std::endl;
178 #endif
179 std::cout << "Input image options" << std::endl;
180 std::cout << " Downscale to half-size : " << (_downscale?"yes":"no") << std::endl;
181 if(_celeste)
182 {
183 std::cout << "Celeste options" << std::endl;
184 std::cout << " Threshold : " << _celesteThreshold << std::endl;
185 std::cout << " Radius : " << _celesteRadius << std::endl;
186 }
187 std::cout << "Sieve 1 Options" << std::endl;
188 std::cout << " Width : " << _sieve1Width << std::endl;
189 std::cout << " Height : " << _sieve1Height << std::endl;
190 std::cout << " Size : " << _sieve1Size << std::endl;
191 std::cout << " ==> Maximum keypoints per image : " << _sieve1Size* _sieve1Height* _sieve1Width << std::endl;
192 std::cout << "KDTree Options" << std::endl;
193 std::cout << " Search steps : " << _kdTreeSearchSteps << std::endl;
194 std::cout << " Second match distance : " << _kdTreeSecondDistance << std::endl;
195 std::cout << "Matching Options" << std::endl;
196 switch(_matchingStrategy)
197 {
198 case ALLPAIRS:
199 std::cout << " Mode : All pairs" << std::endl;
200 break;
201 case LINEAR:
202 std::cout << " Mode : Linear match with length of " << _linearMatchLen << " image" << std::endl;
203 break;
204 case MULTIROW:
205 std::cout << " Mode : Multi row" << std::endl;
206 break;
207 case PREALIGNED:
208 std::cout << " Mode : Prealigned positions" << std::endl;
209 break;
210 };
211 std::cout << " Distance threshold : " << _ransacDistanceThres << std::endl;
212 std::cout << "RANSAC Options" << std::endl;
213 std::cout << " Mode : ";
214 switch (_ransacMode)
215 {
216 case HuginBase::RANSACOptimizer::AUTO:
217 std::cout << "auto" << std::endl;
218 break;
219 case HuginBase::RANSACOptimizer::HOMOGRAPHY:
220 std::cout << "homography" << std::endl;
221 break;
222 case HuginBase::RANSACOptimizer::RPY:
223 std::cout << "roll, pitch, yaw" << std::endl;
224 break;
225 case HuginBase::RANSACOptimizer::RPYV:
226 std::cout << "roll, pitch, yaw, fov" << std::endl;
227 break;
228 case HuginBase::RANSACOptimizer::RPYVB:
229 std::cout << "roll, pitch, yaw, fov, distortion" << std::endl;
230 break;
231 }
232 std::cout << " Iterations : " << _ransacIters << std::endl;
233 std::cout << " Distance threshold : " << _ransacDistanceThres << std::endl;
234 std::cout << "Minimum matches per image pair: " << _minimumMatches << std::endl;
235 std::cout << "Sieve 2 Options" << std::endl;
236 std::cout << " Width : " << _sieve2Width << std::endl;
237 std::cout << " Height : " << _sieve2Height << std::endl;
238 std::cout << " Size : " << _sieve2Size << std::endl;
239 std::cout << " ==> Maximum matches per image pair : " << _sieve2Size* _sieve2Height* _sieve2Width << std::endl;
240 }
241
printFilenames()242 void PanoDetector::printFilenames()
243 {
244 std::cout << std::endl << "Project contains the following images:" << std::endl;
245 for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
246 {
247 std::string name(_panoramaInfo->getImage(i).getFilename());
248 if(name.compare(0,_prefix.length(),_prefix)==0)
249 {
250 name=name.substr(_prefix.length(),name.length()-_prefix.length());
251 }
252 std::cout << "Image " << i << std::endl << " Imagefile: " << name << std::endl;
253 bool writeKeyfileForImage=false;
254 if(!_keyPointsIdx.empty())
255 {
256 for(unsigned j=0; j<_keyPointsIdx.size() && !writeKeyfileForImage; j++)
257 {
258 writeKeyfileForImage=_keyPointsIdx[j]==i;
259 };
260 };
261 if(_cache || _filesData[i]._hasakeyfile || writeKeyfileForImage)
262 {
263 name=_filesData[i]._keyfilename;
264 if(name.compare(0,_prefix.length(),_prefix)==0)
265 {
266 name=name.substr(_prefix.length(),name.length()-_prefix.length());
267 }
268 std::cout << " Keyfile : " << name;
269 if(writeKeyfileForImage)
270 {
271 std::cout << " (will be generated)" << std::endl;
272 }
273 else
274 {
275 std::cout << (_filesData[i]._hasakeyfile?" (will be loaded)":" (will be generated)") << std::endl;
276 };
277 };
278 std::cout << " Remapped : " << (_filesData[i].NeedsRemapping()?"yes":"no") << std::endl;
279 };
280 };
281
282 class Runnable
283 {
284 public:
Runnable()285 Runnable() {};
286 virtual void run() = 0;
~Runnable()287 virtual ~Runnable() {};
288 };
289
290 // definition of a runnable class for image data
291 class ImgDataRunnable : public Runnable
292 {
293 public:
ImgDataRunnable(PanoDetector::ImgData & iImageData,const PanoDetector & iPanoDetector)294 ImgDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) : Runnable(),
295 _panoDetector(iPanoDetector), _imgData(iImageData) {};
296
run()297 virtual void run()
298 {
299 TRACE_IMG("Analyzing image...");
300 if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
301 {
302 return;
303 }
304 PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
305 PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
306 PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
307 PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
308 PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
309 PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
310 }
311 private:
312 const PanoDetector& _panoDetector;
313 PanoDetector::ImgData& _imgData;
314 };
315
316 // definition of a runnable class for writeKeyPoints
317 class WriteKeyPointsRunnable : public Runnable
318 {
319 public:
WriteKeyPointsRunnable(PanoDetector::ImgData & iImageData,const PanoDetector & iPanoDetector)320 WriteKeyPointsRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
321 _panoDetector(iPanoDetector), _imgData(iImageData) {};
322
run()323 virtual void run()
324 {
325 TRACE_IMG("Analyzing image...");
326 if (!PanoDetector::AnalyzeImage(_imgData, _panoDetector))
327 {
328 return;
329 }
330 PanoDetector::FindKeyPointsInImage(_imgData, _panoDetector);
331 PanoDetector::FilterKeyPointsInImage(_imgData, _panoDetector);
332 PanoDetector::MakeKeyPointDescriptorsInImage(_imgData, _panoDetector);
333 PanoDetector::RemapBackKeypoints(_imgData, _panoDetector);
334 PanoDetector::FreeMemoryInImage(_imgData, _panoDetector);
335 }
336 private:
337 const PanoDetector& _panoDetector;
338 PanoDetector::ImgData& _imgData;
339 };
340
341 // definition of a runnable class for keypoints data
342 class LoadKeypointsDataRunnable : public Runnable
343 {
344 public:
LoadKeypointsDataRunnable(PanoDetector::ImgData & iImageData,const PanoDetector & iPanoDetector)345 LoadKeypointsDataRunnable(PanoDetector::ImgData& iImageData, const PanoDetector& iPanoDetector) :
346 _panoDetector(iPanoDetector), _imgData(iImageData) {};
347
run()348 virtual void run()
349 {
350 TRACE_IMG("Loading keypoints...");
351 PanoDetector::LoadKeypoints(_imgData, _panoDetector);
352 PanoDetector::BuildKDTreesInImage(_imgData, _panoDetector);
353 }
354
355 private:
356 const PanoDetector& _panoDetector;
357 PanoDetector::ImgData& _imgData;
358 };
359
360 // definition of a runnable class for MatchData
361 class MatchDataRunnable : public Runnable
362 {
363 public:
MatchDataRunnable(PanoDetector::MatchData & iMatchData,const PanoDetector & iPanoDetector)364 MatchDataRunnable(PanoDetector::MatchData& iMatchData, const PanoDetector& iPanoDetector) :
365 _panoDetector(iPanoDetector), _matchData(iMatchData) {};
366
run()367 virtual void run()
368 {
369 //TRACE_PAIR("Matching...");
370 if(!_matchData._i1->_kp.empty() && !_matchData._i2->_kp.empty())
371 {
372 PanoDetector::FindMatchesInPair(_matchData, _panoDetector);
373 PanoDetector::RansacMatchesInPair(_matchData, _panoDetector);
374 PanoDetector::FilterMatchesInPair(_matchData, _panoDetector);
375 TRACE_PAIR("Found " << _matchData._matches.size() << " matches");
376 };
377 }
378 private:
379 const PanoDetector& _panoDetector;
380 PanoDetector::MatchData& _matchData;
381 };
382
LoadSVMModel()383 bool PanoDetector::LoadSVMModel()
384 {
385 std::string model_file = ("celeste.model");
386 std::ifstream test(model_file.c_str());
387 if (!test.good())
388 {
389 std::string install_path_model=hugin_utils::GetDataDir();
390 install_path_model.append(model_file);
391 std::ifstream test2(install_path_model.c_str());
392 if (!test2.good())
393 {
394 std::cout << std::endl << "Couldn't open SVM model file " << model_file << std::endl;
395 std::cout << "Also tried " << install_path_model << std::endl << std::endl;
396 return false;
397 };
398 model_file = install_path_model;
399 }
400 if(!celeste::loadSVMmodel(svmModel,model_file))
401 {
402 svmModel=NULL;
403 return false;
404 };
405 return true;
406 };
407
408 typedef std::vector<Runnable*> RunnableVector;
409
RunQueue(std::vector<Runnable * > & queue)410 void RunQueue(std::vector<Runnable*>& queue)
411 {
412 #pragma omp parallel for schedule(dynamic)
413 for (int i = 0; i < queue.size(); ++i)
414 {
415 queue[i]->run();
416 };
417 // now clear queue
418 while (!queue.empty())
419 {
420 delete queue.back();
421 queue.pop_back();
422 };
423 };
424
run()425 void PanoDetector::run()
426 {
427 // init the random time generator
428 srandom((unsigned int)time(NULL));
429
430 // Load the input project file
431 if(!loadProject())
432 {
433 return;
434 };
435 if(_writeAllKeyPoints)
436 {
437 for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
438 {
439 _keyPointsIdx.push_back(i);
440 };
441 };
442
443 if(_cleanup)
444 {
445 CleanupKeyfiles();
446 return;
447 };
448
449 //checking, if memory allows running desired number of threads
450 unsigned long maxImageSize=0;
451 bool withRemap=false;
452 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
453 {
454 if(!aB->second._hasakeyfile)
455 {
456 maxImageSize=std::max<unsigned long>(aB->second._detectWidth*aB->second._detectHeight,maxImageSize);
457 if(aB->second.NeedsRemapping())
458 {
459 withRemap=true;
460 };
461 };
462 };
463 #ifdef HAVE_OPENMP
464 if (_cores == 0)
465 {
466 setCores(omp_get_max_threads());
467 };
468 if (maxImageSize != 0)
469 {
470 unsigned long long maxCores;
471 //determinded factors by testing of some projects
472 //the memory usage seems to be very high
473 //if the memory usage could be decreased these numbers can be decreased
474 if(withRemap)
475 {
476 maxCores=utils::getTotalMemory()/(maxImageSize*75);
477 }
478 else
479 {
480 maxCores=utils::getTotalMemory()/(maxImageSize*50);
481 };
482 if(maxCores<1)
483 {
484 maxCores=1;
485 }
486 if(maxCores<_cores)
487 {
488 if(getVerbose()>0)
489 {
490 std::cout << "\nThe available memory does not allow running " << _cores << " threads parallel.\n"
491 << "Running cpfind with " << maxCores << " threads.\n";
492 };
493 setCores(maxCores);
494 };
495 };
496 omp_set_num_threads(_cores);
497 #endif
498 RunnableVector queue;
499 svmModel = NULL;
500 if(_celeste)
501 {
502 TRACE_INFO("\nLoading Celeste model file...\n");
503 if(!LoadSVMModel())
504 {
505 setCeleste(false);
506 };
507 };
508
509 //print some more information about the images
510 if (_verbose > 0)
511 {
512 printFilenames();
513 }
514
515 // 2. run analysis of images or keypoints
516 #if _WIN32
517 //multi threading of image loading results sometime in a race condition
518 //try to prevent this by initialisation of codecManager before
519 //running multi threading part
520 std::string s=vigra::impexListExtensions();
521 #endif
522 if (_keyPointsIdx.size() != 0)
523 {
524 if (_verbose > 0)
525 {
526 TRACE_INFO(std::endl << "--- Analyze Images ---" << std::endl);
527 }
528 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
529 {
530 queue.push_back(new WriteKeyPointsRunnable(_filesData[_keyPointsIdx[i]], *this));
531 };
532 }
533 else
534 {
535 TRACE_INFO(std::endl << "--- Analyze Images ---" << std::endl);
536 if (getMatchingStrategy() == MULTIROW)
537 {
538 // when using multirow, don't analyse stacks with linked positions
539 buildMultiRowImageSets();
540 HuginBase::UIntSet imagesToAnalyse;
541 imagesToAnalyse.insert(_image_layer.begin(), _image_layer.end());
542 for (size_t i = 0; i < _image_stacks.size(); i++)
543 {
544 imagesToAnalyse.insert(_image_stacks[i].begin(), _image_stacks[i].end());
545 }
546 for (HuginBase::UIntSet::const_iterator it = imagesToAnalyse.begin(); it != imagesToAnalyse.end(); ++it)
547 {
548 if (_filesData[*it]._hasakeyfile)
549 {
550 queue.push_back(new LoadKeypointsDataRunnable(_filesData[*it], *this));
551 }
552 else
553 {
554 queue.push_back(new ImgDataRunnable(_filesData[*it], *this));
555 };
556 };
557 }
558 else
559 {
560 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
561 {
562 if (aB->second._hasakeyfile)
563 {
564 queue.push_back(new LoadKeypointsDataRunnable(aB->second, *this));
565 }
566 else
567 {
568 queue.push_back(new ImgDataRunnable(aB->second, *this));
569 }
570 }
571 };
572 }
573 RunQueue(queue);
574
575 if(svmModel!=NULL)
576 {
577 celeste::destroySVMmodel(svmModel);
578 };
579
580 // check if the load of images succeed.
581 if (!checkLoadSuccess())
582 {
583 TRACE_INFO("One or more images failed to load. Exiting.");
584 return;
585 }
586
587 if(_cache)
588 {
589 TRACE_INFO(std::endl << "--- Cache keyfiles to disc ---" << std::endl);
590 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
591 {
592 if (!aB->second._hasakeyfile)
593 {
594 TRACE_INFO("i" << aB->second._number << " : Caching keypoints..." << std::endl);
595 writeKeyfile(aB->second);
596 };
597 };
598 };
599
600 // Detect matches if writeKeyPoints wasn't set
601 if(_keyPointsIdx.empty())
602 {
603 switch (getMatchingStrategy())
604 {
605 case ALLPAIRS:
606 case LINEAR:
607 {
608 std::vector<HuginBase::UIntSet> imgPairs(_panoramaInfo->getNrOfImages());
609 if(!match(imgPairs))
610 {
611 return;
612 };
613 };
614 break;
615 case MULTIROW:
616 if(!matchMultiRow())
617 {
618 return;
619 };
620 break;
621 case PREALIGNED:
622 {
623 //check, which image pairs are already connected by control points
624 std::vector<HuginBase::UIntSet> connectedImages(_panoramaInfo->getNrOfImages());
625 HuginBase::CPVector cps=_panoramaInfo->getCtrlPoints();
626 for(HuginBase::CPVector::const_iterator it=cps.begin();it!=cps.end(); ++it)
627 {
628 if((*it).mode==HuginBase::ControlPoint::X_Y)
629 {
630 connectedImages[(*it).image1Nr].insert((*it).image2Nr);
631 connectedImages[(*it).image2Nr].insert((*it).image1Nr);
632 };
633 };
634 //build dummy map
635 std::vector<size_t> imgMap(_panoramaInfo->getNrOfImages());
636 for(size_t i=0; i<_panoramaInfo->getNrOfImages(); i++)
637 {
638 imgMap[i]=i;
639 };
640 //and the final matching step
641 if(!matchPrealigned(_panoramaInfo, connectedImages, imgMap))
642 {
643 return;
644 };
645 }
646 break;
647 };
648 }
649
650 // 5. write output
651 if (_keyPointsIdx.size() != 0)
652 {
653 //Write all keyfiles
654 TRACE_INFO(std::endl<< "--- Write Keyfiles output ---" << std::endl << std::endl);
655 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
656 {
657 writeKeyfile(_filesData[_keyPointsIdx[i]]);
658 };
659 if(_outputGiven)
660 {
661 std::cout << std::endl << "Warning: You have given the --output switch." << std::endl
662 << "This switch is not compatible with the --writekeyfile or --kall switch." << std::endl
663 << "If you want to generate the keyfiles and" << std::endl
664 << "do the matching in the same run use the --cache switch instead." << std::endl << std::endl;
665 };
666 }
667 else
668 {
669 /// Write output project
670 TRACE_INFO(std::endl<< "--- Write Project output ---" << std::endl);
671 writeOutput();
672 TRACE_INFO("Written output to " << _outputFile << std::endl << std::endl);
673 };
674 }
675
match(std::vector<HuginBase::UIntSet> & checkedPairs)676 bool PanoDetector::match(std::vector<HuginBase::UIntSet> &checkedPairs)
677 {
678 // 3. prepare matches
679 RunnableVector queue;
680 MatchData_t matchesData;
681 unsigned int aLen = _filesData.size();
682 if (getMatchingStrategy()==LINEAR)
683 {
684 aLen = _linearMatchLen;
685 }
686
687 if (aLen >= _filesData.size())
688 {
689 aLen = _filesData.size() - 1;
690 }
691
692 for (unsigned int i1 = 0; i1 < _filesData.size(); ++i1)
693 {
694 unsigned int aEnd = i1 + 1 + aLen;
695 if (_filesData.size() < aEnd)
696 {
697 aEnd = _filesData.size();
698 }
699
700 for (unsigned int i2 = (i1+1); i2 < aEnd; ++i2)
701 {
702 if(set_contains(checkedPairs[i1], i2))
703 {
704 continue;
705 };
706 // create a new entry in the matches map
707 matchesData.push_back(MatchData());
708
709 MatchData& aM = matchesData.back();
710 aM._i1 = &(_filesData[i1]);
711 aM._i2 = &(_filesData[i2]);
712
713 checkedPairs[i1].insert(i2);
714 checkedPairs[i2].insert(i1);
715 }
716 }
717 // 4. find matches
718 TRACE_INFO(std::endl<< "--- Find pair-wise matches ---" << std::endl);
719 for (size_t i = 0; i < matchesData.size(); ++i)
720 {
721 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
722 };
723 RunQueue(queue);
724
725 // Add detected matches to _panoramaInfo
726 for (size_t i = 0; i < matchesData.size(); ++i)
727 {
728 const MatchData& aM = matchesData[i];
729 for (size_t j = 0; j < aM._matches.size(); ++j)
730 {
731 const lfeat::PointMatchPtr& aPM = aM._matches[j];
732 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
733 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
734 };
735 };
736 return true;
737 };
738
loadProject()739 bool PanoDetector::loadProject()
740 {
741 std::ifstream ptoFile(_inputFile.c_str());
742 if (ptoFile.bad())
743 {
744 std::cerr << "ERROR: could not open file: '" << _inputFile << "'!" << std::endl;
745 return false;
746 }
747 _prefix=hugin_utils::getPathPrefix(_inputFile);
748 if(_prefix.empty())
749 {
750 // Get the current working directory:
751 char* buffer;
752 #ifdef _WIN32
753 #define getcwd _getcwd
754 #endif
755 if((buffer=getcwd(NULL,0))!=NULL)
756 {
757 _prefix.append(buffer);
758 free(buffer);
759 _prefix=includeTrailingPathSep(_prefix);
760 }
761 };
762 _panoramaInfo->setFilePrefix(_prefix);
763 AppBase::DocumentData::ReadWriteError err = _panoramaInfo->readData(ptoFile);
764 if (err != AppBase::DocumentData::SUCCESSFUL)
765 {
766 std::cerr << "ERROR: couldn't parse panos tool script: '" << _inputFile << "'!" << std::endl;
767 return false;
768 }
769
770 // Create a copy of panoramaInfo that will be used to define
771 // image options
772 _panoramaInfoCopy=_panoramaInfo->duplicate();
773
774 // Add images found in the project file to _filesData
775 const unsigned int nImg = _panoramaInfo->getNrOfImages();
776 unsigned int imgWithKeyfile=0;
777 for (unsigned int imgNr = 0; imgNr < nImg; ++imgNr)
778 {
779 // insert the image in the map
780 _filesData.insert(std::make_pair(imgNr, ImgData()));
781
782 // get the data
783 ImgData& aImgData = _filesData[imgNr];
784
785 // get a copy of image info
786 HuginBase::SrcPanoImage img = _panoramaInfoCopy.getSrcImage(imgNr);
787
788 // set the name
789 aImgData._name = img.getFilename();
790
791 // modify image position in the copy
792 img.setYaw(0);
793 img.setRoll(0);
794 img.setPitch(0);
795 img.setX(0);
796 img.setY(0);
797 img.setZ(0);
798 img.setActive(true);
799 _panoramaInfoCopy.setImage(imgNr,img);
800
801 // Number pointing to image info in _panoramaInfo
802 aImgData._number = imgNr;
803
804 bool needsremap=(img.getHFOV()>=65 && img.getProjection() != HuginBase::SrcPanoImage::FISHEYE_STEREOGRAPHIC);
805 // set image detection size
806 if(needsremap)
807 {
808 _filesData[imgNr]._detectWidth = std::max(img.getSize().width(),img.getSize().height());
809 _filesData[imgNr]._detectHeight = std::max(img.getSize().width(),img.getSize().height());
810 _filesData[imgNr].SetSizeMode(ImgData::REMAPPED);
811 }
812 else
813 {
814 _filesData[imgNr]._detectWidth = img.getSize().width();
815 _filesData[imgNr]._detectHeight = img.getSize().height();
816 };
817
818 // don't downscale if downscale image is smaller than 1000 pixel
819 if (_downscale && std::min(img.getSize().width(), img.getSize().height()) > 2000)
820 {
821 _filesData[imgNr]._detectWidth >>= 1;
822 _filesData[imgNr]._detectHeight >>= 1;
823 if (!_filesData[imgNr].NeedsRemapping())
824 {
825 _filesData[imgNr].SetSizeMode(ImgData::DOWNSCALED);
826 };
827 }
828
829 // set image remapping options
830 if (aImgData.GetSizeMode() == ImgData::REMAPPED)
831 {
832 aImgData._projOpts.setProjection(HuginBase::PanoramaOptions::STEREOGRAPHIC);
833 aImgData._projOpts.setHFOV(250);
834 aImgData._projOpts.setVFOV(250);
835 aImgData._projOpts.setWidth(250);
836 aImgData._projOpts.setHeight(250);
837
838 // determine size of output image.
839 // The old code did not work with images with images with a FOV
840 // approaching 180 degrees
841 vigra::Rect2D roi=estimateOutputROI(_panoramaInfoCopy,aImgData._projOpts,imgNr);
842 double scalefactor = std::max((double)_filesData[imgNr]._detectWidth / roi.width(),
843 (double)_filesData[imgNr]._detectHeight / roi.height() );
844
845 // resize output canvas
846 vigra::Size2D canvasSize((int)aImgData._projOpts.getWidth() * scalefactor,
847 (int)aImgData._projOpts.getHeight() * scalefactor);
848 aImgData._projOpts.setWidth(canvasSize.width(), false);
849 aImgData._projOpts.setHeight(canvasSize.height());
850
851 // set roi to cover the remapped input image
852 roi *= scalefactor;
853 _filesData[imgNr]._detectWidth = roi.width();
854 _filesData[imgNr]._detectHeight = roi.height();
855 aImgData._projOpts.setROI(roi);
856 }
857
858 // Specify if the image has an associated keypoint file
859
860 aImgData._keyfilename = getKeyfilenameFor(_keypath,aImgData._name);
861 aImgData._hasakeyfile = hugin_utils::FileExists(aImgData._keyfilename);
862 if(aImgData._hasakeyfile)
863 {
864 imgWithKeyfile++;
865 };
866 }
867 //update masks, convert positive masks into negative masks
868 //because positive masks works only if the images are on the final positions
869 _panoramaInfoCopy.updateMasks(true);
870
871 //if all images has keyfile, we don't need to load celeste model file
872 if(nImg==imgWithKeyfile)
873 {
874 _celeste=false;
875 };
876 return true;
877 }
878
checkLoadSuccess()879 bool PanoDetector::checkLoadSuccess()
880 {
881 if(!_keyPointsIdx.empty())
882 {
883 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
884 {
885 if(_filesData[_keyPointsIdx[i]]._loadFail)
886 {
887 return false;
888 };
889 };
890 }
891 else
892 {
893 for (unsigned int aFileN = 0; aFileN < _filesData.size(); ++aFileN)
894 {
895 if(_filesData[aFileN]._loadFail)
896 {
897 return false;
898 };
899 };
900 };
901 return true;
902 }
903
CleanupKeyfiles()904 void PanoDetector::CleanupKeyfiles()
905 {
906 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
907 {
908 if (aB->second._hasakeyfile)
909 {
910 remove(aB->second._keyfilename.c_str());
911 };
912 };
913 };
914
buildMultiRowImageSets()915 void PanoDetector::buildMultiRowImageSets()
916 {
917 std::vector<HuginBase::UIntVector> stacks = HuginBase::getSortedStacks(_panoramaInfo);
918 //get image with median exposure for search with cp generator
919 for (size_t i = 0; i < stacks.size(); ++i)
920 {
921 size_t index = 0;
922 if (_panoramaInfo->getImage(*(stacks[i].begin())).getExposureValue() != _panoramaInfo->getImage(*(stacks[i].rbegin())).getExposureValue())
923 {
924 index = stacks[i].size() / 2;
925 };
926 _image_layer.insert(stacks[i][index]);
927 if (stacks[i].size()>1)
928 {
929 //build list for stacks, consider only unlinked stacks
930 if (!_panoramaInfo->getImage(*(stacks[i].begin())).YawisLinked())
931 {
932 _image_stacks.push_back(stacks[i]);
933 };
934 };
935 };
936 };
937
matchMultiRow()938 bool PanoDetector::matchMultiRow()
939 {
940 RunnableVector queue;
941 MatchData_t matchesData;
942 //step 1
943 std::vector<HuginBase::UIntSet> checkedImagePairs(_panoramaInfo->getNrOfImages());
944 for (size_t i = 0; i < _image_stacks.size();i++)
945 {
946 //build match list for stacks
947 for(unsigned int j=0; j<_image_stacks[i].size()-1; j++)
948 {
949 const size_t img1 = _image_stacks[i][j];
950 const size_t img2 = _image_stacks[i][j + 1];
951 matchesData.push_back(MatchData());
952 MatchData& aM=matchesData.back();
953 if (img1 < img2)
954 {
955 aM._i1 = &(_filesData[img1]);
956 aM._i2 = &(_filesData[img2]);
957 }
958 else
959 {
960 aM._i1 = &(_filesData[img2]);
961 aM._i2 = &(_filesData[img1]);
962 };
963 checkedImagePairs[img1].insert(img2);
964 checkedImagePairs[img2].insert(img1);
965 };
966 };
967 //build match data list for image pairs
968 if(_image_layer.size()>1)
969 {
970 for (HuginBase::UIntSet::const_iterator it = _image_layer.begin(); it != _image_layer.end(); ++it)
971 {
972 const size_t img1 = *it;
973 HuginBase::UIntSet::const_iterator it2 = it;
974 ++it2;
975 if (it2 != _image_layer.end())
976 {
977 const size_t img2 = *it2;
978 matchesData.push_back(MatchData());
979 MatchData& aM = matchesData.back();
980 if (img1 < img2)
981 {
982 aM._i1 = &(_filesData[img1]);
983 aM._i2 = &(_filesData[img2]);
984 }
985 else
986 {
987 aM._i1 = &(_filesData[img2]);
988 aM._i2 = &(_filesData[img1]);
989 };
990 checkedImagePairs[img1].insert(img2);
991 checkedImagePairs[img2].insert(img1);
992 };
993 };
994 };
995 TRACE_INFO(std::endl<< "--- Find matches ---" << std::endl);
996 for (size_t i = 0; i < matchesData.size(); ++i)
997 {
998 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
999 };
1000 RunQueue(queue);
1001
1002 // Add detected matches to _panoramaInfo
1003 for (size_t i = 0; i < matchesData.size(); ++i)
1004 {
1005 const MatchData& aM = matchesData[i];
1006 for (size_t j = 0; j < aM._matches.size(); ++j)
1007 {
1008 const lfeat::PointMatchPtr& aPM = aM._matches[j];
1009 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1010 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1011 };
1012 };
1013
1014 // step 2: connect all image groups
1015 queue.clear();
1016 matchesData.clear();
1017 HuginBase::Panorama mediumPano = _panoramaInfo->getSubset(_image_layer);
1018 HuginGraph::ImageGraph graph(mediumPano);
1019 const HuginGraph::ImageGraph::Components comps = graph.GetComponents();
1020 const size_t n = comps.size();
1021 if(n>1)
1022 {
1023 std::vector<unsigned int> ImagesGroups;
1024 for(size_t i=0; i<n; i++)
1025 {
1026 HuginBase::UIntSet::iterator imgIt = _image_layer.begin();
1027 std::advance(imgIt, *(comps[i].begin()));
1028 ImagesGroups.push_back(*imgIt);
1029 if(comps[i].size()>1)
1030 {
1031 imgIt = _image_layer.begin();
1032 std::advance(imgIt, *(comps[i].rbegin()));
1033 ImagesGroups.push_back(*imgIt);
1034 }
1035 };
1036 for(unsigned int i=0; i<ImagesGroups.size()-1; i++)
1037 {
1038 for(unsigned int j=i+1; j<ImagesGroups.size(); j++)
1039 {
1040 size_t img1=ImagesGroups[i];
1041 size_t img2=ImagesGroups[j];
1042 //skip already checked image pairs
1043 if(set_contains(checkedImagePairs[img1],img2))
1044 {
1045 continue;
1046 };
1047 matchesData.push_back(MatchData());
1048 MatchData& aM = matchesData.back();
1049 if (img1 < img2)
1050 {
1051 aM._i1 = &(_filesData[img1]);
1052 aM._i2 = &(_filesData[img2]);
1053 }
1054 else
1055 {
1056 aM._i1 = &(_filesData[img2]);
1057 aM._i2 = &(_filesData[img1]);
1058 };
1059 checkedImagePairs[img1].insert(img2);
1060 checkedImagePairs[img2].insert(img1);
1061 };
1062 };
1063 TRACE_INFO(std::endl<< "--- Find matches in images groups ---" << std::endl);
1064 for (size_t i = 0; i < matchesData.size(); ++i)
1065 {
1066 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
1067 };
1068 RunQueue(queue);
1069
1070 for (size_t i = 0; i < matchesData.size(); ++i)
1071 {
1072 const MatchData& aM = matchesData[i];
1073 for (size_t j = 0; j < aM._matches.size(); ++j)
1074 {
1075 const lfeat::PointMatchPtr& aPM = aM._matches[j];
1076 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1077 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1078 };
1079 };
1080 };
1081 // step 3: now connect all overlapping images
1082 queue.clear();
1083 matchesData.clear();
1084 HuginBase::Panorama optPano=_panoramaInfo->getSubset(_image_layer);
1085 HuginGraph::ImageGraph graph2(optPano);
1086 if(graph2.IsConnected())
1087 {
1088 if(_image_layer.size()>2)
1089 {
1090 //reset translation parameters
1091 HuginBase::VariableMapVector varMapVec = optPano.getVariables();
1092 for(size_t i=0; i<varMapVec.size(); i++)
1093 {
1094 map_get(varMapVec[i], "TrX").setValue(0);
1095 map_get(varMapVec[i], "TrY").setValue(0);
1096 map_get(varMapVec[i], "TrZ").setValue(0);
1097 };
1098 optPano.updateVariables(varMapVec);
1099 //next steps happens only when all images are connected;
1100 //now optimize panorama
1101 HuginBase::PanoramaOptions opts = optPano.getOptions();
1102 opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
1103 opts.optimizeReferenceImage=0;
1104 // calculate proper scaling, 1:1 resolution.
1105 // Otherwise optimizer distances are meaningless.
1106 opts.setWidth(30000, false);
1107 opts.setHeight(15000);
1108
1109 optPano.setOptions(opts);
1110 int w = hugin_utils::roundi(HuginBase::CalculateOptimalScale::calcOptimalScale(optPano) * opts.getWidth());
1111 opts.setWidth(w);
1112 opts.setHeight(w/2);
1113 optPano.setOptions(opts);
1114
1115 //generate optimize vector, optimize only yaw and pitch
1116 HuginBase::OptimizeVector optvars;
1117 for (unsigned i=0; i < optPano.getNrOfImages(); i++)
1118 {
1119 std::set<std::string> imgopt;
1120 if(i==opts.optimizeReferenceImage)
1121 {
1122 //optimize only anchors pitch, not yaw
1123 imgopt.insert("p");
1124 }
1125 else
1126 {
1127 imgopt.insert("p");
1128 imgopt.insert("y");
1129 };
1130 optvars.push_back(imgopt);
1131 }
1132 optPano.setOptimizeVector(optvars);
1133
1134 // remove vertical and horizontal control points
1135 HuginBase::CPVector cps = optPano.getCtrlPoints();
1136 HuginBase::CPVector newCP;
1137 for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1138 {
1139 if (it->mode == HuginBase::ControlPoint::X_Y)
1140 {
1141 newCP.push_back(*it);
1142 }
1143 }
1144 optPano.setCtrlPoints(newCP);
1145
1146 if (getVerbose() < 2)
1147 {
1148 PT_setProgressFcn(ptProgress);
1149 PT_setInfoDlgFcn(ptinfoDlg);
1150 };
1151 //in a first step do a pairwise optimisation
1152 HuginBase::AutoOptimise::autoOptimise(optPano, false);
1153 //now the final optimisation
1154 HuginBase::PTools::optimize(optPano);
1155 if (getVerbose() < 2)
1156 {
1157 PT_setProgressFcn(NULL);
1158 PT_setInfoDlgFcn(NULL);
1159 };
1160
1161 //now match overlapping images
1162 std::vector<size_t> imgMap(_image_layer.begin(), _image_layer.end());
1163 if(!matchPrealigned(&optPano, checkedImagePairs, imgMap, false))
1164 {
1165 return false;
1166 };
1167 };
1168 }
1169 else
1170 {
1171 if(!match(checkedImagePairs))
1172 {
1173 return false;
1174 };
1175 };
1176 return true;
1177 };
1178
matchPrealigned(HuginBase::Panorama * pano,std::vector<HuginBase::UIntSet> & connectedImages,std::vector<size_t> imgMap,bool exactOverlap)1179 bool PanoDetector::matchPrealigned(HuginBase::Panorama* pano, std::vector<HuginBase::UIntSet> &connectedImages, std::vector<size_t> imgMap, bool exactOverlap)
1180 {
1181 RunnableVector queue;
1182 MatchData_t matchesData;
1183 HuginBase::Panorama tempPano = pano->duplicate();
1184 if(!exactOverlap)
1185 {
1186 // increase hfov by 25 % to handle narrow overlaps (or even no overlap) better
1187 HuginBase::VariableMapVector varMapVec = tempPano.getVariables();
1188 for(size_t i=0; i<tempPano.getNrOfImages(); i++)
1189 {
1190 HuginBase::Variable& hfovVar = map_get(varMapVec[i], "v");
1191 hfovVar.setValue(std::min(360.0, 1.25 * hfovVar.getValue()));
1192 };
1193 tempPano.updateVariables(varMapVec);
1194 };
1195 HuginBase::CalculateImageOverlap overlap(&tempPano);
1196 overlap.calculate(10);
1197 for(size_t i=0; i<tempPano.getNrOfImages()-1; i++)
1198 {
1199 for(size_t j=i+1; j<tempPano.getNrOfImages(); j++)
1200 {
1201 if(set_contains(connectedImages[imgMap[i]],imgMap[j]))
1202 {
1203 continue;
1204 };
1205 if(overlap.getOverlap(i,j)>0)
1206 {
1207 matchesData.push_back(MatchData());
1208 MatchData& aM = matchesData.back();
1209 aM._i1 = &(_filesData[imgMap[i]]);
1210 aM._i2 = &(_filesData[imgMap[j]]);
1211 connectedImages[imgMap[i]].insert(imgMap[j]);
1212 connectedImages[imgMap[j]].insert(imgMap[i]);
1213 };
1214 };
1215 };
1216
1217 TRACE_INFO(std::endl<< "--- Find matches for overlapping images ---" << std::endl);
1218 for (size_t i = 0; i < matchesData.size(); ++i)
1219 {
1220 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
1221 };
1222 RunQueue(queue);
1223
1224 // Add detected matches to _panoramaInfo
1225 for (size_t i = 0; i < matchesData.size(); ++i)
1226 {
1227 const MatchData& aM = matchesData[i];
1228 for (size_t j = 0; j < aM._matches.size(); ++j)
1229 {
1230 const lfeat::PointMatchPtr& aPM = aM._matches[j];
1231 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1232 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1233 };
1234 };
1235
1236 return true;
1237 };
1238