Hugin trunk 0.1
Loading...
Searching...
No Matches
Panorama.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
26#include "Panorama.h"
27
28#include "PTScriptParsing.h"
35
36#include <fstream>
37#include <typeinfo>
38
39namespace HuginBase {
40
41Panorama::Panorama() : dirty(false), m_forceImagesUpdate(false)
42{
43 // init map with ptoptimizer variables.
44 m_ptoptimizerVarNames.insert("a");
45 m_ptoptimizerVarNames.insert("b");
46 m_ptoptimizerVarNames.insert("c");
47 m_ptoptimizerVarNames.insert("d");
48 m_ptoptimizerVarNames.insert("e");
49 m_ptoptimizerVarNames.insert("g");
50 m_ptoptimizerVarNames.insert("t");
51 m_ptoptimizerVarNames.insert("v");
52 m_ptoptimizerVarNames.insert("r");
53 m_ptoptimizerVarNames.insert("p");
54 m_ptoptimizerVarNames.insert("y");
55 m_ptoptimizerVarNames.insert("TrX");
56 m_ptoptimizerVarNames.insert("TrY");
57 m_ptoptimizerVarNames.insert("TrZ");
58 m_ptoptimizerVarNames.insert("Tpy");
59 m_ptoptimizerVarNames.insert("Tpp");
60}
61
63{
64 DEBUG_TRACE("dtor");
65 reset();
66 DEBUG_TRACE("dtor about to finish");
67}
68
70{
71 // delete all images and control points.
72 state.ctrlPoints.clear();
75 state.optvec.clear();
80 dirty=false;
81}
82
83std::vector<unsigned int> Panorama::getCtrlPointsForImage(unsigned int imgNr) const
84{
85 std::vector<unsigned int> result;
86 unsigned int i = 0;
87 for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
88 if ((it->image1Nr == imgNr) || (it->image2Nr == imgNr)) {
89 result.push_back(i);
90 }
91 i++;
92 }
93 return result;
94}
95
97{
98 CPointVector result;
99 for(unsigned int i=0;i<state.ctrlPoints.size();i++)
100 {
102 if(point.image1Nr==imgNr)
103 {
104 result.push_back(std::make_pair(i,point));
105 }
106 else
107 {
108 if(point.image2Nr==imgNr)
109 {
110 point.mirror();
111 result.push_back(std::make_pair(i,point));
112 };
113 };
114 };
115 return result;
116};
117
119{
121 for (size_t i = 0; i < state.images.size(); i++)
122 {
123 map.push_back(state.images[i]->getVariableMap());
124 }
125 return map;
126}
127
128const VariableMap Panorama::getImageVariables(unsigned int imgNr) const
129{
130 assert(imgNr < state.images.size());
131 return state.images[imgNr]->getVariableMap();
132}
133
134
135void Panorama::updateCtrlPointErrors(const UIntSet & imgs, const CPVector & cps)
136{
137 unsigned sc = 0;
138 unsigned ic = 0;
139 std::map<unsigned int, unsigned int> script2CPMap;
140 for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
141 if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
142 script2CPMap[sc] = ic;
143 sc++;
144 }
145 ic++;
146 }
147
148 // need to have same number of control points!
149 assert(cps.size() == script2CPMap.size());
150 unsigned i=0;
151 for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it) {
152 imageChanged(script2CPMap[it->image1Nr]);
153 imageChanged(script2CPMap[it->image2Nr]);
154 state.ctrlPoints[script2CPMap[i]].error = it->error;
155 i++;
156 }
157}
158
159
161{
162 assert(cps.size() == state.ctrlPoints.size());
163 unsigned int nrp = cps.size();
164 for (unsigned int i = 0; i < nrp ; i++) {
165 imageChanged(state.ctrlPoints[i].image1Nr);
166 imageChanged(state.ctrlPoints[i].image2Nr);
167 state.ctrlPoints[i].error = cps[i].error;
168 }
169}
170
172{
173 assert(vars.size() == state.images.size());
174 unsigned int i = 0;
175 for (VariableMapVector::const_iterator it = vars.begin(); it != vars.end(); ++it) {
177 i++;
178 }
179}
180
182{
183 VariableMapVector::const_iterator v_it = vars.begin();
184 for (UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); ++it) {
185 assert(*it < state.images.size());
187 ++v_it;
188 }
189}
190
191void Panorama::updateVariables(unsigned int imgNr, const VariableMap & var)
192{
193 if (imgNr > state.images.size())
194 return;
195 for (VariableMap::const_iterator it = var.begin(); it != var.end() ; ++it) {
196 updateVariable(imgNr,it->second);
197 }
198}
199
200void Panorama::updateVariable(unsigned int imgNr, const Variable &var)
201{
202 if (imgNr > state.images.size())
203 return;
204 // update a single variable, and everything linked to it.
205 state.images[imgNr]->setVar(var.getName(), var.getValue());
206 // call imageChanged for the images affected by this.
207#define image_variable( name, type, default_value ) \
208 if (PTOVariableConverterFor##name::checkApplicability(var.getName())) \
209 {\
210 for (std::size_t i = 0; i < getNrOfImages(); i++)\
211 {\
212 if (state.images[imgNr]->name##isLinkedWith(*state.images[i]))\
213 {\
214 imageChanged(i);\
215 }\
216 }\
217 }\
218 else
219#include "image_variables.h"
220#undef image_variable
221 {// this is for the final else.
222 DEBUG_ERROR("Unknown variable " << var.getName());
223 }
225}
226
228{
229 for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
230 {
231 state.images[*it]->updateFocalLength(newFocalLength);
233 };
234 //search for images with linked HFOV and mark these also as changed
235 for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
236 {
238 if(state.images[*it]->HFOVisLinked())
239 {
240 for(unsigned int j=0;j<getNrOfImages();j++)
241 {
242 if(*it!=j)
243 {
244 if(state.images[*it]->HFOVisLinkedWith(*img))
245 {
247 };
248 };
249 };
250 };
251 };
252};
253
255{
256 //store list of focal length, so we can keep the focal length unchanged and change hfov instead
257 std::vector<double> focalLengthVector(getNrOfImages(),0.0);
258 for(unsigned i=0;i<getNrOfImages();i++)
259 {
260 focalLengthVector[i]=state.images[i]->calcFocalLength(state.images[i]->getProjection(),
261 state.images[i]->getHFOV(),state.images[i]->getCropFactor(),state.images[i]->getSize());
262 };
263 for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
264 {
265 state.images[*it]->updateCropFactor(focalLengthVector[*it],newCropFactor);
267 };
268};
269
270// What counts as changed in terms of the image variable links?
271// Should I call imageChanged on every image linked to sourceImg and destImg?
272// destImg's variable will change value to sourceImg's, so the images linked to
273// destImg should be linked.
275#define image_variable( name, type, default_value )\
276void Panorama::linkImageVariable##name(unsigned int sourceImgNr, unsigned int destImgNr)\
277{\
278 state.images[destImgNr]->link##name(state.images[sourceImgNr]);\
279 imageChanged(destImgNr);\
280 imageChanged(sourceImgNr);\
281 state.needsOptimization = true;\
282}
283#include "image_variables.h"
284#undef image_variable
285
287#define image_variable( name, type, default_value )\
288void Panorama::unlinkImageVariable##name(unsigned int imgNr)\
289{\
290 state.images[imgNr]->unlink##name();\
291 imageChanged(imgNr);\
292 state.needsOptimization = true;\
293}
294#include "image_variables.h"
295#undef image_variable
296
298{
299 DEBUG_ASSERT(optvec.size() == state.images.size());
300 state.optvec = optvec;
301}
302
304{
306 {
308 };
309};
310
318
319unsigned int Panorama::addImage(const SrcPanoImage &img)
320{
321 unsigned int nr = state.images.size();
322 state.images.push_back(new SrcPanoImage(img));
323 // create empty optimisation vector
324 state.optvec.push_back(std::set<std::string>());
326 return nr;
327}
328
329void Panorama::removeImage(unsigned int imgNr)
330{
331 DEBUG_DEBUG("Panorama::removeImage(" << imgNr << ")");
332 assert(imgNr < state.images.size());
333
334 // remove control points
335 CPVector::iterator it = state.ctrlPoints.begin();
336 while (it != state.ctrlPoints.end()) {
337 if ((it->image1Nr == imgNr) || (it->image2Nr == imgNr)) {
338 // remove point that refernce to imgNr
339 it = state.ctrlPoints.erase(it);
340 } else {
341 // correct point references
342 if (it->image1Nr > imgNr) it->image1Nr--;
343 if (it->image2Nr > imgNr) it->image2Nr--;
344 ++it;
345 }
346 }
347
348 DEBUG_TRACE("Remove variables and image from panorama state")
349 delete state.images[imgNr];
350 state.images.erase(state.images.begin() + imgNr);
351 state.optvec.erase(state.optvec.begin() + imgNr);
352
353 // check if reference image has been moved
357 }
358
362 }
363
364 // change all other (moved) images
365 DEBUG_TRACE("flag moved images as dirty");
366 for (unsigned int i=imgNr; i < state.images.size(); i++) {
368 }
369 m_forceImagesUpdate = true;
370}
371
372
373void Panorama::setImageFilename(unsigned int i, const std::string & fname)
374{
375 DEBUG_ASSERT(i < state.images.size());
376 state.images[i]->setFilename(fname);
378 m_forceImagesUpdate = true;
379}
380
381unsigned int Panorama::addCtrlPoint(const ControlPoint & point )
382{
383 unsigned int nr = state.ctrlPoints.size();
384 state.ctrlPoints.push_back(point);
385 imageChanged(point.image1Nr);
386 imageChanged(point.image2Nr);
388 return nr;
389}
390
391void Panorama::removeCtrlPoint(unsigned int pNr)
392{
393 DEBUG_ASSERT(pNr < state.ctrlPoints.size());
394 ControlPoint & point = state.ctrlPoints[pNr];
395 unsigned int i1 = point.image1Nr;
396 unsigned int i2 = point.image2Nr;
397 state.ctrlPoints.erase(state.ctrlPoints.begin() + pNr);
398
399 // update line control points
401 imageChanged(i1);
402 imageChanged(i2);
404}
405
407{
408 std::set<std::string> listOfCPs;
409 std::set<unsigned int> duplicateCPs;
410 for(unsigned int i=0; i<state.ctrlPoints.size();i++)
411 {
412 std::string s=state.ctrlPoints[i].getCPString();
413 std::pair<std::set<std::string>::iterator,bool> it=listOfCPs.insert(s);
414 if(it.second==false)
415 {
416 duplicateCPs.insert(i);
417 };
418 }
419 //now remove duplicate control points, mark affected images as changed
420 if(!duplicateCPs.empty())
421 {
422 for(std::set<unsigned int>::reverse_iterator it=duplicateCPs.rbegin();it!=duplicateCPs.rend();++it)
423 {
425 imageChanged(cp.image1Nr);
426 imageChanged(cp.image2Nr);
428 };
429 };
431}
432
433
434void Panorama::changeControlPoint(unsigned int pNr, const ControlPoint & point)
435{
436 assert(pNr < state.ctrlPoints.size());
437
438 // change notify for all involved images
439 imageChanged(state.ctrlPoints[pNr].image1Nr);
440 imageChanged(state.ctrlPoints[pNr].image2Nr);
441 imageChanged(point.image1Nr);
442 imageChanged(point.image2Nr);
444
445 state.ctrlPoints[pNr] = point;
447}
448
450{
451 for (CPVector::const_iterator it = state.ctrlPoints.begin();
452 it != state.ctrlPoints.end(); ++it)
453 {
454 imageChanged(it->image1Nr);
455 imageChanged(it->image2Nr);
456 }
457
459
460 for (CPVector::const_iterator it = state.ctrlPoints.begin();
461 it != state.ctrlPoints.end(); ++it)
462 {
463 imageChanged(it->image1Nr);
464 imageChanged(it->image2Nr);
465 }
468}
469
470// close holes in line control points
472{
473 // sort all line control points
474 std::map<int, int> lines;
475 for (CPVector::const_iterator it = state.ctrlPoints.begin();
476 it != state.ctrlPoints.end(); ++it)
477 {
478 if (it->mode > 2)
479 lines[it->mode] = 0;
480 }
481 int i=3;
482 for (std::map<int,int >::iterator it = lines.begin(); it != lines.end(); ++it)
483 {
484 (*it).second = i;
485 i++;
486 }
487
488 for (CPVector::iterator it = state.ctrlPoints.begin();
489 it != state.ctrlPoints.end(); ++it)
490 {
491 if (it->mode > 2) {
492 int newmode = lines[it->mode];
493 if (it->mode != newmode) {
494 it->mode = newmode;
495 imageChanged(it->image1Nr);
496 imageChanged(it->image2Nr);
497 }
498 }
499 }
500}
501
502
503void Panorama::printPanoramaScript(std::ostream & o,
504 const OptimizeVector & optvars,
505 const PanoramaOptions & output,
506 const UIntSet & imgs,
507 bool forPTOptimizer,
508 const std::string & stripPrefix) const
509{
510 // set numeric locale to C, for correct number output
513
514 if (forPTOptimizer) {
515 o << "# PTOptimizer script, written by hugin" << std::endl
516 << std::endl;
517 } else {
518 o << "# hugin project file" << std::endl;
519 o << "#hugin_ptoversion 2" << std::endl;
520 }
521 // output options..
522
523 output.printScriptLine(o, forPTOptimizer);
524
525 // map from script img nr -> pano image nr
526 std::map<unsigned int, unsigned int> imageNrMap;
527 o << std::endl
528 << "# image lines" << std::endl;
529
530 // somewhere to store the v lines, which give the variables to be optimised.
531 std::stringstream vlines;
532 std::stringstream masklines;
533 unsigned int ic = 0;
534 for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
535 ++imgNrIt)
536 {
537 unsigned int imgNr = *imgNrIt;
538 imageNrMap[imgNr] = ic;
539 const SrcPanoImage & img = *state.images[imgNr];
540 VariableMap vars;
541
542 // print special comment line with hugin GUI data
543 o << "#-hugin ";
544 if (img.getCropMode() != BaseSrcPanoImage::NO_CROP) {
545 if (img.getAutoCenterCrop())
546 o << " autoCenterCrop=1";
547 }
548 o << " cropFactor=" << img.getCropFactor() ;
549 if (! img.getActive()) {
550 o << " disabled";
551 }
552 o << std::endl;
553
554 o << "i w" << img.getSize().width() << " h" << img.getSize().height()
555 <<" f" << img.getProjection() << " ";
556
557 // print variables with links
558/* Individually do all the variables specified by each SrcPanoImg variable.
559 * Clear the list after each SrcPanoImg variable.
560 * If there is any links to previous images, write that in the script.
561 * If there are no links to previous images, write the value instead.
562 * Each variable in SrcPanoImg may produce any number of variables in the map,
563 * but the linking properties are shared.
564 * Additionally, when we are writing variables by value which are set to be
565 * optimised, we should remember them so we can write them later as 'v' lines.
566 */
567#define image_variable( name, type, default_value )\
568 PTOVariableConverterFor##name::addToVariableMap(state.images[imgNr]->get##name##IV(), vars);\
569 if (!vars.empty())\
570 {\
571 bool linking = false;\
572 std::size_t link_target;\
573 if (ic!=0)\
574 {\
575 if (state.images[imgNr]->name##isLinked())\
576 {\
577 for (link_target = 0; link_target < imgNr; link_target++)\
578 {\
579 if (set_contains(imgs,link_target) && state.images[imgNr]->name##isLinkedWith(*state.images[link_target]))\
580 {\
581 linking = true;\
582 break;\
583 }\
584 }\
585 }\
586 }\
587 for (VariableMap::const_iterator vit = vars.begin();\
588 vit != vars.end(); ++vit)\
589 {\
590 if (forPTOptimizer && !set_contains(m_ptoptimizerVarNames,vit->first))\
591 continue;\
592 else if (linking)\
593 {\
594 o << vit->first << "=" << imageNrMap[link_target] << " ";\
595 } else {\
596 if (set_contains(optvars[imgNr], vit->first))\
597 {\
598 vlines << "v " << vit->first << imageNrMap[imgNr] << std::endl;\
599 }\
600 if (((vit->first == "a" && set_contains(optvars[imgNr], "a") )|| \
601 (vit->first == "b" && set_contains(optvars[imgNr], "b") )|| \
602 (vit->first == "c" && set_contains(optvars[imgNr], "c") )|| \
603 (vit->first == "TrX" && set_contains(optvars[imgNr], "TrX") )|| \
604 (vit->first == "TrY" && set_contains(optvars[imgNr], "TrY") )|| \
605 (vit->first == "TrZ" && set_contains(optvars[imgNr], "TrZ") )\
606 )\
607 && forPTOptimizer && vit->second.getValue() == 0.0) \
608 {\
609 o << vit->first << 1e-5 << " ";\
610 } else if (( (vit->first == "r" && set_contains(optvars[imgNr], "r") ) || \
611 (vit->first == "p" && set_contains(optvars[imgNr], "p") ) || \
612 (vit->first == "y" && set_contains(optvars[imgNr], "y") ) \
613 )\
614 && forPTOptimizer && fabs(vit->second.getValue()) < 1e-13)\
615 {\
616 o << vit->first << 0 << " ";\
617 } else {\
618 vit->second.print(o) << " ";\
619 }\
620 }\
621 }\
622 }\
623 vars.clear();
624#include "image_variables.h"
625#undef image_variable
626
627 if (img.getCropMode()!=SrcPanoImage::NO_CROP) {
628 // print crop parameters
629 vigra::Rect2D c = img.getCropRect();
630 o << " S" << c.left() << "," << c.right() << "," << c.top() << "," << c.bottom();
631 }
632
633 if (!forPTOptimizer) {
634
635 if (img.getVigCorrMode() != SrcPanoImage::VIGCORR_NONE) {
636 o << " Vm" << img.getVigCorrMode();
637 }
638
639 if (!img.getFlatfieldFilename().empty()) {
640 o << " Vf\"" << img.getFlatfieldFilename() << "\"";
641 }
642 if (img.getResponseType() > 0) {
643 o << " Rt" << img.getResponseType();
644 }
645
646 if(img.hasMasks())
648 }
649
650#if 0
651//panotools paramters, currently not used
652 o << " u" << output.featherWidth
653 << (img.getMorph() ? " o" : "");
654#endif
655 std::string fname = img.getFilename();
656 if (!stripPrefix.empty()) {
657 // strip prefix from image names.
658 // check if the prefix is acutally the same
659 std::string tmp = fname.substr(0,stripPrefix.size());
660 if (tmp.compare(stripPrefix) == 0) {
661 DEBUG_DEBUG("striping " << stripPrefix << " from " << fname);
662 fname = fname.erase(0,stripPrefix.size());
663 DEBUG_DEBUG("after stripping: " << fname);
664 } else {
665 DEBUG_DEBUG(stripPrefix << " does not match " << fname);
666 }
667 }
668 o << " n\"" << fname << "\"" << std::endl;
669 ic++;
670 }
671
672 o << std::endl << std::endl
673 << "# specify variables that should be optimized" << std::endl
674 << vlines.str()
675 << "v" << std::endl; // empty v line to work around libpano13 bug.
676
677 o << std::endl << std::endl
678 << "# control points" << std::endl;
679 for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
680 if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
681 o << "c n" << imageNrMap[it->image1Nr]
682 << " N" << imageNrMap[it->image2Nr]
683 << " x" << it->x1 << " y" << it->y1
684 << " X" << it->x2 << " Y" << it->y2
685 << " t" << it->mode << std::endl;
686 }
687 }
688 o << std::endl;
689
690 if(masklines.str().length()>0)
691 o << "# masks" << std::endl
692 << masklines.str()
693 << std::endl;
694
695 // special line with hugins options.
696 o << "#hugin_optimizeReferenceImage " << output.optimizeReferenceImage << std::endl;
697 o << "#hugin_blender ";
698 switch (output.blendMode) {
700 o << "none" << endl;
701 break;
703 o << "PTblender" << endl;
704 break;
706 o << "smartblend" << endl;
707 break;
709 o << "PTmasker" << endl;
710 break;
712 o << "internal" << endl;
713 break;
714 default:
716 o << "enblend" << endl;
717 break;
718 }
719
720 o << "#hugin_remapper ";
721 switch (output.remapper) {
723 o << "PTmender" << endl;
724 break;
725 default:
727 o << "nona" << endl;
728 break;
729 }
730
731 o << "#hugin_enblendOptions " << output.enblendOptions << endl;
732 o << "#hugin_enfuseOptions " << output.enfuseOptions << endl;
733 o << "#hugin_hdrmergeOptions " << output.hdrmergeOptions << endl;
734 o << "#hugin_verdandiOptions " << output.verdandiOptions << endl;
735 o << "#hugin_edgeFillMode " << static_cast<int>(output.edgeFillMode) << endl;
736 o << "#hugin_edgeFillKeepInput " << (output.keepEdgeFillInput ? "true" : "false") << endl;
737
738 o << "#hugin_outputLDRBlended " << (output.outputLDRBlended ? "true" : "false") << endl;
739 o << "#hugin_outputLDRLayers " << (output.outputLDRLayers ? "true" : "false") << endl;
740 o << "#hugin_outputLDRExposureRemapped " << (output.outputLDRExposureRemapped ? "true" : "false") << endl;
741 o << "#hugin_outputLDRExposureLayers " << (output.outputLDRExposureLayers ? "true" : "false") << endl;
742 o << "#hugin_outputLDRExposureBlended " << (output.outputLDRExposureBlended ? "true" : "false") << endl;
743 o << "#hugin_outputLDRStacks " << (output.outputLDRStacks ? "true" : "false") << endl;
744 o << "#hugin_outputLDRExposureLayersFused " << (output.outputLDRExposureLayersFused ? "true" : "false") << endl;
745 o << "#hugin_outputHDRBlended " << (output.outputHDRBlended ? "true" : "false") << endl;
746 o << "#hugin_outputHDRLayers " << (output.outputHDRLayers ? "true" : "false") << endl;
747 o << "#hugin_outputHDRStacks " << (output.outputHDRStacks ? "true" : "false") << endl;
748
749 o << "#hugin_outputLayersCompression " << output.outputLayersCompression << endl;
750 o << "#hugin_outputImageType " << output.outputImageType << endl;
751 o << "#hugin_outputImageTypeCompression " << output.outputImageTypeCompression << endl;
752 o << "#hugin_outputJPEGQuality " << output.quality << endl;
753 o << "#hugin_outputImageTypeHDR " << output.outputImageTypeHDR << endl;
754 o << "#hugin_outputImageTypeHDRCompression " << output.outputImageTypeHDRCompression << endl;
755
756 o << "#hugin_outputStacksMinOverlap " << std::setprecision(3) << output.outputStacksMinOverlap << endl;
757 o << "#hugin_outputLayersExposureDiff " << std::setprecision(2) << output.outputLayersExposureDiff << endl;
758
759 o << "#hugin_outputRangeCompression " << std::setprecision(2) << output.outputRangeCompression << endl;
760
761 if(optvars==getOptimizeVector())
762 {
763 o << "#hugin_optimizerMasterSwitch " << getOptimizerSwitch() << endl;
764 o << "#hugin_optimizerPhotoMasterSwitch " << getPhotometricOptimizerSwitch() << endl;
765 }
766 else
767 {
768 o << "#hugin_optimizerMasterSwitch 0" << endl;
769 o << "#hugin_optimizerPhotoMasterSwitch 0" << endl;
770 };
771
772 // reset locale
775}
776
777
778void Panorama::printStitcherScript(std::ostream & o,
779 const PanoramaOptions & target,
780 const UIntSet & imgs) const
781{
782 // set numeric locale to C, for correct number output
785
786 o << "# PTStitcher script, written by hugin" << std::endl
787 << std::endl;
788 // output options..
789 target.printScriptLine(o, true);
790 o << std::endl
791 << "# output image lines" << std::endl;
792 for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end(); ++imgNrIt) {
793 unsigned int imgNr = *imgNrIt;
794 const SrcPanoImage & img = *state.images[imgNr];
795// DGSW FIXME - Unreferenced
796// const Lens & lens = state.lenses[lensNr];
797 const VariableMap & vars = state.images[imgNr]->getVariableMap();
798
799 o << "o w" << img.getSize().width() << " h" << img.getSize().height()
800 <<" f" << img.getProjection() << " ";
801 // print variables, without links
802 VariableMap::const_iterator vit;
803 for(vit = vars.begin(); vit != vars.end(); ++vit)
804 {
806 continue;
807 }
808 vit->second.print(o) << " ";
809 }
810#if 0
811// panotools parameters, currently not used
812 o << " u" << img.getFeatureWidth()
813 << (img.getMorph() ? " o" : "");
814#endif
815 o << " n\"" << img.getFilename() << "\"";
816 if (img.getCropMode()!=SrcPanoImage::NO_CROP) {
817 // print crop parameters
818 vigra::Rect2D c = img.getCropRect();
819 o << " S" << c.left() << "," << c.right() << "," << c.top() << "," << c.bottom();
820 }
821 o << std::endl;
822 }
823 o << std::endl;
824
825 // reset locale
828
829}
830
832{
833 if (state.images.empty()) {
834 // force an empty update if all images have been
835 // removed
836 DEBUG_DEBUG("forcing images update, with no images");
837 m_forceImagesUpdate = true;
838 }
839 // remove change notification for nonexisting images from set.
840 UIntSet::iterator uB = changedImages.lower_bound(state.images.size());
841 changedImages.erase(uB,changedImages.end());
842
843 std::stringstream t;
844 copy(changedImages.begin(), changedImages.end(),
845 std::ostream_iterator<unsigned int>(t, " "));
846 DEBUG_TRACE("changed image(s) " << t.str() << " begin");
847 //force update of crops
848 if(!changedImages.empty())
849 {
850 for(UIntSet::iterator it=changedImages.begin();it!=changedImages.end();++it)
851 {
852 //if the projection was changed, we need to update the crop mode
854 //now center the crop if user requested it
855 if(state.images[*it]->getAutoCenterCrop())
856 {
857 centerCrop(*it);
858 };
859 };
860 };
861 //update masks
862 updateMasks();
864 std::list<PanoramaObserver *>::iterator it;
865 for(it = observers.begin(); it != observers.end(); ++it) {
866 DEBUG_TRACE("notifying listener");
867 if (!changedImages.empty() || m_forceImagesUpdate) {
868 (*it)->panoramaImagesChanged(*this, changedImages);
869 }
870 (*it)->panoramaChanged(*this);
871 }
872 // reset changed images
873 changedImages.clear();
874 m_forceImagesUpdate = false;
875 if (!keepDirty) {
876 dirty = true;
878 }
879 DEBUG_TRACE("end");
880}
881
883{
884 DEBUG_ASSERT(imgNr < state.images.size());
885 state.images[imgNr]->setMasks(newMasks);
886 imageChanged(imgNr);
887 m_forceImagesUpdate = true;
888};
889
890void Panorama::transferMask(MaskPolygon mask,unsigned int imgNr, const UIntSet& targetImgs)
891{
892 if(targetImgs.empty())
893 {
894 return;
895 };
897 // clip positive mask to image boundaries or clip region
898 vigra::Rect2D clipRect=vigra::Rect2D(0,0,state.images[imgNr]->getWidth(),state.images[imgNr]->getHeight());
899 if(mask.isPositive())
900 {
901 //clip to crop region only positive masks
902 switch(state.images[imgNr]->getCropMode())
903 {
905 clipRect=clipRect & state.images[imgNr]->getCropRect();
906 if(clipRect.isEmpty())
907 {
908 return;
909 };
910 if(clipRect.width()>10 && clipRect.height()>10)
911 {
912 clipRect.addBorder(-2);
913 };
914 break;
916 {
917 vigra::Rect2D cropRect=state.images[imgNr]->getCropRect();
918 hugin_utils::FDiff2D center((cropRect.left()+cropRect.right())/2.0,(cropRect.top()+cropRect.bottom())/2.0);
919 double radius=((cropRect.width()<cropRect.height())?cropRect.width():cropRect.height())/2.0;
920 if(radius>10)
921 {
922 radius-=2;
923 };
924 if(!transformedMask.clipPolygon(center,radius))
925 {
926 return;
927 };
928 };
929 break;
930 default:
931 if(clipRect.width()>10 && clipRect.height()>10)
932 {
933 clipRect.addBorder(-2);
934 };
935 break;
936 };
937 };
938 int origWindingNumber=transformedMask.getTotalWindingNumber();
939 if(transformedMask.clipPolygon(clipRect))
940 {
941 //increase resolution of positive mask to get better transformation
942 //of vertices, especially for fisheye images
943 transformedMask.subSample(20);
944 //transform polygon to panorama space
947 transformedMask.transformPolygon(trans);
948 for(UIntSet::const_iterator it=targetImgs.begin();it!=targetImgs.end();++it)
949 {
950 if(imgNr==(*it))
951 {
952 continue;
953 };
955 if(state.images[imgNr]->YawisLinkedWith(*(state.images[*it])))
956 {
957 //if yaw is linked, we simply copy the mask
958 targetMask=mask;
959 }
960 else
961 {
965 targetMask.transformPolygon(targetTrans);
966 //check if transformation has produced invalid polygon
967 if(targetMask.getMaskPolygon().size()<3)
968 {
969 continue;
970 };
971 //check if mask was inverted - outside became inside and vice versa
972 //if so, invert mask
973 int newWindingNumber=targetMask.getTotalWindingNumber();
975 };
976 //now clip polygon to image rectangle, add mask only when polygon is inside image
977 if(targetMask.clipPolygon(vigra::Rect2D(-maskOffset,-maskOffset,
978 state.images[*it]->getWidth()+maskOffset,state.images[*it]->getHeight()+maskOffset)))
979 {
981 targetMask.setImgNr(*it);
982 state.images[*it]->addActiveMask(targetMask);
983 };
984 };
985 };
986};
987
989{
990 // update masks
992 for(unsigned int i=0;i<state.images.size();i++)
993 {
994 state.images[i]->clearActiveMasks();
995 if(state.images[i]->hasPositiveMasks())
996 {
997 imgWithPosMasks.insert(i);
998 };
999 };
1001 overlap.limitToImages(imgWithPosMasks);
1002 overlap.calculate(10);
1005 for(unsigned int i=0;i<state.images.size();i++)
1006 {
1007 if(state.images[i]->hasMasks())
1008 {
1009 MaskPolygonVector masks=state.images[i]->getMasks();
1010 for(unsigned int j=0;j<masks.size();j++)
1011 {
1013 {
1014 //this is used for masking in the cp finder, we are consider
1015 //all masks as negative masks, because at this moment
1016 //the final position of the images is not known
1017 switch(masks[j].getMaskType())
1018 {
1021 masks[j].setImgNr(i);
1022 masks[j].setMaskType(MaskPolygon::Mask_negative);
1023 state.images[i]->addActiveMask(masks[j]);
1024 break;
1027 {
1028 //copy mask to all images of the same stack
1030 for(unsigned int k=0;k<getNrOfImages();k++)
1031 {
1032 if(i!=k)
1033 {
1034 if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1035 {
1036 imgStack.insert(k);
1037 };
1038 };
1039 };
1040 masks[j].setImgNr(i);
1041 masks[j].setMaskType(MaskPolygon::Mask_negative);
1042 state.images[i]->addActiveMask(masks[j]);
1043 transferMask(masks[j],i,imgStack);
1044 };
1045 break;
1047 {
1048 unsigned int lensNr=lenses.getPartNumber(i);
1049 //copy masks to all image of the same lens
1050 for(unsigned int k=0;k<getNrOfImages();k++)
1051 {
1052 if(lenses.getPartNumber(k)==lensNr)
1053 {
1054 masks[j].setImgNr(k);
1055 masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
1056 state.images[k]->addActiveMask(masks[j]);
1057 };
1058 };
1059 };
1060 break;
1061 };
1062 }
1063 else
1064 {
1065 switch(masks[j].getMaskType())
1066 {
1068 //negative mask, simply copy mask to active mask
1069 masks[j].setImgNr(i);
1070 state.images[i]->addActiveMask(masks[j]);
1071 break;
1073 //propagate positive mask only if image is active
1074 if(state.images[i]->getActive())
1075 {
1076 UIntSet overlapImgs=overlap.getOverlapForImage(i);
1077 transferMask(masks[j],i,overlapImgs);
1078 };
1079 break;
1081 {
1082 //search all images of the stack
1084 for(unsigned int k=0;k<getNrOfImages();k++)
1085 {
1086 if(i!=k)
1087 {
1088 if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1089 {
1090 imgStack.insert(k);
1091 };
1092 };
1093 };
1094 //copy mask also to the image which contains the mask
1095 masks[j].setImgNr(i);
1096 masks[j].setMaskType(MaskPolygon::Mask_negative);
1097 state.images[i]->addActiveMask(masks[j]);
1098 transferMask(masks[j],i,imgStack);
1099 };
1100 break;
1102 {
1103 //remove all images from the stack from the set
1106 imgStack.erase(i);
1107 for(unsigned int k=0;k<getNrOfImages();k++)
1108 {
1109 if(i!=k)
1110 {
1111 if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1112 {
1113 imgStack.erase(k);
1114 };
1115 };
1116 };
1117 //only leave overlapping images in set
1118 UIntSet imgOverlap=overlap.getOverlapForImage(i);
1119 UIntSet imgs;
1120 std::set_intersection(imgStack.begin(),imgStack.end(),imgOverlap.begin(),imgOverlap.end(),inserter(imgs,imgs.begin()));
1121 //now transfer mask
1122 transferMask(masks[j],i,imgs);
1123 };
1124 break;
1126 {
1127 unsigned int lensNr=lenses.getPartNumber(i);
1128 //copy masks to all image of the same lens
1129 for(unsigned int k=0;k<getNrOfImages();k++)
1130 {
1131 if(lenses.getPartNumber(k)==lensNr)
1132 {
1133 masks[j].setImgNr(k);
1134 masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
1135 state.images[k]->addActiveMask(masks[j]);
1136 };
1137 };
1138 };
1139 break;
1140 };
1141 };
1142 };
1143 };
1144 };
1145};
1146
1147void Panorama::updateCropMode(unsigned int imgNr)
1148{
1149 vigra::Rect2D r=state.images[imgNr]->getCropRect();
1150 if(r.isEmpty() || r==vigra::Rect2D(state.images[imgNr]->getSize()))
1151 {
1152 state.images[imgNr]->setCropMode(SrcPanoImage::NO_CROP);
1153 }
1154 else
1155 {
1156 if (state.images[imgNr]->isCircularCrop())
1157 {
1158 state.images[imgNr]->setCropMode(SrcPanoImage::CROP_CIRCLE);
1159 }
1160 else
1161 {
1162 state.images[imgNr]->setCropMode(SrcPanoImage::CROP_RECTANGLE);
1163 };
1164 };
1165};
1166
1167vigra::Rect2D Panorama::centerCropImage(unsigned int imgNr)
1168{
1169 vigra::Rect2D cropRect;
1170 if(state.images[imgNr]->getCropMode()==SrcPanoImage::NO_CROP)
1171 {
1172 return cropRect;
1173 };
1174 int dx = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().x);
1175 int dy = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().y);
1176 vigra::Point2D center = vigra::Point2D(state.images[imgNr]->getSize().width()/2 + dx, state.images[imgNr]->getSize().height()/2 + dy);
1177
1178 vigra::Diff2D d(state.images[imgNr]->getCropRect().width() / 2, state.images[imgNr]->getCropRect().height() / 2);
1179 cropRect.setUpperLeft( center - d);
1180 cropRect.setLowerRight( center + d);
1181 return cropRect;
1182};
1183
1184void Panorama::centerCrop(unsigned int imgNr)
1185{
1186 vigra::Rect2D cropRect;
1187 if( state.images[imgNr]->getCropMode()!=SrcPanoImage::NO_CROP &&
1188 state.images[imgNr]->getAutoCenterCrop() &&
1189 !(state.images[imgNr]->getCropRect().isEmpty())
1190 )
1191 {
1193 if(!cropRect.isEmpty())
1194 {
1195 state.images[imgNr]->setCropRect(cropRect);
1196 imageChanged(imgNr);
1197 };
1198 };
1199 for (std::size_t i = 0; i < getNrOfImages(); i++)
1200 {
1201 if(i==imgNr)
1202 {
1203 continue;
1204 };
1205 if (state.images[imgNr]->RadialDistortionCenterShiftisLinkedWith(*state.images[i]))
1206 {
1207 if( state.images[i]->getCropMode()!=SrcPanoImage::NO_CROP &&
1208 state.images[i]->getAutoCenterCrop() &&
1209 !(state.images[i]->getCropRect().isEmpty())
1210 )
1211 {
1213 if(!cropRect.isEmpty())
1214 {
1215 state.images[i]->setCropRect(cropRect);
1216 imageChanged(i);
1217 };
1218 };
1219 };
1220 };
1221};
1222
1223void UpdateOptVectorSet(std::set<std::string>& imgVar, const std::string& var, const bool opt)
1224{
1225 if(opt)
1226 {
1227 imgVar.insert(var);
1228 }
1229 else
1230 {
1231 imgVar.erase(var);
1232 };
1233};
1234
1235std::set<size_t> Panorama::getRefImages()
1236{
1237 unsigned int refImg = getOptions().optimizeReferenceImage;
1238 std::set<size_t> refImgs;
1239 refImgs.insert(refImg);
1240 const HuginBase::SrcPanoImage & refImage = getImage(refImg);
1241 for (size_t imgNr = 0; imgNr < getNrOfImages(); imgNr++)
1242 {
1243 if(imgNr!=refImg)
1244 {
1246 if (refImage.YawisLinkedWith(compImage))
1247 {
1248 refImgs.insert(imgNr);
1249 };
1250 };
1251 };
1252 return refImgs;
1253};
1254
1256{
1257 // count number of vertical/horizontal control points
1258 int nHCP = 0;
1259 int nVCP = 0;
1260 const CPVector & cps = getCtrlPoints();
1261 for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1262 {
1263 // control points
1264 if (it->mode == ControlPoint::X)
1265 {
1266 nVCP++;
1267 }
1268 else
1269 {
1270 if (it->mode == ControlPoint::Y)
1271 {
1272 nHCP++;
1273 }
1274 };
1275 };
1276
1277 // try to select sensible position optimisation parameters,
1278 // dependent on output projection
1279 linkRefImgsYaw=false;
1280 linkRefImgsPitch=false;
1281 linkRefImgsRoll=false;
1282 switch (getOptions().getProjection())
1283 {
1285 linkRefImgsRoll = nVCP + nHCP >= 1;
1286 linkRefImgsYaw = nVCP + nHCP >= 3 && nVCP >= 1 && nHCP >= 1;
1287 linkRefImgsPitch = nVCP + nHCP >= 2;
1288 break;
1291 linkRefImgsPitch = nHCP + nVCP > 1;
1292 linkRefImgsRoll = nHCP + nVCP >= 1;
1293 break;
1294 default:
1295 break;
1296 };
1297};
1298
1300{
1301 if(state.images.empty())
1302 {
1303 return;
1304 };
1305 if(state.optSwitch!=0)
1306 {
1307 std::set<size_t> refImgs=getRefImages();
1308 bool linkRefImgsYaw=false;
1309 bool linkRefImgsPitch=false;
1310 bool linkRefImgsRoll=false;
1312
1313 for(size_t i=0;i<getNrOfImages();i++)
1314 {
1316 {
1318 {
1322 //don't optimize translation parameters of anchor
1323 UpdateOptVectorSet(state.optvec[i],"TrX",false);
1324 UpdateOptVectorSet(state.optvec[i],"TrY",false);
1325 UpdateOptVectorSet(state.optvec[i],"TrZ",false);
1326 }
1327 else
1328 {
1329 UpdateOptVectorSet(state.optvec[i],"y",true);
1330 UpdateOptVectorSet(state.optvec[i],"p",true);
1331 UpdateOptVectorSet(state.optvec[i],"r",true);
1335 };
1336 }
1337 else
1338 {
1339 UpdateOptVectorSet(state.optvec[i],"y",false);
1340 UpdateOptVectorSet(state.optvec[i],"p",false);
1341 UpdateOptVectorSet(state.optvec[i],"r",false);
1342 UpdateOptVectorSet(state.optvec[i],"Trx",false);
1343 UpdateOptVectorSet(state.optvec[i],"Try",false);
1344 UpdateOptVectorSet(state.optvec[i],"Trz",false);
1345 };
1352 //shear and translation plane not include in master switches
1353 UpdateOptVectorSet(state.optvec[i],"g",false);
1354 UpdateOptVectorSet(state.optvec[i],"t",false);
1355 UpdateOptVectorSet(state.optvec[i],"Tpy", false);
1356 UpdateOptVectorSet(state.optvec[i],"Tpp", false);
1357 };
1358 };
1359 if(state.optPhotoSwitch!=0)
1360 {
1361 for(size_t i=0;i<getNrOfImages();i++)
1362 {
1376 };
1377 };
1378};
1379
1380void Panorama::swapImages(unsigned int img1, unsigned int img2)
1381{
1382 DEBUG_TRACE("swapping images " << img1 << ", " << img2);
1383 DEBUG_ASSERT(img1 < state.images.size());
1384 DEBUG_ASSERT(img2 < state.images.size());
1385
1386 // first, swap image pointers in the list.
1387 SrcPanoImage * pimg1 = state.images[img1];
1388 state.images[img1] = state.images[img2];
1389 state.images[img2] = pimg1;
1390
1391 // update control points
1392 for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
1393 int n1 = (*it).image1Nr;
1394 int n2 = (*it).image2Nr;
1395 if ((*it).image1Nr == img1) {
1396 n1 = img2;
1397 } else if ((*it).image1Nr == img2) {
1398 n1 = img1;
1399 }
1400 if ((*it).image2Nr == img1) {
1401 n2 = img2;
1402 } else if ((*it).image2Nr == img2) {
1403 n2 = img1;
1404 }
1405 (*it).image1Nr = n1;
1406 (*it).image2Nr = n2;
1407 }
1408
1409 // update panorama options
1410 if (state.options.colorReferenceImage == img1) {
1412 } else if (state.options.colorReferenceImage == img2) {
1414 }
1415 if (state.options.optimizeReferenceImage == img1) {
1417 } else if (state.options.optimizeReferenceImage == img2) {
1419 }
1420 imageChanged(img1);
1421 imageChanged(img2);
1422}
1423
1424void Panorama::moveImage(size_t img1, size_t img2)
1425{
1426 //generate a vector with the translated image numbers
1427 std::vector<size_t> imgList(getNrOfImages(),-1);
1428 for(size_t i=0; i<getNrOfImages(); i++)
1429 {
1430 imgList[i]=i;
1431 };
1432 imgList.erase(imgList.begin()+img1);
1433 if(img2<imgList.size())
1434 {
1435 imgList.insert(imgList.begin()+img2, img1);
1436 }
1437 else
1438 {
1439 imgList.push_back(img1);
1440 };
1441 //generate map for translation of old -> new image numbers
1442 std::map<size_t,size_t> imgMap;
1443 for(size_t i=0; i<imgList.size(); i++)
1444 {
1445 imgMap[imgList[i]]=i;
1446 };
1447 // now generate the new images list
1448 std::vector<SrcPanoImage *> new_images(getNrOfImages());
1449 for(size_t i=0; i<imgList.size(); i++)
1450 {
1452 if(i!=imgList[i])
1453 {
1455 };
1456 };
1458
1459 // update optimize vector
1461 for(size_t i=0; i<state.optvec.size(); i++)
1462 {
1463 newOptVec.push_back(state.optvec[imgList[i]]);
1464 };
1466
1467 // update control points
1468 for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
1469 {
1470 (*it).image1Nr = imgMap[(*it).image1Nr];
1471 (*it).image2Nr = imgMap[(*it).image2Nr];
1472 // mirror points if first image number is higher than second
1473 if ((*it).image1Nr > (*it).image2Nr)
1474 {
1475 (*it).mirror();
1476 };
1477 }
1478
1479 // update panorama options
1482};
1483
1485{
1486 if(memento==NULL)
1487 return false;
1488
1490
1491 try {
1492
1493 mymemento = dynamic_cast<const PanoramaMemento*>(memento);
1494
1495 } catch (std::bad_cast&) {
1496// std::cerr << "Incompatible memento type." << std::endl;
1497 DEBUG_DEBUG("Incompatible memento type.");
1498 return false;
1499 }
1500
1502 return true;
1503}
1504
1505
1508{
1509 DEBUG_TRACE("");
1510
1511 // remove old content.
1512 reset();
1513 DEBUG_DEBUG("nr of images in memento:" << memento.images.size());
1514
1515 state = memento;
1516 updateMasks();
1517 unsigned int nNewImages = state.images.size();
1518 DEBUG_DEBUG("nNewImages:" << nNewImages);
1519
1520 // send changes for all images
1521 for (unsigned int i = 0; i < nNewImages; i++) {
1522 imageChanged(i);
1523 }
1524}
1525
1530
1532{
1533 if (state.options.optimizeReferenceImage != opt.optimizeReferenceImage) {
1534 imageChanged(opt.optimizeReferenceImage);
1536 }
1537
1538 if (state.options.colorReferenceImage != opt.colorReferenceImage) {
1539 imageChanged(opt.colorReferenceImage);
1541 }
1542
1543 state.options = opt;
1544}
1545
1547{
1548 observers.push_back(o);
1549}
1550
1552{
1553 size_t oldCount=observers.size();
1554 observers.remove(o);
1555 return observers.size()!=oldCount;
1556}
1557
1559{
1560 observers.clear();
1561}
1562
1564{
1565 return !changedImages.empty();
1566}
1567
1568void Panorama::imageChanged(unsigned int imgNr)
1569{
1570// DEBUG_TRACE("adding image " << imgNr);
1571 changedImages.insert(imgNr);
1572 assert(changedImages.find(imgNr) != changedImages.end());
1573}
1574
1575void Panorama::activateImage(unsigned int imgNr, bool active)
1576{
1577 assert(imgNr < state.images.size());
1578 if (state.images[imgNr]->getActive() != active)
1579 {
1580 state.images[imgNr]->setActive(active);
1581 imageChanged(imgNr);
1582 }
1583}
1584
1586{
1588
1589 for (unsigned int i = 0; i < state.images.size(); i++) {
1590 if (state.images[i]->getActive())
1591 {
1592 activeImgs.insert(i);
1593 }
1594 }
1595 return activeImgs;
1596}
1597
1598const std::string Panorama::getICCProfileDesc() const
1599{
1600 return state.iccProfileDesc;
1601};
1602
1603void Panorama::setICCProfileDesc(const std::string& newDesc)
1604{
1606};
1607
1608const int Panorama::getNrOfBands() const
1609{
1610 return state.bands;
1611};
1612
1614{
1616};
1617
1618//==== internal function for variable management
1619
1621{
1622 DEBUG_ASSERT(imgNr < state.images.size());
1623 return *state.images[imgNr];
1624}
1625
1626void Panorama::setSrcImage(unsigned int imgNr, const SrcPanoImage & img)
1627{
1628 DEBUG_ASSERT(imgNr < state.images.size());
1629
1630 /* Copy the variables. We don't assign directly so we can do the changes to
1631 * any linked variables.
1632 */
1633 SrcPanoImage *dest = state.images[imgNr];
1634 #define image_variable( name, type, default_value ) \
1635 dest->set##name (img.get##name());
1636 #include "image_variables.h"
1637 #undef image_variable
1638
1639 // mark the potentially changed images.
1640#define image_variable( name, type, default_value ) \
1641 for (std::size_t i = 0; i < getNrOfImages(); i++)\
1642 {\
1643 if(state.images[imgNr]->name##isLinkedWith(*state.images[i]))\
1644 {\
1645 imageChanged(i);\
1646 }\
1647 }
1648#include "image_variables.h"
1649#undef image_variable
1650}
1651
1652
1654{
1655 Panorama pano(*this);
1656 pano.observers.clear();
1657 return pano;
1658}
1659
1661{
1663 // copy data except for listners
1664
1665 // bits that don't change in the subset.
1667 subset.dirty = dirty;
1668 subset.state.options = state.options;
1669 subset.state.optSwitch=0;
1670 subset.state.optPhotoSwitch=0;
1671 subset.state.needsOptimization = state.needsOptimization;
1672 subset.changedImages = changedImages;
1673 subset.m_forceImagesUpdate = m_forceImagesUpdate;
1674 subset.m_ptoptimizerVarNames = m_ptoptimizerVarNames;
1675
1676 // check optimizer vector and update if necessary
1677 // optvec contains only the variables for the first image,
1678 // but if the first image is not in the subset then the variables is missing also for the next image
1679 // which can be in optvec
1682 {
1684 fill_set(allImgs, 0, getNrOfImages() - 1);
1685 std::set_difference(allImgs.begin(), allImgs.end(), imgs.begin(), imgs.end(), std::inserter(unusedImgs, unusedImgs.end()));
1686 };
1687 if (!unusedImgs.empty())
1688 {
1689 for (auto& i : unusedImgs)
1690 {
1691 for (auto& var : state.optvec[i])
1692 {
1693 for (auto& j : imgs)
1694 {
1695#define image_variable(name, type, default_value)\
1696 if (PTOVariableConverterFor##name::checkApplicability(var))\
1697 {\
1698 if (state.images[i]->name##isLinkedWith(*state.images[j]))\
1699 {\
1700 internalOptvec[j].insert(var);\
1701 break;\
1702 }\
1703 }
1704#include "image_variables.h"
1705#undef image_variable
1706 };
1707 };
1708 };
1709 };
1710
1711 // create image number map.
1712 std::map<unsigned int, unsigned int> imageNrMap;
1713
1714 // copy image information
1715 unsigned int ic = 0;
1716 for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
1717 ++imgNrIt)
1718 {
1719 subset.state.images.push_back(new SrcPanoImage(*state.images[*imgNrIt]));
1720 subset.state.optvec.push_back(internalOptvec[*imgNrIt]);
1721 imageNrMap[*imgNrIt] = ic;
1722 ic++;
1723 }
1724
1725 // recreate links between image variables.
1726 ic = 0;
1727 for (UIntSet::const_iterator i = imgs.begin(); i != imgs.end(); ++i)
1728 {
1729 unsigned int jc = ic + 1;
1730 UIntSet::const_iterator j = i;
1731 for (++j; j != imgs.end(); ++j)
1732 {
1737#define image_variable( name, type, default_value )\
1738 if (state.images[*i]->name##isLinkedWith(*state.images[*j]))\
1739 {\
1740 subset.state.images[ic]->link##name(subset.state.images[jc]);\
1741 }
1742#include "image_variables.h"
1743#undef image_variable
1744 jc++;
1745 }
1746 ic++;
1747 }
1748
1749 // select and translate control points.
1750 subset.state.ctrlPoints.clear();
1751 for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
1752 if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
1753 ControlPoint pnt = *it;
1754 pnt.image1Nr = imageNrMap[pnt.image1Nr];
1755 pnt.image2Nr = imageNrMap[pnt.image2Nr];
1756 subset.state.ctrlPoints.push_back(pnt);
1757 }
1758 }
1759
1760 //update optimizeReferenceImage and colorReferenceImage number
1761 unsigned int newRefImg=0;
1762 std::map<unsigned int, unsigned int>::iterator it=imageNrMap.find(state.options.optimizeReferenceImage);
1763 if(it!=imageNrMap.end())
1764 {
1765 newRefImg=it->second;
1766 };
1768 subset.state.options.optimizeReferenceImage=newRefImg;
1769 newRefImg=0;
1770 if(it!=imageNrMap.end())
1771 {
1772 newRefImg=it->second;
1773 }
1774 subset.state.options.colorReferenceImage=newRefImg;
1775 return subset;
1776}
1777
1778int FindStackNumberForImage(const std::vector<UIntSet>& imageGroups, const unsigned int imgNr)
1779{
1780 for (size_t i = 0; i < imageGroups.size(); ++i)
1781 {
1782 if (set_contains(imageGroups[i], imgNr))
1783 {
1784 return i;
1785 };
1786 };
1787 return -1;
1788};
1789
1791{
1792 const CPVector cps = getCtrlPoints();
1794
1795 // remove all connected images, keep only a single image for each connected stack
1796 imageGroups.clear();
1797 std::vector<bool> visitedImages(getNrOfImages(), false);
1798 for (size_t i = 0; i < getNrOfImages(); ++i)
1799 {
1800 if (visitedImages[i])
1801 {
1802 continue;
1803 };
1804 const SrcPanoImage& img1 = getImage(i);
1805 UIntSet imgs;
1806 imgs.insert(i);
1807 visitedImages[i] = true;
1808 if (img1.YawisLinked())
1809 {
1810 for (size_t j = i + 1; j < getNrOfImages(); ++j)
1811 {
1812 if (img1.YawisLinkedWith(getImage(j)))
1813 {
1814 imgs.insert(j);
1815 visitedImages[j] = true;
1816 }
1817 }
1818 };
1819 imageGroups.push_back(imgs);
1820 };
1822 for (size_t i = 0; i < imageGroups.size(); ++i)
1823 {
1824 singleStackImgs.insert(*imageGroups[i].begin());
1825 };
1826 // new generate subpano
1828 // translate now reference image
1829 int newRefImage = FindStackNumberForImage(imageGroups, getOptions().optimizeReferenceImage);
1830 if (newRefImage != -1)
1831 {
1832 PanoramaOptions opts = subPano->getOptions();
1834 subPano->setOptions(opts);
1835 };
1836 // remove all vertical and horizontal cp, also remap all cps to new subpano
1837 // all cps from removed images will be mapped to first image of corresponding stack
1838 for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1839 {
1840 if (it->mode == ControlPoint::X_Y)
1841 {
1842 ControlPoint cp(*it);
1845 if (newImg1 != -1 && newImg2 != -1 && newImg1 != newImg2)
1846 {
1847 cp.image1Nr = newImg1;
1848 cp.image2Nr = newImg2;
1849 newCP.push_back(cp);
1850 };
1851 };
1852 };
1853 subPano->setCtrlPoints(newCP);
1854 return subPano;
1855}
1856
1858{
1859 if(newPano.getNrOfImages()>0)
1860 {
1861 std::vector<unsigned int> new_image_nr(newPano.getNrOfImages());
1864 const size_t oldImgNumber = getNrOfImages();
1867 //add only new images
1868 for(unsigned int i=0;i<newPano.getNrOfImages();i++)
1869 {
1870 std::string filename=newPano.getImage(i).getFilename();
1871 bool found=false;
1872 for(unsigned int j=0;j<getNrOfImages();j++)
1873 {
1874 if(getImage(j).getFilename()==filename)
1875 {
1876 //image is already in panorama, we remember the image nr
1877 found=true;
1878 new_image_nr[i]=j;
1879 imgsAlreadyInPano.insert(i);
1880 // now check if we have to update the masks
1882 HuginBase::MaskPolygonVector masksNew=newPano.getImage(i).getMasks();
1883 if(!masksNew.empty())
1884 {
1885 for(unsigned int k=0;k<masksNew.size();k++)
1886 {
1887 bool usedMasks=false;
1888 unsigned int l=0;
1889 while((!usedMasks) && l<masksOld.size())
1890 {
1892 l++;
1893 };
1894 if(!usedMasks)
1895 masksOld.push_back(masksNew[k]);
1896 };
1898 };
1899 break;
1900 };
1901 };
1902 if(!found)
1903 {
1904 //new image found, read EXIF data and add it
1905 SrcPanoImage newImg(newPano.getImage(i));
1906 newImg.readEXIF();
1908 imgsCheckLens.insert(i);
1909 //copy also optimise vector
1910 optVec.push_back(optVecNew[i]);
1911 };
1912 };
1914 // check and create lens for new added images
1918 if (!imgsAlreadyInPano.empty())
1919 {
1920 for (auto img : imgsAlreadyInPano)
1921 {
1922 const size_t initialLensNumber = newLenses.getPartNumber(img);
1923 const size_t newLensNumber = oldLenses.getPartNumber(new_image_nr[img]);
1924 // create copy of UIntSet, because we can modifying the set in the for loop
1925 // and this invalidates the iterators
1927 for (auto j : imgs)
1928 {
1930 {
1931 oldLenses.switchParts(new_image_nr[j], newLensNumber);
1932 imgsCheckLens.erase(j);
1934 };
1935 };
1936 lensImgs[initialLensNumber].erase(img);
1937 };
1938 };
1939 if (!imgsCheckLens.empty())
1940 {
1941 // find first lens not already handled
1942 size_t i = 0;
1943 while (i < lensImgs.size() && lensImgs[i].empty())
1944 {
1945 i++;
1946 };
1947 if (i < lensImgs.size())
1948 {
1949 const HuginBase::SrcPanoImage& srcImage = getImage(new_image_nr[*lensImgs[i].begin()]);
1950 size_t matchingLensNumber = -1;
1951 for (size_t j = 0; j < oldImgNumber; ++j)
1952 {
1954 if (compareImage.getSize() == srcImage.getSize() &&
1955 compareImage.getExifModel() == srcImage.getExifModel() &&
1956 compareImage.getExifMake() == srcImage.getExifMake() &&
1957 compareImage.getExifFocalLength() == srcImage.getExifFocalLength())
1958 {
1959 matchingLensNumber = oldLenses.getPartNumber(j);
1960 break;
1961 };
1962 };
1963 // we found a matching lens
1964 if (matchingLensNumber >= 0)
1965 {
1966 for (size_t j : lensImgs[i])
1967 {
1969 };
1970 };
1971 };
1972 };
1973 // recreate links between image variables.
1974 for (unsigned int i=0; i<newPano.getNrOfImages(); i++)
1975 {
1976 for(unsigned int j=i+1;j<newPano.getNrOfImages();j++)
1977 {
1978 const HuginBase::SrcPanoImage &img=newPano.getImage(i);
1979#define image_variable( name, type, default_value )\
1980 if(img.name##isLinkedWith(newPano.getImage(j)))\
1981 {\
1982 linkImageVariable##name(new_image_nr[i],new_image_nr[j]);\
1983 };
1985#undef image_variable
1986 }
1987 }
1988 //now translate cp
1989 CPVector cps=newPano.getCtrlPoints();
1990 const int nextLineCPOffset = getNextCPTypeLineNumber() - 3;
1991 for(unsigned int i=0;i<cps.size();i++)
1992 {
1993 // special treatment of line control points
1994 if (cps[i].mode > 2)
1995 {
1997 new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode + nextLineCPOffset));
1998 }
1999 else
2000 {
2001 // normal, horizontal and vertical cp keep their mode
2003 new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode));
2004 };
2005 };
2007 };
2008};
2009
2011{
2012 int t=0;
2013 for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
2014 {
2015 t = std::max(t, it->mode);
2016 }
2017 if (t <= 2) {
2018 t=2;
2019 }
2020 return t+1;
2021}
2022
2023bool Panorama::ReadPTOFile(const std::string& filename, const std::string& prefix)
2024{
2025 // check if filename is an image file
2026 if (vigra::isImage(filename.c_str()))
2027 {
2028 std::cerr << "file \"" << filename << "\" seems to be an image file and not a PTO file." << std::endl;
2029 return false;
2030 };
2031 // open stream and check
2032 std::ifstream dataInput(filename.c_str());
2033 if (!dataInput.good() || dataInput.eof())
2034 {
2035 std::cerr << "could not open script : " << filename << std::endl;
2036 return false;
2037 };
2038 // finally read pto file
2039 PanoramaMemento newPano;
2040 int ptoVersion;
2041 const bool result = newPano.loadPTScript(dataInput, ptoVersion, prefix);
2042 // close stream
2043 dataInput.close();
2044 if (result)
2045 {
2046 // reading was sucessful, update current object
2047 this->setFilePrefix(prefix);
2048 this->setMemento(newPano);
2049 return true;
2050 }
2051 else
2052 {
2053 // failed to parse pto file
2054 std::cerr << "error while parsing panos tool script: " << filename << std::endl;
2055 return false;
2056 };
2057}
2058
2059bool Panorama::WritePTOFile(const std::string& filename, const std::string& prefix)
2060{
2061 std::ofstream outputStream(filename, std::ofstream::out | std::ofstream::trunc);
2062 if (outputStream.good())
2063 {
2065 if (getNrOfImages() > 0)
2066 {
2067 fill_set(allImages, 0, getNrOfImages() - 1);
2068 };
2069 try
2070 {
2072 }
2073 catch (std::exception& e)
2074 {
2075 // error occured, return false
2076 outputStream.close();
2077 std::cerr << "Failed to write project file " << filename << " (error: " << e.what() << ")" << std::endl;
2078 return false;
2079 };
2080 outputStream.close();
2081 return true;
2082 }
2083 else
2084 {
2085 std::cerr << "Failed to write project file " << filename << " (Can not create file.)" << std::endl;
2086 return false;
2087 };
2088}
2089
2091{
2093 for(unsigned int i=0;i<getNrOfImages();i++)
2094 {
2096 {
2097 state.images[i]->setWhiteBalanceRed(redFactor * state.images[i]->getWhiteBalanceRed());
2098 state.images[i]->setWhiteBalanceBlue(blueFactor * state.images[i]->getWhiteBalanceBlue());
2099 modified_images.insert(i);
2100 imageChanged(i);
2101 //check linked images and remember for later
2102 if(state.images[i]->WhiteBalanceRedisLinked())
2103 {
2104 if(i+1<getNrOfImages())
2105 {
2106 for(unsigned int j=i+1;j<getNrOfImages();j++)
2107 {
2108 if(state.images[i]->WhiteBalanceRedisLinkedWith(*(state.images[j])))
2109 {
2110 modified_images.insert(j);
2111 imageChanged(j);
2112 };
2113 };
2114 };
2115 };
2116 };
2117 };
2118};
2119
2121{
2122 if (state.images.empty())
2123 {
2124 return 0;
2125 }
2126 double minEv = 1000;
2127 double maxEv = -1000;
2128 for (size_t i = 0; i < state.images.size(); i++)
2129 {
2130 const double ev = state.images[i]->getExposureValue();
2131 minEv = std::min(minEv, ev);
2132 maxEv = std::max(maxEv, ev);
2133 };
2134 return maxEv - minEv;
2135};
2136
2138{
2139 if (state.images.empty())
2140 {
2141 return false;
2142 }
2143 // this algorithm is based on panostart by Bruno Postle
2144 // bracketed pano has at least a dynamic range of 1.2 ev values (corresponds to bracket with +-2/3)
2145 if (getMaxExposureDifference()<1.2)
2146 {
2147 return false;
2148 }
2149 //now get all exposure layers
2151 fill_set(allImg, 0, state.images.size() - 1);
2153 //if there is only one unique exposure value then there are no stacks
2154 if (evValues.size()<2)
2155 {
2156 return false;
2157 }
2158 //if number of unique exposure values is equal the number of images then there are no stacks
2159 if (evValues.size() == state.images.size())
2160 {
2161 return false;
2162 }
2163 //if number of images is not a multiple of number of unique exposure values
2164 //then the stacks are incomplete, skipping
2165 if (state.images.size() % evValues.size() != 0)
2166 {
2167 return false;
2168 }
2169 //check that all exposure layer have the same size indicating that all stacks are of the same size
2170 for (size_t layerNr = 1; layerNr < evValues.size(); ++layerNr)
2171 {
2172 if (evValues[0].size() != evValues[layerNr].size())
2173 {
2174 return false;
2175 };
2176 };
2177 return true;
2178};
2179
2182{
2183 // we need at least 2 images
2184 if (state.images.size()<=1)
2185 {
2186 return;
2187 };
2188 // unlink all existing stacks
2189 for (size_t imgNr = 0; imgNr < state.images.size(); imgNr++)
2190 {
2191 if (state.images[imgNr]->YawisLinked())
2192 {
2196 unlinkImageVariableX(imgNr);
2197 unlinkImageVariableY(imgNr);
2198 unlinkImageVariableZ(imgNr);
2201 };
2202 if (state.images[imgNr]->StackisLinked())
2203 {
2205 };
2206 };
2207 // now link all possible stacks
2209 fill_set(allImg, 0, state.images.size() - 1);
2211 if (evValues.empty())
2212 {
2213 return;
2214 };
2215 unsigned int imgNr = 0;
2216 for (size_t i = 1; i<state.images.size(); i++)
2217 {
2218 if (set_contains(evValues[0], i))
2219 {
2220 imgNr = i;
2221 }
2222 else
2223 {
2224 linkImageVariableStack(imgNr, i);
2225 if (linkPosition)
2226 {
2227 linkImageVariableYaw(imgNr, i);
2228 linkImageVariablePitch(imgNr, i);
2229 linkImageVariableRoll(imgNr, i);
2230 linkImageVariableX(imgNr, i);
2231 linkImageVariableY(imgNr, i);
2232 linkImageVariableZ(imgNr, i);
2235 };
2236 };
2237 };
2238};
2239
2241{
2242 // Use the assignment operator to get the work done: see the next function.
2243 *this = data;
2244}
2245
2247{
2248 // Copy the PanoramaMemento.
2249
2250 // Don't do anything in the case of self assignment. This is important as we
2251 // are about to delete the image information.
2252 if (&data == this)
2253 {
2254 return *this;
2255 }
2256
2257 // Remove any images we currently had.
2259 // copy image variables
2260 for (std::vector<SrcPanoImage *>::const_iterator it = data.images.begin();
2261 it != data.images.end(); ++it)
2262 {
2263 images.push_back(new SrcPanoImage(*(*it)));
2264 }
2265 // Copies of SrcPanoImage's variables aren't linked, so we have to create
2266 // new links in the same pattern.
2272 std::size_t num_imgs = images.size();
2273 for (std::size_t i = 0; i < num_imgs; i++)
2274 {
2275 // copy this image's links.
2276 // links to lower numbered images will have already been spotted, since
2277 // they are bi-directional.
2278 for (std::size_t j = i + 1; j < num_imgs; j++)
2279 {
2280#define image_variable( name, type, default_value )\
2281 if (data.images[i]->name##isLinkedWith(*data.images[j]))\
2282 {\
2283 images[i]->link##name(images[j]);\
2284 }
2285#include "image_variables.h"
2286#undef image_variable
2287 }
2288 }
2289
2290 ctrlPoints = data.ctrlPoints;
2292 bands = data.bands;
2293
2294 options = data.options;
2295
2296 optSwitch = data.optSwitch;
2298 optvec = data.optvec;
2299
2301
2302 return *this;
2303}
2304
2309
2311{
2312 // delete all the images pointed to by the images vector.
2313 for (std::vector<SrcPanoImage *>::iterator it = images.begin();
2314 it != images.end(); ++it)
2315 {
2316 delete *it;
2317 }
2318 // now clear the pointers themselves.
2319 images.clear();
2320}
2321
2322bool PanoramaMemento::loadPTScript(std::istream &i, int & ptoVersion, const std::string &prefix)
2323{
2324 DEBUG_TRACE("");
2325 // set numeric locale to C, for correct number output
2326 char * p = setlocale(LC_NUMERIC,NULL);
2327 char * old_locale = strdup(p);
2328 setlocale(LC_NUMERIC,"C");
2329 std::string line;
2330 // list of all control characters (char 0 .. 31), except tab stop 0x09 and carriage return 0x0d, otherwise pto files from Windows will be rejected
2331 const std::string controlCharacters { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
2332
2333 // vector with the different information lines about images
2334 std::vector<PTScriptParsing::ImgInfo> oImgInfo;
2335 std::vector<PTScriptParsing::ImgInfo> iImgInfo;
2336 // strange comment information.
2337 std::vector<PTScriptParsing::ImgInfo> cImgInfo;
2338 // hugin additional information
2339 std::vector<PTScriptParsing::ImgInfo> huginImgInfo;
2340 // vector with readed masks
2343
2344 // indicate lines that should be skipped for whatever reason
2345 bool skipNextLine = false;
2346
2347 bool PTGUIScriptFile = false;
2348 int PTGUIScriptVersion = 0;
2349 // PTGui lens line detected
2350 int ctrlPointsImgNrOffset = 0;
2351 bool PTGUILensLine = false;
2352
2353 bool PTGUILensLoaded = false;
2355
2356 // set new options to some sensible default.
2357 options.reset();
2358 options.tiff_saveROI = false;
2359
2360 ptoVersion = 1;
2361
2362 bool firstOptVecParse = true;
2363 unsigned int lineNr = 0;
2364 while (i.good()) {
2365 std::getline(i, line);
2366 if (line.find_first_of(controlCharacters, 0) != std::string::npos)
2367 {
2368 // we found a binary character /control sequence in the text
2369 // this is not allowed in a pto file
2370 // reset locale
2373 // stop processing
2374 return false;
2375 };
2376 lineNr++;
2377 DEBUG_DEBUG(lineNr << ": " << line);
2378 if (skipNextLine) {
2379 skipNextLine = false;
2380 continue;
2381 }
2382 //skip emtpy lines
2383 if(line.empty())
2384 continue;
2385 // check for a known line
2386 switch(line[0]) {
2387 case 'p':
2388 {
2389 DEBUG_DEBUG("p line: " << line);
2390 int i;
2391 if (PTScriptParsing::getIntParam(i, line, "f"))
2393 unsigned int w;
2394 if (PTScriptParsing::getIntParam(w, line, "w"))
2396 double v;
2397 if (PTScriptParsing::getDoubleParam(v, line, "v"))
2398 options.setHFOV(v, false);
2399 int height;
2400 if (PTScriptParsing::getIntParam(height, line, "h"))
2401 options.setHeight(height);
2402
2403 double newE;
2404 if (PTScriptParsing::getDoubleParam(newE, line, "E"))
2406 int ar=0;
2407 if (PTScriptParsing::getIntParam(ar, line, "R"))
2409
2410 std::string format;
2411 if (PTScriptParsing::getPTParam(format, line, "T"))
2413
2414 if (PTScriptParsing::getPTParam(format, line, "S")) {
2415 int left, right, top, bottom;
2416 int n = sscanf(format.c_str(), "%d,%d,%d,%d", &left, &right, &top, &bottom);
2417 if (n == 4) {
2418 options.setROI(vigra::Rect2D(left, top, right, bottom));
2419 } else {
2420 DEBUG_WARN("Could not parse crop string: " << format);
2421 }
2422 }
2423
2424 // parse projection parameters
2425 if (PTScriptParsing::getPTParam(format, line, "P")) {
2426 char * tstr = strdup(format.c_str());
2427 std::vector<double> projParam;
2428 char * b = strtok(tstr, " \"");
2429 if (b != NULL) {
2430 while (b != NULL) {
2431 double tempDbl;
2432 if (sscanf(b, "%lf", &tempDbl) == 1) {
2433 projParam.push_back(tempDbl);
2434 b = strtok(NULL, " \"");
2435 }
2436 }
2437 }
2438 free(tstr);
2439 // only set projection parameters, if the have the right size.
2440 if (projParam.size() == options.getProjectionParameters().size()) {
2442 }
2443 }
2444
2445 // this is fragile.. hope nobody adds additional whitespace
2446 // and other arguments than q...
2447 // n"JPEG q80"
2448 if (PTScriptParsing::getPTParam(format, line, "n")) {
2449 int t = format.find(' ');
2451
2452 // parse output format options.
2453 switch (options.outputFormat)
2454 {
2457 {
2458 // "parse" jpg quality
2459 int q;
2461 options.quality = (int) q;
2462 }
2463 }
2464 break;
2466 {
2467 int coordImgs = 0;
2469 if (coordImgs)
2470 options.saveCoordImgs = true;
2471 }
2476 {
2477 // parse tiff compression mode
2478 std::string comp;
2480 if (comp == "NONE" || comp == "LZW" ||
2481 comp == "PACKBITS" || comp == "DEFLATE")
2482 {
2484 } else {
2485 DEBUG_WARN("No valid tiff compression found");
2486 }
2487 }
2488 // read tiff roi
2490 if (comp == "CROP") {
2491 options.tiff_saveROI = true;
2492 } else {
2493 options.tiff_saveROI = false;
2494 }
2495 }
2496 }
2497 break;
2498 default:
2499 break;
2500 }
2501 }
2502
2503 int cRefImg = 0;
2504 if (PTScriptParsing::getIntParam(cRefImg, line, "k")) {
2506 }
2507 break;
2508
2509 }
2510 case 'm':
2511 {
2512 DEBUG_DEBUG("m line: " << line);
2513 // parse misc options
2514 int i;
2515 if (PTScriptParsing::getIntParam(i, line, "i"))
2517 {
2519 }
2520 break;
2521 }
2522 case 'v':
2523 {
2524 DEBUG_DEBUG("v line: " << line);
2525 if (!PTGUIScriptFile) {
2526 if (firstOptVecParse) {
2527 int nImg = std::max(iImgInfo.size(), oImgInfo.size());
2528 DEBUG_DEBUG("nImg: " << nImg);
2530 firstOptVecParse = false;
2531 }
2532 std::stringstream optstream;
2533 optstream << line.substr(1);
2534 std::string var;
2535 while (!(optstream >> std::ws).eof()) {
2536 optstream >> var;
2537 if (var.length() == 1) {
2538 // special case for PTGUI
2539 var += "0";
2540 }
2541 // find first numerical character
2542 std::string::size_type np = var.find_first_of("0123456789");
2543 if (np == std::string::npos) {
2544 // invalid, continue
2545 continue;
2546 }
2547 std::string name=var.substr(0,np);
2548 unsigned int nr;
2549 if (!hugin_utils::stringToUInt(var.substr(np), nr))
2550 {
2551 // invalid, continue
2552 continue;
2553 };
2554 DEBUG_ASSERT(nr < optvec.size());
2555 if(nr < optvec.size())
2556 {
2557 optvec[nr].insert(name);
2558 DEBUG_DEBUG("parsing opt: >" << var << "< : var:" << name << " image:" << nr);
2559 };
2560 }
2561 }
2562 break;
2563 }
2564 case 'c':
2565 {
2566 DEBUG_DEBUG("c line: " << line);
2567 int t;
2568 // read control points
2569 ControlPoint point;
2570 // TODO - should verify that line syntax is correct
2571 PTScriptParsing::getIntParam(point.image1Nr, line, "n");
2573 PTScriptParsing::getIntParam(point.image2Nr, line, "N");
2575 PTScriptParsing::getDoubleParam(point.x1, line, "x");
2576 PTScriptParsing::getDoubleParam(point.x2, line, "X");
2577 PTScriptParsing::getDoubleParam(point.y1, line, "y");
2578 PTScriptParsing::getDoubleParam(point.y2, line, "Y");
2579 if (!PTScriptParsing::getIntParam(t, line, "t")){
2580 t = 0;
2581 }
2582
2583 point.mode = t;
2584 loadedCp.push_back(point);
2585 break;
2586 }
2587
2588 // handle the complicated part.. the image & lens settings.
2589 // treat i and o lines the same.. however, o lines have priority
2590 // over i lines.(i lines often do not contain link information!)
2591 case 'i':
2592 {
2593 if (PTGUILensLine) {
2594 PTGUILensLine = false;
2595 PTGUILensLoaded = true;
2596 PTGUILens.parse(line);
2597 } else {
2598 iImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2599 }
2600 break;
2601 }
2602 case 'o':
2603 {
2604 if (PTGUILensLine) {
2605 PTGUILensLine = false;
2606 PTGUILensLoaded = true;
2607 PTGUILens.parse(line);
2608 } else {
2609 oImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2610 }
2611 break;
2612 }
2613
2614 case 'k':
2615 {
2616 unsigned int param;
2617 if (PTScriptParsing::getIntParam(param, line, "i"))
2618 {
2620 newPolygon.setImgNr(param);
2621 if (PTScriptParsing::getIntParam(param, line, "t"))
2622 newPolygon.setMaskType((HuginBase::MaskPolygon::MaskType)param);
2623 std::string format;
2624 if (PTScriptParsing::getPTParam(format, line, "p"))
2625 {
2626 if(newPolygon.parsePolygonString(format))
2627 ImgMasks.push_back(newPolygon);
2628 };
2629 };
2630 break;
2631 }
2632
2633 case '#':
2634 {
2635 // parse special comments...
2636 if (line.substr(0,20) == std::string("# ptGui project file")) {
2637 PTGUIScriptFile = true;
2638 }
2639 if (line.substr(0,12) == "#-dummyimage") {
2640 PTGUILensLine = true;
2641 }
2642 if (PTGUIScriptFile) {
2643 // parse special PTGUI stuff.
2644 if (sscanf(line.c_str(), "#-fileversion %d", &PTGUIScriptVersion) > 0) {
2645 DEBUG_DEBUG("Detected PTGUI script version: " << PTGUIScriptVersion);
2646 switch (PTGUIScriptVersion) {
2647 case 0:
2648 break;
2649 case 1:
2650 break;
2651 case 2:
2652 break;
2653 case 3:
2654 break;
2655 case 4:
2656 break;
2657 case 5:
2658 break;
2659 case 6:
2660 break;
2661 case 7:
2662 break;
2663 default:
2665 // latest known version is 8
2666 break;
2667 }
2668 }
2669 }
2670
2671 if (line.substr(0,8) == "#-hugin ") {
2672 // read hugin image line
2674 info.autoCenterCrop = (line.find("autoCenterCrop=1") != std::string::npos);
2675 size_t pos = line.find("cropFactor=");
2676 if (pos > 0 && pos < line.length()) {
2677 double cropFactor=1;
2678 const char * s = line.c_str() + pos;
2679 sscanf(s,"cropFactor=%lf", & cropFactor);
2681 cropFactor=1;
2682 info.cropFactor = cropFactor;
2683 }
2684 pos = line.find("disabled");
2685 if (pos > 0 && pos < line.length()) {
2686 info.enabled = false;
2687 }
2688 huginImgInfo.push_back(info);
2689 }
2690
2691 // PTGui and PTAssember project files:
2692 // #-imgfile 960 1280 "D:\data\bruno\074-098\087.jpg"
2693 if (line.substr(0,10) == "#-imgfile ") {
2694
2695 // arghhh. I like string processing without regexps.
2696 int b = line.find_first_not_of(" ",9);
2697 int e = line.find_first_of(" ",b);
2698 DEBUG_DEBUG(" width:" << line.substr(b, e - b) << ":")
2699 int nextWidth;
2700 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextWidth))
2701 {
2702 continue;
2703 };
2704 DEBUG_DEBUG("next width " << nextWidth);
2705 b = line.find_first_not_of(" ",e);
2706 e = line.find_first_of(" ",b);
2707 DEBUG_DEBUG(" height:" << line.substr(b, e - b) << ":")
2708 int nextHeight;
2709 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextHeight))
2710 {
2711 continue;
2712 };
2713 DEBUG_DEBUG("next height " << nextHeight);
2714
2715 std::string nextFilename;
2716 try {
2717 b = line.find_first_not_of(" \"",e);
2718 e = line.find_first_of("\"",b);
2719 nextFilename = line.substr(b,e-b);
2720 } catch (std::out_of_range& e) {
2721 DEBUG_ERROR("ERROR PARSING INPUT FILE" << e.what( ));
2722 return false;
2723 }
2724 DEBUG_DEBUG("next filename " << nextFilename);
2725
2727 info.width = nextWidth;
2728 info.height = nextHeight;
2729 info.filename = nextFilename;
2730 cImgInfo.push_back(info);
2731 }
2732
2733
2734 // parse hugin properties
2735 if (line.substr(0,7) == "#hugin_") {
2736 std::istringstream is(line);
2737 std::string var,value;
2738 is >> var >> value;
2739 if (!is.fail()) {
2740 if (var == "#hugin_ptoversion") {
2741 ptoVersion = atoi(value.c_str());
2742 }
2743
2744 if (var == "#hugin_optimizeReferenceImage") {
2745 options.optimizeReferenceImage = atoi(value.c_str());
2746 } else if (var == "#hugin_remapper") {
2747 if (value == "nona") {
2749 } else if (value == "PTmender") {
2751 }
2752 } else if (var == "#hugin_blender") {
2753 if (value == "none") {
2755 } else if (value == "PTblender") {
2757 } else if (value == "enblend") {
2759 } else if (value == "PTmasker") {
2761 } else if (value == "smartblend") {
2763 } else if (value == "internal") {
2765 }
2766
2767 } else if (var == "#hugin_enblendOptions") {
2768 options.enblendOptions = value;
2769 while (!is.eof()) {
2770 is >> value;
2771 if (!is.fail() && value.length() > 0) {
2772 options.enblendOptions += " ";
2773 options.enblendOptions += value;
2774 }
2775 }
2776 } else if (var == "#hugin_enfuseOptions") {
2777 options.enfuseOptions = value;
2778 while (!is.eof()) {
2779 is >> value;
2780 if (!is.fail() && value.length() > 0) {
2781 options.enfuseOptions += " ";
2782 options.enfuseOptions += value;
2783 }
2784 }
2785 } else if (var == "#hugin_hdrmergeOptions") {
2786 options.hdrmergeOptions = value;
2787 while (!is.eof()) {
2788 is >> value;
2789 if (!is.fail() && value.length() > 0) {
2790 options.hdrmergeOptions += " ";
2791 options.hdrmergeOptions += value;
2792 }
2793 }
2794 } else if (var == "#hugin_verdandiOptions") {
2795 options.verdandiOptions = value;
2796 while (!is.eof()) {
2797 is >> value;
2798 if (!is.fail() && value.length() > 0) {
2799 options.verdandiOptions += " ";
2800 options.verdandiOptions += value;
2801 }
2802 }
2803 } else if (var == "#hugin_edgeFillMode") {
2804 if (value == "0")
2805 {
2807 }
2808 else
2809 {
2810 if (value == "1")
2811 {
2813 };
2814 };
2815 } else if (var == "#hugin_edgeFillKeepInput") {
2816 options.keepEdgeFillInput = (value == "true");
2817 } else if (var == "#hugin_outputLDRBlended") {
2818 options.outputLDRBlended = (value == "true");
2819 } else if (var == "#hugin_outputLDRLayers") {
2820 options.outputLDRLayers = (value == "true");
2821 } else if (var == "#hugin_outputLDRExposureRemapped") {
2822 options.outputLDRExposureRemapped = (value == "true");
2823 } else if (var == "#hugin_outputLDRExposureLayers") {
2824 options.outputLDRExposureLayers = (value == "true");
2825 } else if (var == "#hugin_outputLDRExposureBlended") {
2826 options.outputLDRExposureBlended = (value == "true");
2827 } else if (var == "#hugin_outputLDRExposureLayersFused") {
2828 options.outputLDRExposureLayersFused = (value == "true");
2829 } else if (var == "#hugin_outputLDRStacks") {
2830 options.outputLDRStacks = (value == "true");
2831 } else if (var == "#hugin_outputHDRBlended") {
2832 options.outputHDRBlended = (value == "true");
2833 } else if (var == "#hugin_outputHDRLayers") {
2834 options.outputHDRLayers = (value == "true");
2835 } else if (var == "#hugin_outputHDRStacks") {
2836 options.outputHDRStacks = (value == "true");
2837
2838 } else if (var == "#hugin_outputStacksMinOverlap") {
2839 double val=atof(value.c_str());
2840 if(val>0 && val <= 1)
2841 {
2843 };
2844 if (val < 0)
2845 {
2847 };
2848 } else if (var == "#hugin_outputLayersExposureDiff") {
2849 double val=atof(value.c_str());
2850 if(val>0.01)
2851 {
2853 }
2854
2855 } else if (var == "#hugin_outputLayersCompression") {
2857 } else if (var == "#hugin_outputImageType") {
2858 options.outputImageType = value;
2859 } else if (var == "#hugin_outputImageTypeCompression") {
2861 } else if (var == "#hugin_outputJPEGQuality") {
2862 options.quality = atoi(value.c_str());
2863 } else if (var == "#hugin_outputImageTypeHDR") {
2865 } else if (var == "#hugin_outputImageTypeHDRCompression") {
2867 } else if (var == "#hugin_outputRangeCompression") {
2868 options.outputRangeCompression = atof(value.c_str());
2869 options.outputRangeCompression = std::max(0.0, std::min(options.outputRangeCompression, 20.0));
2870 } else if (var == "#hugin_optimizerMasterSwitch") {
2871 optSwitch = atoi(value.c_str());
2872 } else if (var == "#hugin_optimizerPhotoMasterSwitch") {
2873 optPhotoSwitch = atoi(value.c_str());
2874 };
2875
2876 }
2877 }
2878 break;
2879 }
2880
2881 } // case
2882 }
2883
2884 // assemble images from the information read before..
2885
2888#if 0
2889 // handle PTGUI special case
2890 if (PTGUILensLoaded) {
2891 // create lens with dummy info
2892 Lens l;
2893 for (const char **v = Lens::variableNames; *v != 0; v++) {
2894 map_get(l.variables, *v).setValue(PTGUILens.vars[*v]);
2895 }
2896 l.setImageSize(vigra::Size2D(PTGUILens.width, PTGUILens.height));
2897 l.setCropFactor(1);
2899 lenses.push_back(l);
2900 }
2901#endif
2902
2903/*
2904 // ugly hack to load PTGui script files
2905 if (ptGUIDummyImage) {
2906 DEBUG_DEBUG("loading default PTGUI line: " << line);
2907 Lens l;
2908 // skip ptgui's dummy image
2909 // load parameters into default lens...
2910 for (LensVarMap::iterator it = l.variables.begin();
2911 it != l.variables.end();
2912 ++it)
2913 {
2914 DEBUG_DEBUG("reading default lens variable " << it->first);
2915 int link;
2916 bool ok = readVar(it->second, link, line);
2917 DEBUG_ASSERT(ok);
2918 DEBUG_ASSERT(link == -1);
2919 }
2920 lenses.push_back(l);
2921
2922 ptGUIDummyImage = false;
2923 break;
2924 }
2925*/
2926
2927 // merge image info from the 3 different lines...
2928 // i lines are the main reference.
2929
2930 int nImgs = iImgInfo.size();
2931 int nOLines = oImgInfo.size();
2932 int nCLines = cImgInfo.size();
2933
2934 if (nImgs < nOLines) {
2935 // no, or less i lines found. scrap i lines.
2936 DEBUG_DEBUG("throwing away " << nImgs << " i lines");
2938 nImgs = nOLines;
2939 }
2940 if (nOLines < nImgs) {
2942 }
2943
2944 // merge o lines and i lines into i lines.
2945 for (int i=0; i < nImgs; i++) {
2946
2947 // move parameters from o lines -> i (only if it isn't given in the
2948 // i lines. or it is linked on the o lines)
2949
2950 // ordinary variables
2951 for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v; v++) {
2952
2953 if ((iImgInfo[i].links[*v] == -2 && oImgInfo[i].links[*v] != -2) || (iImgInfo[i].links[*v] == -1 && oImgInfo[i].links[*v] >=0)) {
2954 DEBUG_DEBUG(*v << ": o -> i");
2955 iImgInfo[i].vars[*v] = oImgInfo[i].vars[*v];
2956 iImgInfo[i].links[*v] = oImgInfo[i].links[*v];
2957 }
2958 }
2959
2960 if (iImgInfo[i].filename == "" && oImgInfo[i].filename != "") {
2961 DEBUG_DEBUG("filename: o -> i");
2962 iImgInfo[i].filename = oImgInfo[i].filename;
2963 }
2964
2965 if (iImgInfo[i].crop.isEmpty() && !oImgInfo[i].crop.isEmpty()) {
2966 DEBUG_DEBUG("crop: o -> i");
2967 iImgInfo[i].crop = oImgInfo[i].crop;
2968 }
2969
2970 if (iImgInfo[i].width <= 0 && oImgInfo[i].width > 0) {
2971 DEBUG_DEBUG("width: o -> i");
2972 iImgInfo[i].width = oImgInfo[i].width;
2973 }
2974
2975 if (iImgInfo[i].height <= 0 && oImgInfo[i].height > 0) {
2976 DEBUG_DEBUG("height: o -> i");
2977 iImgInfo[i].height = oImgInfo[i].height;
2978 }
2979
2980 if (iImgInfo[i].f < 0 && oImgInfo[i].f > 0) {
2981 DEBUG_DEBUG("f: o -> i");
2982 iImgInfo[i].f = oImgInfo[i].f;
2983 }
2984
2985 if (nCLines == nImgs) {
2986 // img file & size in clines
2987 if (cImgInfo[i].filename != "" && cImgInfo[i].width > 0) {
2988 DEBUG_DEBUG("filename, width, height: c -> i");
2989 iImgInfo[i].filename = cImgInfo[i].filename;
2990 iImgInfo[i].width = cImgInfo[i].width;
2991 iImgInfo[i].height = cImgInfo[i].height;
2992 }
2993 }
2994 if (huginImgInfo.size() == (size_t)nImgs) {
2995 iImgInfo[i].cropFactor = huginImgInfo[i].cropFactor;
2996 iImgInfo[i].autoCenterCrop = huginImgInfo[i].autoCenterCrop;
2997 iImgInfo[i].enabled= huginImgInfo[i].enabled;
2998 }
2999 }
3000
3001 // create images.
3002 for (int i=0; i < nImgs; i++) {
3003
3004 DEBUG_DEBUG("i line: " << i);
3005 // read the variables
3006 VariableMap vars;
3007 int link = -2;
3008 fillVariableMap(vars);
3009
3010 for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v != 0; v++) {
3011 std::string name(*v);
3012 double val = iImgInfo[i].vars[*v];
3013 map_get(vars,name).setValue(val);
3014 if (iImgInfo[i].links[*v] >= 0) {
3015 link = iImgInfo[i].links[*v];
3016 }
3017 }
3018
3019 std::string file = iImgInfo[i].filename;
3020 // add prefix if only a relative path.
3021#ifdef _WIN32
3022 bool absPath = ( (file[1]==':' && file[2]=='\\') || (file[1]==':' && file[2]=='/') || (file[0] == '\\' && file[1] == '\\'));
3023#else
3024 bool absPath = file[0] == '/';
3025#endif
3026 if (!absPath) {
3027 file.insert(0, prefix);
3028 }
3029 DEBUG_DEBUG("filename: " << file);
3030
3031 // Make a new SrcPanoImage in this list for expressing this one.
3033 images.push_back(new_img_p);
3034 // and make a reference to it so we dont keep using images.back(),
3036 new_img.setFilename(file);
3037 new_img.setSize(vigra::Size2D(iImgInfo[i].width, iImgInfo[i].height));
3038 new_img.checkImageSizeKnown();
3039
3040 // Panotools Script variables for the current SrcPanoImage variable.
3041 // We just need the names.
3043
3044 // A dummy SrcPanoImage to use to fill vars_for_name.
3046
3047/* Set image variables in new_img using the PanoTools Script names, with
3048 * linking where specified in the file.
3049 *
3050 * We create a list of PanoTools script variable names for each variable in
3051 * SrcPanoImg (vars_for_name). It may have multiple variables or none at all.
3052 * If a link is found between any of the variables inside it we can link them up
3053 * in new_img.
3054 *
3055 * The Panorama Tools script format makes it possible to link parts of the same
3056 * SrcPanoImage variable differently (e.g. you can link the horizontal component
3057 * of shearing to a different target to the vertical component, not link the
3058 * vertical component at all). SrcPanoImage cannot handle this, so we end up
3059 * linking all components of the same variable to anything specified in the PTO
3060 * script for one of the components: most often we specify the same link
3061 * multiple times.
3062 */
3066#define RESET_LOCALE setlocale(LC_NUMERIC,old_locale); free(old_locale);
3067#define image_variable( name, type, default_value )\
3068 PTOVariableConverterFor##name::addToVariableMap(name_src.get##name##IV(), vars_for_name);\
3069 for (VariableMap::iterator vit = vars_for_name.begin();\
3070 vit != vars_for_name.end(); vit++)\
3071 {\
3072 if (link >= 0 && iImgInfo[i].links[vit->first] >= 0)\
3073 {\
3074 if ( !(PTGUILensLoaded && link == 0)\
3075 && (int) images.size() < link && (!PTGUILensLoaded))\
3076 {\
3077 DEBUG_ERROR("variables must be linked to an image with a lower number" << endl\
3078 << "number links: " << link << " images: " << images.size() << endl\
3079 << "error on line " << lineNr << ":" << endl\
3080 << line);\
3081 RESET_LOCALE\
3082 return false;\
3083 }\
3084 DEBUG_DEBUG("anchored to image " << iImgInfo[i].links[vit->first]);\
3085 new_img.link##name(images[iImgInfo[i].links[vit->first]]);\
3086 } else {\
3087 double val = map_get(vars, vit->first).getValue();\
3088 new_img.setVar(vit->first, val);\
3089 }\
3090 }\
3091 vars_for_name.clear();
3092#include "image_variables.h"
3093#undef image_variable
3094 new_img.setProjection((SrcPanoImage::Projection) iImgInfo[i].f);
3095
3096 // check, if stacks are correctly linked
3097#define check_stack_link(name) \
3098 if(!new_img.YawisLinked() && new_img.name##isLinked())\
3099 {\
3100 new_img.unlink##name();\
3101 };\
3102 if(new_img.YawisLinked() && !new_img.name##isLinked())\
3103 {\
3104 for(size_t j=0; j<i; j++)\
3105 {\
3106 if(new_img.YawisLinkedWith(*images[j]))\
3107 {\
3108 new_img.link##name(images[j]);\
3109 break;\
3110 };\
3111 };\
3112 }
3120#undef check_stack_link
3121
3122#if 0
3123 new_img.setFeatherWidth((unsigned int) iImgInfo[i].blend_radius);
3124#endif
3125
3126 // is this right?
3127 new_img.setCropFactor(iImgInfo[i].cropFactor);
3128 new_img.setVigCorrMode(iImgInfo[i].vigcorrMode);
3129 new_img.setFlatfieldFilename(iImgInfo[i].flatfieldname);
3130 new_img.setResponseType((SrcPanoImage::ResponseType)iImgInfo[i].responseType);
3131 new_img.setAutoCenterCrop(iImgInfo[i].autoCenterCrop);
3132 new_img.setActive(iImgInfo[i].enabled);
3133
3134 if (!iImgInfo[i].crop.isEmpty()) {
3135 if (new_img.isCircularCrop())
3136 {
3138 } else {
3140 }
3141 new_img.setCropRect(iImgInfo[i].crop);
3142 }
3143
3144 //now fill the mask
3145 for(unsigned int j=0;j<ImgMasks.size();j++)
3146 if(ImgMasks[j].getImgNr()==i)
3147 //now clip mask to image size + offset
3148 if(ImgMasks[j].clipPolygon(vigra::Rect2D(-0.9*maskOffset,-0.9*maskOffset,
3149 new_img.getWidth()+0.9*maskOffset,new_img.getHeight()+0.9*maskOffset)))
3150 {
3151 new_img.addMask(ImgMasks[j]);
3152 };
3153 }
3154
3155 // if we haven't found a v line in the project file
3156 if (optvec.size() != images.size()) {
3157 optvec = OptimizeVector(images.size());
3158 }
3159
3160 if (!loadedCp.empty())
3161 {
3162 // check if control points are linked with existing images
3163 const size_t nrImg = images.size();
3164 for (CPVector::const_iterator it = loadedCp.begin(); it != loadedCp.end(); ++it)
3165 {
3167 if (cp.image1Nr > cp.image2Nr)
3168 {
3169 // mirror control points if first images has a higher number than the second
3170 cp.mirror();
3171 };
3172 if (cp.image1Nr < nrImg && cp.image2Nr < nrImg)
3173 {
3174 ctrlPoints.push_back(cp);
3175 };
3176 };
3177 if (loadedCp.size() != ctrlPoints.size())
3178 {
3179 std::cout << "WARNING: Project file contains control points that are connected with" << std::endl
3180 << " non existing images. Ignoring these control points." << std::endl;
3181 };
3182 };
3183
3184 if (images.empty())
3185 {
3186 std::cerr << "ERROR: Project file contains no images." << std::endl;
3187 }
3188 else
3189 {
3191 {
3192 // optimize reference images reference to non-existing image
3194 std::cout << "WARNING: Optimize reference image refers to non existing image. Reset to default value." << std::endl;
3195 };
3197 {
3198 // optimize reference images reference to non-existing image
3200 std::cout << "WARNING: Optimize photometric reference image refers to non existing image. Reset to default value." << std::endl;
3201 };
3202 };
3203 // reset locale
3206
3207 return !images.empty();
3208}
3209
3210} // namespace
image1Nr
image2Nr
definitions of classes to calculate overlap between different images
Convenience functions for SrcPanoImage to use on the image variables.
declaration of functions to handle stacks and layers
some definitions to work with optimizer master switches
#define check_stack_link(name)
Somewhere to specify what variables belong to what.
virtual void setDirty(const bool &dirty=true)
@ VIGCORR_NONE
no vignetting correction
@ RESPONSE_EMOR
empirical model of response
class for calculating overlap of images
An ImageVariableGroup is a collection of image variables that can have some shared variable values.
Make an ImageVariableGroup for lenses and other common concepts.
static const std::set< ConstImageVariableGroup::ImageVariableEnum > & getLensVariables()
Get the set of lens image variables.
represents a control point
void mirror()
swap (image1Nr,x1,y1) with (image2Nr,x2,y2)
@ X
evaluate x, points are on a vertical line
@ Y
evaluate y, points are on a horizontal line
Same as above, but use a non const panorama.
void setImageSize(const vigra::Size2D &sz)
set image size in pixels
Definition Lens.h:93
void setProjection(LensProjectionFormat l)
set projection type
Definition Lens.h:60
void setCropFactor(double newCropFactor)
sets the crop factor
Definition Lens.h:77
LensVarMap variables
Definition Lens.h:106
static const char * variableNames[]
Definition Lens.h:110
base class, which stores one mask polygon
Definition Mask.h:53
bool isPositive() const
returns true, if mask type is positive
Definition Mask.cpp:97
void setImgNr(const unsigned int newImgNr)
sets the associated image number, only used when loading a project, otherwise discarded
Definition Mask.h:87
MaskType
enumeration with type of possible masks
Definition Mask.h:57
Holds transformations for Image -> Pano and the other way.
void createTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
initialize pano->image transformation
void createInvTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
create image->pano transformation
Memento class for a PanoramaData object.
Model for a panorama.
Memento class for a Panorama object.
Definition Panorama.h:50
std::vector< SrcPanoImage * > images
The images inside the panorama.
Definition Panorama.h:90
PanoramaOptions options
Definition Panorama.h:99
OptimizeVector optvec
Definition Panorama.h:101
std::string iccProfileDesc
description of the icc profile
Definition Panorama.h:92
bool loadPTScript(std::istream &i, int &ptoVersion, const std::string &prefix="")
load a Hugin file
int bands
number of bands of first image (without alpha channel), currently we can't mix grayscale and RGB imag...
Definition Panorama.h:95
int optSwitch
stores the optimizer switch, use OR of HuginBase::OptimizerSwitches
Definition Panorama.h:103
PanoramaMemento & operator=(const PanoramaMemento &o)
assignment operator
int optPhotoSwitch
stores the photometric optimizer switch, use OR of HuginBase::OptimizerSwitches
Definition Panorama.h:105
this handler class will receive change events from the Panorama.
Panorama image options.
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
bool outputHDRLayers
save remapped layers (HDR)
bool outputLDRExposureLayers
save blended exposure layers, do not perform fusion (no exposure adjustment)
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
bool outputLDRExposureBlended
< save exposure fused stacks (no exposure adjustment)
vigra_ext::Interpolator interpolator
void setHeight(unsigned int h)
set panorama height
const std::vector< double > & getProjectionParameters() const
Get the optional projection parameters.
bool outputLDRExposureRemapped
save remapped layers (no exposure adjustment)
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
void printScriptLine(std::ostream &o, bool forPTOptimizer=false) const
void setROI(const vigra::Rect2D &val)
bool outputLDRBlended
save blended panorama (LDR)
bool outputLDRLayers
save remapped layers (LDR)
PanoramaOptions::ProjectionFormat getProjection() const
bool outputHDRStacks
save image stacks (HDR)
ProjectionFormat
Projection of final panorama.
void setProjectionParameters(const std::vector< double > &params)
set the optional parameters (they need to be of the correct size)
bool outputLDRExposureLayersFused
save blended exposure layers which are then fused (no exposure adjustment)
std::string outputImageTypeHDRCompression
static FileFormat getFormatFromName(const std::string &name)
returns the FileFormat corrosponding to name.
bool outputHDRBlended
save blended panorama (HDR)
Model for a panorama.
Definition Panorama.h:153
UIntSet changedImages
the images that have been changed since the last changeFinished()
Definition Panorama.h:697
const VariableMap getImageVariables(unsigned int imgNr) const
Get the variables of an image.
Definition Panorama.cpp:128
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
void imageChanged(unsigned int imgNr)
mark image for change notification.
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
void updateMasksForImage(unsigned int imgNr, MaskPolygonVector newMasks)
set complete mask list for image with number
Definition Panorama.cpp:882
CPointVector getCtrlPointsVectorForImage(unsigned int imgNr) const
return a vector of std::pairs with global ctrl point nr and ControlPoint In the class ControlPoint th...
Definition Panorama.cpp:96
void setOptimizeVector(const OptimizeVector &optvec)
set optimize setting
Definition Panorama.cpp:297
virtual void UpdateCropFactor(UIntSet imgs, double newCropFactor)
updates the crop factor, try to keep focal length constant
Definition Panorama.cpp:254
virtual PanoramaDataMemento * getNewMemento() const
get the internal state
std::list< PanoramaObserver * > observers
Definition Panorama.h:695
void setMemento(const PanoramaMemento &memento)
set the internal state
std::vector< unsigned int > getCtrlPointsForImage(unsigned int imgNr) const
return all control points for a given image.
Definition Panorama.cpp:83
virtual void updateWhiteBalance(double redFactor, double blueFactor)
update the global white balace of the panorama by multiplying the red and blue factor of each image w...
void removeDuplicateCtrlPoints()
removes duplicates control points
Definition Panorama.cpp:406
void removeImage(unsigned int nr)
creates an image, from filename, and a Lens, if needed
Definition Panorama.cpp:329
void transferMask(MaskPolygon mask, unsigned int imgNr, const UIntSet &targetImgs)
transfers given mask from image imgNr to all targetImgs
Definition Panorama.cpp:890
const int getNrOfBands() const
return number of bands of first image (without alpha channel) so it can be 1 for grayscale or 3 for r...
int getNextCPTypeLineNumber() const
get the number of a control point
void updateMasks(bool convertPosMaskToNeg=false)
updates all active masks
Definition Panorama.cpp:988
void setCtrlPoints(const CPVector &points)
set all control points (Ippei: Is this supposed to be 'add' method?)
Definition Panorama.cpp:449
void addObserver(PanoramaObserver *o)
add a panorama observer.
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...
void clearObservers()
remove all panorama observers.
const double getMaxExposureDifference() const
returns the maximum exposure value difference of all images in the project
void mergePanorama(const Panorama &newPano)
merges the panorama with the given pano
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
virtual bool setMementoToCopyOf(const PanoramaDataMemento *const memento)
set the internal state
const int getPhotometricOptimizerSwitch() const
return the photometric optimizer master switch
Definition Panorama.h:467
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition Panorama.cpp:381
void printPanoramaScript(std::ostream &o, const OptimizeVector &optvars, const PanoramaOptions &options, const UIntSet &imgs, bool forPTOptimizer, const std::string &stripPrefix="") const
create an optimizer script
Definition Panorama.cpp:503
void checkRefOptStatus(bool &linkRefImgsYaw, bool &linkRefImgsPitch, bool &linkRefImgsRoll)
checks if yaw/pitch/roll of reference image can be check, it depends on number and type of control po...
std::set< std::string > m_ptoptimizerVarNames
Definition Panorama.h:701
std::set< size_t > getRefImages()
returns set of reference image and images linked with reference images
void updateOptimizeVector()
updates the optimize vector according to master switches
virtual void updateVariable(unsigned int imgNr, const Variable &var)
update a single variable
Definition Panorama.cpp:200
virtual void UpdateFocalLength(UIntSet imgs, double newFocalLength)
updates the focal length by changing hfov
Definition Panorama.cpp:227
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition Panorama.cpp:319
void swapImages(unsigned int img1, unsigned int img2)
swap images.
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
Definition Panorama.cpp:373
PanoramaMemento state
Definition Panorama.h:694
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
const std::string getICCProfileDesc() const
return description of icc profile used for pano
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
void activateImage(unsigned int imgNr, bool active=true)
mark an image as active or inactive.
void changeFinished()
notify observers about changes in this class
Definition Panorama.h:564
void centerCrop(unsigned int imgNr)
center the crop for given image and all linked images
const bool hasPendingChanges() const
return if the panorama class has pending changes normally all controls listen to panoramaChanges noti...
void moveImage(size_t img1, size_t img2)
moves images.
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
void updateCropMode(unsigned int imgNr)
update the crop mode in dependence of crop rect and lens projection
virtual void updateVariables(const VariableMapVector &vars)
Set the variables.
Definition Panorama.cpp:171
Panorama getSubset(const UIntSet &imgs) const
get a subset of the panorama
bool dirty
this indicates that there are unsaved changes
Definition Panorama.h:692
void changeControlPoint(unsigned int pNr, const ControlPoint &point)
change a control Point.
Definition Panorama.cpp:434
void updateCtrlPointErrors(const CPVector &controlPoints)
update control points distances.
Definition Panorama.cpp:160
bool removeObserver(PanoramaObserver *observer)
remove a panorama observer.
PanoramaData * getUnlinkedSubset(UIntSetVector &imageGroups) const
get a panorama, which does not contain images linked with positions, the cps are moved to the first i...
PanoramaMemento getMemento() const
get the internal state
Definition Panorama.h:602
void setNrOfBands(const int nrBands)
sets the number of bands
const OptimizeVector & getOptimizeVector() const
return the optimize settings stored inside panorama
Definition Panorama.h:454
Panorama duplicate() const
duplicate the panorama
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
PanoramaData * getNewSubset(const UIntSet &imgs) const
Definition Panorama.h:183
void setICCProfileDesc(const std::string &newDesc)
sets the icc profile description for check of same profile
void printStitcherScript(std::ostream &o, const PanoramaOptions &target, const UIntSet &imgs) const
create the stitcher script
Definition Panorama.cpp:778
std::string imgFilePrefix
Definition Panorama.h:689
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Definition Panorama.cpp:311
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
const int getOptimizerSwitch() const
returns optimizer master switch
Definition Panorama.h:461
UIntSet getActiveImages() const
get active images
vigra::Rect2D centerCropImage(unsigned int imgNr)
return the centered crop for given image
void reset()
clear the internal state.
Definition Panorama.cpp:69
void removeCtrlPoint(unsigned int pNr)
remove a control point.
Definition Panorama.cpp:391
void setFilePrefix(std::string prefix)
sets the path prefix of the images reffered with relative paths
Definition Panorama.h:664
void updateLineCtrlPoints()
assign new mode line numbers, if required
Definition Panorama.cpp:471
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
Definition Panorama.cpp:303
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
void setSize(vigra::Size2D val)
Set the image size in pixels.
bool hasMasks() const
returns true, if image has masks associated
void printMaskLines(std::ostream &o, unsigned int newImgNr) const
writes all mask lines to stream, using given image number
a variable has a value and a name.
double getValue() const
const std::string & getName() const
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_TRACE(msg)
Definition utils.h:67
#define DEBUG_WARN(msg)
Definition utils.h:74
This file specifies what image variables SrcPanoImg should have.
double Z
double X
double TranslationPlaneYaw
double Roll
double Pitch
double Y
double TranslationPlanePitch
bool getIntParam(T &value, const std::string &line, const std::string &name)
bool getDoubleParam(double &d, const std::string &line, const std::string &name)
bool getPTParam(std::string &output, const std::string &line, const std::string &parameter)
helper functions for parsing of a script line
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
std::vector< VariableMap > VariableMapVector
void fillVariableMap(VariableMap &vars)
fill map with all image & lens variables
std::vector< ControlPoint > CPVector
std::vector< std::set< std::string > > OptimizeVector
std::vector< CPoint > CPointVector
const int maskOffset
polygon can exceed the image maximal maskOffset pixels in each direction bigger polygons will be clip...
Definition Mask.h:44
void UpdateOptVectorSet(std::set< std::string > &imgVar, const std::string &var, const bool opt)
std::vector< UIntSet > UIntSetVector
std::set< unsigned int > UIntSet
std::map< std::string, Variable > VariableMap
int FindStackNumberForImage(const std::vector< UIntSet > &imageGroups, const unsigned int imgNr)
std::vector< UIntSet > getExposureLayers(const PanoramaData &pano, UIntSet allImgs, PanoramaOptions opts)
returns vector of set of output exposure layers
bool stringToUInt(const std::string &s, unsigned int &val)
convert string to unsigned integer value, returns true, if sucessful
Definition utils.cpp:280
int roundi(T x)
Definition hugin_math.h:73
bool CheckInterpolator(int value)
check that interpolator number is valid
Definition utils.cpp:986
bool stringToInt(const std::string &s, int &val)
convert string to integer value, returns true, if sucessful
Definition utils.cpp:264
Interpolator
enum with all interpolation methods
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
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition stl_utils.h:81
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