Hugin trunk 0.1
Loading...
Searching...
No Matches
PanoDetector.cpp
Go to the documentation of this file.
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"
32
37#include <nona/ImageRemapper.h>
38
39//for multi row strategy
41#include <panodata/Panorama.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
67std::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
84std::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
91 }
92 else
93 {
96 };
97 newfilename.append(".key");
98 return newfilename;
99};
100
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(MULTIROW), _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{
113}
114
119
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
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 {
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 std::cout << "Input image options" << std::endl;
177 std::cout << " Downscale to half-size : " << (_downscale?"yes":"no") << std::endl;
178 if(_celeste)
179 {
180 std::cout << "Celeste options" << std::endl;
181 std::cout << " Threshold : " << _celesteThreshold << std::endl;
182 std::cout << " Radius : " << _celesteRadius << std::endl;
183 }
184 std::cout << "Sieve 1 Options" << std::endl;
185 std::cout << " Width : " << _sieve1Width << std::endl;
186 std::cout << " Height : " << _sieve1Height << std::endl;
187 std::cout << " Size : " << _sieve1Size << std::endl;
188 std::cout << " ==> Maximum keypoints per image : " << _sieve1Size* _sieve1Height* _sieve1Width << std::endl;
189 std::cout << "KDTree Options" << std::endl;
190 std::cout << " Search steps : " << _kdTreeSearchSteps << std::endl;
191 std::cout << " Second match distance : " << _kdTreeSecondDistance << std::endl;
192 std::cout << "Matching Options" << std::endl;
193 switch(_matchingStrategy)
194 {
195 case ALLPAIRS:
196 std::cout << " Mode : All pairs" << std::endl;
197 break;
198 case LINEAR:
199 std::cout << " Mode : Linear match with length of " << _linearMatchLen << " image" << std::endl;
200 break;
201 case MULTIROW:
202 std::cout << " Mode : Multi row" << std::endl;
203 break;
204 case PREALIGNED:
205 std::cout << " Mode : Prealigned positions" << std::endl;
206 break;
207 };
208 std::cout << " Distance threshold : " << _ransacDistanceThres << std::endl;
209 std::cout << "RANSAC Options" << std::endl;
210 std::cout << " Mode : ";
211 switch (_ransacMode)
212 {
214 std::cout << "auto" << std::endl;
215 break;
217 std::cout << "homography" << std::endl;
218 break;
220 std::cout << "roll, pitch, yaw" << std::endl;
221 break;
223 std::cout << "roll, pitch, yaw, fov" << std::endl;
224 break;
226 std::cout << "roll, pitch, yaw, fov, distortion" << std::endl;
227 break;
228 }
229 std::cout << " Iterations : " << _ransacIters << std::endl;
230 std::cout << " Distance threshold : " << _ransacDistanceThres << std::endl;
231 std::cout << "Minimum matches per image pair: " << _minimumMatches << std::endl;
232 std::cout << "Sieve 2 Options" << std::endl;
233 std::cout << " Width : " << _sieve2Width << std::endl;
234 std::cout << " Height : " << _sieve2Height << std::endl;
235 std::cout << " Size : " << _sieve2Size << std::endl;
236 std::cout << " ==> Maximum matches per image pair : " << _sieve2Size* _sieve2Height* _sieve2Width << std::endl;
237}
238
240{
241 std::cout << std::endl << "Project contains the following images:" << std::endl;
242 for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
243 {
244 std::string name(_panoramaInfo->getImage(i).getFilename());
245 if(name.compare(0,_prefix.length(),_prefix)==0)
246 {
247 name=name.substr(_prefix.length(),name.length()-_prefix.length());
248 }
249 std::cout << "Image " << i << std::endl << " Imagefile: " << name << std::endl;
250 bool writeKeyfileForImage=false;
251 if(!_keyPointsIdx.empty())
252 {
253 for(unsigned j=0; j<_keyPointsIdx.size() && !writeKeyfileForImage; j++)
254 {
256 };
257 };
258 if(_cache || _filesData[i]._hasakeyfile || writeKeyfileForImage)
259 {
260 name=_filesData[i]._keyfilename;
261 if(name.compare(0,_prefix.length(),_prefix)==0)
262 {
263 name=name.substr(_prefix.length(),name.length()-_prefix.length());
264 }
265 std::cout << " Keyfile : " << name;
267 {
268 std::cout << " (will be generated)" << std::endl;
269 }
270 else
271 {
272 std::cout << (_filesData[i]._hasakeyfile?" (will be loaded)":" (will be generated)") << std::endl;
273 };
274 };
275 std::cout << " Remapped : " << (_filesData[i].NeedsRemapping()?"yes":"no") << std::endl;
276 };
277};
278
280{
281public:
283 virtual void run() = 0;
284 virtual ~Runnable() {};
285};
286
287// definition of a runnable class for image data
312
313// definition of a runnable class for writeKeyPoints
337
338// definition of a runnable class for keypoints data
356
357// definition of a runnable class for MatchData
379
381{
382 std::string model_file = ("celeste.model");
383 std::ifstream test(model_file.c_str());
384 if (!test.good())
385 {
388 std::ifstream test2(install_path_model.c_str());
389 if (!test2.good())
390 {
391 std::cout << std::endl << "Couldn't open SVM model file " << model_file << std::endl;
392 std::cout << "Also tried " << install_path_model << std::endl << std::endl;
393 return false;
394 };
396 }
398 {
400 return false;
401 };
402 return true;
403};
404
405typedef std::vector<Runnable*> RunnableVector;
406
407void RunQueue(std::vector<Runnable*>& queue)
408{
409#pragma omp parallel for schedule(dynamic)
410 for (int i = 0; i < queue.size(); ++i)
411 {
412 queue[i]->run();
413 };
414 // now clear queue
415 while (!queue.empty())
416 {
417 delete queue.back();
418 queue.pop_back();
419 };
420};
421
423{
424 // init the random time generator
425 srandom((unsigned int)time(NULL));
426
427 // Load the input project file
428 if(!loadProject())
429 {
430 return;
431 };
433 {
434 for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++)
435 {
436 _keyPointsIdx.push_back(i);
437 };
438 };
439
440 if(_cleanup)
441 {
443 return;
444 };
445
446 //checking, if memory allows running desired number of threads
447 unsigned long maxImageSize=0;
448 bool withRemap=false;
449 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
450 {
451 if(!aB->second._hasakeyfile)
452 {
453 maxImageSize=std::max<unsigned long>(aB->second._detectWidth*aB->second._detectHeight,maxImageSize);
454 if(aB->second.NeedsRemapping())
455 {
456 withRemap=true;
457 };
458 };
459 };
460#ifdef HAVE_OPENMP
461 if (_cores == 0)
462 {
463 // use number of physical cores - 1
465 if (nrCores > 4)
466 {
467 --nrCores;
468 };
470 };
471 if (maxImageSize != 0)
472 {
473 unsigned long long maxCores;
474 //determinded factors by testing of some projects
475 //the memory usage seems to be very high
476 //if the memory usage could be decreased these numbers can be decreased
477 if(withRemap)
478 {
480 }
481 else
482 {
484 };
485 if(maxCores<1)
486 {
487 maxCores=1;
488 }
489 if(maxCores<_cores)
490 {
491 if(getVerbose()>0)
492 {
493 std::cout << "\nThe available memory does not allow running " << _cores << " threads parallel.\n"
494 << "Running cpfind with " << maxCores << " threads.\n";
495 };
497 };
498 };
500 std::cout << "Using " << _cores << " threads" << std::endl;
501#endif
502 RunnableVector queue;
503 svmModel = NULL;
504 if(_celeste)
505 {
506 TRACE_INFO("\nLoading Celeste model file...\n");
507 if(!LoadSVMModel())
508 {
509 setCeleste(false);
510 };
511 };
512
513 //print some more information about the images
514 if (_verbose > 0)
515 {
517 }
518
519 // 2. run analysis of images or keypoints
520#if _WIN32
521 //multi threading of image loading results sometime in a race condition
522 //try to prevent this by initialisation of codecManager before
523 //running multi threading part
524 std::string s=vigra::impexListExtensions();
525#endif
526 if (_keyPointsIdx.size() != 0)
527 {
528 if (_verbose > 0)
529 {
530 TRACE_INFO(std::endl << "--- Analyze Images ---" << std::endl);
531 }
532 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
533 {
534 queue.push_back(new WriteKeyPointsRunnable(_filesData[_keyPointsIdx[i]], *this));
535 };
536 }
537 else
538 {
539 TRACE_INFO(std::endl << "--- Analyze Images ---" << std::endl);
541 {
542 // when using multirow, don't analyse stacks with linked positions
545 imagesToAnalyse.insert(_image_layer.begin(), _image_layer.end());
546 for (size_t i = 0; i < _image_stacks.size(); i++)
547 {
548 imagesToAnalyse.insert(_image_stacks[i].begin(), _image_stacks[i].end());
549 }
550 for (HuginBase::UIntSet::const_iterator it = imagesToAnalyse.begin(); it != imagesToAnalyse.end(); ++it)
551 {
552 if (_filesData[*it]._hasakeyfile)
553 {
554 queue.push_back(new LoadKeypointsDataRunnable(_filesData[*it], *this));
555 }
556 else
557 {
558 queue.push_back(new ImgDataRunnable(_filesData[*it], *this));
559 };
560 };
561 }
562 else
563 {
564 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
565 {
566 if (aB->second._hasakeyfile)
567 {
568 queue.push_back(new LoadKeypointsDataRunnable(aB->second, *this));
569 }
570 else
571 {
572 queue.push_back(new ImgDataRunnable(aB->second, *this));
573 }
574 }
575 };
576 }
577 RunQueue(queue);
578
579 if(svmModel!=NULL)
580 {
582 };
583
584 // check if the load of images succeed.
585 if (!checkLoadSuccess())
586 {
587 TRACE_INFO("One or more images failed to load. Exiting.");
588 return;
589 }
590
591 if(_cache)
592 {
593 TRACE_INFO(std::endl << "--- Cache keyfiles to disc ---" << std::endl);
594 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
595 {
596 if (!aB->second._hasakeyfile)
597 {
598 TRACE_INFO("i" << aB->second._number << " : Caching keypoints..." << std::endl);
599 writeKeyfile(aB->second);
600 };
601 };
602 };
603
604 // Detect matches if writeKeyPoints wasn't set
605 if(_keyPointsIdx.empty())
606 {
607 switch (getMatchingStrategy())
608 {
609 case ALLPAIRS:
610 case LINEAR:
611 {
612 std::vector<HuginBase::UIntSet> imgPairs(_panoramaInfo->getNrOfImages());
613 if(!match(imgPairs))
614 {
615 return;
616 };
617 };
618 break;
619 case MULTIROW:
620 if(!matchMultiRow())
621 {
622 return;
623 };
624 break;
625 case PREALIGNED:
626 {
627 //check, which image pairs are already connected by control points
628 std::vector<HuginBase::UIntSet> connectedImages(_panoramaInfo->getNrOfImages());
630 for(HuginBase::CPVector::const_iterator it=cps.begin();it!=cps.end(); ++it)
631 {
632 if((*it).mode==HuginBase::ControlPoint::X_Y)
633 {
634 connectedImages[(*it).image1Nr].insert((*it).image2Nr);
635 connectedImages[(*it).image2Nr].insert((*it).image1Nr);
636 };
637 };
638 //build dummy map
639 std::vector<size_t> imgMap(_panoramaInfo->getNrOfImages());
640 for(size_t i=0; i<_panoramaInfo->getNrOfImages(); i++)
641 {
642 imgMap[i]=i;
643 };
644 //and the final matching step
646 {
647 return;
648 };
649 }
650 break;
651 };
652 }
653
654 // 5. write output
655 if (_keyPointsIdx.size() != 0)
656 {
657 //Write all keyfiles
658 TRACE_INFO(std::endl<< "--- Write Keyfiles output ---" << std::endl << std::endl);
659 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
660 {
662 };
663 if(_outputGiven)
664 {
665 std::cout << std::endl << "Warning: You have given the --output switch." << std::endl
666 << "This switch is not compatible with the --writekeyfile or --kall switch." << std::endl
667 << "If you want to generate the keyfiles and" << std::endl
668 << "do the matching in the same run use the --cache switch instead." << std::endl << std::endl;
669 };
670 }
671 else
672 {
674 TRACE_INFO(std::endl<< "--- Write Project output ---" << std::endl);
675 writeOutput();
676 TRACE_INFO("Written output to " << _outputFile << std::endl << std::endl);
677 };
678}
679
680bool PanoDetector::match(std::vector<HuginBase::UIntSet> &checkedPairs)
681{
682 // 3. prepare matches
683 RunnableVector queue;
685 unsigned int aLen = _filesData.size();
687 {
689 }
690
691 if (aLen >= _filesData.size())
692 {
693 aLen = _filesData.size() - 1;
694 }
695
696 for (unsigned int i1 = 0; i1 < _filesData.size(); ++i1)
697 {
698 unsigned int aEnd = i1 + 1 + aLen;
699 if (_filesData.size() < aEnd)
700 {
701 aEnd = _filesData.size();
702 }
703
704 for (unsigned int i2 = (i1+1); i2 < aEnd; ++i2)
705 {
706 if(set_contains(checkedPairs[i1], i2))
707 {
708 continue;
709 };
710 // create a new entry in the matches map
711 matchesData.push_back(MatchData());
712
713 MatchData& aM = matchesData.back();
714 aM._i1 = &(_filesData[i1]);
715 aM._i2 = &(_filesData[i2]);
716
717 checkedPairs[i1].insert(i2);
718 checkedPairs[i2].insert(i1);
719 }
720 }
721 // 4. find matches
722 TRACE_INFO(std::endl<< "--- Find pair-wise matches ---" << std::endl);
723 for (size_t i = 0; i < matchesData.size(); ++i)
724 {
725 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
726 };
727 RunQueue(queue);
728
729 // Add detected matches to _panoramaInfo
730 for (size_t i = 0; i < matchesData.size(); ++i)
731 {
732 const MatchData& aM = matchesData[i];
733 for (size_t j = 0; j < aM._matches.size(); ++j)
734 {
736 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
737 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
738 };
739 };
740 return true;
741};
742
744{
746 if(_prefix.empty())
747 {
748 // Get the current working directory:
749 char* buffer;
750#ifdef _WIN32
751#define getcwd _getcwd
752#endif
753 if((buffer=getcwd(NULL,0))!=NULL)
754 {
755 _prefix.append(buffer);
756 free(buffer);
758 }
759 };
761 {
762 return false;
763 }
764
765 // Create a copy of panoramaInfo that will be used to define
766 // image options
768
769 // create a hash set for all known control points
770 {
772 for (auto& cp : allCps)
773 {
774 _cpsHashSet.insert(cp.getCPString());
775 };
776 }
777 // Add images found in the project file to _filesData
778 const unsigned int nImg = _panoramaInfo->getNrOfImages();
779 unsigned int imgWithKeyfile=0;
780 for (unsigned int imgNr = 0; imgNr < nImg; ++imgNr)
781 {
782 // insert the image in the map
783 _filesData.insert(std::make_pair(imgNr, ImgData()));
784
785 // get the data
786 ImgData& aImgData = _filesData[imgNr];
787
788 // get a copy of image info
790
791 // set the name
792 aImgData._name = img.getFilename();
793
794 // modify image position in the copy
795 img.setYaw(0);
796 img.setRoll(0);
797 img.setPitch(0);
798 img.setX(0);
799 img.setY(0);
800 img.setZ(0);
801 img.setActive(true);
802 _panoramaInfoCopy.setImage(imgNr,img);
803
804 // Number pointing to image info in _panoramaInfo
805 aImgData._number = imgNr;
806
807 bool needsremap=(img.getHFOV()>=65 && img.getProjection() != HuginBase::SrcPanoImage::FISHEYE_STEREOGRAPHIC);
808 // set image detection size
809 if(needsremap)
810 {
811 _filesData[imgNr]._detectWidth = std::max(img.getSize().width(),img.getSize().height());
812 _filesData[imgNr]._detectHeight = std::max(img.getSize().width(),img.getSize().height());
813 _filesData[imgNr].SetSizeMode(ImgData::REMAPPED);
814 }
815 else
816 {
817 _filesData[imgNr]._detectWidth = img.getSize().width();
818 _filesData[imgNr]._detectHeight = img.getSize().height();
819 };
820
821 // don't downscale if downscale image is smaller than 1000 pixel
822 if (_downscale && std::min(img.getSize().width(), img.getSize().height()) > 2000)
823 {
824 _filesData[imgNr]._detectWidth >>= 1;
825 _filesData[imgNr]._detectHeight >>= 1;
826 if (!_filesData[imgNr].NeedsRemapping())
827 {
828 _filesData[imgNr].SetSizeMode(ImgData::DOWNSCALED);
829 };
830 }
831
832 // set image remapping options
833 if (aImgData.GetSizeMode() == ImgData::REMAPPED)
834 {
836 aImgData._projOpts.setHFOV(250);
837 aImgData._projOpts.setVFOV(250);
838 aImgData._projOpts.setWidth(250);
839 aImgData._projOpts.setHeight(250);
840
841 // determine size of output image.
842 // The old code did not work with images with images with a FOV
843 // approaching 180 degrees
844 vigra::Rect2D roi=estimateOutputROI(_panoramaInfoCopy,aImgData._projOpts,imgNr);
845 double scalefactor = std::max((double)_filesData[imgNr]._detectWidth / roi.width(),
846 (double)_filesData[imgNr]._detectHeight / roi.height() );
847
848 // resize output canvas
849 vigra::Size2D canvasSize((int)aImgData._projOpts.getWidth() * scalefactor,
850 (int)aImgData._projOpts.getHeight() * scalefactor);
851 aImgData._projOpts.setWidth(canvasSize.width(), false);
852 aImgData._projOpts.setHeight(canvasSize.height());
853
854 // set roi to cover the remapped input image
855 roi *= scalefactor;
856 _filesData[imgNr]._detectWidth = roi.width();
857 _filesData[imgNr]._detectHeight = roi.height();
858 aImgData._projOpts.setROI(roi);
859 }
860
861 // Specify if the image has an associated keypoint file
862
863 aImgData._keyfilename = getKeyfilenameFor(_keypath,aImgData._name);
864 aImgData._hasakeyfile = hugin_utils::FileExists(aImgData._keyfilename);
865 if(aImgData._hasakeyfile)
866 {
868 };
869 }
870 //update masks, convert positive masks into negative masks
871 //because positive masks works only if the images are on the final positions
873
874 //if all images has keyfile, we don't need to load celeste model file
876 {
877 _celeste=false;
878 };
879 return true;
880}
881
883{
884 if(!_keyPointsIdx.empty())
885 {
886 for (unsigned int i = 0; i < _keyPointsIdx.size(); ++i)
887 {
888 if(_filesData[_keyPointsIdx[i]]._loadFail)
889 {
890 return false;
891 };
892 };
893 }
894 else
895 {
896 for (unsigned int aFileN = 0; aFileN < _filesData.size(); ++aFileN)
897 {
898 if(_filesData[aFileN]._loadFail)
899 {
900 return false;
901 };
902 };
903 };
904 return true;
905}
906
908{
909 for (ImgDataIt_t aB = _filesData.begin(); aB != _filesData.end(); ++aB)
910 {
911 if (aB->second._hasakeyfile)
912 {
913 remove(aB->second._keyfilename.c_str());
914 };
915 };
916};
917
919{
920 std::vector<HuginBase::UIntVector> stacks = HuginBase::getSortedStacks(_panoramaInfo);
921 //get image with median exposure for search with cp generator
922 for (size_t i = 0; i < stacks.size(); ++i)
923 {
924 size_t index = 0;
925 if (_panoramaInfo->getImage(*(stacks[i].begin())).getExposureValue() != _panoramaInfo->getImage(*(stacks[i].rbegin())).getExposureValue())
926 {
927 index = stacks[i].size() / 2;
928 };
929 _image_layer.insert(stacks[i][index]);
930 if (stacks[i].size()>1)
931 {
932 //build list for stacks, consider only unlinked stacks
933 if (!_panoramaInfo->getImage(*(stacks[i].begin())).YawisLinked())
934 {
935 _image_stacks.push_back(stacks[i]);
936 };
937 };
938 };
939};
940
942{
943 RunnableVector queue;
945 //step 1
946 std::vector<HuginBase::UIntSet> checkedImagePairs(_panoramaInfo->getNrOfImages());
947 for (size_t i = 0; i < _image_stacks.size();i++)
948 {
949 //build match list for stacks
950 for(unsigned int j=0; j<_image_stacks[i].size()-1; j++)
951 {
952 const size_t img1 = _image_stacks[i][j];
953 const size_t img2 = _image_stacks[i][j + 1];
954 matchesData.push_back(MatchData());
955 MatchData& aM=matchesData.back();
956 if (img1 < img2)
957 {
958 aM._i1 = &(_filesData[img1]);
959 aM._i2 = &(_filesData[img2]);
960 }
961 else
962 {
963 aM._i1 = &(_filesData[img2]);
964 aM._i2 = &(_filesData[img1]);
965 };
966 checkedImagePairs[img1].insert(img2);
967 checkedImagePairs[img2].insert(img1);
968 };
969 };
970 //build match data list for image pairs
971 if(_image_layer.size()>1)
972 {
973 for (HuginBase::UIntSet::const_iterator it = _image_layer.begin(); it != _image_layer.end(); ++it)
974 {
975 const size_t img1 = *it;
976 HuginBase::UIntSet::const_iterator it2 = it;
977 ++it2;
978 if (it2 != _image_layer.end())
979 {
980 const size_t img2 = *it2;
981 matchesData.push_back(MatchData());
982 MatchData& aM = matchesData.back();
983 if (img1 < img2)
984 {
985 aM._i1 = &(_filesData[img1]);
986 aM._i2 = &(_filesData[img2]);
987 }
988 else
989 {
990 aM._i1 = &(_filesData[img2]);
991 aM._i2 = &(_filesData[img1]);
992 };
993 checkedImagePairs[img1].insert(img2);
994 checkedImagePairs[img2].insert(img1);
995 };
996 };
997 };
998 TRACE_INFO(std::endl<< "--- Find matches ---" << std::endl);
999 for (size_t i = 0; i < matchesData.size(); ++i)
1000 {
1001 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
1002 };
1003 RunQueue(queue);
1004
1005 // Add detected matches to _panoramaInfo
1006 for (size_t i = 0; i < matchesData.size(); ++i)
1007 {
1008 const MatchData& aM = matchesData[i];
1009 for (size_t j = 0; j < aM._matches.size(); ++j)
1010 {
1012 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1013 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1014 };
1015 };
1016
1017 // step 2: connect all image groups
1018 queue.clear();
1019 matchesData.clear();
1022 const HuginGraph::ImageGraph::Components comps = graph.GetComponents();
1023 const size_t n = comps.size();
1024 if(n>1)
1025 {
1026 std::vector<unsigned int> ImagesGroups;
1027 for(size_t i=0; i<n; i++)
1028 {
1029 HuginBase::UIntSet::iterator imgIt = _image_layer.begin();
1030 std::advance(imgIt, *(comps[i].begin()));
1031 ImagesGroups.push_back(*imgIt);
1032 if(comps[i].size()>1)
1033 {
1034 imgIt = _image_layer.begin();
1035 std::advance(imgIt, *(comps[i].rbegin()));
1036 ImagesGroups.push_back(*imgIt);
1037 }
1038 };
1039 for(unsigned int i=0; i<ImagesGroups.size()-1; i++)
1040 {
1041 for(unsigned int j=i+1; j<ImagesGroups.size(); j++)
1042 {
1043 size_t img1=ImagesGroups[i];
1044 size_t img2=ImagesGroups[j];
1045 //skip already checked image pairs
1046 if(set_contains(checkedImagePairs[img1],img2))
1047 {
1048 continue;
1049 };
1050 matchesData.push_back(MatchData());
1051 MatchData& aM = matchesData.back();
1052 if (img1 < img2)
1053 {
1054 aM._i1 = &(_filesData[img1]);
1055 aM._i2 = &(_filesData[img2]);
1056 }
1057 else
1058 {
1059 aM._i1 = &(_filesData[img2]);
1060 aM._i2 = &(_filesData[img1]);
1061 };
1062 checkedImagePairs[img1].insert(img2);
1063 checkedImagePairs[img2].insert(img1);
1064 };
1065 };
1066 TRACE_INFO(std::endl<< "--- Find matches in images groups ---" << std::endl);
1067 for (size_t i = 0; i < matchesData.size(); ++i)
1068 {
1069 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
1070 };
1071 RunQueue(queue);
1072
1073 for (size_t i = 0; i < matchesData.size(); ++i)
1074 {
1075 const MatchData& aM = matchesData[i];
1076 for (size_t j = 0; j < aM._matches.size(); ++j)
1077 {
1079 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1080 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1081 };
1082 };
1083 };
1084 // step 3: now connect all overlapping images
1085 queue.clear();
1086 matchesData.clear();
1089 if(graph2.IsConnected())
1090 {
1091 if(_image_layer.size()>2)
1092 {
1093 //reset translation parameters
1095 for(size_t i=0; i<varMapVec.size(); i++)
1096 {
1097 map_get(varMapVec[i], "TrX").setValue(0);
1098 map_get(varMapVec[i], "TrY").setValue(0);
1099 map_get(varMapVec[i], "TrZ").setValue(0);
1100 };
1101 optPano.updateVariables(varMapVec);
1102 //next steps happens only when all images are connected;
1103 //now optimize panorama
1104 HuginBase::PanoramaOptions opts = optPano.getOptions();
1107 // calculate proper scaling, 1:1 resolution.
1108 // Otherwise optimizer distances are meaningless.
1109 opts.setWidth(30000, false);
1110 opts.setHeight(15000);
1111
1112 optPano.setOptions(opts);
1114 opts.setWidth(w);
1115 opts.setHeight(w/2);
1116 optPano.setOptions(opts);
1117
1118 //generate optimize vector, optimize only yaw and pitch
1120 for (unsigned i=0; i < optPano.getNrOfImages(); i++)
1121 {
1122 std::set<std::string> imgopt;
1123 if(i==opts.optimizeReferenceImage)
1124 {
1125 //optimize only anchors pitch, not yaw
1126 imgopt.insert("p");
1127 }
1128 else
1129 {
1130 imgopt.insert("p");
1131 imgopt.insert("y");
1132 };
1133 optvars.push_back(imgopt);
1134 }
1135 optPano.setOptimizeVector(optvars);
1136
1137 // remove vertical and horizontal control points
1138 HuginBase::CPVector cps = optPano.getCtrlPoints();
1140 for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1141 {
1142 if (it->mode == HuginBase::ControlPoint::X_Y)
1143 {
1144 newCP.push_back(*it);
1145 }
1146 }
1147 optPano.setCtrlPoints(newCP);
1148
1149 if (getVerbose() < 2)
1150 {
1153 };
1154 //in a first step do a pairwise optimisation
1156 //now the final optimisation
1158 if (getVerbose() < 2)
1159 {
1162 };
1163
1164 //now match overlapping images
1165 std::vector<size_t> imgMap(_image_layer.begin(), _image_layer.end());
1167 {
1168 return false;
1169 };
1170 };
1171 }
1172 else
1173 {
1175 {
1176 return false;
1177 };
1178 };
1179 return true;
1180};
1181
1182bool PanoDetector::matchPrealigned(HuginBase::Panorama* pano, std::vector<HuginBase::UIntSet> &connectedImages, std::vector<size_t> imgMap, bool exactOverlap)
1183{
1184 RunnableVector queue;
1187 if(!exactOverlap)
1188 {
1189 // increase hfov by 25 % to handle narrow overlaps (or even no overlap) better
1191 for(size_t i=0; i<tempPano.getNrOfImages(); i++)
1192 {
1194 double newHFOV = 1.25 * hfovVar.getValue();
1195 switch (tempPano.getImage(i).getProjection())
1196 {
1197 // range checking
1199 newHFOV = std::min(170.0, newHFOV);
1200 break;
1201 default:
1202 newHFOV = std::min(360.0, newHFOV);
1203 break;
1204 }
1205 hfovVar.setValue(newHFOV);
1206 };
1207 tempPano.updateVariables(varMapVec);
1208 };
1210 overlap.calculate(10);
1211 for(size_t i=0; i<tempPano.getNrOfImages()-1; i++)
1212 {
1213 for(size_t j=i+1; j<tempPano.getNrOfImages(); j++)
1214 {
1216 {
1217 continue;
1218 };
1219 if(overlap.getOverlap(i,j)>0)
1220 {
1221 matchesData.push_back(MatchData());
1222 MatchData& aM = matchesData.back();
1223 aM._i1 = &(_filesData[imgMap[i]]);
1224 aM._i2 = &(_filesData[imgMap[j]]);
1225 connectedImages[imgMap[i]].insert(imgMap[j]);
1226 connectedImages[imgMap[j]].insert(imgMap[i]);
1227 };
1228 };
1229 };
1230
1231 TRACE_INFO(std::endl<< "--- Find matches for overlapping images ---" << std::endl);
1232 for (size_t i = 0; i < matchesData.size(); ++i)
1233 {
1234 queue.push_back(new MatchDataRunnable(matchesData[i], *this));
1235 };
1236 RunQueue(queue);
1237
1238 // Add detected matches to _panoramaInfo
1239 for (size_t i = 0; i < matchesData.size(); ++i)
1240 {
1241 const MatchData& aM = matchesData[i];
1242 for (size_t j = 0; j < aM._matches.size(); ++j)
1243 {
1245 _panoramaInfo->addCtrlPoint(HuginBase::ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y,
1246 aM._i2->_number, aPM->_img2_x, aPM->_img2_y));
1247 };
1248 };
1249
1250 return true;
1251};
definitions of classes to calculate overlap between different images
declaration of functions to handle stacks and layers
!! from PTOptimise.h 1951
std::string includeTrailingPathSep(std::string path)
std::vector< Runnable * > RunnableVector
#define srandom
std::string getKeyfilenameFor(std::string keyfilesPath, std::string filename)
returns the filename for the keyfile for a given image
#define TRACE_PAIR(X)
void RunQueue(std::vector< Runnable * > &queue)
#define TRACE_IMG(X)
static int ptProgress(int command, char *argument)
static int ptinfoDlg(int command, char *argument)
std::string getKeyfilenameFor(std::string keyfilesPath, std::string filename)
returns the filename for the keyfile for a given image
Contains functions to transform whole images.
Somewhere to specify what variables belong to what.
#define TRACE_INFO(x)
Definition Tracer.h:26
static void autoOptimise(PanoramaData &pano, bool optRoll=true)
class for calculating overlap of images
static double calcOptimalScale(PanoramaData &panorama)
represents a control point
Panorama image options.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
void setHeight(unsigned int h)
set panorama height
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
unsigned int getWidth() const
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
void setImage(std::size_t nr, const SrcPanoImage &img)
set a panorama image, counting starts with 0
Definition Panorama.h:218
void updateMasks(bool convertPosMaskToNeg=false)
updates all active masks
Definition Panorama.cpp:988
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition Panorama.cpp:381
VariableMapVector getVariables() const
get variables of this panorama
Definition Panorama.cpp:118
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition Panorama.h:319
Panorama getSubset(const UIntSet &imgs) const
get a subset of the panorama
Panorama duplicate() const
duplicate the panorama
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
a variable has a value and a name.
class to work with images graphs created from a HuginBase::Panorama class it creates a graph based on...
Definition ImageGraph.h:45
std::vector< HuginBase::UIntSet > Components
stores the components of the graph
Definition ImageGraph.h:50
const PanoDetector & _panoDetector
ImgDataRunnable(PanoDetector::ImgData &iImageData, const PanoDetector &iPanoDetector)
virtual void run()
PanoDetector::ImgData & _imgData
LoadKeypointsDataRunnable(PanoDetector::ImgData &iImageData, const PanoDetector &iPanoDetector)
PanoDetector::ImgData & _imgData
const PanoDetector & _panoDetector
virtual void run()
PanoDetector::MatchData & _matchData
const PanoDetector & _panoDetector
MatchDataRunnable(PanoDetector::MatchData &iMatchData, const PanoDetector &iPanoDetector)
ImgData_t _filesData
std::string _keypath
double _kdTreeSecondDistance
static bool AnalyzeImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool MakeKeyPointDescriptorsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void writeKeyfile(ImgData &imgInfo)
int _ransacDistanceThres
int getVerbose() const
std::string _prefix
static bool FindMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
std::string _outputFile
void CleanupKeyfiles()
double _celesteThreshold
static bool FindKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void buildMultiRowImageSets()
search for image layer and image stacks for the multirow matching step
MatchingStrategy getMatchingStrategy() const
struct celeste::svm_model * svmModel
HuginBase::Panorama * _panoramaInfo
static bool FilterKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
std::vector< HuginBase::UIntVector > _image_stacks
vector with image numbers of all stacks, contains only the unlinked stacks
std::vector< int > _keyPointsIdx
std::map< int, ImgData >::iterator ImgDataIt_t
std::string _inputFile
bool match(std::vector< HuginBase::UIntSet > &checkedPairs)
static bool BuildKDTreesInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
bool matchPrealigned(HuginBase::Panorama *pano, std::vector< HuginBase::UIntSet > &connectedImages, std::vector< size_t > imgMap, bool exactOverlap=true)
does only matches image pairs which overlaps and don't have control points
bool _writeAllKeyPoints
std::vector< MatchData > MatchData_t
HuginBase::RANSACOptimizer::Mode _ransacMode
static bool FreeMemoryInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool RansacMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
void printFilenames()
HuginBase::Panorama _panoramaInfoCopy
static bool LoadKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void setCores(int iCores)
HuginBase::UIntSet _image_layer
image set contains only the images with the median exposure of each stack
void setCeleste(bool iCeleste)
std::set< std::string > _cpsHashSet
MatchingStrategy _matchingStrategy
static bool RemapBackKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
bool checkLoadSuccess()
static bool FilterMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
virtual ~Runnable()
virtual void run()=0
const PanoDetector & _panoDetector
WriteKeyPointsRunnable(PanoDetector::ImgData &iImageData, const PanoDetector &iPanoDetector)
PanoDetector::ImgData & _imgData
platform/compiler specific stuff.
unsigned int optimize(PanoramaData &pano, const char *userScript)
optimize the images imgs, for variables optvec, using vars as start.
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
std::vector< VariableMap > VariableMapVector
std::vector< ControlPoint > CPVector
std::vector< std::set< std::string > > OptimizeVector
std::vector< HuginBase::UIntVector > getSortedStacks(const HuginBase::Panorama *pano)
returns vector of UIntVector with image numbers of each stack sorted by exposure
std::set< unsigned int > UIntSet
void destroySVMmodel(struct svm_model *&model)
frees the resource of model
Definition Celeste.cpp:60
bool loadSVMmodel(struct svm_model *&model, std::string &model_file)
loads the SVM model from file
Definition Celeste.cpp:45
std::string stripExtension(const std::string &basename2)
remove extension of a filename
Definition utils.cpp:130
std::string GetDataDir()
returns the full path to the data directory
Definition utils.cpp:441
int roundi(T x)
Definition hugin_math.h:73
bool FileExists(const std::string &filename)
checks if file exists
Definition utils.cpp:362
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
std::shared_ptr< PointMatch > PointMatchPtr
Definition PointMatch.h:52
unsigned long long getTotalMemory()
returns the total memory in byte
Definition Utils.cpp:65
unsigned int GetPhysicalCPUCount()
return the number of physical cpu cores
Definition Utils.cpp:126
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
Map::mapped_type & map_get(Map &m, const typename Map::key_type &key)
get a map element.
Definition stl_utils.h:98
lfeat::KeyPointVect_t _kp
lfeat::PointMatchVector_t _matches
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41