1 // -*- c-basic-offset: 4 -*-
2 /** @file Panorama.cpp
3  *
4  *  @brief implementation of Panorama Class
5  *
6  *  @author Pablo d'Angelo <pablo.dangelo@web.de>
7  *
8  *  $Id$
9  *
10  *  This program is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2 of the License, or (at your option) any later version.
14  *
15  *  This software is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public
21  *  License along with this software. If not, see
22  *  <http://www.gnu.org/licenses/>.
23  *
24  */
25 
26 #include "Panorama.h"
27 
28 #include "PTScriptParsing.h"
29 #include "ImageVariableTranslate.h"
30 #include "StandardImageVariableGroups.h"
31 #include <panotools/PanoToolsInterface.h>
32 #include <algorithms/basic/CalculateOverlap.h>
33 #include <algorithms/basic/LayerStacks.h>
34 #include <panodata/OptimizerSwitches.h>
35 
36 #include <fstream>
37 #include <typeinfo>
38 
39 namespace HuginBase {
40 
Panorama()41 Panorama::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 
~Panorama()62 Panorama::~Panorama()
63 {
64     DEBUG_TRACE("dtor");
65     reset();
66     DEBUG_TRACE("dtor about to finish");
67 }
68 
reset()69 void Panorama::reset()
70 {
71     // delete all images and control points.
72     state.ctrlPoints.clear();
73     state.deleteAllImages();
74     state.options.reset();
75     state.optvec.clear();
76     state.optSwitch=0;
77     state.optPhotoSwitch=0;
78     state.needsOptimization = false;
79     AppBase::DocumentData::setDirty(false);
80     dirty=false;
81 }
82 
getCtrlPointsForImage(unsigned int imgNr) const83 std::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 
getCtrlPointsVectorForImage(unsigned int imgNr) const96 CPointVector Panorama::getCtrlPointsVectorForImage(unsigned int imgNr) const
97 {
98     CPointVector result;
99     for(unsigned int i=0;i<state.ctrlPoints.size();i++)
100     {
101         ControlPoint point=state.ctrlPoints[i];
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 
getVariables() const118 VariableMapVector Panorama::getVariables() const
119 {
120     VariableMapVector map;
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 
getImageVariables(unsigned int imgNr) const128 const VariableMap Panorama::getImageVariables(unsigned int imgNr) const
129 {
130     assert(imgNr < state.images.size());
131     return state.images[imgNr]->getVariableMap();
132 }
133 
134 
updateCtrlPointErrors(const UIntSet & imgs,const CPVector & cps)135 void 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 
updateCtrlPointErrors(const CPVector & cps)160 void Panorama::updateCtrlPointErrors(const CPVector & cps)
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 
updateVariables(const VariableMapVector & vars)171 void Panorama::updateVariables(const VariableMapVector & vars)
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) {
176         updateVariables(i, *it);
177         i++;
178     }
179 }
180 
updateVariables(const UIntSet & imgs,const VariableMapVector & vars)181 void Panorama::updateVariables(const UIntSet & imgs, const VariableMapVector & vars)
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());
186         updateVariables(*it, *v_it);
187         ++v_it;
188     }
189 }
190 
updateVariables(unsigned int imgNr,const VariableMap & var)191 void 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 
updateVariable(unsigned int imgNr,const Variable & var)200 void 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     }
224     state.needsOptimization = true;
225 }
226 
UpdateFocalLength(UIntSet imgs,double newFocalLength)227 void Panorama::UpdateFocalLength(UIntSet imgs, double newFocalLength)
228 {
229     for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
230     {
231         state.images[*it]->updateFocalLength(newFocalLength);
232         imageChanged(*it);
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     {
237         SrcPanoImage *img=state.images[*it];
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                     {
246                         imageChanged(j);
247                     };
248                 };
249             };
250         };
251     };
252 };
253 
UpdateCropFactor(UIntSet imgs,double newCropFactor)254 void Panorama::UpdateCropFactor(UIntSet imgs, double newCropFactor)
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);
266         imageChanged(*it);
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.
274 /// @todo call imageChanged on those images changed by the linking.
275 #define image_variable( name, type, default_value )\
276 void 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 
286 /// @todo call imageChanged on those images changed by the unlinking.
287 #define image_variable( name, type, default_value )\
288 void 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 
setOptimizeVector(const OptimizeVector & optvec)297 void Panorama::setOptimizeVector(const OptimizeVector & optvec)
298 {
299     DEBUG_ASSERT(optvec.size() == state.images.size());
300     state.optvec = optvec;
301 }
302 
setOptimizerSwitch(const int newSwitch)303 void Panorama::setOptimizerSwitch(const int newSwitch)
304 {
305     if(state.optSwitch!=newSwitch)
306     {
307         state.optSwitch=newSwitch;
308     };
309 };
310 
setPhotometricOptimizerSwitch(const int newSwitch)311 void Panorama::setPhotometricOptimizerSwitch(const int newSwitch)
312 {
313     if(state.optPhotoSwitch!=newSwitch)
314     {
315         state.optPhotoSwitch=newSwitch;
316     };
317 };
318 
addImage(const SrcPanoImage & img)319 unsigned 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>());
325     imageChanged(nr);
326     return nr;
327 }
328 
removeImage(unsigned int imgNr)329 void 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
354 	if (state.options.optimizeReferenceImage >= state.images.size()) {
355 		state.options.optimizeReferenceImage = 0;
356         imageChanged(state.options.optimizeReferenceImage);
357 	}
358 
359 	if (state.options.colorReferenceImage >= state.images.size()) {
360 		state.options.colorReferenceImage = 0;
361         imageChanged(state.options.colorReferenceImage);
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++) {
367         imageChanged(i);
368     }
369     m_forceImagesUpdate = true;
370 }
371 
372 
setImageFilename(unsigned int i,const std::string & fname)373 void Panorama::setImageFilename(unsigned int i, const std::string & fname)
374 {
375     DEBUG_ASSERT(i < state.images.size());
376     state.images[i]->setFilename(fname);
377     imageChanged(i);
378     m_forceImagesUpdate = true;
379 }
380 
addCtrlPoint(const ControlPoint & point)381 unsigned 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);
387     state.needsOptimization = true;
388     return nr;
389 }
390 
removeCtrlPoint(unsigned int pNr)391 void 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
400     updateLineCtrlPoints();
401     imageChanged(i1);
402     imageChanged(i2);
403     state.needsOptimization = true;
404 }
405 
removeDuplicateCtrlPoints()406 void Panorama::removeDuplicateCtrlPoints()
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         {
424             ControlPoint cp=state.ctrlPoints[*it];
425             imageChanged(cp.image1Nr);
426             imageChanged(cp.image2Nr);
427             removeCtrlPoint(*it);
428         };
429     };
430     updateLineCtrlPoints();
431 }
432 
433 
changeControlPoint(unsigned int pNr,const ControlPoint & point)434 void 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);
443     state.needsOptimization = true;
444 
445     state.ctrlPoints[pNr] = point;
446     updateLineCtrlPoints();
447 }
448 
setCtrlPoints(const CPVector & points)449 void Panorama::setCtrlPoints(const CPVector & points)
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 
458     state.ctrlPoints = points;
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     }
466     state.needsOptimization = true;
467     updateLineCtrlPoints();
468 }
469 
470 // close holes in line control points
updateLineCtrlPoints()471 void Panorama::updateLineCtrlPoints()
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 
printPanoramaScript(std::ostream & o,const OptimizeVector & optvars,const PanoramaOptions & output,const UIntSet & imgs,bool forPTOptimizer,const std::string & stripPrefix) const503 void 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
511     char * old_locale = strdup(setlocale(LC_NUMERIC, NULL));
512     setlocale(LC_NUMERIC,"C");
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())
647                 img.printMaskLines(masklines,ic);
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) {
699         case PanoramaOptions::NO_BLEND:
700             o << "none" << endl;
701             break;
702         case PanoramaOptions::PTBLENDER_BLEND:
703             o << "PTblender" << endl;
704             break;
705         case PanoramaOptions::SMARTBLEND_BLEND:
706             o << "smartblend" << endl;
707             break;
708         case PanoramaOptions::PTMASKER_BLEND:
709             o << "PTmasker" << endl;
710             break;
711         case PanoramaOptions::INTERNAL_BLEND:
712             o << "internal" << endl;
713             break;
714         default:
715         case PanoramaOptions::ENBLEND_BLEND:
716             o << "enblend" << endl;
717             break;
718     }
719 
720     o << "#hugin_remapper ";
721     switch (output.remapper) {
722         case PanoramaOptions::PTMENDER:
723             o << "PTmender" << endl;
724             break;
725         default:
726         case PanoramaOptions::NONA:
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 
736     o << "#hugin_outputLDRBlended " << (output.outputLDRBlended ? "true" : "false") << endl;
737     o << "#hugin_outputLDRLayers " << (output.outputLDRLayers ? "true" : "false") << endl;
738     o << "#hugin_outputLDRExposureRemapped " << (output.outputLDRExposureRemapped ? "true" : "false") << endl;
739     o << "#hugin_outputLDRExposureLayers " << (output.outputLDRExposureLayers ? "true" : "false") << endl;
740     o << "#hugin_outputLDRExposureBlended " << (output.outputLDRExposureBlended ? "true" : "false") << endl;
741     o << "#hugin_outputLDRStacks " << (output.outputLDRStacks ? "true" : "false") << endl;
742     o << "#hugin_outputLDRExposureLayersFused " << (output.outputLDRExposureLayersFused ? "true" : "false") << endl;
743     o << "#hugin_outputHDRBlended " << (output.outputHDRBlended ? "true" : "false") << endl;
744     o << "#hugin_outputHDRLayers " << (output.outputHDRLayers ? "true" : "false") << endl;
745     o << "#hugin_outputHDRStacks " << (output.outputHDRStacks ? "true" : "false") << endl;
746 
747     o << "#hugin_outputLayersCompression " << output.outputLayersCompression << endl;
748     o << "#hugin_outputImageType " << output.outputImageType << endl;
749     o << "#hugin_outputImageTypeCompression " << output.outputImageTypeCompression << endl;
750     o << "#hugin_outputJPEGQuality " << output.quality << endl;
751     o << "#hugin_outputImageTypeHDR " << output.outputImageTypeHDR << endl;
752     o << "#hugin_outputImageTypeHDRCompression " << output.outputImageTypeHDRCompression << endl;
753 
754     o << "#hugin_outputStacksMinOverlap " << std::setprecision(3) << output.outputStacksMinOverlap << endl;
755     o << "#hugin_outputLayersExposureDiff " << std::setprecision(2) << output.outputLayersExposureDiff << endl;
756 
757     o << "#hugin_outputRangeCompression " << std::setprecision(2) << output.outputRangeCompression << endl;
758 
759     if(optvars==getOptimizeVector())
760     {
761         o << "#hugin_optimizerMasterSwitch " << getOptimizerSwitch() << endl;
762         o << "#hugin_optimizerPhotoMasterSwitch " << getPhotometricOptimizerSwitch() << endl;
763     }
764     else
765     {
766         o << "#hugin_optimizerMasterSwitch 0" << endl;
767         o << "#hugin_optimizerPhotoMasterSwitch 0" << endl;
768     };
769 
770     // reset locale
771     setlocale(LC_NUMERIC,old_locale);
772     free(old_locale);
773 }
774 
775 
printStitcherScript(std::ostream & o,const PanoramaOptions & target,const UIntSet & imgs) const776 void Panorama::printStitcherScript(std::ostream & o,
777                                    const PanoramaOptions & target,
778                                    const UIntSet & imgs) const
779 {
780     // set numeric locale to C, for correct number output
781     char * old_locale = strdup(setlocale(LC_NUMERIC, NULL));
782     setlocale(LC_NUMERIC,"C");
783 
784     o << "# PTStitcher script, written by hugin" << std::endl
785       << std::endl;
786     // output options..
787     target.printScriptLine(o, true);
788     o << std::endl
789       << "# output image lines" << std::endl;
790     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end(); ++imgNrIt) {
791         unsigned int imgNr = *imgNrIt;
792         const SrcPanoImage & img = *state.images[imgNr];
793 // DGSW FIXME - Unreferenced
794 //		const Lens & lens = state.lenses[lensNr];
795         const VariableMap & vars = state.images[imgNr]->getVariableMap();
796 
797         o << "o w" << img.getSize().width() << " h" << img.getSize().height()
798           <<" f" << img.getProjection() << " ";
799         // print variables, without links
800         VariableMap::const_iterator vit;
801         for(vit = vars.begin(); vit != vars.end();  ++vit)
802         {
803             if (!set_contains(m_ptoptimizerVarNames,vit->first)) {
804                 continue;
805             }
806             vit->second.print(o) << " ";
807         }
808 #if 0
809 // panotools parameters, currently not used
810         o << " u" << img.getFeatureWidth()
811           << (img.getMorph() ? " o" : "");
812 #endif
813         o << " n\"" << img.getFilename() << "\"";
814         if (img.getCropMode()!=SrcPanoImage::NO_CROP) {
815             // print crop parameters
816             vigra::Rect2D c = img.getCropRect();
817             o << " S" << c.left() << "," << c.right() << "," << c.top() << "," << c.bottom();
818         }
819         o << std::endl;
820     }
821     o << std::endl;
822 
823     // reset locale
824     setlocale(LC_NUMERIC,old_locale);
825     free(old_locale);
826 
827 }
828 
parseOptimizerScript(std::istream & i,const UIntSet & imgs,VariableMapVector & imgVars,CPVector & CPs) const829 void Panorama::parseOptimizerScript(std::istream & i, const UIntSet & imgs,
830                                     VariableMapVector & imgVars, CPVector & CPs) const
831 {
832     DEBUG_TRACE("");
833     // set numeric locale to C, for correct number output
834     char * old_locale = strdup(setlocale(LC_NUMERIC, ""));
835     setlocale(LC_NUMERIC,"C");
836 
837     unsigned int ic=0;
838     std::map<unsigned int, unsigned int> script2ImgMap;
839     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
840          ++imgNrIt)
841     {
842         unsigned int imgNr = *imgNrIt;
843         script2ImgMap[ic] = imgNr;
844         ic++;
845     }
846     ic = 0;
847     unsigned int sc = 0;
848     std::map<unsigned int, unsigned int> script2CPMap;
849     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
850         if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
851             script2CPMap[sc] = ic;
852             sc++;
853         }
854         ic++;
855     }
856 
857 
858 
859     // 0 = read output (image lines), 1 = read control point distances
860     int state = 0;
861     std::string line;
862     unsigned int lineNr = 0;
863     unsigned int scriptImgCounter = 0;
864     unsigned int scriptCPCounter = 0;
865 //    VariableMapVector::iterator varIt = imgVars.begin();
866 //    CPVector::iterator pointIt = CPs.begin();
867 
868 // DGSW FIXME - Unreferenced
869 //	int pnr=0;
870 
871     while (!i.eof()) {
872         std::getline(i, line);
873         lineNr++;
874         switch (state) {
875         case 0:
876         {
877             // we are reading the output lines:
878             // o f3 r0 p0 y0 v89.2582 a-0.027803 b0.059851 c-0.073115 d10.542470 e16.121145 u10 -buf
879             if ((line.compare("# Control Points: Distance between desired and fitted Position") == 0 )
880              || (line.compare("# Control Points: Distance between desired and fitted Position (in Pixels)") == 0 )
881              || (line.compare("# Control Points: Distance between desired and fitted Position (in \"Pixels\")") == 0 )) {
882 
883                 // switch to reading the control point distance
884                 if (scriptImgCounter != imgs.size()) {
885                     DEBUG_ERROR("Read only " << scriptImgCounter << " images from PTOptimizer file");
886                 }
887                 DEBUG_DEBUG("Changing state to read control point distances");
888                 state = 1;
889                 break;
890             }
891             if (line[0] != 'o') continue;
892             // select variables of the image
893             VariableMap & var = imgVars[script2ImgMap[scriptImgCounter]];
894             DEBUG_DEBUG("reading image variables for image:" << scriptImgCounter);
895             // read position variables
896             int link;
897             PTScriptParsing::readVar(map_get(var, "r"), link, line);
898             DEBUG_ASSERT(link == -1);
899             PTScriptParsing::readVar(map_get(var, "p"), link, line);
900             DEBUG_ASSERT(link == -1);
901             PTScriptParsing::readVar(map_get(var, "y"), link, line);
902             DEBUG_ASSERT(link == -1);
903 
904             DEBUG_DEBUG("yaw: " << map_get(var, "y").getValue()
905                         << " pitch " << map_get(var, "p").getValue()
906                         << " roll " << map_get(var, "r").getValue());
907             // read lens variables
908 
909             PTScriptParsing::readVar(map_get(var, "TrX"), link, line);
910             DEBUG_ASSERT(link == -1);
911             PTScriptParsing::readVar(map_get(var, "TrY"), link, line);
912             DEBUG_ASSERT(link == -1);
913             PTScriptParsing::readVar(map_get(var, "TrZ"), link, line);
914             DEBUG_ASSERT(link == -1);
915             PTScriptParsing::readVar(map_get(var, "Tpy"), link, line);
916             DEBUG_ASSERT(link == -1);
917             PTScriptParsing::readVar(map_get(var, "Tpp"), link, line);
918             DEBUG_ASSERT(link == -1);
919 
920             DEBUG_DEBUG("X: " << map_get(var, "TrX").getValue()
921                         << " Y " << map_get(var, "TrY").getValue()
922                         << " Z " << map_get(var, "TrZ").getValue());
923             // read lens variables
924 
925 
926             for (const char **c = Lens::variableNames; *c != 0; ++c) {
927                 Variable & curVar = map_get(var, *c);
928                 if (!PTScriptParsing::readVar(curVar, link, line)) {
929                     DEBUG_ERROR("Could not read "<< *c << " at script line " << lineNr);
930                 }
931                 // linking in output forbidden
932                 DEBUG_ASSERT(link == -1);
933             }
934             scriptImgCounter++;
935             break;
936         }
937         case 1:
938         {
939             // read ctrl point distances:
940             // # Control Point No 0:  0.428994
941             if (line[0] == 'C') {
942 //                DEBUG_DEBUG(CPs.size() << " points, read: " << pnr);
943                 state = 2;
944                 break;
945             }
946             if (line.find("# Control Point No") != 0) continue;
947             DEBUG_DEBUG("reading cp dist line: " << line);
948             std::string::size_type p;
949             if ((p=line.find(':')) == std::string::npos) assert(0);
950             p++;
951             DEBUG_DEBUG("parsing point " << scriptCPCounter << " (idx:" << p << "): " << line.substr(p));
952             double err = -1;
953 
954             hugin_utils::stringToDouble(line.substr(p), err);
955             CPs[script2CPMap[scriptCPCounter]].error = err;
956             DEBUG_DEBUG("read CP distance " << err);
957             scriptCPCounter++;
958             break;
959         }
960         default:
961             // ignore line..
962             break;
963         }
964     }
965 
966     // reset locale
967     setlocale(LC_NUMERIC,old_locale);
968     free(old_locale);
969 }
970 
changeFinished(bool keepDirty)971 void Panorama::changeFinished(bool keepDirty)
972 {
973     if (state.images.empty()) {
974         // force an empty update if all images have been
975         // removed
976         DEBUG_DEBUG("forcing images update, with no images");
977         m_forceImagesUpdate = true;
978     }
979     // remove change notification for nonexisting images from set.
980     UIntSet::iterator uB = changedImages.lower_bound(state.images.size());
981     changedImages.erase(uB,changedImages.end());
982 
983     std::stringstream t;
984     copy(changedImages.begin(), changedImages.end(),
985          std::ostream_iterator<unsigned int>(t, " "));
986     DEBUG_TRACE("changed image(s) " << t.str() << " begin");
987     //force update of crops
988     if(!changedImages.empty())
989     {
990         for(UIntSet::iterator it=changedImages.begin();it!=changedImages.end();++it)
991         {
992             //if the projection was changed, we need to update the crop mode
993             updateCropMode(*it);
994             //now center the crop if user requested it
995             if(state.images[*it]->getAutoCenterCrop())
996             {
997                 centerCrop(*it);
998             };
999         };
1000     };
1001     //update masks
1002     updateMasks();
1003     updateOptimizeVector();
1004     std::list<PanoramaObserver *>::iterator it;
1005     for(it = observers.begin(); it != observers.end(); ++it) {
1006         DEBUG_TRACE("notifying listener");
1007         if (!changedImages.empty() || m_forceImagesUpdate) {
1008             (*it)->panoramaImagesChanged(*this, changedImages);
1009         }
1010         (*it)->panoramaChanged(*this);
1011     }
1012     // reset changed images
1013     changedImages.clear();
1014     m_forceImagesUpdate = false;
1015     if (!keepDirty) {
1016         dirty = true;
1017         AppBase::DocumentData::setDirty(dirty);
1018     }
1019     DEBUG_TRACE("end");
1020 }
1021 
updateMasksForImage(unsigned int imgNr,MaskPolygonVector newMasks)1022 void Panorama::updateMasksForImage(unsigned int imgNr, MaskPolygonVector newMasks)
1023 {
1024     DEBUG_ASSERT(imgNr < state.images.size());
1025     state.images[imgNr]->setMasks(newMasks);
1026     imageChanged(imgNr);
1027     m_forceImagesUpdate = true;
1028 };
1029 
transferMask(MaskPolygon mask,unsigned int imgNr,const UIntSet & targetImgs)1030 void Panorama::transferMask(MaskPolygon mask,unsigned int imgNr, const UIntSet& targetImgs)
1031 {
1032     if(targetImgs.empty())
1033     {
1034         return;
1035     };
1036     MaskPolygon transformedMask=mask;
1037     // clip positive mask to image boundaries or clip region
1038     vigra::Rect2D clipRect=vigra::Rect2D(0,0,state.images[imgNr]->getWidth(),state.images[imgNr]->getHeight());
1039     if(mask.isPositive())
1040     {
1041         //clip to crop region only positive masks
1042         switch(state.images[imgNr]->getCropMode())
1043         {
1044             case BaseSrcPanoImage::CROP_RECTANGLE:
1045                 clipRect=clipRect & state.images[imgNr]->getCropRect();
1046                 if(clipRect.isEmpty())
1047                 {
1048                     return;
1049                 };
1050                 if(clipRect.width()>10 && clipRect.height()>10)
1051                 {
1052                     clipRect.addBorder(-2);
1053                 };
1054                 break;
1055             case BaseSrcPanoImage::CROP_CIRCLE:
1056                 {
1057                     vigra::Rect2D cropRect=state.images[imgNr]->getCropRect();
1058                     hugin_utils::FDiff2D center((cropRect.left()+cropRect.right())/2.0,(cropRect.top()+cropRect.bottom())/2.0);
1059                     double radius=((cropRect.width()<cropRect.height())?cropRect.width():cropRect.height())/2.0;
1060                     if(radius>10)
1061                     {
1062                         radius-=2;
1063                     };
1064                     if(!transformedMask.clipPolygon(center,radius))
1065                     {
1066                         return;
1067                     };
1068                 };
1069                 break;
1070             default:
1071                 if(clipRect.width()>10 && clipRect.height()>10)
1072                 {
1073                     clipRect.addBorder(-2);
1074                 };
1075                 break;
1076         };
1077     };
1078     int origWindingNumber=transformedMask.getTotalWindingNumber();
1079     if(transformedMask.clipPolygon(clipRect))
1080     {
1081         //increase resolution of positive mask to get better transformation
1082         //of vertices, especially for fisheye images
1083         transformedMask.subSample(20);
1084         //transform polygon to panorama space
1085         HuginBase::PTools::Transform trans;
1086         trans.createInvTransform(getImage(imgNr),getOptions());
1087         transformedMask.transformPolygon(trans);
1088         for(UIntSet::const_iterator it=targetImgs.begin();it!=targetImgs.end();++it)
1089         {
1090             if(imgNr==(*it))
1091             {
1092                 continue;
1093             };
1094             MaskPolygon targetMask;
1095             if(state.images[imgNr]->YawisLinkedWith(*(state.images[*it])))
1096             {
1097                 //if yaw is linked, we simply copy the mask
1098                 targetMask=mask;
1099             }
1100             else
1101             {
1102                 targetMask=transformedMask;
1103                 PTools::Transform targetTrans;
1104                 targetTrans.createTransform(getImage(*it),getOptions());
1105                 targetMask.transformPolygon(targetTrans);
1106                 //check if transformation has produced invalid polygon
1107                 if(targetMask.getMaskPolygon().size()<3)
1108                 {
1109                     continue;
1110                 };
1111                 //check if mask was inverted - outside became inside and vice versa
1112                 //if so, invert mask
1113                 int newWindingNumber=targetMask.getTotalWindingNumber();
1114                 targetMask.setInverted(origWindingNumber * newWindingNumber < 0);
1115             };
1116             //now clip polygon to image rectangle, add mask only when polygon is inside image
1117             if(targetMask.clipPolygon(vigra::Rect2D(-maskOffset,-maskOffset,
1118                                       state.images[*it]->getWidth()+maskOffset,state.images[*it]->getHeight()+maskOffset)))
1119             {
1120                 targetMask.setMaskType(MaskPolygon::Mask_negative);
1121                 targetMask.setImgNr(*it);
1122                 state.images[*it]->addActiveMask(targetMask);
1123             };
1124         };
1125     };
1126 };
1127 
updateMasks(bool convertPosMaskToNeg)1128 void Panorama::updateMasks(bool convertPosMaskToNeg)
1129 {
1130     // update masks
1131     UIntSet imgWithPosMasks;
1132     for(unsigned int i=0;i<state.images.size();i++)
1133     {
1134         state.images[i]->clearActiveMasks();
1135         if(state.images[i]->hasPositiveMasks())
1136         {
1137             imgWithPosMasks.insert(i);
1138         };
1139     };
1140     CalculateImageOverlap overlap(this);
1141     overlap.limitToImages(imgWithPosMasks);
1142     overlap.calculate(10);
1143     ConstStandardImageVariableGroups variable_groups(*this);
1144     ConstImageVariableGroup & lenses = variable_groups.getLenses();
1145     for(unsigned int i=0;i<state.images.size();i++)
1146     {
1147         if(state.images[i]->hasMasks())
1148         {
1149             MaskPolygonVector masks=state.images[i]->getMasks();
1150             for(unsigned int j=0;j<masks.size();j++)
1151             {
1152                 if(convertPosMaskToNeg)
1153                 {
1154                     //this is used for masking in the cp finder, we are consider
1155                     //all masks as negative masks, because at this moment
1156                     //the final position of the images is not known
1157                     switch(masks[j].getMaskType())
1158                     {
1159                         case MaskPolygon::Mask_negative:
1160                         case MaskPolygon::Mask_positive:
1161                             masks[j].setImgNr(i);
1162                             masks[j].setMaskType(MaskPolygon::Mask_negative);
1163                             state.images[i]->addActiveMask(masks[j]);
1164                             break;
1165                         case MaskPolygon::Mask_Stack_negative:
1166                         case MaskPolygon::Mask_Stack_positive:
1167                             {
1168                                 //copy mask to all images of the same stack
1169                                 UIntSet imgStack;
1170                                 for(unsigned int k=0;k<getNrOfImages();k++)
1171                                 {
1172                                     if(i!=k)
1173                                     {
1174                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1175                                         {
1176                                             imgStack.insert(k);
1177                                         };
1178                                     };
1179                                 };
1180                                 masks[j].setImgNr(i);
1181                                 masks[j].setMaskType(MaskPolygon::Mask_negative);
1182                                 state.images[i]->addActiveMask(masks[j]);
1183                                 transferMask(masks[j],i,imgStack);
1184                             };
1185                             break;
1186                         case MaskPolygon::Mask_negative_lens:
1187                             {
1188                                 unsigned int lensNr=lenses.getPartNumber(i);
1189                                 //copy masks to all image of the same lens
1190                                 for(unsigned int k=0;k<getNrOfImages();k++)
1191                                 {
1192                                     if(lenses.getPartNumber(k)==lensNr)
1193                                     {
1194                                         masks[j].setImgNr(k);
1195                                         masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
1196                                         state.images[k]->addActiveMask(masks[j]);
1197                                     };
1198                                 };
1199                             };
1200                             break;
1201                     };
1202                 }
1203                 else
1204                 {
1205                     switch(masks[j].getMaskType())
1206                     {
1207                         case MaskPolygon::Mask_negative:
1208                             //negative mask, simply copy mask to active mask
1209                             masks[j].setImgNr(i);
1210                             state.images[i]->addActiveMask(masks[j]);
1211                             break;
1212                         case MaskPolygon::Mask_positive:
1213                             //propagate positive mask only if image is active
1214                             if(state.images[i]->getActive())
1215                             {
1216                                 UIntSet overlapImgs=overlap.getOverlapForImage(i);
1217                                 transferMask(masks[j],i,overlapImgs);
1218                             };
1219                             break;
1220                         case MaskPolygon::Mask_Stack_negative:
1221                             {
1222                                 //search all images of the stack
1223                                 UIntSet imgStack;
1224                                 for(unsigned int k=0;k<getNrOfImages();k++)
1225                                 {
1226                                     if(i!=k)
1227                                     {
1228                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1229                                         {
1230                                             imgStack.insert(k);
1231                                         };
1232                                     };
1233                                 };
1234                                 //copy mask also to the image which contains the mask
1235                                 masks[j].setImgNr(i);
1236                                 masks[j].setMaskType(MaskPolygon::Mask_negative);
1237                                 state.images[i]->addActiveMask(masks[j]);
1238                                 transferMask(masks[j],i,imgStack);
1239                             };
1240                             break;
1241                         case MaskPolygon::Mask_Stack_positive:
1242                             {
1243                                 //remove all images from the stack from the set
1244                                 UIntSet imgStack;
1245                                 fill_set(imgStack,0,getNrOfImages()-1);
1246                                 imgStack.erase(i);
1247                                 for(unsigned int k=0;k<getNrOfImages();k++)
1248                                 {
1249                                     if(i!=k)
1250                                     {
1251                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
1252                                         {
1253                                             imgStack.erase(k);
1254                                         };
1255                                     };
1256                                 };
1257                                 //only leave overlapping images in set
1258                                 UIntSet imgOverlap=overlap.getOverlapForImage(i);
1259                                 UIntSet imgs;
1260                                 std::set_intersection(imgStack.begin(),imgStack.end(),imgOverlap.begin(),imgOverlap.end(),inserter(imgs,imgs.begin()));
1261                                 //now transfer mask
1262                                 transferMask(masks[j],i,imgs);
1263                             };
1264                             break;
1265                         case MaskPolygon::Mask_negative_lens:
1266                             {
1267                                 unsigned int lensNr=lenses.getPartNumber(i);
1268                                 //copy masks to all image of the same lens
1269                                 for(unsigned int k=0;k<getNrOfImages();k++)
1270                                 {
1271                                     if(lenses.getPartNumber(k)==lensNr)
1272                                     {
1273                                         masks[j].setImgNr(k);
1274                                         masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
1275                                         state.images[k]->addActiveMask(masks[j]);
1276                                     };
1277                                 };
1278                             };
1279                             break;
1280                     };
1281                 };
1282             };
1283         };
1284     };
1285 };
1286 
updateCropMode(unsigned int imgNr)1287 void Panorama::updateCropMode(unsigned int imgNr)
1288 {
1289     vigra::Rect2D r=state.images[imgNr]->getCropRect();
1290     if(r.isEmpty() || r==vigra::Rect2D(state.images[imgNr]->getSize()))
1291     {
1292         state.images[imgNr]->setCropMode(SrcPanoImage::NO_CROP);
1293     }
1294     else
1295     {
1296         if (state.images[imgNr]->isCircularCrop())
1297         {
1298             state.images[imgNr]->setCropMode(SrcPanoImage::CROP_CIRCLE);
1299         }
1300         else
1301         {
1302             state.images[imgNr]->setCropMode(SrcPanoImage::CROP_RECTANGLE);
1303         };
1304     };
1305 };
1306 
centerCropImage(unsigned int imgNr)1307 vigra::Rect2D Panorama::centerCropImage(unsigned int imgNr)
1308 {
1309     vigra::Rect2D cropRect;
1310     if(state.images[imgNr]->getCropMode()==SrcPanoImage::NO_CROP)
1311     {
1312         return cropRect;
1313     };
1314     int dx = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().x);
1315     int dy = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().y);
1316     vigra::Point2D center = vigra::Point2D(state.images[imgNr]->getSize().width()/2 + dx, state.images[imgNr]->getSize().height()/2 + dy);
1317 
1318     vigra::Diff2D d(state.images[imgNr]->getCropRect().width() / 2, state.images[imgNr]->getCropRect().height() / 2);
1319     cropRect.setUpperLeft( center - d);
1320     cropRect.setLowerRight( center + d);
1321     return cropRect;
1322 };
1323 
centerCrop(unsigned int imgNr)1324 void Panorama::centerCrop(unsigned int imgNr)
1325 {
1326     vigra::Rect2D cropRect;
1327     if(  state.images[imgNr]->getCropMode()!=SrcPanoImage::NO_CROP &&
1328          state.images[imgNr]->getAutoCenterCrop() &&
1329        !(state.images[imgNr]->getCropRect().isEmpty())
1330        )
1331     {
1332         cropRect=centerCropImage(imgNr);
1333         if(!cropRect.isEmpty())
1334         {
1335             state.images[imgNr]->setCropRect(cropRect);
1336             imageChanged(imgNr);
1337         };
1338     };
1339     for (std::size_t i = 0; i < getNrOfImages(); i++)
1340     {
1341         if(i==imgNr)
1342         {
1343             continue;
1344         };
1345         if (state.images[imgNr]->RadialDistortionCenterShiftisLinkedWith(*state.images[i]))
1346         {
1347             if(  state.images[i]->getCropMode()!=SrcPanoImage::NO_CROP &&
1348                  state.images[i]->getAutoCenterCrop() &&
1349                !(state.images[i]->getCropRect().isEmpty())
1350                )
1351             {
1352                 cropRect=centerCropImage(i);
1353                 if(!cropRect.isEmpty())
1354                 {
1355                     state.images[i]->setCropRect(cropRect);
1356                     imageChanged(i);
1357                 };
1358             };
1359         };
1360     };
1361 };
1362 
UpdateOptVectorSet(std::set<std::string> & imgVar,const std::string & var,const bool opt)1363 void UpdateOptVectorSet(std::set<std::string>& imgVar, const std::string& var, const bool opt)
1364 {
1365     if(opt)
1366     {
1367         imgVar.insert(var);
1368     }
1369     else
1370     {
1371         imgVar.erase(var);
1372     };
1373 };
1374 
getRefImages()1375 std::set<size_t> Panorama::getRefImages()
1376 {
1377     unsigned int refImg = getOptions().optimizeReferenceImage;
1378     std::set<size_t> refImgs;
1379     refImgs.insert(refImg);
1380     const HuginBase::SrcPanoImage & refImage = getImage(refImg);
1381     for (size_t imgNr = 0; imgNr < getNrOfImages(); imgNr++)
1382     {
1383         if(imgNr!=refImg)
1384         {
1385             const HuginBase::SrcPanoImage & compImage = getImage(imgNr);
1386             if (refImage.YawisLinkedWith(compImage))
1387             {
1388                 refImgs.insert(imgNr);
1389             };
1390         };
1391     };
1392     return refImgs;
1393 };
1394 
checkRefOptStatus(bool & linkRefImgsYaw,bool & linkRefImgsPitch,bool & linkRefImgsRoll)1395 void Panorama::checkRefOptStatus(bool& linkRefImgsYaw, bool& linkRefImgsPitch, bool& linkRefImgsRoll)
1396 {
1397     // count number of vertical/horizontal control points
1398     int nHCP = 0;
1399     int nVCP = 0;
1400     const CPVector & cps = getCtrlPoints();
1401     for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1402     {
1403         // control points
1404         if (it->mode == ControlPoint::X)
1405         {
1406             nVCP++;
1407         }
1408         else
1409         {
1410             if (it->mode == ControlPoint::Y)
1411             {
1412                 nHCP++;
1413             }
1414         };
1415     };
1416 
1417     // try to select sensible position optimisation parameters,
1418     // dependent on output projection
1419     linkRefImgsYaw=false;
1420     linkRefImgsPitch=false;
1421     linkRefImgsRoll=false;
1422     switch (getOptions().getProjection())
1423     {
1424         case PanoramaOptions::RECTILINEAR:
1425             linkRefImgsRoll = nVCP + nHCP >= 1;
1426             linkRefImgsYaw = nVCP + nHCP >= 3 && nVCP >= 1 && nHCP >= 1;
1427             linkRefImgsPitch = nVCP + nHCP >= 2;
1428             break;
1429         case PanoramaOptions::CYLINDRICAL:
1430         case PanoramaOptions::EQUIRECTANGULAR:
1431             linkRefImgsPitch =  nHCP + nVCP > 1;
1432             linkRefImgsRoll = nHCP + nVCP >= 1;
1433             break;
1434         default:
1435             break;
1436     };
1437 };
1438 
updateOptimizeVector()1439 void Panorama::updateOptimizeVector()
1440 {
1441     if(state.images.empty())
1442     {
1443         return;
1444     };
1445     if(state.optSwitch!=0)
1446     {
1447         std::set<size_t> refImgs=getRefImages();
1448         bool linkRefImgsYaw=false;
1449         bool linkRefImgsPitch=false;
1450         bool linkRefImgsRoll=false;
1451         checkRefOptStatus(linkRefImgsYaw, linkRefImgsPitch, linkRefImgsRoll);
1452 
1453         for(size_t i=0;i<getNrOfImages();i++)
1454         {
1455             if(state.optSwitch & OPT_PAIR || state.optSwitch & OPT_POSITION || state.optSwitch & OPT_ALL)
1456             {
1457                 if(set_contains(refImgs,i))
1458                 {
1459                     UpdateOptVectorSet(state.optvec[i],"y",linkRefImgsYaw);
1460                     UpdateOptVectorSet(state.optvec[i],"p",linkRefImgsPitch);
1461                     UpdateOptVectorSet(state.optvec[i],"r",linkRefImgsRoll);
1462                     //don't optimize translation parameters of anchor
1463                     UpdateOptVectorSet(state.optvec[i],"TrX",false);
1464                     UpdateOptVectorSet(state.optvec[i],"TrY",false);
1465                     UpdateOptVectorSet(state.optvec[i],"TrZ",false);
1466                 }
1467                 else
1468                 {
1469                     UpdateOptVectorSet(state.optvec[i],"y",true);
1470                     UpdateOptVectorSet(state.optvec[i],"p",true);
1471                     UpdateOptVectorSet(state.optvec[i],"r",true);
1472                     UpdateOptVectorSet(state.optvec[i],"TrX",(state.optSwitch & OPT_TRANSLATION)>0);
1473                     UpdateOptVectorSet(state.optvec[i],"TrY",(state.optSwitch & OPT_TRANSLATION)>0);
1474                     UpdateOptVectorSet(state.optvec[i],"TrZ",(state.optSwitch & OPT_TRANSLATION)>0);
1475                 };
1476             }
1477             else
1478             {
1479                 UpdateOptVectorSet(state.optvec[i],"y",false);
1480                 UpdateOptVectorSet(state.optvec[i],"p",false);
1481                 UpdateOptVectorSet(state.optvec[i],"r",false);
1482                 UpdateOptVectorSet(state.optvec[i],"Trx",false);
1483                 UpdateOptVectorSet(state.optvec[i],"Try",false);
1484                 UpdateOptVectorSet(state.optvec[i],"Trz",false);
1485             };
1486             UpdateOptVectorSet(state.optvec[i],"v",state.optSwitch & OPT_VIEW || state.optSwitch & OPT_ALL);
1487             UpdateOptVectorSet(state.optvec[i],"a",(state.optSwitch & OPT_ALL)>0);
1488             UpdateOptVectorSet(state.optvec[i],"b",state.optSwitch & OPT_BARREL || state.optSwitch & OPT_ALL);
1489             UpdateOptVectorSet(state.optvec[i],"c",(state.optSwitch & OPT_ALL)>0);
1490             UpdateOptVectorSet(state.optvec[i],"d",(state.optSwitch & OPT_ALL)>0);
1491             UpdateOptVectorSet(state.optvec[i],"e",(state.optSwitch & OPT_ALL)>0);
1492             //shear and translation plane not include in master switches
1493             UpdateOptVectorSet(state.optvec[i],"g",false);
1494             UpdateOptVectorSet(state.optvec[i],"t",false);
1495             UpdateOptVectorSet(state.optvec[i],"Tpy", false);
1496             UpdateOptVectorSet(state.optvec[i],"Tpp", false);
1497         };
1498     };
1499     if(state.optPhotoSwitch!=0)
1500     {
1501         for(size_t i=0;i<getNrOfImages();i++)
1502         {
1503             UpdateOptVectorSet(state.optvec[i],"Eev",state.optPhotoSwitch & OPT_EXPOSURE && i!=state.options.colorReferenceImage);
1504             UpdateOptVectorSet(state.optvec[i],"Er", state.optPhotoSwitch & OPT_WHITEBALANCE && i!=state.options.colorReferenceImage);
1505             UpdateOptVectorSet(state.optvec[i],"Eb", state.optPhotoSwitch & OPT_WHITEBALANCE && i!=state.options.colorReferenceImage);
1506             UpdateOptVectorSet(state.optvec[i],"Vb", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
1507             UpdateOptVectorSet(state.optvec[i],"Vc", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
1508             UpdateOptVectorSet(state.optvec[i],"Vd", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
1509             UpdateOptVectorSet(state.optvec[i],"Vx", (state.optPhotoSwitch & OPT_VIGNETTING_CENTER)>0);
1510             UpdateOptVectorSet(state.optvec[i],"Vy", (state.optPhotoSwitch & OPT_VIGNETTING_CENTER)>0);
1511             UpdateOptVectorSet(state.optvec[i],"Ra", (state.optPhotoSwitch & OPT_RESPONSE)>0);
1512             UpdateOptVectorSet(state.optvec[i],"Rb", (state.optPhotoSwitch & OPT_RESPONSE)>0);
1513             UpdateOptVectorSet(state.optvec[i],"Rc", (state.optPhotoSwitch & OPT_RESPONSE)>0);
1514             UpdateOptVectorSet(state.optvec[i],"Rd", (state.optPhotoSwitch & OPT_RESPONSE)>0);
1515             UpdateOptVectorSet(state.optvec[i],"Re", (state.optPhotoSwitch & OPT_RESPONSE)>0);
1516         };
1517     };
1518 };
1519 
swapImages(unsigned int img1,unsigned int img2)1520 void Panorama::swapImages(unsigned int img1, unsigned int img2)
1521 {
1522     DEBUG_TRACE("swapping images " << img1 << ", " << img2);
1523     DEBUG_ASSERT(img1 < state.images.size());
1524     DEBUG_ASSERT(img2 < state.images.size());
1525 
1526     // first, swap image pointers in the list.
1527     SrcPanoImage * pimg1 = state.images[img1];
1528     state.images[img1] = state.images[img2];
1529     state.images[img2] = pimg1;
1530 
1531     // update control points
1532     for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
1533         int n1 = (*it).image1Nr;
1534         int n2 = (*it).image2Nr;
1535         if ((*it).image1Nr == img1) {
1536             n1 = img2;
1537         } else if ((*it).image1Nr == img2) {
1538             n1 = img1;
1539         }
1540         if ((*it).image2Nr == img1) {
1541             n2 = img2;
1542         } else if ((*it).image2Nr == img2) {
1543             n2 = img1;
1544         }
1545         (*it).image1Nr = n1;
1546         (*it).image2Nr = n2;
1547     }
1548 
1549     // update panorama options
1550     if (state.options.colorReferenceImage == img1) {
1551         state.options.colorReferenceImage = img2;
1552     } else if (state.options.colorReferenceImage == img2) {
1553         state.options.colorReferenceImage = img1;
1554     }
1555     if (state.options.optimizeReferenceImage == img1) {
1556         state.options.optimizeReferenceImage = img2;
1557     } else if (state.options.optimizeReferenceImage == img2) {
1558         state.options.optimizeReferenceImage = img1;
1559     }
1560     imageChanged(img1);
1561     imageChanged(img2);
1562 }
1563 
moveImage(size_t img1,size_t img2)1564 void Panorama::moveImage(size_t img1, size_t img2)
1565 {
1566     //generate a vector with the translated image numbers
1567     std::vector<size_t> imgList(getNrOfImages(),-1);
1568     for(size_t i=0; i<getNrOfImages(); i++)
1569     {
1570         imgList[i]=i;
1571     };
1572     imgList.erase(imgList.begin()+img1);
1573     if(img2<imgList.size())
1574     {
1575         imgList.insert(imgList.begin()+img2, img1);
1576     }
1577     else
1578     {
1579         imgList.push_back(img1);
1580     };
1581     //generate map for translation of old -> new image numbers
1582     std::map<size_t,size_t> imgMap;
1583     for(size_t i=0; i<imgList.size(); i++)
1584     {
1585         imgMap[imgList[i]]=i;
1586     };
1587     // now generate the new images list
1588     std::vector<SrcPanoImage *> new_images(getNrOfImages());
1589     for(size_t i=0; i<imgList.size(); i++)
1590     {
1591         new_images[i]=state.images[imgList[i]];
1592         if(i!=imgList[i])
1593         {
1594             imageChanged(imgList[i]);
1595         };
1596     };
1597     state.images=new_images;
1598 
1599     // update optimize vector
1600     OptimizeVector newOptVec;
1601     for(size_t i=0; i<state.optvec.size(); i++)
1602     {
1603         newOptVec.push_back(state.optvec[imgList[i]]);
1604     };
1605     state.optvec=newOptVec;
1606 
1607     // update control points
1608     for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
1609     {
1610         (*it).image1Nr = imgMap[(*it).image1Nr];
1611         (*it).image2Nr = imgMap[(*it).image2Nr];
1612     }
1613 
1614     // update panorama options
1615     state.options.colorReferenceImage=imgMap[state.options.colorReferenceImage];
1616     state.options.optimizeReferenceImage=imgMap[state.options.optimizeReferenceImage];
1617 };
1618 
setMementoToCopyOf(const PanoramaDataMemento * memento)1619 bool Panorama::setMementoToCopyOf(const PanoramaDataMemento* memento)
1620 {
1621     if(memento==NULL)
1622         return false;
1623 
1624     const PanoramaMemento* mymemento;
1625 
1626     try {
1627 
1628         mymemento = dynamic_cast<const PanoramaMemento*>(memento);
1629 
1630     } catch (std::bad_cast&) {
1631 //        std::cerr << "Incompatible memento type." << std::endl;
1632         DEBUG_DEBUG("Incompatible memento type.");
1633         return false;
1634     }
1635 
1636     setMemento(PanoramaMemento(*mymemento));
1637     return true;
1638 }
1639 
1640 
1641 /// set the internal state
setMemento(const PanoramaMemento & memento)1642 void Panorama::setMemento(const PanoramaMemento& memento)
1643 {
1644     DEBUG_TRACE("");
1645 
1646     // remove old content.
1647     reset();
1648     DEBUG_DEBUG("nr of images in memento:" << memento.images.size());
1649 
1650     state = memento;
1651     updateMasks();
1652     unsigned int nNewImages = state.images.size();
1653     DEBUG_DEBUG("nNewImages:" << nNewImages);
1654 
1655     // send changes for all images
1656     for (unsigned int i = 0; i < nNewImages; i++) {
1657         imageChanged(i);
1658     }
1659 }
1660 
getNewMemento() const1661 PanoramaDataMemento* Panorama::getNewMemento() const
1662 {
1663     return new PanoramaMemento(getMemento());
1664 }
1665 
setOptions(const PanoramaOptions & opt)1666 void Panorama::setOptions(const PanoramaOptions & opt)
1667 {
1668     if (state.options.optimizeReferenceImage != opt.optimizeReferenceImage) {
1669         imageChanged(opt.optimizeReferenceImage);
1670         imageChanged(state.options.optimizeReferenceImage);
1671     }
1672 
1673     if (state.options.colorReferenceImage != opt.colorReferenceImage) {
1674         imageChanged(opt.colorReferenceImage);
1675         imageChanged(state.options.colorReferenceImage);
1676     }
1677 
1678     state.options = opt;
1679 }
1680 
addObserver(PanoramaObserver * o)1681 void Panorama::addObserver(PanoramaObserver * o)
1682 {
1683     observers.push_back(o);
1684 }
1685 
removeObserver(PanoramaObserver * o)1686 bool Panorama::removeObserver(PanoramaObserver * o)
1687 {
1688     size_t oldCount=observers.size();
1689     observers.remove(o);
1690     return observers.size()!=oldCount;
1691 }
1692 
clearObservers()1693 void Panorama::clearObservers()
1694 {
1695     observers.clear();
1696 }
1697 
hasPendingChanges() const1698 const bool Panorama::hasPendingChanges() const
1699 {
1700     return !changedImages.empty();
1701 }
1702 
imageChanged(unsigned int imgNr)1703 void Panorama::imageChanged(unsigned int imgNr)
1704 {
1705 //    DEBUG_TRACE("adding image " << imgNr);
1706     changedImages.insert(imgNr);
1707     assert(changedImages.find(imgNr) != changedImages.end());
1708 }
1709 
activateImage(unsigned int imgNr,bool active)1710 void Panorama::activateImage(unsigned int imgNr, bool active)
1711 {
1712     assert(imgNr < state.images.size());
1713     if (state.images[imgNr]->getActive() != active)
1714     {
1715         state.images[imgNr]->setActive(active);
1716         imageChanged(imgNr);
1717     }
1718 }
1719 
getActiveImages() const1720 UIntSet Panorama::getActiveImages() const
1721 {
1722 	UIntSet activeImgs;
1723 
1724     for (unsigned int i = 0; i < state.images.size(); i++) {
1725         if (state.images[i]->getActive())
1726         {
1727             activeImgs.insert(i);
1728         }
1729     }
1730 	return activeImgs;
1731 }
1732 
getICCProfileDesc() const1733 const std::string Panorama::getICCProfileDesc() const
1734 {
1735     return state.iccProfileDesc;
1736 };
1737 
setICCProfileDesc(const std::string & newDesc)1738 void Panorama::setICCProfileDesc(const std::string& newDesc)
1739 {
1740     state.iccProfileDesc = newDesc;
1741 };
1742 
getNrOfBands() const1743 const int Panorama::getNrOfBands() const
1744 {
1745     return state.bands;
1746 };
1747 
setNrOfBands(const int nrBands)1748 void Panorama::setNrOfBands(const int nrBands)
1749 {
1750     state.bands = nrBands;
1751 };
1752 
1753 //==== internal function for variable management
1754 
getSrcImage(unsigned imgNr) const1755 SrcPanoImage Panorama::getSrcImage(unsigned imgNr) const
1756 {
1757     DEBUG_ASSERT(imgNr < state.images.size());
1758     return *state.images[imgNr];
1759 }
1760 
setSrcImage(unsigned int imgNr,const SrcPanoImage & img)1761 void Panorama::setSrcImage(unsigned int imgNr, const SrcPanoImage & img)
1762 {
1763     DEBUG_ASSERT(imgNr < state.images.size());
1764 
1765     /* Copy the variables. We don't assign directly so we can do the changes to
1766      * any linked variables.
1767      */
1768     SrcPanoImage *dest = state.images[imgNr];
1769     #define image_variable( name, type, default_value ) \
1770     dest->set##name (img.get##name());
1771     #include "image_variables.h"
1772     #undef image_variable
1773 
1774     // mark the potentially changed images.
1775 #define image_variable( name, type, default_value ) \
1776     for (std::size_t i = 0; i < getNrOfImages(); i++)\
1777     {\
1778         if(state.images[imgNr]->name##isLinkedWith(*state.images[i]))\
1779         {\
1780             imageChanged(i);\
1781         }\
1782     }
1783 #include "image_variables.h"
1784 #undef image_variable
1785 }
1786 
1787 
duplicate() const1788 Panorama Panorama::duplicate() const
1789 {
1790     Panorama pano(*this);
1791     pano.observers.clear();
1792     return pano;
1793 }
1794 
getSubset(const UIntSet & imgs) const1795 Panorama Panorama::getSubset(const UIntSet & imgs) const
1796 {
1797     Panorama subset;
1798     // copy data except for listners
1799 
1800     // bits that don't change in the subset.
1801     subset.imgFilePrefix = imgFilePrefix;
1802     subset.dirty = dirty;
1803     subset.state.options = state.options;
1804     subset.state.optSwitch=0;
1805     subset.state.optPhotoSwitch=0;
1806     subset.state.needsOptimization = state.needsOptimization;
1807     subset.changedImages = changedImages;
1808     subset.m_forceImagesUpdate = m_forceImagesUpdate;
1809     subset.m_ptoptimizerVarNames = m_ptoptimizerVarNames;
1810 
1811     // check optimizer vector and update if necessary
1812     // optvec contains only the variables for the first image,
1813     // but if the first image is not in the subset then the variables is missing also for the next image
1814     // which can be in optvec
1815     OptimizeVector internalOptvec=state.optvec;
1816     UIntSet unusedImgs;
1817     {
1818         UIntSet allImgs;
1819         fill_set(allImgs, 0, getNrOfImages() - 1);
1820         std::set_difference(allImgs.begin(), allImgs.end(), imgs.begin(), imgs.end(), std::inserter(unusedImgs, unusedImgs.end()));
1821     };
1822     if (!unusedImgs.empty())
1823     {
1824         for (auto& i : unusedImgs)
1825         {
1826             for (auto& var : state.optvec[i])
1827             {
1828                 for (auto& j : imgs)
1829                 {
1830 #define image_variable(name, type, default_value)\
1831                     if (PTOVariableConverterFor##name::checkApplicability(var))\
1832                     {\
1833                         if (state.images[i]->name##isLinkedWith(*state.images[j]))\
1834                         {\
1835                             internalOptvec[j].insert(var);\
1836                             break;\
1837                         }\
1838                     }
1839 #include "image_variables.h"
1840 #undef image_variable
1841                 };
1842             };
1843         };
1844     };
1845 
1846     // create image number map.
1847     std::map<unsigned int, unsigned int> imageNrMap;
1848 
1849     // copy image information
1850     unsigned int ic = 0;
1851     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
1852          ++imgNrIt)
1853     {
1854         subset.state.images.push_back(new SrcPanoImage(*state.images[*imgNrIt]));
1855         subset.state.optvec.push_back(internalOptvec[*imgNrIt]);
1856         imageNrMap[*imgNrIt] = ic;
1857         ic++;
1858     }
1859 
1860     // recreate links between image variables.
1861     ic = 0;
1862     for (UIntSet::const_iterator i = imgs.begin(); i != imgs.end(); ++i)
1863     {
1864         unsigned int jc = ic + 1;
1865         UIntSet::const_iterator j = i;
1866         for (++j; j != imgs.end(); ++j)
1867         {
1868             /** @todo It should be possible to speed this up by not linking
1869              * things that have been already linked to something previously
1870              * linked to the target.
1871              */
1872 #define image_variable( name, type, default_value )\
1873             if (state.images[*i]->name##isLinkedWith(*state.images[*j]))\
1874             {\
1875                 subset.state.images[ic]->link##name(subset.state.images[jc]);\
1876             }
1877 #include "image_variables.h"
1878 #undef image_variable
1879             jc++;
1880         }
1881         ic++;
1882     }
1883 
1884     // select and translate control points.
1885     subset.state.ctrlPoints.clear();
1886     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
1887         if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
1888             ControlPoint pnt = *it;
1889             pnt.image1Nr = imageNrMap[pnt.image1Nr];
1890             pnt.image2Nr = imageNrMap[pnt.image2Nr];
1891             subset.state.ctrlPoints.push_back(pnt);
1892         }
1893     }
1894 
1895     //update optimizeReferenceImage and colorReferenceImage number
1896     unsigned int newRefImg=0;
1897     std::map<unsigned int, unsigned int>::iterator it=imageNrMap.find(state.options.optimizeReferenceImage);
1898     if(it!=imageNrMap.end())
1899     {
1900         newRefImg=it->second;
1901     };
1902     it=imageNrMap.find(state.options.colorReferenceImage);
1903     subset.state.options.optimizeReferenceImage=newRefImg;
1904     newRefImg=0;
1905     if(it!=imageNrMap.end())
1906     {
1907         newRefImg=it->second;
1908     }
1909     subset.state.options.colorReferenceImage=newRefImg;
1910     return subset;
1911 }
1912 
FindStackNumberForImage(const std::vector<UIntSet> & imageGroups,const unsigned int imgNr)1913 int FindStackNumberForImage(const std::vector<UIntSet>& imageGroups, const unsigned int imgNr)
1914 {
1915     for (size_t i = 0; i < imageGroups.size(); ++i)
1916     {
1917         if (set_contains(imageGroups[i], imgNr))
1918         {
1919             return i;
1920         };
1921     };
1922     return -1;
1923 };
1924 
getUnlinkedSubset(UIntSetVector & imageGroups) const1925 PanoramaData* Panorama::getUnlinkedSubset(UIntSetVector& imageGroups) const
1926 {
1927     const CPVector cps = getCtrlPoints();
1928     CPVector newCP;
1929 
1930     // remove all connected images, keep only a single image for each connected stack
1931     imageGroups.clear();
1932     std::vector<bool> visitedImages(getNrOfImages(), false);
1933     for (size_t i = 0; i < getNrOfImages(); ++i)
1934     {
1935         if (visitedImages[i])
1936         {
1937             continue;
1938         };
1939         const SrcPanoImage& img1 = getImage(i);
1940         UIntSet imgs;
1941         imgs.insert(i);
1942         visitedImages[i] = true;
1943         if (img1.YawisLinked())
1944         {
1945             for (size_t j = i + 1; j < getNrOfImages(); ++j)
1946             {
1947                 if (img1.YawisLinkedWith(getImage(j)))
1948                 {
1949                     imgs.insert(j);
1950                     visitedImages[j] = true;
1951                 }
1952             }
1953         };
1954         imageGroups.push_back(imgs);
1955     };
1956     UIntSet singleStackImgs;
1957     for (size_t i = 0; i < imageGroups.size(); ++i)
1958     {
1959         singleStackImgs.insert(*imageGroups[i].begin());
1960     };
1961     // new generate subpano
1962     PanoramaData* subPano = getNewSubset(singleStackImgs);
1963     // translate now reference image
1964     int newRefImage = FindStackNumberForImage(imageGroups, getOptions().optimizeReferenceImage);
1965     if (newRefImage != -1)
1966     {
1967         PanoramaOptions opts = subPano->getOptions();
1968         opts.optimizeReferenceImage = newRefImage;
1969         subPano->setOptions(opts);
1970     };
1971     // remove all vertical and horizontal cp, also remap all cps to new subpano
1972     // all cps from removed images will be mapped to first image of corresponding stack
1973     for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
1974     {
1975         if (it->mode == ControlPoint::X_Y)
1976         {
1977             ControlPoint cp(*it);
1978             int newImg1 = FindStackNumberForImage(imageGroups, cp.image1Nr);
1979             int newImg2 = FindStackNumberForImage(imageGroups, cp.image2Nr);
1980             if (newImg1 != -1 && newImg2 != -1 && newImg1 != newImg2)
1981             {
1982                 cp.image1Nr = newImg1;
1983                 cp.image2Nr = newImg2;
1984                 newCP.push_back(cp);
1985             };
1986         };
1987     };
1988     subPano->setCtrlPoints(newCP);
1989     return subPano;
1990 }
1991 
mergePanorama(const Panorama & newPano)1992 void Panorama::mergePanorama(const Panorama &newPano)
1993 {
1994     if(newPano.getNrOfImages()>0)
1995     {
1996         std::vector<unsigned int> new_image_nr(newPano.getNrOfImages());
1997         HuginBase::OptimizeVector optVec=getOptimizeVector();
1998         HuginBase::OptimizeVector optVecNew=newPano.getOptimizeVector();
1999         const size_t oldImgNumber = getNrOfImages();
2000         HuginBase::UIntSet imgsAlreadyInPano;
2001         HuginBase::UIntSet imgsCheckLens;
2002         //add only new images
2003         for(unsigned int i=0;i<newPano.getNrOfImages();i++)
2004         {
2005             std::string filename=newPano.getImage(i).getFilename();
2006             bool found=false;
2007             for(unsigned int j=0;j<getNrOfImages();j++)
2008             {
2009                 if(getImage(j).getFilename()==filename)
2010                 {
2011                     //image is already in panorama, we remember the image nr
2012                     found=true;
2013                     new_image_nr[i]=j;
2014                     imgsAlreadyInPano.insert(i);
2015                     // now check if we have to update the masks
2016                     HuginBase::MaskPolygonVector masksOld=getImage(j).getMasks();
2017                     HuginBase::MaskPolygonVector masksNew=newPano.getImage(i).getMasks();
2018                     if(!masksNew.empty())
2019                     {
2020                         for(unsigned int k=0;k<masksNew.size();k++)
2021                         {
2022                             bool usedMasks=false;
2023                             unsigned int l=0;
2024                             while((!usedMasks) && l<masksOld.size())
2025                             {
2026                                 usedMasks=(masksNew[k]==masksOld[l]);
2027                                 l++;
2028                             };
2029                             if(!usedMasks)
2030                                 masksOld.push_back(masksNew[k]);
2031                         };
2032                         updateMasksForImage(j,masksOld);
2033                     };
2034                     break;
2035                 };
2036             };
2037             if(!found)
2038             {
2039                 //new image found, read EXIF data and add it
2040                 SrcPanoImage newImg(newPano.getImage(i));
2041                 newImg.readEXIF();
2042                 new_image_nr[i]=addImage(newImg);
2043                 imgsCheckLens.insert(i);
2044                 //copy also optimise vector
2045                 optVec.push_back(optVecNew[i]);
2046             };
2047         };
2048         setOptimizeVector(optVec);
2049         // check and create lens for new added images
2050         HuginBase::ConstImageVariableGroup newLenses(HuginBase::StandardImageVariableGroups::getLensVariables(), newPano);
2051         HuginBase::ImageVariableGroup oldLenses(HuginBase::StandardImageVariableGroups::getLensVariables(), *this);
2052         HuginBase::UIntSetVector lensImgs = newLenses.getPartsSet();
2053         if (!imgsAlreadyInPano.empty())
2054         {
2055             for (auto img : imgsAlreadyInPano)
2056             {
2057                 const size_t initialLensNumber = newLenses.getPartNumber(img);
2058                 const size_t newLensNumber = oldLenses.getPartNumber(new_image_nr[img]);
2059                 // create copy of UIntSet, because we can modifying the set in the for loop
2060                 // and this invalidates the iterators
2061                 const HuginBase::UIntSet imgs(imgsCheckLens);
2062                 for (auto j : imgs)
2063                 {
2064                     if (set_contains(lensImgs[initialLensNumber], j))
2065                     {
2066                         oldLenses.switchParts(new_image_nr[j], newLensNumber);
2067                         imgsCheckLens.erase(j);
2068                         lensImgs[initialLensNumber].erase(j);
2069                     };
2070                 };
2071                 lensImgs[initialLensNumber].erase(img);
2072             };
2073         };
2074         if (!imgsCheckLens.empty())
2075         {
2076             // find first lens not already handled
2077             size_t i = 0;
2078             while (i < lensImgs.size() && lensImgs[i].empty())
2079             {
2080                 i++;
2081             };
2082             if (i < lensImgs.size())
2083             {
2084                 const HuginBase::SrcPanoImage& srcImage = getImage(new_image_nr[*lensImgs[i].begin()]);
2085                 size_t matchingLensNumber = -1;
2086                 for (size_t j = 0; j < oldImgNumber; ++j)
2087                 {
2088                     const HuginBase::SrcPanoImage& compareImage = getImage(j);
2089                     if (compareImage.getSize() == srcImage.getSize() &&
2090                         compareImage.getExifModel() == srcImage.getExifModel() &&
2091                         compareImage.getExifMake() == srcImage.getExifMake() &&
2092                         compareImage.getExifFocalLength() == srcImage.getExifFocalLength())
2093                     {
2094                         matchingLensNumber = oldLenses.getPartNumber(j);
2095                         break;
2096                     };
2097                 };
2098                 // we found a matching lens
2099                 if (matchingLensNumber >= 0)
2100                 {
2101                     for (size_t j : lensImgs[i])
2102                     {
2103                         oldLenses.switchParts(new_image_nr[j], matchingLensNumber);
2104                     };
2105                 };
2106             };
2107         };
2108         // recreate links between image variables.
2109         for (unsigned int i=0; i<newPano.getNrOfImages(); i++)
2110         {
2111             for(unsigned int j=i+1;j<newPano.getNrOfImages();j++)
2112             {
2113                 const HuginBase::SrcPanoImage &img=newPano.getImage(i);
2114 #define image_variable( name, type, default_value )\
2115                 if(img.name##isLinkedWith(newPano.getImage(j)))\
2116                 {\
2117                     linkImageVariable##name(new_image_nr[i],new_image_nr[j]);\
2118                 };
2119 #include "panodata/image_variables.h"
2120 #undef image_variable
2121             }
2122         }
2123         //now translate cp
2124         CPVector cps=newPano.getCtrlPoints();
2125         const int nextLineCPOffset = getNextCPTypeLineNumber() - 3;
2126         for(unsigned int i=0;i<cps.size();i++)
2127         {
2128             // special treatment of line control points
2129             if (cps[i].mode > 2)
2130             {
2131                 addCtrlPoint(HuginBase::ControlPoint(new_image_nr[cps[i].image1Nr], cps[i].x1, cps[i].y1,
2132                     new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode + nextLineCPOffset));
2133             }
2134             else
2135             {
2136                 // normal, horizontal and vertical cp keep their mode
2137                 addCtrlPoint(HuginBase::ControlPoint(new_image_nr[cps[i].image1Nr], cps[i].x1, cps[i].y1,
2138                     new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode));
2139             };
2140         };
2141         removeDuplicateCtrlPoints();
2142     };
2143 };
2144 
getNextCPTypeLineNumber() const2145 int Panorama::getNextCPTypeLineNumber() const
2146 {
2147     int t=0;
2148     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
2149     {
2150         t = std::max(t, it->mode);
2151     }
2152     if (t <= 2) {
2153         t=2;
2154     }
2155     return t+1;
2156 }
2157 
2158 
readData(std::istream & dataInput,std::string documentType)2159 Panorama::ReadWriteError Panorama::readData(std::istream& dataInput, std::string documentType)
2160 {
2161     // [TODO] check the document type, return INCOMPATIBLE_TYPE
2162 
2163     if(!dataInput.good() || dataInput.eof())
2164     {
2165         DEBUG_WARN("Failed to read from dataInput.");
2166         return INVALID_DATA;
2167     }
2168 
2169     PanoramaMemento newPano;
2170     int ptoVersion;
2171     if (newPano.loadPTScript(dataInput, ptoVersion, getFilePrefix())) {
2172 
2173         this->setMemento(newPano);
2174         return SUCCESSFUL;
2175 
2176     } else {
2177         DEBUG_FATAL("Could not parse the data input successfully.");
2178         return PARSER_ERROR;
2179     }
2180 }
2181 
2182 ///
writeData(std::ostream & dataOutput,std::string documentType)2183 Panorama::ReadWriteError Panorama::writeData(std::ostream& dataOutput, std::string documentType)
2184 {
2185     UIntSet all;
2186 
2187     if (getNrOfImages() > 0)
2188         fill_set(all, 0, getNrOfImages()-1);
2189 
2190     printPanoramaScript(dataOutput, getOptimizeVector(), getOptions(), all, false, getFilePrefix());
2191 
2192     return SUCCESSFUL;
2193 }
2194 
updateWhiteBalance(double redFactor,double blueFactor)2195 void Panorama::updateWhiteBalance(double redFactor, double blueFactor)
2196 {
2197     UIntSet modified_images;
2198     for(unsigned int i=0;i<getNrOfImages();i++)
2199     {
2200         if(!set_contains(modified_images,i))
2201         {
2202             state.images[i]->setWhiteBalanceRed(redFactor * state.images[i]->getWhiteBalanceRed());
2203             state.images[i]->setWhiteBalanceBlue(blueFactor * state.images[i]->getWhiteBalanceBlue());
2204             modified_images.insert(i);
2205             imageChanged(i);
2206             //check linked images and remember for later
2207             if(state.images[i]->WhiteBalanceRedisLinked())
2208             {
2209                 if(i+1<getNrOfImages())
2210                 {
2211                     for(unsigned int j=i+1;j<getNrOfImages();j++)
2212                     {
2213                         if(state.images[i]->WhiteBalanceRedisLinkedWith(*(state.images[j])))
2214                         {
2215                             modified_images.insert(j);
2216                             imageChanged(j);
2217                         };
2218                     };
2219                 };
2220             };
2221         };
2222     };
2223 };
2224 
getMaxExposureDifference() const2225 const double Panorama::getMaxExposureDifference() const
2226 {
2227     if (state.images.empty())
2228     {
2229         return 0;
2230     }
2231     double minEv = 1000;
2232     double maxEv = -1000;
2233     for (size_t i = 0; i < state.images.size(); i++)
2234     {
2235         const double ev = state.images[i]->getExposureValue();
2236         minEv = std::min(minEv, ev);
2237         maxEv = std::max(maxEv, ev);
2238     };
2239     return maxEv - minEv;
2240 };
2241 
hasPossibleStacks() const2242 const bool Panorama::hasPossibleStacks() const
2243 {
2244     if (state.images.empty())
2245     {
2246         return false;
2247     }
2248     // this algorithm is based on panostart by Bruno Postle
2249     // bracketed pano has at least a dynamic range of 1.2 ev values (corresponds to bracket with +-2/3)
2250     if (getMaxExposureDifference()<1.2)
2251     {
2252         return false;
2253     }
2254     //now get all exposure layers
2255     UIntSet allImg;
2256     fill_set(allImg, 0, state.images.size() - 1);
2257     UIntSetVector evValues = getExposureLayers(*this, allImg, 0.3);
2258     //if there is only one unique exposure value then there are no stacks
2259     if (evValues.size()<2)
2260     {
2261         return false;
2262     }
2263     //if number of unique exposure values is equal the number of images then there are no stacks
2264     if (evValues.size() == state.images.size())
2265     {
2266         return false;
2267     }
2268     //if number of images is not a multiple of number of unique exposure values
2269     //then the stacks are incomplete, skipping
2270     if (state.images.size() % evValues.size() != 0)
2271     {
2272         return false;
2273     }
2274     //check if exposure value is repeated with step size of bracket size
2275     if (set_contains(evValues[0], evValues.size()))
2276     {
2277         return true;
2278     }
2279     else
2280     {
2281         return false;
2282     };
2283 };
2284 
2285 /** create automatically stacks as indicated by metadata */
linkPossibleStacks(bool linkPosition)2286 void Panorama::linkPossibleStacks(bool linkPosition)
2287 {
2288     // we need at least 2 images
2289     if (state.images.size()<=1)
2290     {
2291         return;
2292     };
2293     // unlink all existing stacks
2294     for (size_t imgNr = 0; imgNr < state.images.size(); imgNr++)
2295     {
2296         if (state.images[imgNr]->YawisLinked())
2297         {
2298             unlinkImageVariableYaw(imgNr);
2299             unlinkImageVariablePitch(imgNr);
2300             unlinkImageVariableRoll(imgNr);
2301             unlinkImageVariableX(imgNr);
2302             unlinkImageVariableY(imgNr);
2303             unlinkImageVariableZ(imgNr);
2304             unlinkImageVariableTranslationPlaneYaw(imgNr);
2305             unlinkImageVariableTranslationPlanePitch(imgNr);
2306         };
2307         if (state.images[imgNr]->StackisLinked())
2308         {
2309             unlinkImageVariableStack(imgNr);
2310         };
2311     };
2312     // now link all possible stacks
2313     UIntSet allImg;
2314     fill_set(allImg, 0, state.images.size() - 1);
2315     UIntSetVector evValues = getExposureLayers(*this, allImg, 0.3);
2316     if (evValues.empty())
2317     {
2318         return;
2319     };
2320     unsigned int imgNr = 0;
2321     for (size_t i = 1; i<state.images.size(); i++)
2322     {
2323         if (set_contains(evValues[0], i))
2324         {
2325             imgNr = i;
2326         }
2327         else
2328         {
2329             linkImageVariableStack(imgNr, i);
2330             if (linkPosition)
2331             {
2332                 linkImageVariableYaw(imgNr, i);
2333                 linkImageVariablePitch(imgNr, i);
2334                 linkImageVariableRoll(imgNr, i);
2335                 linkImageVariableX(imgNr, i);
2336                 linkImageVariableY(imgNr, i);
2337                 linkImageVariableZ(imgNr, i);
2338                 linkImageVariableTranslationPlaneYaw(imgNr, i);
2339                 linkImageVariableTranslationPlanePitch(imgNr, i);
2340             };
2341         };
2342     };
2343 };
2344 
PanoramaMemento(const PanoramaMemento & data)2345 PanoramaMemento::PanoramaMemento(const PanoramaMemento & data)
2346 {
2347     // Use the assignment operator to get the work done: see the next function.
2348     *this = data;
2349 }
2350 
operator =(const PanoramaMemento & data)2351 PanoramaMemento & PanoramaMemento::operator=(const PanoramaMemento & data)
2352 {
2353     // Copy the PanoramaMemento.
2354 
2355     // Don't do anything in the case of self assignment. This is important as we
2356     // are about to delete the image information.
2357     if (&data == this)
2358     {
2359         return *this;
2360     }
2361 
2362     // Remove any images we currently had.
2363     deleteAllImages();
2364     // copy image variables
2365     for (std::vector<SrcPanoImage *>::const_iterator it = data.images.begin();
2366          it != data.images.end(); ++it)
2367     {
2368         images.push_back(new SrcPanoImage(*(*it)));
2369     }
2370     // Copies of SrcPanoImage's variables aren't linked, so we have to create
2371     // new links in the same pattern.
2372     /** @todo This is quite inefficent, maybe we should store the links as a
2373      * vector of sets of image numbers for each variable to speed up this?
2374      * Links / unlinks should all go through the Panorama object, so we
2375      * could keep track of them easily.
2376      */
2377     std::size_t num_imgs = images.size();
2378     for (std::size_t i = 0; i < num_imgs; i++)
2379     {
2380         // copy this image's links.
2381         // links to lower numbered images will have already been spotted, since
2382         // they are bi-directional.
2383         for (std::size_t j = i + 1; j < num_imgs; j++)
2384         {
2385 #define image_variable( name, type, default_value )\
2386             if (data.images[i]->name##isLinkedWith(*data.images[j]))\
2387             {\
2388                 images[i]->link##name(images[j]);\
2389             }
2390 #include "image_variables.h"
2391 #undef image_variable
2392         }
2393     }
2394 
2395     ctrlPoints = data.ctrlPoints;
2396     iccProfileDesc = data.iccProfileDesc;
2397     bands = data.bands;
2398 
2399     options = data.options;
2400 
2401     optSwitch = data.optSwitch;
2402     optPhotoSwitch = data.optPhotoSwitch;
2403     optvec = data.optvec;
2404 
2405     needsOptimization = data.needsOptimization;
2406 
2407     return *this;
2408 }
2409 
~PanoramaMemento()2410 PanoramaMemento::~PanoramaMemento()
2411 {
2412     deleteAllImages();
2413 }
2414 
deleteAllImages()2415 void PanoramaMemento::deleteAllImages()
2416 {
2417     // delete all the images pointed to by the images vector.
2418     for (std::vector<SrcPanoImage *>::iterator it = images.begin();
2419          it != images.end(); ++it)
2420     {
2421         delete *it;
2422     }
2423     // now clear the pointers themselves.
2424     images.clear();
2425 }
2426 
loadPTScript(std::istream & i,int & ptoVersion,const std::string & prefix)2427 bool PanoramaMemento::loadPTScript(std::istream &i, int & ptoVersion, const std::string &prefix)
2428 {
2429     DEBUG_TRACE("");
2430     // set numeric locale to C, for correct number output
2431     char * p = setlocale(LC_NUMERIC,NULL);
2432     char * old_locale = strdup(p);
2433     setlocale(LC_NUMERIC,"C");
2434     std::string line;
2435 
2436     // vector with the different information lines about images
2437     std::vector<PTScriptParsing::ImgInfo> oImgInfo;
2438     std::vector<PTScriptParsing::ImgInfo> iImgInfo;
2439     // strange comment information.
2440     std::vector<PTScriptParsing::ImgInfo> cImgInfo;
2441     // hugin additional information
2442     std::vector<PTScriptParsing::ImgInfo> huginImgInfo;
2443     // vector with readed masks
2444     MaskPolygonVector ImgMasks;
2445     CPVector loadedCp;
2446 
2447     // indicate lines that should be skipped for whatever reason
2448     bool skipNextLine = false;
2449 
2450     bool PTGUIScriptFile = false;
2451     int PTGUIScriptVersion = 0;
2452     // PTGui lens line detected
2453     int ctrlPointsImgNrOffset = 0;
2454     bool PTGUILensLine = false;
2455 
2456     bool PTGUILensLoaded = false;
2457     PTScriptParsing::ImgInfo PTGUILens;
2458 
2459     // set new options to some sensible default.
2460     options.reset();
2461     options.tiff_saveROI = false;
2462 
2463     ptoVersion = 1;
2464 
2465     bool firstOptVecParse = true;
2466     unsigned int lineNr = 0;
2467     while (i.good()) {
2468         std::getline(i, line);
2469         lineNr++;
2470         DEBUG_DEBUG(lineNr << ": " << line);
2471         if (skipNextLine) {
2472             skipNextLine = false;
2473             continue;
2474         }
2475         //skip emtpy lines
2476         if(line.empty())
2477             continue;
2478         // check for a known line
2479         switch(line[0]) {
2480         case 'p':
2481         {
2482             DEBUG_DEBUG("p line: " << line);
2483             int i;
2484             if (PTScriptParsing::getIntParam(i, line, "f"))
2485                 options.setProjection( (PanoramaOptions::ProjectionFormat) i );
2486             unsigned int w;
2487             if (PTScriptParsing::getIntParam(w, line, "w"))
2488                 options.setWidth(w);
2489             double v;
2490             if (PTScriptParsing::getDoubleParam(v, line, "v"))
2491                 options.setHFOV(v, false);
2492             int height;
2493             if (PTScriptParsing::getIntParam(height, line, "h"))
2494                 options.setHeight(height);
2495 
2496             double newE;
2497             if (PTScriptParsing::getDoubleParam(newE, line, "E"))
2498                 options.outputExposureValue = newE;
2499             int ar=0;
2500             if (PTScriptParsing::getIntParam(ar, line, "R"))
2501                 options.outputMode = (PanoramaOptions::OutputMode) ar;
2502 
2503             std::string format;
2504             if (PTScriptParsing::getPTParam(format, line, "T"))
2505                 options.outputPixelType = format;
2506 
2507             if (PTScriptParsing::getPTParam(format, line, "S")) {
2508                 int left, right, top, bottom;
2509                 int n = sscanf(format.c_str(), "%d,%d,%d,%d", &left, &right, &top, &bottom);
2510                 if (n == 4) {
2511                     options.setROI(vigra::Rect2D(left, top, right, bottom));
2512                 } else {
2513                     DEBUG_WARN("Could not parse crop string: " << format);
2514                 }
2515             }
2516 
2517             // parse projection parameters
2518             if (PTScriptParsing::getPTParam(format, line, "P")) {
2519                 char * tstr = strdup(format.c_str());
2520                 std::vector<double> projParam;
2521                 char * b = strtok(tstr, " \"");
2522                 if (b != NULL) {
2523                     while (b != NULL) {
2524                     double tempDbl;
2525                     if (sscanf(b, "%lf", &tempDbl) == 1) {
2526                         projParam.push_back(tempDbl);
2527                         b = strtok(NULL, " \"");
2528                     }
2529                     }
2530                 }
2531                 free(tstr);
2532                 // only set projection parameters, if the have the right size.
2533                 if (projParam.size() == options.getProjectionParameters().size()) {
2534                     options.setProjectionParameters(projParam);
2535                 }
2536             }
2537 
2538             // this is fragile.. hope nobody adds additional whitespace
2539             // and other arguments than q...
2540             // n"JPEG q80"
2541             if (PTScriptParsing::getPTParam(format, line, "n")) {
2542                 int t = format.find(' ');
2543                 options.outputFormat = options.getFormatFromName(format.substr(0,t));
2544 
2545                 // parse output format options.
2546                 switch (options.outputFormat)
2547                 {
2548                     case PanoramaOptions::JPEG:
2549                     case PanoramaOptions::JPEG_m:
2550                     {
2551                         // "parse" jpg quality
2552                         int q;
2553                         if (PTScriptParsing::getIntParam(q, format, "q")) {
2554                         options.quality = (int) q;
2555                         }
2556                     }
2557                     break;
2558                     case PanoramaOptions::TIFF_m:
2559                     {
2560                         int coordImgs = 0;
2561                         if (PTScriptParsing::getIntParam(coordImgs, format, "p"))
2562                         if (coordImgs)
2563                             options.saveCoordImgs = true;
2564                     }
2565                     case PanoramaOptions::TIFF:
2566                     case PanoramaOptions::TIFF_mask:
2567                     case PanoramaOptions::TIFF_multilayer:
2568                     case PanoramaOptions::TIFF_multilayer_mask:
2569                     {
2570                         // parse tiff compression mode
2571                         std::string comp;
2572                         if (PTScriptParsing::getPTParam(comp, format, "c:")) {
2573                             if (comp == "NONE" || comp == "LZW" ||
2574                                 comp == "PACKBITS" || comp == "DEFLATE")
2575                             {
2576                                 options.tiffCompression = comp;
2577                             } else {
2578                                 DEBUG_WARN("No valid tiff compression found");
2579                             }
2580                         }
2581                         // read tiff roi
2582                         if (PTScriptParsing::getPTParam(comp, format, "r:")) {
2583                             if (comp == "CROP") {
2584                                 options.tiff_saveROI = true;
2585                             } else {
2586                                 options.tiff_saveROI = false;
2587                             }
2588                         }
2589                     }
2590                 break;
2591                 default:
2592                 break;
2593                 }
2594             }
2595 
2596             int cRefImg = 0;
2597             if (PTScriptParsing::getIntParam(cRefImg, line, "k")) {
2598                 options.colorCorrection = PanoramaOptions::BRIGHTNESS_COLOR;
2599             } else if (PTScriptParsing::getIntParam(cRefImg, line,"b")) {
2600                 options.colorCorrection = PanoramaOptions::BRIGHTNESS;
2601             } else if (PTScriptParsing::getIntParam(cRefImg, line,"d")) {
2602                 options.colorCorrection = PanoramaOptions::COLOR;
2603             } else {
2604                 options.colorCorrection = PanoramaOptions::NONE;
2605             }
2606             options.colorReferenceImage=cRefImg;
2607             break;
2608 
2609         }
2610         case 'm':
2611         {
2612             DEBUG_DEBUG("m line: " << line);
2613             // parse misc options
2614             int i;
2615             if (PTScriptParsing::getIntParam(i, line, "i"))
2616                 options.interpolator = (vigra_ext::Interpolator) i;
2617             break;
2618         }
2619         case 'v':
2620         {
2621             DEBUG_DEBUG("v line: " << line);
2622             if (!PTGUIScriptFile) {
2623                 if (firstOptVecParse) {
2624                     int nImg = std::max(iImgInfo.size(), oImgInfo.size());
2625                     DEBUG_DEBUG("nImg: " << nImg);
2626                     optvec = OptimizeVector(nImg);
2627                     firstOptVecParse = false;
2628                 }
2629                 std::stringstream optstream;
2630                 optstream << line.substr(1);
2631                 std::string var;
2632                 while (!(optstream >> std::ws).eof()) {
2633                     optstream >> var;
2634                     if (var.length() == 1) {
2635                         // special case for PTGUI
2636                         var += "0";
2637                     }
2638                     // find first numerical character
2639                     std::string::size_type np = var.find_first_of("0123456789");
2640                     if (np == std::string::npos) {
2641                         // invalid, continue
2642                         continue;
2643                     }
2644                     std::string name=var.substr(0,np);
2645                     unsigned int nr;
2646                     if (!hugin_utils::stringToUInt(var.substr(np), nr))
2647                     {
2648                         // invalid, continue
2649                         continue;
2650                     };
2651                     DEBUG_ASSERT(nr < optvec.size());
2652                     if(nr < optvec.size())
2653                     {
2654                         optvec[nr].insert(name);
2655                         DEBUG_DEBUG("parsing opt: >" << var << "< : var:" << name << " image:" << nr);
2656                     };
2657                 }
2658             }
2659             break;
2660         }
2661         case 'c':
2662         {
2663             DEBUG_DEBUG("c line: " << line);
2664             int t;
2665             // read control points
2666             ControlPoint point;
2667 	    // TODO - should verify that line syntax is correct
2668             PTScriptParsing::getIntParam(point.image1Nr, line, "n");
2669             point.image1Nr += ctrlPointsImgNrOffset;
2670             PTScriptParsing::getIntParam(point.image2Nr, line, "N");
2671             point.image2Nr += ctrlPointsImgNrOffset;
2672             PTScriptParsing::getDoubleParam(point.x1, line, "x");
2673             PTScriptParsing::getDoubleParam(point.x2, line, "X");
2674             PTScriptParsing::getDoubleParam(point.y1, line, "y");
2675             PTScriptParsing::getDoubleParam(point.y2, line, "Y");
2676             if (!PTScriptParsing::getIntParam(t, line, "t")){
2677                 t = 0;
2678             }
2679 
2680             point.mode = t;
2681             loadedCp.push_back(point);
2682             break;
2683         }
2684 
2685         // handle the complicated part.. the image & lens settings.
2686         // treat i and o lines the same.. however, o lines have priority
2687         // over i lines.(i lines often do not contain link information!)
2688         case 'i':
2689         {
2690             if (PTGUILensLine) {
2691                 PTGUILensLine = false;
2692                 PTGUILensLoaded = true;
2693                 PTGUILens.parse(line);
2694             } else {
2695                 iImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2696             }
2697             break;
2698         }
2699         case 'o':
2700         {
2701             if (PTGUILensLine) {
2702                 PTGUILensLine = false;
2703                 PTGUILensLoaded = true;
2704                 PTGUILens.parse(line);
2705             } else {
2706                 oImgInfo.push_back(PTScriptParsing::ImgInfo(line));
2707             }
2708             break;
2709         }
2710 
2711         case 'k':
2712         {
2713             unsigned int param;
2714             if (PTScriptParsing::getIntParam(param, line, "i"))
2715             {
2716                 MaskPolygon newPolygon;
2717                 newPolygon.setImgNr(param);
2718                 if (PTScriptParsing::getIntParam(param, line, "t"))
2719                     newPolygon.setMaskType((HuginBase::MaskPolygon::MaskType)param);
2720                 std::string format;
2721                 if (PTScriptParsing::getPTParam(format, line, "p"))
2722                 {
2723                     if(newPolygon.parsePolygonString(format))
2724                         ImgMasks.push_back(newPolygon);
2725                 };
2726             };
2727             break;
2728         }
2729 
2730         case '#':
2731         {
2732             // parse special comments...
2733             if (line.substr(0,20) == std::string("# ptGui project file")) {
2734                 PTGUIScriptFile = true;
2735             }
2736             if (line.substr(0,12) == "#-dummyimage") {
2737                 PTGUILensLine = true;
2738             }
2739             if (PTGUIScriptFile) {
2740                 // parse special PTGUI stuff.
2741                 if (sscanf(line.c_str(), "#-fileversion %d", &PTGUIScriptVersion) > 0) {
2742                     DEBUG_DEBUG("Detected PTGUI script version: " << PTGUIScriptVersion);
2743                     switch (PTGUIScriptVersion) {
2744                         case 0:
2745                             break;
2746                         case 1:
2747                             break;
2748                         case 2:
2749                             break;
2750                         case 3:
2751                             break;
2752                         case 4:
2753                             break;
2754                         case 5:
2755                             break;
2756                         case 6:
2757                             break;
2758                         case 7:
2759                             break;
2760                         default:
2761                             ctrlPointsImgNrOffset = -1;
2762                             // latest known version is 8
2763                             break;
2764                     }
2765                 }
2766             }
2767 
2768             if (line.substr(0,8) == "#-hugin ") {
2769                 // read hugin image line
2770                 PTScriptParsing::ImgInfo info;
2771                 info.autoCenterCrop = (line.find("autoCenterCrop=1") != std::string::npos);
2772                 size_t pos = line.find("cropFactor=");
2773                 if (pos > 0 && pos < line.length()) {
2774                     double cropFactor=1;
2775                     const char * s = line.c_str() + pos;
2776                     sscanf(s,"cropFactor=%lf", & cropFactor);
2777                     if(cropFactor<0.01 || cropFactor > 100)
2778                         cropFactor=1;
2779                     info.cropFactor = cropFactor;
2780                 }
2781                 pos = line.find("disabled");
2782                 if (pos > 0 && pos < line.length()) {
2783                     info.enabled = false;
2784                 }
2785                 huginImgInfo.push_back(info);
2786             }
2787 
2788             // PTGui and PTAssember project files:
2789             // #-imgfile 960 1280 "D:\data\bruno\074-098\087.jpg"
2790             if (line.substr(0,10) == "#-imgfile ") {
2791 
2792                 // arghhh. I like string processing without regexps.
2793                 int b = line.find_first_not_of(" ",9);
2794                 int e = line.find_first_of(" ",b);
2795                 DEBUG_DEBUG(" width:" << line.substr(b, e - b) << ":")
2796                 int nextWidth;
2797                 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextWidth))
2798                 {
2799                     continue;
2800                 };
2801                 DEBUG_DEBUG("next width " << nextWidth);
2802                 b = line.find_first_not_of(" ",e);
2803                 e = line.find_first_of(" ",b);
2804                 DEBUG_DEBUG(" height:" << line.substr(b, e - b) << ":")
2805                 int nextHeight;
2806                 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextHeight))
2807                 {
2808                     continue;
2809                 };
2810                 DEBUG_DEBUG("next height " << nextHeight);
2811 
2812                 std::string nextFilename;
2813                 try {
2814                     b = line.find_first_not_of(" \"",e);
2815                     e = line.find_first_of("\"",b);
2816                     nextFilename = line.substr(b,e-b);
2817                 } catch (std::out_of_range& e) {
2818                     DEBUG_ERROR("ERROR PARSING INPUT FILE" << e.what( ));
2819                     return false;
2820                 }
2821                 DEBUG_DEBUG("next filename " << nextFilename);
2822 
2823                 PTScriptParsing::ImgInfo info;
2824                 info.width  = nextWidth;
2825                 info.height = nextHeight;
2826                 info.filename = nextFilename;
2827                 cImgInfo.push_back(info);
2828             }
2829 
2830 
2831             // parse hugin properties
2832             if (line.substr(0,7) == "#hugin_") {
2833                 std::istringstream is(line);
2834                 std::string var,value;
2835                 is >> var >> value;
2836                 if (!is.fail()) {
2837                     if (var == "#hugin_ptoversion") {
2838                         ptoVersion = atoi(value.c_str());
2839                     }
2840 
2841                     if (var == "#hugin_optimizeReferenceImage") {
2842                         options.optimizeReferenceImage = atoi(value.c_str());
2843                     } else if (var == "#hugin_remapper") {
2844                         if (value == "nona") {
2845                             options.remapper = PanoramaOptions::NONA;
2846                         } else if (value == "PTmender") {
2847                             options.remapper = PanoramaOptions::PTMENDER;
2848                         }
2849                     } else if (var == "#hugin_blender") {
2850                         if (value == "none") {
2851                             options.blendMode = PanoramaOptions::NO_BLEND;
2852                         } else if (value == "PTblender") {
2853                             options.blendMode = PanoramaOptions::PTBLENDER_BLEND;
2854                         } else if (value == "enblend") {
2855                             options.blendMode = PanoramaOptions::ENBLEND_BLEND;
2856                         } else if (value == "PTmasker") {
2857                             options.blendMode = PanoramaOptions::PTMASKER_BLEND;
2858                         } else if (value == "smartblend") {
2859                             options.blendMode = PanoramaOptions::SMARTBLEND_BLEND;
2860                         } else if (value == "internal") {
2861                             options.blendMode = PanoramaOptions::INTERNAL_BLEND;
2862                         }
2863 
2864                     } else if (var == "#hugin_enblendOptions") {
2865                         options.enblendOptions = value;
2866                         while (!is.eof()) {
2867                             is >> value;
2868                             if (!is.fail() && value.length() > 0) {
2869                                 options.enblendOptions += " ";
2870                                 options.enblendOptions += value;
2871                             }
2872                         }
2873                     } else if (var == "#hugin_enfuseOptions") {
2874                         options.enfuseOptions = value;
2875                         while (!is.eof()) {
2876                             is >> value;
2877                             if (!is.fail() && value.length() > 0) {
2878                                 options.enfuseOptions += " ";
2879                                 options.enfuseOptions += value;
2880                             }
2881                         }
2882                     } else if (var == "#hugin_hdrmergeOptions") {
2883                         options.hdrmergeOptions = value;
2884                         while (!is.eof()) {
2885                             is >> value;
2886                             if (!is.fail() && value.length() > 0) {
2887                                 options.hdrmergeOptions += " ";
2888                                 options.hdrmergeOptions += value;
2889                             }
2890                         }
2891                     } else if (var == "#hugin_verdandiOptions") {
2892                         options.verdandiOptions = value;
2893                         while (!is.eof()) {
2894                             is >> value;
2895                             if (!is.fail() && value.length() > 0) {
2896                                 options.verdandiOptions += " ";
2897                                 options.verdandiOptions += value;
2898                             }
2899                         }
2900 
2901                     } else if (var == "#hugin_outputLDRBlended") {
2902                         options.outputLDRBlended = (value == "true");
2903                     } else if (var == "#hugin_outputLDRLayers") {
2904                         options.outputLDRLayers = (value == "true");
2905                     } else if (var == "#hugin_outputLDRExposureRemapped") {
2906                         options.outputLDRExposureRemapped = (value == "true");
2907                     } else if (var == "#hugin_outputLDRExposureLayers") {
2908                         options.outputLDRExposureLayers = (value == "true");
2909                     } else if (var == "#hugin_outputLDRExposureBlended") {
2910                         options.outputLDRExposureBlended = (value == "true");
2911                     } else if (var == "#hugin_outputLDRExposureLayersFused") {
2912                         options.outputLDRExposureLayersFused = (value == "true");
2913                     } else if (var == "#hugin_outputLDRStacks") {
2914                         options.outputLDRStacks = (value == "true");
2915                     } else if (var == "#hugin_outputHDRBlended") {
2916                         options.outputHDRBlended = (value == "true");
2917                     } else if (var == "#hugin_outputHDRLayers") {
2918                         options.outputHDRLayers = (value == "true");
2919                     } else if (var == "#hugin_outputHDRStacks") {
2920                         options.outputHDRStacks = (value == "true");
2921 
2922                     } else if (var == "#hugin_outputStacksMinOverlap") {
2923                         double val=atof(value.c_str());
2924                         if(val>0 && val <= 1)
2925                         {
2926                             options.outputStacksMinOverlap = val;
2927                         };
2928                         if (val < 0)
2929                         {
2930                             options.outputStacksMinOverlap = -1;
2931                         };
2932                     } else if (var == "#hugin_outputLayersExposureDiff") {
2933                         double val=atof(value.c_str());
2934                         if(val>0.01)
2935                         {
2936                             options.outputLayersExposureDiff = val;
2937                         }
2938 
2939                     } else if (var == "#hugin_outputLayersCompression") {
2940                         options.outputLayersCompression = value;
2941                     } else if (var == "#hugin_outputImageType") {
2942                         options.outputImageType = value;
2943                     } else if (var == "#hugin_outputImageTypeCompression") {
2944                         options.outputImageTypeCompression = value;
2945                     } else if (var == "#hugin_outputJPEGQuality") {
2946                         options.quality = atoi(value.c_str());
2947                     } else if (var == "#hugin_outputImageTypeHDR") {
2948                         options.outputImageTypeHDR = value;
2949                     } else if (var == "#hugin_outputImageTypeHDRCompression") {
2950                         options.outputImageTypeHDRCompression = value;
2951                     } else if (var == "#hugin_outputRangeCompression") {
2952                         options.outputRangeCompression = atof(value.c_str());
2953                         options.outputRangeCompression = std::max(0.0, std::min(options.outputRangeCompression, 20.0));
2954                     } else if (var == "#hugin_optimizerMasterSwitch") {
2955                         optSwitch = atoi(value.c_str());
2956                     } else if (var == "#hugin_optimizerPhotoMasterSwitch") {
2957                         optPhotoSwitch = atoi(value.c_str());
2958                     };
2959 
2960                 }
2961             }
2962             break;
2963         }
2964 
2965         } // case
2966     }
2967 
2968     // assemble images from the information read before..
2969 
2970 /** @todo What is the PTGUI special case? What images use the lens created here?
2971  */
2972 #if 0
2973     // handle PTGUI special case
2974     if (PTGUILensLoaded) {
2975         // create lens with dummy info
2976         Lens l;
2977         for (const char **v = Lens::variableNames; *v != 0; v++) {
2978             map_get(l.variables, *v).setValue(PTGUILens.vars[*v]);
2979         }
2980         l.setImageSize(vigra::Size2D(PTGUILens.width, PTGUILens.height));
2981         l.setCropFactor(1);
2982         l.setProjection((Lens::LensProjectionFormat) PTGUILens.f);
2983         lenses.push_back(l);
2984     }
2985 #endif
2986 
2987 /*
2988     // ugly hack to load PTGui script files
2989     if (ptGUIDummyImage) {
2990         DEBUG_DEBUG("loading default PTGUI line: " << line);
2991             Lens l;
2992             // skip ptgui's dummy image
2993             // load parameters into default lens...
2994             for (LensVarMap::iterator it = l.variables.begin();
2995              it != l.variables.end();
2996              ++it)
2997             {
2998                 DEBUG_DEBUG("reading default lens variable " << it->first);
2999                 int link;
3000                 bool ok = readVar(it->second, link, line);
3001                 DEBUG_ASSERT(ok);
3002                 DEBUG_ASSERT(link == -1);
3003             }
3004             lenses.push_back(l);
3005 
3006             ptGUIDummyImage = false;
3007             break;
3008         }
3009 */
3010 
3011     // merge image info from the 3 different lines...
3012     // i lines are the main reference.
3013 
3014     int nImgs = iImgInfo.size();
3015     int nOLines = oImgInfo.size();
3016     int nCLines = cImgInfo.size();
3017 
3018     if (nImgs < nOLines) {
3019         // no, or less i lines found. scrap i lines.
3020         DEBUG_DEBUG("throwing away " << nImgs << " i lines");
3021         iImgInfo = oImgInfo;
3022         nImgs = nOLines;
3023     }
3024     if (nOLines < nImgs) {
3025         oImgInfo = iImgInfo;
3026     }
3027 
3028     // merge o lines and i lines into i lines.
3029     for (int i=0; i < nImgs; i++) {
3030 
3031         // move parameters from o lines -> i (only if it isn't given in the
3032         // i lines. or it is linked on the o lines)
3033 
3034         // ordinary variables
3035         for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v; v++) {
3036 
3037             if ((iImgInfo[i].links[*v] == -2 && oImgInfo[i].links[*v] != -2) || (iImgInfo[i].links[*v] == -1 && oImgInfo[i].links[*v] >=0)) {
3038                 DEBUG_DEBUG(*v << ": o -> i");
3039                 iImgInfo[i].vars[*v] = oImgInfo[i].vars[*v];
3040                 iImgInfo[i].links[*v] = oImgInfo[i].links[*v];
3041             }
3042         }
3043 
3044         if (iImgInfo[i].filename == "" && oImgInfo[i].filename != "") {
3045             DEBUG_DEBUG("filename: o -> i");
3046             iImgInfo[i].filename = oImgInfo[i].filename;
3047         }
3048 
3049         if (iImgInfo[i].crop.isEmpty() && !oImgInfo[i].crop.isEmpty()) {
3050             DEBUG_DEBUG("crop: o -> i");
3051             iImgInfo[i].crop = oImgInfo[i].crop;
3052         }
3053 
3054         if (iImgInfo[i].width <= 0 && oImgInfo[i].width > 0) {
3055             DEBUG_DEBUG("width: o -> i");
3056             iImgInfo[i].width = oImgInfo[i].width;
3057         }
3058 
3059         if (iImgInfo[i].height <= 0 && oImgInfo[i].height > 0) {
3060             DEBUG_DEBUG("height: o -> i");
3061             iImgInfo[i].height = oImgInfo[i].height;
3062         }
3063 
3064         if (iImgInfo[i].f < 0 && oImgInfo[i].f > 0) {
3065             DEBUG_DEBUG("f: o -> i");
3066             iImgInfo[i].f = oImgInfo[i].f;
3067         }
3068 
3069         if (nCLines == nImgs) {
3070             // img file & size in clines
3071             if (cImgInfo[i].filename != "" && cImgInfo[i].width > 0) {
3072                 DEBUG_DEBUG("filename, width, height: c -> i");
3073                 iImgInfo[i].filename = cImgInfo[i].filename;
3074                 iImgInfo[i].width = cImgInfo[i].width;
3075                 iImgInfo[i].height = cImgInfo[i].height;
3076             }
3077         }
3078         if (huginImgInfo.size() == (size_t)nImgs) {
3079             iImgInfo[i].cropFactor = huginImgInfo[i].cropFactor;
3080             iImgInfo[i].autoCenterCrop = huginImgInfo[i].autoCenterCrop;
3081             iImgInfo[i].enabled= huginImgInfo[i].enabled;
3082         }
3083     }
3084 
3085     // create images.
3086     for (int i=0; i < nImgs; i++) {
3087 
3088         DEBUG_DEBUG("i line: " << i);
3089         // read the variables
3090         VariableMap vars;
3091         int link = -2;
3092         fillVariableMap(vars);
3093 
3094         for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v != 0; v++) {
3095             std::string name(*v);
3096             double val = iImgInfo[i].vars[*v];
3097             map_get(vars,name).setValue(val);
3098             if (iImgInfo[i].links[*v] >= 0) {
3099                 link = iImgInfo[i].links[*v];
3100             }
3101         }
3102 
3103         std::string file = iImgInfo[i].filename;
3104         // add prefix if only a relative path.
3105 #ifdef _WIN32
3106         bool absPath = ( (file[1]==':' && file[2]=='\\') || (file[1]==':' && file[2]=='/') || (file[0] == '\\' && file[1] == '\\'));
3107 #else
3108         bool absPath = file[0] == '/';
3109 #endif
3110         if (!absPath) {
3111             file.insert(0, prefix);
3112         }
3113         DEBUG_DEBUG("filename: " << file);
3114 
3115         // Make a new SrcPanoImage in this list for expressing this one.
3116         SrcPanoImage * new_img_p = new SrcPanoImage();
3117         images.push_back(new_img_p);
3118         // and make a reference to it so we dont keep using images.back(),
3119         SrcPanoImage & new_img = *new_img_p;
3120         new_img.setFilename(file);
3121         new_img.setSize(vigra::Size2D(iImgInfo[i].width, iImgInfo[i].height));
3122         new_img.checkImageSizeKnown();
3123 
3124         // Panotools Script variables for the current SrcPanoImage variable.
3125         // We just need the names.
3126         VariableMap vars_for_name;
3127 
3128         // A dummy SrcPanoImage to use to fill vars_for_name.
3129         SrcPanoImage name_src;
3130 
3131 /* Set image variables in new_img using the PanoTools Script names, with
3132  * linking where specified in the file.
3133  *
3134  * We create a list of PanoTools script variable names for each variable in
3135  * SrcPanoImg (vars_for_name). It may have multiple variables or none at all.
3136  * If a link is found between any of the variables inside it we can link them up
3137  * in new_img.
3138  *
3139  * The Panorama Tools script format makes it possible to link parts of the same
3140  * SrcPanoImage variable differently (e.g. you can link the horizontal component
3141  * of shearing to a different target to the vertical component, not link the
3142  * vertical component at all). SrcPanoImage cannot handle this, so we end up
3143  * linking all components of the same variable to anything specified in the PTO
3144  * script for one of the components: most often we specify the same link
3145  * multiple times.
3146  */
3147 /** @todo Warn the user when the script links variables in a way not expressable
3148  * by SrcPanoImage.
3149  */
3150 #define RESET_LOCALE setlocale(LC_NUMERIC,old_locale); free(old_locale);
3151 #define image_variable( name, type, default_value )\
3152         PTOVariableConverterFor##name::addToVariableMap(name_src.get##name##IV(), vars_for_name);\
3153         for (VariableMap::iterator vit = vars_for_name.begin();\
3154              vit != vars_for_name.end(); vit++)\
3155         {\
3156             if (link >= 0 && iImgInfo[i].links[vit->first] >= 0)\
3157             {\
3158                 if (   !(PTGUILensLoaded && link == 0)\
3159                     && (int) images.size() < link && (!PTGUILensLoaded))\
3160                 {\
3161                     DEBUG_ERROR("variables must be linked to an image with a lower number" << endl\
3162                                 << "number links: " << link << " images: " << images.size() << endl\
3163                                 << "error on line " << lineNr << ":" << endl\
3164                                 << line);\
3165                     RESET_LOCALE\
3166                     return false;\
3167                 }\
3168                 DEBUG_DEBUG("anchored to image " << iImgInfo[i].links[vit->first]);\
3169                 new_img.link##name(images[iImgInfo[i].links[vit->first]]);\
3170             } else {\
3171                 double val = map_get(vars, vit->first).getValue();\
3172                 new_img.setVar(vit->first, val);\
3173             }\
3174         }\
3175         vars_for_name.clear();
3176 #include "image_variables.h"
3177 #undef image_variable
3178         new_img.setProjection((SrcPanoImage::Projection) iImgInfo[i].f);
3179 
3180         // check, if stacks are correctly linked
3181 #define check_stack_link(name) \
3182         if(!new_img.YawisLinked() && new_img.name##isLinked())\
3183         {\
3184             new_img.unlink##name();\
3185         };\
3186         if(new_img.YawisLinked() && !new_img.name##isLinked())\
3187         {\
3188             for(size_t j=0; j<i; j++)\
3189             {\
3190                 if(new_img.YawisLinkedWith(*images[j]))\
3191                 {\
3192                     new_img.link##name(images[j]);\
3193                     break;\
3194                 };\
3195             };\
3196         }
3197         check_stack_link(Pitch);
3198         check_stack_link(Roll);
3199         check_stack_link(X);
3200         check_stack_link(Y);
3201         check_stack_link(Z);
3202         check_stack_link(TranslationPlaneYaw);
3203         check_stack_link(TranslationPlanePitch);
3204 #undef check_stack_link
3205 
3206 #if 0
3207         new_img.setFeatherWidth((unsigned int) iImgInfo[i].blend_radius);
3208 #endif
3209 
3210         // is this right?
3211         new_img.setCropFactor(iImgInfo[i].cropFactor);
3212         new_img.setVigCorrMode(iImgInfo[i].vigcorrMode);
3213         new_img.setFlatfieldFilename(iImgInfo[i].flatfieldname);
3214         new_img.setResponseType((SrcPanoImage::ResponseType)iImgInfo[i].responseType);
3215         new_img.setAutoCenterCrop(iImgInfo[i].autoCenterCrop);
3216         new_img.setActive(iImgInfo[i].enabled);
3217 
3218         if (!iImgInfo[i].crop.isEmpty()) {
3219             if (new_img.isCircularCrop())
3220             {
3221                 new_img.setCropMode(SrcPanoImage::CROP_CIRCLE);
3222             } else {
3223                 new_img.setCropMode(SrcPanoImage::CROP_RECTANGLE);
3224             }
3225             new_img.setCropRect(iImgInfo[i].crop);
3226         }
3227 
3228         //now fill the mask
3229         for(unsigned int j=0;j<ImgMasks.size();j++)
3230             if(ImgMasks[j].getImgNr()==i)
3231                 //now clip mask to image size + offset
3232                 if(ImgMasks[j].clipPolygon(vigra::Rect2D(-0.9*maskOffset,-0.9*maskOffset,
3233                     new_img.getWidth()+0.9*maskOffset,new_img.getHeight()+0.9*maskOffset)))
3234                 {
3235                     new_img.addMask(ImgMasks[j]);
3236                 };
3237     }
3238 
3239     // if we haven't found a v line in the project file
3240     if (optvec.size() != images.size()) {
3241         optvec = OptimizeVector(images.size());
3242     }
3243 
3244     if (!loadedCp.empty())
3245     {
3246         // check if control points are linked with existing images
3247         const size_t nrImg = images.size();
3248         for (CPVector::const_iterator it = loadedCp.begin(); it != loadedCp.end(); ++it)
3249         {
3250             HuginBase::ControlPoint cp = *it;
3251             if (cp.image1Nr < nrImg && cp.image2Nr < nrImg)
3252             {
3253                 ctrlPoints.push_back(cp);
3254             };
3255         };
3256         if (loadedCp.size() != ctrlPoints.size())
3257         {
3258             std::cout << "WARNING: Project file contains control points that are connected with" << std::endl
3259                 << "  non existing images. Ignoring these control points." << std::endl;
3260         };
3261     };
3262 
3263     if (images.empty())
3264     {
3265         std::cerr << "ERROR: Project file contains no images." << std::endl;
3266     }
3267     else
3268     {
3269         if (options.optimizeReferenceImage < 0 || options.optimizeReferenceImage >= images.size())
3270         {
3271             // optimize reference images reference to non-existing image
3272             options.optimizeReferenceImage = 0;
3273             std::cout << "WARNING: Optimize reference image refers to non existing image. Reset to default value." << std::endl;
3274         };
3275         if (options.colorReferenceImage < 0 || options.colorReferenceImage >= images.size())
3276         {
3277             // optimize reference images reference to non-existing image
3278             options.colorReferenceImage = 0;
3279             std::cout << "WARNING: Optimize photometric reference image refers to non existing image. Reset to default value." << std::endl;
3280         };
3281     };
3282     // reset locale
3283     setlocale(LC_NUMERIC,old_locale);
3284     free(old_locale);
3285 
3286     return !images.empty();
3287 }
3288 
3289 } // namespace
3290