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