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 break;
2518 }
2519 case 'v':
2520 {
2521 DEBUG_DEBUG("v line: " << line);
2522 if (!PTGUIScriptFile) {
2523 if (firstOptVecParse) {
2524 int nImg = std::max(iImgInfo.size(), oImgInfo.size());
2525 DEBUG_DEBUG("nImg: " << nImg);
2527 firstOptVecParse = false;
2528 }
2529 std::stringstream optstream;
2530 optstream << line.substr(1);
2531 std::string var;
2532 while (!(optstream >> std::ws).eof()) {
2533 optstream >> var;
2534 if (var.length() == 1) {
2535 // special case for PTGUI
2536 var += "0";
2537 }
2538 // find first numerical character
2539 std::string::size_type np = var.find_first_of("0123456789");
2540 if (np == std::string::npos) {
2541 // invalid, continue
2542 continue;
2543 }
2544 std::string name=var.substr(0,np);
2545 unsigned int nr;
2546 if (!hugin_utils::stringToUInt(var.substr(np), nr))
2547 {
2548 // invalid, continue
2549 continue;
2550 };
2551 DEBUG_ASSERT(nr < optvec.size());
2552 if(nr < optvec.size())
2553 {
2554 optvec[nr].insert(name);
2555 DEBUG_DEBUG("parsing opt: >" << var << "< : var:" << name << " image:" << nr);
2556 };
2557 }
2558 }
2559 break;
2560 }
2561 case 'c':
2562 {
2563 DEBUG_DEBUG("c line: " << line);
2564 int t;
2565 // read control points
2566 ControlPoint point;
2567 // TODO - should verify that line syntax is correct
2568 PTScriptParsing::getIntParam(point.image1Nr, line, "n");
2570 PTScriptParsing::getIntParam(point.image2Nr, line, "N");
2572 PTScriptParsing::getDoubleParam(point.x1, line, "x");
2573 PTScriptParsing::getDoubleParam(point.x2, line, "X");
2574 PTScriptParsing::getDoubleParam(point.y1, line, "y");
2575 PTScriptParsing::getDoubleParam(point.y2, line, "Y");
2576 if (!PTScriptParsing::getIntParam(t, line, "t")){
2577 t = 0;
2578 }
2579
2580 point.mode = t;
2581 loadedCp.push_back(point);
2582 break;
2583 }
2584
2585 // handle the complicated part.. the image & lens settings.
2586 // treat i and o lines the same.. however, o lines have priority
2587 // over i lines.(i lines often do not contain link information!)
2588 case 'i':
2589 {
2590 if (PTGUILensLine) {
2591 PTGUILensLine = false;
2592 PTGUILensLoaded = true;
2593 PTGUILens.parse(line);
2594 } else {
2595 iImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2596 }
2597 break;
2598 }
2599 case 'o':
2600 {
2601 if (PTGUILensLine) {
2602 PTGUILensLine = false;
2603 PTGUILensLoaded = true;
2604 PTGUILens.parse(line);
2605 } else {
2606 oImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2607 }
2608 break;
2609 }
2610
2611 case 'k':
2612 {
2613 unsigned int param;
2614 if (PTScriptParsing::getIntParam(param, line, "i"))
2615 {
2617 newPolygon.setImgNr(param);
2618 if (PTScriptParsing::getIntParam(param, line, "t"))
2619 newPolygon.setMaskType((HuginBase::MaskPolygon::MaskType)param);
2620 std::string format;
2621 if (PTScriptParsing::getPTParam(format, line, "p"))
2622 {
2623 if(newPolygon.parsePolygonString(format))
2624 ImgMasks.push_back(newPolygon);
2625 };
2626 };
2627 break;
2628 }
2629
2630 case '#':
2631 {
2632 // parse special comments...
2633 if (line.substr(0,20) == std::string("# ptGui project file")) {
2634 PTGUIScriptFile = true;
2635 }
2636 if (line.substr(0,12) == "#-dummyimage") {
2637 PTGUILensLine = true;
2638 }
2639 if (PTGUIScriptFile) {
2640 // parse special PTGUI stuff.
2641 if (sscanf(line.c_str(), "#-fileversion %d", &PTGUIScriptVersion) > 0) {
2642 DEBUG_DEBUG("Detected PTGUI script version: " << PTGUIScriptVersion);
2643 switch (PTGUIScriptVersion) {
2644 case 0:
2645 break;
2646 case 1:
2647 break;
2648 case 2:
2649 break;
2650 case 3:
2651 break;
2652 case 4:
2653 break;
2654 case 5:
2655 break;
2656 case 6:
2657 break;
2658 case 7:
2659 break;
2660 default:
2662 // latest known version is 8
2663 break;
2664 }
2665 }
2666 }
2667
2668 if (line.substr(0,8) == "#-hugin ") {
2669 // read hugin image line
2671 info.autoCenterCrop = (line.find("autoCenterCrop=1") != std::string::npos);
2672 size_t pos = line.find("cropFactor=");
2673 if (pos > 0 && pos < line.length()) {
2674 double cropFactor=1;
2675 const char * s = line.c_str() + pos;
2676 sscanf(s,"cropFactor=%lf", & cropFactor);
2678 cropFactor=1;
2679 info.cropFactor = cropFactor;
2680 }
2681 pos = line.find("disabled");
2682 if (pos > 0 && pos < line.length()) {
2683 info.enabled = false;
2684 }
2685 huginImgInfo.push_back(info);
2686 }
2687
2688 // PTGui and PTAssember project files:
2689 // #-imgfile 960 1280 "D:\data\bruno\074-098\087.jpg"
2690 if (line.substr(0,10) == "#-imgfile ") {
2691
2692 // arghhh. I like string processing without regexps.
2693 int b = line.find_first_not_of(" ",9);
2694 int e = line.find_first_of(" ",b);
2695 DEBUG_DEBUG(" width:" << line.substr(b, e - b) << ":")
2696 int nextWidth;
2697 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextWidth))
2698 {
2699 continue;
2700 };
2701 DEBUG_DEBUG("next width " << nextWidth);
2702 b = line.find_first_not_of(" ",e);
2703 e = line.find_first_of(" ",b);
2704 DEBUG_DEBUG(" height:" << line.substr(b, e - b) << ":")
2705 int nextHeight;
2706 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextHeight))
2707 {
2708 continue;
2709 };
2710 DEBUG_DEBUG("next height " << nextHeight);
2711
2712 std::string nextFilename;
2713 try {
2714 b = line.find_first_not_of(" \"",e);
2715 e = line.find_first_of("\"",b);
2716 nextFilename = line.substr(b,e-b);
2717 } catch (std::out_of_range& e) {
2718 DEBUG_ERROR("ERROR PARSING INPUT FILE" << e.what( ));
2719 return false;
2720 }
2721 DEBUG_DEBUG("next filename " << nextFilename);
2722
2724 info.width = nextWidth;
2725 info.height = nextHeight;
2726 info.filename = nextFilename;
2727 cImgInfo.push_back(info);
2728 }
2729
2730
2731 // parse hugin properties
2732 if (line.substr(0,7) == "#hugin_") {
2733 std::istringstream is(line);
2734 std::string var,value;
2735 is >> var >> value;
2736 if (!is.fail()) {
2737 if (var == "#hugin_ptoversion") {
2738 ptoVersion = atoi(value.c_str());
2739 }
2740
2741 if (var == "#hugin_optimizeReferenceImage") {
2742 options.optimizeReferenceImage = atoi(value.c_str());
2743 } else if (var == "#hugin_remapper") {
2744 if (value == "nona") {
2746 } else if (value == "PTmender") {
2748 }
2749 } else if (var == "#hugin_blender") {
2750 if (value == "none") {
2752 } else if (value == "PTblender") {
2754 } else if (value == "enblend") {
2756 } else if (value == "PTmasker") {
2758 } else if (value == "smartblend") {
2760 } else if (value == "internal") {
2762 }
2763
2764 } else if (var == "#hugin_enblendOptions") {
2765 options.enblendOptions = value;
2766 while (!is.eof()) {
2767 is >> value;
2768 if (!is.fail() && value.length() > 0) {
2769 options.enblendOptions += " ";
2770 options.enblendOptions += value;
2771 }
2772 }
2773 } else if (var == "#hugin_enfuseOptions") {
2774 options.enfuseOptions = value;
2775 while (!is.eof()) {
2776 is >> value;
2777 if (!is.fail() && value.length() > 0) {
2778 options.enfuseOptions += " ";
2779 options.enfuseOptions += value;
2780 }
2781 }
2782 } else if (var == "#hugin_hdrmergeOptions") {
2783 options.hdrmergeOptions = value;
2784 while (!is.eof()) {
2785 is >> value;
2786 if (!is.fail() && value.length() > 0) {
2787 options.hdrmergeOptions += " ";
2788 options.hdrmergeOptions += value;
2789 }
2790 }
2791 } else if (var == "#hugin_verdandiOptions") {
2792 options.verdandiOptions = value;
2793 while (!is.eof()) {
2794 is >> value;
2795 if (!is.fail() && value.length() > 0) {
2796 options.verdandiOptions += " ";
2797 options.verdandiOptions += value;
2798 }
2799 }
2800 } else if (var == "#hugin_edgeFillMode") {
2801 if (value == "0")
2802 {
2804 }
2805 else
2806 {
2807 if (value == "1")
2808 {
2810 };
2811 };
2812 } else if (var == "#hugin_edgeFillKeepInput") {
2813 options.keepEdgeFillInput = (value == "true");
2814 } else if (var == "#hugin_outputLDRBlended") {
2815 options.outputLDRBlended = (value == "true");
2816 } else if (var == "#hugin_outputLDRLayers") {
2817 options.outputLDRLayers = (value == "true");
2818 } else if (var == "#hugin_outputLDRExposureRemapped") {
2819 options.outputLDRExposureRemapped = (value == "true");
2820 } else if (var == "#hugin_outputLDRExposureLayers") {
2821 options.outputLDRExposureLayers = (value == "true");
2822 } else if (var == "#hugin_outputLDRExposureBlended") {
2823 options.outputLDRExposureBlended = (value == "true");
2824 } else if (var == "#hugin_outputLDRExposureLayersFused") {
2825 options.outputLDRExposureLayersFused = (value == "true");
2826 } else if (var == "#hugin_outputLDRStacks") {
2827 options.outputLDRStacks = (value == "true");
2828 } else if (var == "#hugin_outputHDRBlended") {
2829 options.outputHDRBlended = (value == "true");
2830 } else if (var == "#hugin_outputHDRLayers") {
2831 options.outputHDRLayers = (value == "true");
2832 } else if (var == "#hugin_outputHDRStacks") {
2833 options.outputHDRStacks = (value == "true");
2834
2835 } else if (var == "#hugin_outputStacksMinOverlap") {
2836 double val=atof(value.c_str());
2837 if(val>0 && val <= 1)
2838 {
2840 };
2841 if (val < 0)
2842 {
2844 };
2845 } else if (var == "#hugin_outputLayersExposureDiff") {
2846 double val=atof(value.c_str());
2847 if(val>0.01)
2848 {
2850 }
2851
2852 } else if (var == "#hugin_outputLayersCompression") {
2854 } else if (var == "#hugin_outputImageType") {
2855 options.outputImageType = value;
2856 } else if (var == "#hugin_outputImageTypeCompression") {
2858 } else if (var == "#hugin_outputJPEGQuality") {
2859 options.quality = atoi(value.c_str());
2860 } else if (var == "#hugin_outputImageTypeHDR") {
2862 } else if (var == "#hugin_outputImageTypeHDRCompression") {
2864 } else if (var == "#hugin_outputRangeCompression") {
2865 options.outputRangeCompression = atof(value.c_str());
2866 options.outputRangeCompression = std::max(0.0, std::min(options.outputRangeCompression, 20.0));
2867 } else if (var == "#hugin_optimizerMasterSwitch") {
2868 optSwitch = atoi(value.c_str());
2869 } else if (var == "#hugin_optimizerPhotoMasterSwitch") {
2870 optPhotoSwitch = atoi(value.c_str());
2871 };
2872
2873 }
2874 }
2875 break;
2876 }
2877
2878 } // case
2879 }
2880
2881 // assemble images from the information read before..
2882
2885#if 0
2886 // handle PTGUI special case
2887 if (PTGUILensLoaded) {
2888 // create lens with dummy info
2889 Lens l;
2890 for (const char **v = Lens::variableNames; *v != 0; v++) {
2891 map_get(l.variables, *v).setValue(PTGUILens.vars[*v]);
2892 }
2893 l.setImageSize(vigra::Size2D(PTGUILens.width, PTGUILens.height));
2894 l.setCropFactor(1);
2896 lenses.push_back(l);
2897 }
2898#endif
2899
2900/*
2901 // ugly hack to load PTGui script files
2902 if (ptGUIDummyImage) {
2903 DEBUG_DEBUG("loading default PTGUI line: " << line);
2904 Lens l;
2905 // skip ptgui's dummy image
2906 // load parameters into default lens...
2907 for (LensVarMap::iterator it = l.variables.begin();
2908 it != l.variables.end();
2909 ++it)
2910 {
2911 DEBUG_DEBUG("reading default lens variable " << it->first);
2912 int link;
2913 bool ok = readVar(it->second, link, line);
2914 DEBUG_ASSERT(ok);
2915 DEBUG_ASSERT(link == -1);
2916 }
2917 lenses.push_back(l);
2918
2919 ptGUIDummyImage = false;
2920 break;
2921 }
2922*/
2923
2924 // merge image info from the 3 different lines...
2925 // i lines are the main reference.
2926
2927 int nImgs = iImgInfo.size();
2928 int nOLines = oImgInfo.size();
2929 int nCLines = cImgInfo.size();
2930
2931 if (nImgs < nOLines) {
2932 // no, or less i lines found. scrap i lines.
2933 DEBUG_DEBUG("throwing away " << nImgs << " i lines");
2935 nImgs = nOLines;
2936 }
2937 if (nOLines < nImgs) {
2939 }
2940
2941 // merge o lines and i lines into i lines.
2942 for (int i=0; i < nImgs; i++) {
2943
2944 // move parameters from o lines -> i (only if it isn't given in the
2945 // i lines. or it is linked on the o lines)
2946
2947 // ordinary variables
2948 for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v; v++) {
2949
2950 if ((iImgInfo[i].links[*v] == -2 && oImgInfo[i].links[*v] != -2) || (iImgInfo[i].links[*v] == -1 && oImgInfo[i].links[*v] >=0)) {
2951 DEBUG_DEBUG(*v << ": o -> i");
2952 iImgInfo[i].vars[*v] = oImgInfo[i].vars[*v];
2953 iImgInfo[i].links[*v] = oImgInfo[i].links[*v];
2954 }
2955 }
2956
2957 if (iImgInfo[i].filename == "" && oImgInfo[i].filename != "") {
2958 DEBUG_DEBUG("filename: o -> i");
2959 iImgInfo[i].filename = oImgInfo[i].filename;
2960 }
2961
2962 if (iImgInfo[i].crop.isEmpty() && !oImgInfo[i].crop.isEmpty()) {
2963 DEBUG_DEBUG("crop: o -> i");
2964 iImgInfo[i].crop = oImgInfo[i].crop;
2965 }
2966
2967 if (iImgInfo[i].width <= 0 && oImgInfo[i].width > 0) {
2968 DEBUG_DEBUG("width: o -> i");
2969 iImgInfo[i].width = oImgInfo[i].width;
2970 }
2971
2972 if (iImgInfo[i].height <= 0 && oImgInfo[i].height > 0) {
2973 DEBUG_DEBUG("height: o -> i");
2974 iImgInfo[i].height = oImgInfo[i].height;
2975 }
2976
2977 if (iImgInfo[i].f < 0 && oImgInfo[i].f > 0) {
2978 DEBUG_DEBUG("f: o -> i");
2979 iImgInfo[i].f = oImgInfo[i].f;
2980 }
2981
2982 if (nCLines == nImgs) {
2983 // img file & size in clines
2984 if (cImgInfo[i].filename != "" && cImgInfo[i].width > 0) {
2985 DEBUG_DEBUG("filename, width, height: c -> i");
2986 iImgInfo[i].filename = cImgInfo[i].filename;
2987 iImgInfo[i].width = cImgInfo[i].width;
2988 iImgInfo[i].height = cImgInfo[i].height;
2989 }
2990 }
2991 if (huginImgInfo.size() == (size_t)nImgs) {
2992 iImgInfo[i].cropFactor = huginImgInfo[i].cropFactor;
2993 iImgInfo[i].autoCenterCrop = huginImgInfo[i].autoCenterCrop;
2994 iImgInfo[i].enabled= huginImgInfo[i].enabled;
2995 }
2996 }
2997
2998 // create images.
2999 for (int i=0; i < nImgs; i++) {
3000
3001 DEBUG_DEBUG("i line: " << i);
3002 // read the variables
3003 VariableMap vars;
3004 int link = -2;
3005 fillVariableMap(vars);
3006
3007 for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v != 0; v++) {
3008 std::string name(*v);
3009 double val = iImgInfo[i].vars[*v];
3010 map_get(vars,name).setValue(val);
3011 if (iImgInfo[i].links[*v] >= 0) {
3012 link = iImgInfo[i].links[*v];
3013 }
3014 }
3015
3016 std::string file = iImgInfo[i].filename;
3017 // add prefix if only a relative path.
3018#ifdef _WIN32
3019 bool absPath = ( (file[1]==':' && file[2]=='\\') || (file[1]==':' && file[2]=='/') || (file[0] == '\\' && file[1] == '\\'));
3020#else
3021 bool absPath = file[0] == '/';
3022#endif
3023 if (!absPath) {
3024 file.insert(0, prefix);
3025 }
3026 DEBUG_DEBUG("filename: " << file);
3027
3028 // Make a new SrcPanoImage in this list for expressing this one.
3030 images.push_back(new_img_p);
3031 // and make a reference to it so we dont keep using images.back(),
3033 new_img.setFilename(file);
3034 new_img.setSize(vigra::Size2D(iImgInfo[i].width, iImgInfo[i].height));
3035 new_img.checkImageSizeKnown();
3036
3037 // Panotools Script variables for the current SrcPanoImage variable.
3038 // We just need the names.
3040
3041 // A dummy SrcPanoImage to use to fill vars_for_name.
3043
3044/* Set image variables in new_img using the PanoTools Script names, with
3045 * linking where specified in the file.
3046 *
3047 * We create a list of PanoTools script variable names for each variable in
3048 * SrcPanoImg (vars_for_name). It may have multiple variables or none at all.
3049 * If a link is found between any of the variables inside it we can link them up
3050 * in new_img.
3051 *
3052 * The Panorama Tools script format makes it possible to link parts of the same
3053 * SrcPanoImage variable differently (e.g. you can link the horizontal component
3054 * of shearing to a different target to the vertical component, not link the
3055 * vertical component at all). SrcPanoImage cannot handle this, so we end up
3056 * linking all components of the same variable to anything specified in the PTO
3057 * script for one of the components: most often we specify the same link
3058 * multiple times.
3059 */
3063#define RESET_LOCALE setlocale(LC_NUMERIC,old_locale); free(old_locale);
3064#define image_variable( name, type, default_value )\
3065 PTOVariableConverterFor##name::addToVariableMap(name_src.get##name##IV(), vars_for_name);\
3066 for (VariableMap::iterator vit = vars_for_name.begin();\
3067 vit != vars_for_name.end(); vit++)\
3068 {\
3069 if (link >= 0 && iImgInfo[i].links[vit->first] >= 0)\
3070 {\
3071 if ( !(PTGUILensLoaded && link == 0)\
3072 && (int) images.size() < link && (!PTGUILensLoaded))\
3073 {\
3074 DEBUG_ERROR("variables must be linked to an image with a lower number" << endl\
3075 << "number links: " << link << " images: " << images.size() << endl\
3076 << "error on line " << lineNr << ":" << endl\
3077 << line);\
3078 RESET_LOCALE\
3079 return false;\
3080 }\
3081 DEBUG_DEBUG("anchored to image " << iImgInfo[i].links[vit->first]);\
3082 new_img.link##name(images[iImgInfo[i].links[vit->first]]);\
3083 } else {\
3084 double val = map_get(vars, vit->first).getValue();\
3085 new_img.setVar(vit->first, val);\
3086 }\
3087 }\
3088 vars_for_name.clear();
3089#include "image_variables.h"
3090#undef image_variable
3091 new_img.setProjection((SrcPanoImage::Projection) iImgInfo[i].f);
3092
3093 // check, if stacks are correctly linked
3094#define check_stack_link(name) \
3095 if(!new_img.YawisLinked() && new_img.name##isLinked())\
3096 {\
3097 new_img.unlink##name();\
3098 };\
3099 if(new_img.YawisLinked() && !new_img.name##isLinked())\
3100 {\
3101 for(size_t j=0; j<i; j++)\
3102 {\
3103 if(new_img.YawisLinkedWith(*images[j]))\
3104 {\
3105 new_img.link##name(images[j]);\
3106 break;\
3107 };\
3108 };\
3109 }
3117#undef check_stack_link
3118
3119#if 0
3120 new_img.setFeatherWidth((unsigned int) iImgInfo[i].blend_radius);
3121#endif
3122
3123 // is this right?
3124 new_img.setCropFactor(iImgInfo[i].cropFactor);
3125 new_img.setVigCorrMode(iImgInfo[i].vigcorrMode);
3126 new_img.setFlatfieldFilename(iImgInfo[i].flatfieldname);
3127 new_img.setResponseType((SrcPanoImage::ResponseType)iImgInfo[i].responseType);
3128 new_img.setAutoCenterCrop(iImgInfo[i].autoCenterCrop);
3129 new_img.setActive(iImgInfo[i].enabled);
3130
3131 if (!iImgInfo[i].crop.isEmpty()) {
3132 if (new_img.isCircularCrop())
3133 {
3135 } else {
3137 }
3138 new_img.setCropRect(iImgInfo[i].crop);
3139 }
3140
3141 //now fill the mask
3142 for(unsigned int j=0;j<ImgMasks.size();j++)
3143 if(ImgMasks[j].getImgNr()==i)
3144 //now clip mask to image size + offset
3145 if(ImgMasks[j].clipPolygon(vigra::Rect2D(-0.9*maskOffset,-0.9*maskOffset,
3146 new_img.getWidth()+0.9*maskOffset,new_img.getHeight()+0.9*maskOffset)))
3147 {
3148 new_img.addMask(ImgMasks[j]);
3149 };
3150 }
3151
3152 // if we haven't found a v line in the project file
3153 if (optvec.size() != images.size()) {
3154 optvec = OptimizeVector(images.size());
3155 }
3156
3157 if (!loadedCp.empty())
3158 {
3159 // check if control points are linked with existing images
3160 const size_t nrImg = images.size();
3161 for (CPVector::const_iterator it = loadedCp.begin(); it != loadedCp.end(); ++it)
3162 {
3164 if (cp.image1Nr > cp.image2Nr)
3165 {
3166 // mirror control points if first images has a higher number than the second
3167 cp.mirror();
3168 };
3169 if (cp.image1Nr < nrImg && cp.image2Nr < nrImg)
3170 {
3171 ctrlPoints.push_back(cp);
3172 };
3173 };
3174 if (loadedCp.size() != ctrlPoints.size())
3175 {
3176 std::cout << "WARNING: Project file contains control points that are connected with" << std::endl
3177 << " non existing images. Ignoring these control points." << std::endl;
3178 };
3179 };
3180
3181 if (images.empty())
3182 {
3183 std::cerr << "ERROR: Project file contains no images." << std::endl;
3184 }
3185 else
3186 {
3188 {
3189 // optimize reference images reference to non-existing image
3191 std::cout << "WARNING: Optimize reference image refers to non existing image. Reset to default value." << std::endl;
3192 };
3194 {
3195 // optimize reference images reference to non-existing image
3197 std::cout << "WARNING: Optimize photometric reference image refers to non existing image. Reset to default value." << std::endl;
3198 };
3199 };
3200 // reset locale
3203
3204 return !images.empty();
3205}
3206
3207} // 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 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