1 // -*- c-basic-offset: 4 -*-
2 /** @file PanoCommand.cpp
3 *
4 *  @author Pablo d'Angelo <pablo.dangelo@web.de>
5 */
6 /*
7 *  This is free software; you can redistribute it and/or
8 *  modify it under the terms of the GNU General Public
9 *  License as published by the Free Software Foundation; either
10 *  version 2 of the License, or (at your option) any later version.
11 *
12 *  This software is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 *  Lesser General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public
18 *  License along with this software. If not, see
19 *  <http://www.gnu.org/licenses/>.
20 *
21 */
22 
23 #include "PanoCommand.h"
24 #include <fstream>
25 #include <panotools/PanoToolsUtils.h>
26 
27 #include <algorithms/nona/CenterHorizontally.h>
28 #include <algorithms/nona/FitPanorama.h>
29 #include <algorithms/basic/RotatePanorama.h>
30 #include <algorithms/basic/TranslatePanorama.h>
31 #include <algorithms/basic/CalculateMeanExposure.h>
32 #include <algorithms/basic/StraightenPanorama.h>
33 #include <panodata/ParseExp.h>
34 
35 namespace PanoCommand
36 {
~CombinedPanoCommand()37     CombinedPanoCommand::~CombinedPanoCommand()
38     {
39         for (std::vector<PanoCommand*>::iterator it = commands.begin(); it != commands.end(); ++it)
40         {
41             delete *it;
42         }
43     }
44 
processPanorama(HuginBase::Panorama & pano)45     bool CombinedPanoCommand::processPanorama(HuginBase::Panorama & pano)
46     {
47         bool result = true;
48         for (std::vector<PanoCommand*>::iterator it = commands.begin(); it != commands.end(); ++it)
49         {
50             result &= (**it).processPanorama(pano);
51         }
52         /// @todo Should I revert if processing fails?
53         return result;
54     };
55 
processPanorama(HuginBase::Panorama & pano)56     bool NewPanoCmd::processPanorama(HuginBase::Panorama& pano)
57     {
58         pano.reset();
59         return true;
60     }
61 
processPanorama(HuginBase::Panorama & pano)62     bool AddImagesCmd::processPanorama(HuginBase::Panorama& pano)
63     {
64         std::vector<HuginBase::SrcPanoImage>::const_iterator it;
65         for (it = imgs.begin(); it != imgs.end(); ++it)
66         {
67             pano.addImage(*it);
68         }
69         return true;
70     }
71 
processPanorama(HuginBase::Panorama & pano)72     bool RemoveImageCmd::processPanorama(HuginBase::Panorama& pano)
73     {
74         pano.removeImage(imgNr);
75         return true;
76     }
77 
processPanorama(HuginBase::Panorama & pano)78     bool RemoveImagesCmd::processPanorama(HuginBase::Panorama& pano)
79     {
80         for (HuginBase::UIntSet::reverse_iterator it = imgNrs.rbegin();
81             it != imgNrs.rend(); ++it)
82         {
83             pano.removeImage(*it);
84         }
85         return true;
86     }
87 
processPanorama(HuginBase::Panorama & pano)88     bool UpdateVariablesCmd::processPanorama(HuginBase::Panorama& pano)
89     {
90         pano.updateVariables(vars);
91         HuginBase::PTools::calcCtrlPointErrors(pano);
92         return true;
93     }
94 
processPanorama(HuginBase::Panorama & pano)95     bool UpdateCPsCmd::processPanorama(HuginBase::Panorama& pano)
96     {
97         HuginBase::CPVector::const_iterator it;
98         unsigned int i = 0;
99         for (it = cps.begin(); it != cps.end(); ++it, i++)
100         {
101             pano.changeControlPoint(i, *it);
102         }
103         if (updateCPError)
104         {
105             HuginBase::PTools::calcCtrlPointErrors(pano);
106         };
107 
108         return true;
109     }
110 
processPanorama(HuginBase::Panorama & pano)111     bool UpdateVariablesCPCmd::processPanorama(HuginBase::Panorama& pano)
112     {
113         pano.updateVariables(vars);
114         pano.updateCtrlPointErrors(cps);
115         return true;
116     }
117 
processPanorama(HuginBase::Panorama & pano)118     bool UpdateVariablesCPSetCmd::processPanorama(HuginBase::Panorama& pano)
119     {
120         pano.updateVariables(m_imgs, vars);
121         pano.updateCtrlPointErrors(m_imgs, cps);
122         pano.markAsOptimized();
123         return true;
124     }
125 
processPanorama(HuginBase::Panorama & pano)126     bool UpdateImageVariablesCmd::processPanorama(HuginBase::Panorama& pano)
127     {
128         pano.updateVariables(imgNr, vars);
129         HuginBase::PTools::calcCtrlPointErrors(pano);
130         return true;
131     }
132 
processPanorama(HuginBase::Panorama & pano)133     bool UpdateImagesVariablesCmd::processPanorama(HuginBase::Panorama& pano)
134     {
135         HuginBase::UIntSet::iterator it;
136         HuginBase::VariableMapVector::const_iterator v_it = vars.begin();
137         for (it = change.begin(); it != change.end(); ++it)
138         {
139             pano.updateVariables(*it, *v_it);
140             ++v_it;
141         }
142         HuginBase::PTools::calcCtrlPointErrors(pano);
143         return true;
144     }
145 
processPanorama(HuginBase::Panorama & pano)146     bool UpdateVariablesByParseExpression::processPanorama(HuginBase::Panorama& pano)
147     {
148         if (!m_expression.empty())
149         {
150             Parser::PanoParseExpression(pano, m_expression);
151             HuginBase::PTools::calcCtrlPointErrors(pano);
152             return true;
153         }
154         else
155         {
156             return false;
157         };
158     }
159 
processPanorama(HuginBase::Panorama & pano)160     bool UpdateOptimizeVectorCmd::processPanorama(HuginBase::Panorama & pano)
161     {
162         pano.setOptimizeVector(m_optvec);
163         return true;
164     }
165 
166 
processPanorama(HuginBase::Panorama & pano)167     bool UpdateOptimizerSwitchCmd::processPanorama(HuginBase::Panorama & pano)
168     {
169         pano.setOptimizerSwitch(m_mode);
170         return true;
171     }
172 
processPanorama(HuginBase::Panorama & pano)173     bool UpdatePhotometricOptimizerSwitchCmd::processPanorama(HuginBase::Panorama & pano)
174     {
175         pano.setPhotometricOptimizerSwitch(m_mode);
176         return true;
177     }
178 
processPanorama(HuginBase::Panorama & pano)179     bool SetVariableCmd::processPanorama(HuginBase::Panorama& pano)
180     {
181         HuginBase::UIntSet::iterator it;
182         for (it = images.begin(); it != images.end(); ++it)
183         {
184             pano.updateVariable(*it, var);
185         }
186         HuginBase::PTools::calcCtrlPointErrors(pano);
187 
188         return true;
189     }
190 
processPanorama(HuginBase::Panorama & pano)191     bool CenterPanoCmd::processPanorama(HuginBase::Panorama& pano)
192     {
193         HuginBase::CenterHorizontally(pano).run();
194         // adjust canvas size
195         HuginBase::CalculateFitPanorama fitPano(pano);
196         fitPano.run();
197         HuginBase::PanoramaOptions opts = pano.getOptions();
198         opts.setHFOV(fitPano.getResultHorizontalFOV());
199         opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight()));
200         pano.setOptions(opts);
201         HuginBase::PTools::calcCtrlPointErrors(pano);
202 
203         return true;
204     }
205 
processPanorama(HuginBase::Panorama & pano)206     bool StraightenPanoCmd::processPanorama(HuginBase::Panorama& pano)
207     {
208         HuginBase::StraightenPanorama(pano).run();
209 
210         HuginBase::PanoramaOptions opts = pano.getOptions();
211         if (opts.getHFOV()<360)
212         {
213             // center non 360 deg panos
214             HuginBase::CenterHorizontally(pano).run();
215         };
216         // adjust canvas size
217         HuginBase::CalculateFitPanorama fitPano(pano);
218         fitPano.run();
219         opts.setHFOV(fitPano.getResultHorizontalFOV());
220         opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight()));
221         pano.setOptions(opts);
222         HuginBase::PTools::calcCtrlPointErrors(pano);
223 
224         return true;
225     }
226 
processPanorama(HuginBase::Panorama & pano)227     bool AddCtrlPointCmd::processPanorama(HuginBase::Panorama& pano)
228     {
229         pano.addCtrlPoint(point);
230         HuginBase::PTools::calcCtrlPointErrors(pano);
231         return true;
232     }
233 
processPanorama(HuginBase::Panorama & pano)234     bool AddCtrlPointsCmd::processPanorama(HuginBase::Panorama& pano)
235     {
236         for (HuginBase::CPVector::iterator it = cps.begin(); it != cps.end(); ++it)
237         {
238             pano.addCtrlPoint(*it);
239         }
240         HuginBase::PTools::calcCtrlPointErrors(pano);
241         return true;
242     }
243 
processPanorama(HuginBase::Panorama & pano)244     bool RemoveCtrlPointCmd::processPanorama(HuginBase::Panorama& pano)
245     {
246         pano.removeCtrlPoint(pointNr);
247         return true;
248     }
249 
processPanorama(HuginBase::Panorama & pano)250     bool RemoveCtrlPointsCmd::processPanorama(HuginBase::Panorama& pano)
251     {
252         for (HuginBase::UIntSet::reverse_iterator it = m_points.rbegin(); it != m_points.rend(); ++it)
253         {
254             pano.removeCtrlPoint(*it);
255         }
256         return true;
257     }
258 
processPanorama(HuginBase::Panorama & pano)259     bool ChangeCtrlPointCmd::processPanorama(HuginBase::Panorama& pano)
260     {
261         pano.changeControlPoint(pNr, point);
262         HuginBase::PTools::calcCtrlPointErrors(pano);
263         return true;
264     }
265 
processPanorama(HuginBase::Panorama & pano)266     bool SetActiveImagesCmd::processPanorama(HuginBase::Panorama& pano)
267     {
268         HuginBase::UIntSet::iterator it;
269         for (unsigned int i = 0; i < pano.getNrOfImages(); i++)
270         {
271             if (set_contains(m_active, i))
272             {
273                 pano.activateImage(i, true);
274             }
275             else
276             {
277                 pano.activateImage(i, false);
278             };
279         }
280         return true;
281     }
282 
processPanorama(HuginBase::Panorama & pano)283     bool SwapImagesCmd::processPanorama(HuginBase::Panorama& pano)
284     {
285         pano.swapImages(m_i1, m_i2);
286         return true;
287     }
288 
processPanorama(HuginBase::Panorama & pano)289     bool MoveImageCmd::processPanorama(HuginBase::Panorama& pano)
290     {
291         pano.moveImage(m_i1, m_i2);
292         return true;
293     }
294 
processPanorama(HuginBase::Panorama & pano)295     bool MergePanoCmd::processPanorama(HuginBase::Panorama& pano)
296     {
297         pano.mergePanorama(newPano);
298         HuginBase::PTools::calcCtrlPointErrors(pano);
299         return true;
300     }
301 
processPanorama(HuginBase::Panorama & pano)302     bool UpdateSrcImageCmd::processPanorama(HuginBase::Panorama& pano)
303     {
304         pano.setSrcImage(imgNr, img);
305         HuginBase::PTools::calcCtrlPointErrors(pano);
306         return true;
307     }
308 
processPanorama(HuginBase::Panorama & pano)309     bool UpdateSrcImagesCmd::processPanorama(HuginBase::Panorama& pano)
310     {
311         int i = 0;
312         for (HuginBase::UIntSet::iterator it = imgNrs.begin(); it != imgNrs.end(); ++it)
313         {
314             pano.setSrcImage(*it, imgs[i]);
315             i++;
316         }
317         HuginBase::PTools::calcCtrlPointErrors(pano);
318         return true;
319     }
320 
processPanorama(HuginBase::Panorama & pano)321     bool SetPanoOptionsCmd::processPanorama(HuginBase::Panorama& pano)
322     {
323         pano.setOptions(options);
324         HuginBase::PTools::calcCtrlPointErrors(pano);
325         return true;
326     }
327 
LoadPTProjectCmd(HuginBase::Panorama & p,const std::string & filename,const std::string & prefix)328     LoadPTProjectCmd::LoadPTProjectCmd(HuginBase::Panorama & p, const std::string  & filename, const std::string & prefix)
329         : PanoCommand(p), filename(filename), prefix(prefix)
330     {
331         m_clearDirty = true;
332     }
333 
processPanorama(HuginBase::Panorama & pano)334     bool LoadPTProjectCmd::processPanorama(HuginBase::Panorama& pano)
335     {
336         std::ifstream in(filename.c_str());
337         AppBase::DocumentData::ReadWriteError err = pano.readData(in);
338         if (err != AppBase::DocumentData::SUCCESSFUL)
339         {
340             DEBUG_ERROR("could not load panotools script");
341             return false;
342         }
343         in.close();
344         return true;
345     }
346 
processPanorama(HuginBase::Panorama & pano)347     bool RotatePanoCmd::processPanorama(HuginBase::Panorama& pano)
348     {
349         HuginBase::RotatePanorama(pano, y, p, r).run();
350         HuginBase::PTools::calcCtrlPointErrors(pano);
351         return true;
352     }
353 
processPanorama(HuginBase::Panorama & pano)354     bool TranslatePanoCmd::processPanorama(HuginBase::Panorama& pano)
355     {
356         HuginBase::TranslatePanorama(pano, X, Y, Z).run();
357         HuginBase::PTools::calcCtrlPointErrors(pano);
358         return true;
359     }
360 
processPanorama(HuginBase::Panorama & pano)361     bool UpdateFocalLengthCmd::processPanorama(HuginBase::Panorama& pano)
362     {
363         pano.UpdateFocalLength(imgNrs, m_focalLength);
364         HuginBase::PTools::calcCtrlPointErrors(pano);
365         return true;
366     }
367 
processPanorama(HuginBase::Panorama & pano)368     bool UpdateCropFactorCmd::processPanorama(HuginBase::Panorama& pano)
369     {
370         //search all image with the same lens, otherwise the crop factor is updated via links,
371         //but not the hfov if the image is not the given HuginBase::UIntSet
372         HuginBase::UIntSet allImgWithSameLens;
373         HuginBase::UIntSet testedLens;
374         HuginBase::StandardImageVariableGroups variable_groups(pano);
375         HuginBase::ImageVariableGroup & lenses = variable_groups.getLenses();
376         for (HuginBase::UIntSet::const_iterator it = imgNrs.begin(); it != imgNrs.end(); ++it)
377         {
378             allImgWithSameLens.insert(*it);
379             unsigned int lensNr = lenses.getPartNumber(*it);
380             if (set_contains(testedLens, lensNr))
381             {
382                 continue;
383             };
384             testedLens.insert(lensNr);
385             for (unsigned int i = 0; i<pano.getNrOfImages(); i++)
386             {
387                 if (lenses.getPartNumber(i) == lensNr)
388                 {
389                     allImgWithSameLens.insert(i);
390                 };
391             };
392         };
393         pano.UpdateCropFactor(allImgWithSameLens, m_cropFactor);
394         HuginBase::PTools::calcCtrlPointErrors(pano);
395         return true;
396     }
397 
processPanorama(HuginBase::Panorama & pano)398     bool ChangePartNumberCmd::processPanorama(HuginBase::Panorama& pano)
399     {
400         // it might change as we are setting them
401         std::size_t new_new_part_number = new_part_number;
402         HuginBase::ImageVariableGroup group(variables, pano);
403         for (HuginBase::UIntSet::iterator it = image_numbers.begin(); it != image_numbers.end(); ++it)
404         {
405             group.switchParts(*it, new_new_part_number);
406             // update the lens number if it changes.
407             new_new_part_number = group.getPartNumber(*it);
408         }
409         return true;
410     }
411 
processPanorama(HuginBase::Panorama & pano)412     bool ChangePartImagesLinkingCmd::processPanorama(HuginBase::Panorama& pano)
413     {
414         HuginBase::ImageVariableGroup group(groupVariables, pano);
415         if (new_linked_state)
416         {
417             for (HuginBase::UIntSet::iterator imageIt = image_numbers.begin(); imageIt != image_numbers.end(); ++imageIt)
418             {
419                 // link the variables
420                 for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator variableIt = changeVariables.begin();
421                     variableIt != changeVariables.end(); ++variableIt)
422                 {
423                     group.linkVariableImage(*variableIt, *imageIt);
424                 }
425             }
426         }
427         else
428         {
429             for (HuginBase::UIntSet::iterator imageIt = image_numbers.begin(); imageIt != image_numbers.end(); ++imageIt)
430             {
431                 // unlink the variable
432                 for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator variableIt = changeVariables.begin();
433                     variableIt != changeVariables.end(); ++variableIt)
434                 {
435                     group.unlinkVariableImage(*variableIt, *imageIt);
436                     group.updatePartNumbers();
437                 }
438             }
439         }
440         return true;
441     }
442 
processPanorama(HuginBase::Panorama & pano)443     bool LinkLensVarsCmd::processPanorama(HuginBase::Panorama& pano)
444     {
445         HuginBase::StandardImageVariableGroups variable_groups(pano);
446         HuginBase::ImageVariableGroup & lenses = variable_groups.getLenses();
447         std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it;
448         for (it = variables.begin(); it != variables.end(); ++it)
449         {
450             lenses.linkVariablePart(*it, lens_number);
451         }
452         return true;
453     }
454 
455     /// @todo avoid copying image data in processPanorama
456 #define image_variable( name, type, default_value )\
457     bool ChangeImage##name##Cmd::processPanorama(HuginBase::Panorama& pano)\
458         {\
459         for (HuginBase::UIntSet::iterator it = image_numbers.begin(); it != image_numbers.end(); it++)\
460         {\
461             HuginBase::SrcPanoImage img = pano.getSrcImage(*it);\
462             img.set##name(value);\
463             pano.setSrcImage(*it, img);\
464         }\
465         return true;\
466         };
467 #include <panodata/image_variables.h>
468 #undef image_variable
469 
processPanorama(HuginBase::Panorama & pano)470     bool NewPartCmd::processPanorama(HuginBase::Panorama& pano)
471     {
472         // unlink all the variables in the first image.
473         DEBUG_ASSERT(!image_numbers.empty());
474         unsigned int image_index = *image_numbers.begin();
475         for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it = vars.begin(); it != vars.end(); ++it)
476         {
477             switch (*it)
478             {
479 #define image_variable( name, type, default_value )\
480                     case HuginBase::ImageVariableGroup::IVE_##name:\
481                         pano.unlinkImageVariable##name(image_index);\
482                         break;
483 #include <panodata/image_variables.h>
484 #undef image_variable
485             }
486         }
487         // now the first image should have a new part in the group.
488         // we want to switch the rest of the images to the new part.
489         HuginBase::ImageVariableGroup group(vars, pano);
490         for (HuginBase::UIntSet::iterator it = ++image_numbers.begin(); it != image_numbers.end(); ++it)
491         {
492             std::size_t part_number = group.getPartNumber(image_index);
493             group.switchParts(*it, part_number);
494         }
495         return true;
496     }
497 
processPanorama(HuginBase::Panorama & pano)498     bool UpdateMaskForImgCmd::processPanorama(HuginBase::Panorama& pano)
499     {
500         pano.updateMasksForImage(m_img, m_mask);
501         return true;
502     }
503 
processPanorama(HuginBase::Panorama & pano)504     bool UpdateWhiteBalance::processPanorama(HuginBase::Panorama& pano)
505     {
506         pano.updateWhiteBalance(m_red, m_blue);
507         return true;
508     }
509 
processPanorama(HuginBase::Panorama & pano)510     bool ResetToMeanExposure::processPanorama(HuginBase::Panorama& pano)
511     {
512         HuginBase::PanoramaOptions opts = pano.getOptions();
513         opts.outputExposureValue = HuginBase::CalculateMeanExposure::calcMeanExposure(pano);
514         pano.setOptions(opts);
515         return true;
516     }
517 
processPanorama(HuginBase::Panorama & pano)518     bool DistributeImagesCmd::processPanorama(HuginBase::Panorama& pano)
519     {
520         const size_t nrImages = pano.getNrOfImages();
521         if (nrImages>0)
522         {
523             const HuginBase::SrcPanoImage& img = pano.getImage(0);
524             const double hfov = img.getHFOV();
525             size_t imgsPerRow;
526             //distribute all images
527             //for rectilinear images calculate number of rows
528             if (img.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR)
529             {
530                 imgsPerRow = std::max(3, int(360 / (0.8*hfov)));
531                 imgsPerRow = std::min(imgsPerRow, nrImages);
532             }
533             else
534             {
535                 //all other images do in one row to prevent cluttered images with fisheye images and the like
536                 imgsPerRow = nrImages;
537             };
538             double offset = 0.75*hfov;
539             if ((imgsPerRow - 1.0)*offset>360)
540             {
541                 offset = 360 / (imgsPerRow - 1.0);
542             };
543             double yaw = -(imgsPerRow - 1.0) / 2.0*offset;
544             double pitch = 0;
545             if (imgsPerRow<nrImages)
546             {
547                 pitch = (-(std::ceil(double(nrImages) / double(imgsPerRow)) - 1.0) / 2.0*offset);
548             };
549             HuginBase::VariableMapVector varsVec = pano.getVariables();
550             size_t counter = 0;
551             for (size_t i = 0; i<nrImages; i++)
552             {
553                 HuginBase::VariableMap::iterator it = varsVec[i].find("y");
554                 if (it != varsVec[i].end())
555                 {
556                     it->second.setValue(yaw);
557                 };
558                 it = varsVec[i].find("p");
559                 if (it != varsVec[i].end())
560                 {
561                     it->second.setValue(pitch);
562                 };
563                 yaw += offset;
564                 counter++;
565                 if (counter == imgsPerRow)
566                 {
567                     counter = 0;
568                     pitch += offset;
569                     yaw = -(imgsPerRow - 1.0) / 2.0*offset;
570                 };
571             };
572             pano.updateVariables(varsVec);
573         };
574         return true;
575     }
576 
577 } // namespace PanoCommand
578