1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file PanoOperation.cpp
4  *
5  *  @brief Implementation of PanoOperation class
6  *
7  */
8 
9 /*  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This software is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public
20  *  License along with this software. If not, see
21  *  <http://www.gnu.org/licenses/>.
22  *
23  */
24 
25 #include "hugin/PanoOperation.h"
26 #include "hugin/config_defaults.h"
27 #include "base_wx/PanoCommand.h"
28 #include "base_wx/wxPanoCommand.h"
29 #include "huginapp/ImageCache.h"
30 #include "base_wx/MyProgressDialog.h"
31 #include "base_wx/PTWXDlg.h"
32 #include "algorithms/optimizer/ImageGraph.h"
33 #include "algorithms/control_points/CleanCP.h"
34 #include "celeste/Celeste.h"
35 #ifdef _WIN32
36 // workaround for a conflict between exiv2 and wxWidgets/CMake built
37 #define HAVE_PID_T 1
38 #endif
39 #include <exiv2/exiv2.hpp>
40 #include "base_wx/LensTools.h"
41 #include "base_wx/wxLensDB.h"
42 #include "hugin/ResetDialog.h"
43 #include "hugin/ChangeImageVariableDialog.h"
44 #include "hugin/MainFrame.h"
45 #include "hugin/RawImport.h"
46 #include <vigra_ext/openmp_vigra.h>
47 #include <vigra_ext/cms.h>
48 
49 namespace PanoOperation
50 {
51 
GetLabel()52 wxString PanoOperation::GetLabel()
53 {
54     return wxEmptyString;
55 };
56 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)57 bool PanoOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
58 {
59     return true;
60 };
61 
GetCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)62 PanoCommand::PanoCommand* PanoOperation::GetCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
63 {
64     //remember gui level, only used by some PanoOperation's
65     m_guiLevel=guiLevel;
66     if(IsEnabled(pano, images, m_guiLevel))
67     {
68         return GetInternalCommand(parent,pano,images);
69     }
70     else
71     {
72         return NULL;
73     };
74 };
75 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)76 bool PanoSingleImageOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
77 {
78     return images.size()==1;
79 };
80 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)81 bool PanoMultiImageOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
82 {
83     return !images.empty();
84 };
85 
86 /** small function to show add image dialog
87   * @param parent pointer to window, for showing dialog
88   * @param files vector, to which the selected valid filenames will be added
89   * @returns true, if a valid image was selected, otherwise false
90   */
AddImageDialog(wxWindow * parent,std::vector<std::string> & files,bool & withRaws)91 bool AddImageDialog(wxWindow* parent, std::vector<std::string>& files, bool& withRaws)
92 {
93     // get stored path
94     wxConfigBase* config = wxConfigBase::Get();
95     wxString path = config->Read(wxT("/actualPath"), wxT(""));
96     wxFileDialog dlg(parent,_("Add images"),
97                      path, wxT(""),
98                      withRaws ? GetFileDialogImageAndRawFilters() : GetFileDialogImageFilters(),
99                      wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST | wxFD_PREVIEW, wxDefaultPosition);
100     dlg.SetDirectory(path);
101 
102     // remember the image extension
103     wxString img_ext;
104     if (config->HasEntry(wxT("lastImageType")))
105     {
106       img_ext = config->Read(wxT("lastImageType")).c_str();
107     }
108     if (img_ext == wxT("all images"))
109     {
110         dlg.SetFilterIndex(0);
111     }
112     else
113     {
114         if (!withRaws)
115         {
116             if (img_ext == wxT("jpg"))
117                 dlg.SetFilterIndex(1);
118             else if (img_ext == wxT("tiff"))
119                 dlg.SetFilterIndex(2);
120             else if (img_ext == wxT("png"))
121                 dlg.SetFilterIndex(3);
122             else if (img_ext == wxT("hdr"))
123                 dlg.SetFilterIndex(4);
124             else if (img_ext == wxT("exr"))
125                 dlg.SetFilterIndex(5);
126             else if (img_ext == wxT("all files"))
127                 dlg.SetFilterIndex(6);
128         }
129         else
130         {
131             if (img_ext == wxT("all raws"))
132                 dlg.SetFilterIndex(1);
133             else if (img_ext == wxT("jpg"))
134                 dlg.SetFilterIndex(2);
135             else if (img_ext == wxT("tiff"))
136                 dlg.SetFilterIndex(3);
137             else if (img_ext == wxT("png"))
138                 dlg.SetFilterIndex(4);
139             else if (img_ext == wxT("hdr"))
140                 dlg.SetFilterIndex(5);
141             else if (img_ext == wxT("exr"))
142                 dlg.SetFilterIndex(6);
143         };
144     };
145 
146     // call the file dialog
147     if (dlg.ShowModal() == wxID_OK)
148     {
149         // get the selections
150         wxArrayString Pathnames;
151         dlg.GetPaths(Pathnames);
152 
153         // remember path for later
154 #ifdef __WXGTK__
155         //workaround a bug in GTK, see https://bugzilla.redhat.com/show_bug.cgi?id=849692 and http://trac.wxwidgets.org/ticket/14525
156         config->Write(wxT("/actualPath"), wxPathOnly(Pathnames[0]));
157 #else
158         config->Write(wxT("/actualPath"), dlg.GetDirectory());
159 #endif
160         // save the image extension
161         if (!withRaws)
162         {
163             switch (dlg.GetFilterIndex())
164             {
165                 case 0: config->Write(wxT("lastImageType"), wxT("all images")); break;
166                 case 1: config->Write(wxT("lastImageType"), wxT("jpg")); break;
167                 case 2: config->Write(wxT("lastImageType"), wxT("tiff")); break;
168                 case 3: config->Write(wxT("lastImageType"), wxT("png")); break;
169                 case 4: config->Write(wxT("lastImageType"), wxT("hdr")); break;
170                 case 5: config->Write(wxT("lastImageType"), wxT("exr")); break;
171                 case 6: config->Write(wxT("lastImageType"), wxT("all files")); break;
172             };
173         }
174         else
175         {
176             switch (dlg.GetFilterIndex())
177             {
178                 case 0: config->Write(wxT("lastImageType"), wxT("all images")); break;
179                 case 1: config->Write(wxT("lastImageType"), wxT("all raws")); break;
180                 case 2: config->Write(wxT("lastImageType"), wxT("jpg")); break;
181                 case 3: config->Write(wxT("lastImageType"), wxT("tiff")); break;
182                 case 4: config->Write(wxT("lastImageType"), wxT("png")); break;
183                 case 5: config->Write(wxT("lastImageType"), wxT("hdr")); break;
184                 case 6: config->Write(wxT("lastImageType"), wxT("exr")); break;
185             }
186             withRaws = dlg.GetFilterIndex() == 1;
187         };
188 
189         //check for forbidden/non working chars
190         wxArrayString invalidFiles;
191         for(unsigned int i=0;i<Pathnames.GetCount(); i++)
192         {
193            if(containsInvalidCharacters(Pathnames[i]))
194            {
195                invalidFiles.Add(Pathnames[i]);
196            };
197         };
198         if(!invalidFiles.empty())
199         {
200             ShowFilenameWarning(parent, invalidFiles);
201             return false;
202         }
203         for (unsigned int i=0; i<Pathnames.GetCount(); i++)
204         {
205             files.push_back((const char *)Pathnames[i].mb_str(HUGIN_CONV_FILENAME));
206         };
207         return true;
208     };
209     return false;
210 };
211 
GetLabel()212 wxString AddImageOperation::GetLabel()
213 {
214     return _("Add individual images...");
215 };
216 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)217 PanoCommand::PanoCommand* AddImageOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
218 {
219     std::vector<std::string> files;
220     bool withRaws = true;
221     if(AddImageDialog(parent, files, withRaws))
222     {
223         if(!files.empty())
224         {
225             if (withRaws)
226             {
227                 if (files.size() == 1)
228                 {
229                     wxMessageDialog message(parent, _("You selected only one raw file. This is not recommended.\nAll raw files should be converted at once."),
230 #ifdef _WIN32
231                         _("Hugin"),
232 #else
233                         wxT(""),
234 #endif
235                         wxICON_EXCLAMATION | wxYES_NO );
236                     message.SetYesNoLabels(_("Convert anyway"), _("Let me select several raw files"));
237                     if (message.ShowModal() == wxID_NO)
238                     {
239                         // post new add image event to open dialog again
240                         wxCommandEvent newAddEvent(wxEVT_COMMAND_BUTTON_CLICKED, XRCID("action_add_images"));
241                         MainFrame::Get()->GetEventHandler()->AddPendingEvent(newAddEvent);
242                         return NULL;
243                     };
244                 };
245                 RawImportDialog dlg(parent, &pano, files);
246                 // check that raw files are from same camera and that all can be read
247                 if (dlg.CheckRawFiles())
248                 {
249                     // now show dialog
250                     if (dlg.ShowModal() == wxID_OK)
251                     {
252                         return dlg.GetPanoCommand();
253                     };
254                 };
255             }
256             else
257             {
258                 return new PanoCommand::wxAddImagesCmd(pano, files);
259             };
260         };
261     };
262     return NULL;
263 };
264 
265 WX_DECLARE_STRING_HASH_MAP(time_t, StringToPointerHash);
266 WX_DECLARE_STRING_HASH_MAP(int, StringToFlagHash);
267 
ReadExifTime(const char * filename)268 time_t ReadExifTime(const char* filename)
269 {
270 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
271     Exiv2::Image::UniquePtr image;
272 #else
273     Exiv2::Image::AutoPtr image;
274 #endif
275     try
276     {
277         image = Exiv2::ImageFactory::open(filename);
278     }
279     catch(...)
280     {
281         return 0;
282     }
283     if (image.get() == 0)
284     {
285         return 0;
286     }
287 
288     try
289     {
290         image->readMetadata();
291     }
292     catch (const Exiv2::Error& e)
293     {
294         std::cerr << "Exiv2: Error reading metadata (" << e.what() << ")" << std::endl;
295         return 0;
296     }
297     Exiv2::ExifData &exifData = image->exifData();
298     if (exifData.empty())
299     {
300         return 0;
301     }
302 
303     Exiv2::Exifdatum& tag = exifData["Exif.Image.DateTime"];
304     const std::string date_time = tag.toString();
305 
306     // Remember the file and a shutter timestamp.
307     struct tm when;
308     memset(&when, 0, sizeof(when));
309     when.tm_wday = -1;
310 
311     // parse into the tm_structure
312     const int a = sscanf(date_time.c_str(), "%d:%d:%d %d:%d:%d",
313             &when.tm_year, &when.tm_mon, &when.tm_mday,
314             &when.tm_hour, &when.tm_min, &when.tm_sec);
315 
316     if (a == 6)
317     {
318         when.tm_isdst = -1;
319         when.tm_mon -= 1;      // Adjust for unix zero-based months
320         when.tm_year -= 1900;  // Adjust for year starting at 1900
321     }
322     else
323     {
324         // Not in EXIF format
325         return 0;
326     }
327 
328     time_t stamp;
329     stamp = mktime(&when);
330     if (stamp == (time_t)(-1))
331         return 0;
332 
333     return stamp;
334 }
335 
336 struct sortbytime
337 {
sortbytimePanoOperation::sortbytime338     explicit sortbytime(std::map<std::string, time_t> & h) : m_time(h) {};
operator ()PanoOperation::sortbytime339     bool operator()(const std::string & s1, const std::string & s2)
340     {
341         time_t t1 = m_time[s1];
342         time_t t2 = m_time[s2];
343         return t1 < t2;
344     };
345     std::map<std::string, time_t> & m_time;
346 };
347 
GetLabel()348 wxString AddImagesSeriesOperation::GetLabel()
349 {
350     return _("Add time-series of images...");
351 };
352 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)353 PanoCommand::PanoCommand* AddImagesSeriesOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
354 {
355     //load image if pano contains no images
356     std::vector<std::string> files;
357     if(pano.getNrOfImages()==0)
358     {
359         bool withRaws = false;
360         if(!AddImageDialog(parent,files, withRaws))
361         {
362             return NULL;
363         };
364         //just in case
365         if(files.empty())
366         {
367             return NULL;
368         };
369     }
370     else
371     {
372         for(size_t i=0;i<pano.getNrOfImages();i++)
373         {
374             files.push_back(pano.getImage(i).getFilename());
375         };
376     };
377 
378     DEBUG_TRACE("seeking similarly timed images");
379 
380     // Collect potential image-mates.
381     StringToPointerHash filenames;
382     StringToFlagHash preloaded;
383     for(size_t i=0;i<files.size();i++)
384     {
385         wxString file(files[i].c_str(), HUGIN_CONV_FILENAME);
386         preloaded[file] = 1;
387 
388         // Glob for all files of same type in same directory.
389         wxString path = ::wxPathOnly(file) + wxT("/*");
390         file = ::wxFindFirstFile(path);
391         while (!file.IsEmpty())
392         {
393             // Associated with a NULL dummy timestamp for now.
394             if(vigra::isImage(files[i].c_str()))
395             {
396                 filenames[file] = 0;
397             };
398             file = ::wxFindNextFile();
399         }
400     }
401 
402     DEBUG_INFO("found " << filenames.size() << " candidate files to search.");
403 
404     // For each globbed or loaded file,
405     StringToPointerHash::iterator found;
406     std::map<std::string, time_t> timeMap;
407     for (found = filenames.begin(); found != filenames.end(); ++found)
408     {
409         wxString file = found->first;
410         // Check the time if it's got a camera EXIF timestamp.
411         time_t stamp = ReadExifTime(file.mb_str(HUGIN_CONV_FILENAME));
412         if (stamp)
413         {
414             filenames[file] = stamp;
415             timeMap[(const char *)file.mb_str(HUGIN_CONV_FILENAME)] = stamp;
416         }
417     }
418 
419     //TODO: sorting the filenames keys by timestamp would be useful
420     int maxtimediff = wxConfigBase::Get()->Read(wxT("CaptureTimeSpan"), HUGIN_CAPTURE_TIMESPAN);
421     // For each timestamped file,
422     for (found = filenames.begin(); found != filenames.end(); ++found)
423     {
424         wxString recruit = found->first;
425         if (preloaded[recruit] == 1)
426             continue;
427         time_t pledge = filenames[recruit];
428         if (!pledge)
429             continue;
430 
431         // For each other image already loaded,
432         for(size_t i=0;i<files.size();i++)
433         {
434             wxString file(files[i].c_str(), HUGIN_CONV_FILENAME);
435             if (file == recruit)
436                 continue;
437 
438             // If it is within threshold time,
439             time_t stamp = filenames[file];
440             if (abs((int)(pledge - stamp)) < maxtimediff)
441             {
442                 // Load this file, and remember it.
443                 DEBUG_TRACE("Recruited " << recruit.mb_str(wxConvLocal));
444                 std::string file = (const char *)recruit.mb_str(HUGIN_CONV_FILENAME);
445                 files.push_back(file);
446                 // Don't recruit it again.
447                 filenames[recruit] = 0;
448                 break;
449             }
450         }
451     }
452 
453     if(!files.empty())
454     {
455         // sort files by date
456         sortbytime spred(timeMap);
457         sort(files.begin(), files.end(), spred);
458         // Load all of the named files.
459         return new PanoCommand::wxAddImagesCmd(pano,files);
460     }
461     else
462     {
463         wxMessageBox(
464             _("No matching images found."),
465 #ifdef _WIN32
466             _("Hugin"),
467 #else
468             wxT(""),
469 #endif
470             wxOK | wxICON_INFORMATION, parent);
471         return NULL;
472     };
473 };
474 
GetLabel()475 wxString ImageVariablesExpressionOperation::GetLabel()
476 {
477     return _("Manipulate image variables...");
478 }
479 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)480 PanoCommand::PanoCommand * ImageVariablesExpressionOperation::GetInternalCommand(wxWindow * parent, HuginBase::Panorama & pano, HuginBase::UIntSet images)
481 {
482     ImageVariablesExpressionDialog dlg(parent, &pano);
483     if (dlg.ShowModal() == wxID_OK)
484     {
485         const std::string expression = dlg.GetExpression();
486         if (!expression.empty())
487         {
488             return new PanoCommand::UpdateVariablesByParseExpression(pano, expression);
489         };
490     };
491     return NULL;
492 }
493 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)494 bool ImageVariablesExpressionOperation::IsEnabled(HuginBase::Panorama & pano, HuginBase::UIntSet images, GuiLevel guiLevel)
495 {
496     return pano.getNrOfImages() > 0 && guiLevel >= GuiLevel::GUI_ADVANCED;
497 }
498 
GetLabel()499 wxString RemoveImageOperation::GetLabel()
500 {
501     return _("Remove selected image(s)");
502 };
503 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)504 PanoCommand::PanoCommand* RemoveImageOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
505 {
506     //remove images from cache
507     for (HuginBase::UIntSet::iterator it = images.begin(); it != images.end(); ++it)
508     {
509         HuginBase::ImageCache::getInstance().removeImage(pano.getImage(*it).getFilename());
510     }
511     return new PanoCommand::RemoveImagesCmd(pano, images);
512 };
513 
GetLabel()514 wxString ChangeAnchorImageOperation::GetLabel()
515 {
516     return _("Anchor this image for position");
517 };
518 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)519 PanoCommand::PanoCommand* ChangeAnchorImageOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
520 {
521     HuginBase::PanoramaOptions opt = pano.getOptions();
522     opt.optimizeReferenceImage = *(images.begin());
523     return new PanoCommand::SetPanoOptionsCmd(pano,opt);
524 };
525 
GetLabel()526 wxString ChangeColorAnchorImageOperation::GetLabel()
527 {
528     return _("Anchor this image for exposure");
529 };
530 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)531 PanoCommand::PanoCommand* ChangeColorAnchorImageOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
532 {
533     HuginBase::PanoramaOptions opt = pano.getOptions();
534     opt.colorReferenceImage = *(images.begin());
535     // Set the color correction mode so that the anchor image is persisted
536     if (opt.colorCorrection == 0)
537     {
538         opt.colorCorrection = (HuginBase::PanoramaOptions::ColorCorrection) 1;
539     }
540     return new PanoCommand::SetPanoOptionsCmd(pano, opt);
541 };
542 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)543 bool NewLensOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
544 {
545     if(pano.getNrOfImages()==0 || images.empty())
546     {
547         return false;
548     }
549     else
550     {
551         HuginBase::StandardImageVariableGroups variable_groups(pano);
552         return variable_groups.getLenses().getNumberOfParts()<pano.getNrOfImages();
553     };
554 };
555 
GetLabel()556 wxString NewLensOperation::GetLabel()
557 {
558     return _("New lens");
559 };
560 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)561 PanoCommand::PanoCommand* NewLensOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
562 {
563     return new PanoCommand::NewPartCmd(pano, images, HuginBase::StandardImageVariableGroups::getLensVariables());
564 };
565 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)566 bool ChangeLensOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
567 {
568     if(pano.getNrOfImages()==0 || images.empty())
569     {
570         return false;
571     }
572     else
573     {
574         //project must have more than 1 lens before you can assign another lens number
575         HuginBase::StandardImageVariableGroups variableGroups(pano);
576         return variableGroups.getLenses().getNumberOfParts() > 1;
577     };
578 };
579 
GetLabel()580 wxString ChangeLensOperation::GetLabel()
581 {
582     return _("Change lens...");
583 };
584 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)585 PanoCommand::PanoCommand* ChangeLensOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
586 {
587     HuginBase::StandardImageVariableGroups variable_groups(pano);
588     long nr = wxGetNumberFromUser(
589                             _("Enter new lens number"),
590                             _("Lens number"),
591                             _("Change lens number"), 0, 0,
592                             variable_groups.getLenses().getNumberOfParts()-1
593                                  );
594     if (nr >= 0)
595     {
596         // user accepted
597         // check that image size are the same
598         const vigra::Size2D lensImgSize = variable_groups.getLens(nr).getImageSize();
599         for (const auto& img : images)
600         {
601             if (pano.getImage(img).getSize() != lensImgSize)
602             {
603                 wxMessageBox(wxString::Format(_("Selected images and selected lens have different sizes. All images of the same lens should have the same size.\n\nImage %d has size %dx%d, while lens %d has images with size of %dx%d pixel."),
604                     img, pano.getImage(img).getWidth(), pano.getImage(img).getHeight(), nr, lensImgSize.width(), lensImgSize.height()),
605 #ifdef __WXMSW__
606                     wxT("Hugin"),
607 #else
608                     wxT(""),
609 #endif
610                     wxICON_EXCLAMATION | wxOK);
611                 return NULL;
612             };
613         };
614         return new PanoCommand::ChangePartNumberCmd(pano, images, nr, HuginBase::StandardImageVariableGroups::getLensVariables());
615     }
616     else
617     {
618         return NULL;
619     };
620 };
621 
LoadLensOperation(bool fromDatabase)622 LoadLensOperation::LoadLensOperation(bool fromDatabase)
623 {
624     m_fromDatabase = fromDatabase;
625 };
626 
GetLabel()627 wxString LoadLensOperation::GetLabel()
628 {
629     if (m_fromDatabase)
630     {
631         return _("Load lens from lens database");
632     }
633     else
634     {
635         return _("Load lens from ini file");
636     };
637 };
638 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)639 PanoCommand::PanoCommand* LoadLensOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
640 {
641     HuginBase::StandardImageVariableGroups variable_groups(pano);
642     HuginBase::UIntSet lensImages = variable_groups.getLenses().getPartsSet()[variable_groups.getLenses().getPartNumber(*images.begin())];
643     if (!m_fromDatabase && images.size() == 1 && lensImages.size() > 1)
644     {
645         // database is always linking the parameters, so no need to ask user
646         if(wxMessageBox(_("You selected only one image.\nShould the loaded parameters be applied to all images with the same lens?"),_("Question"), wxICON_QUESTION | wxYES_NO)==wxYES)
647         {
648             // get all images with the current lens.
649             std::copy(lensImages.begin(), lensImages.end(), std::inserter(images, images.end()));
650         };
651     };
652     vigra::Size2D sizeImg0=pano.getImage(*(images.begin())).getSize();
653     //check if all images have the same size
654     bool differentImageSize=false;
655     for(HuginBase::UIntSet::const_iterator it=images.begin();it!=images.end() && !differentImageSize;++it)
656     {
657         differentImageSize=(pano.getImage(*it).getSize()!=sizeImg0);
658     };
659     if(differentImageSize)
660     {
661         if(wxMessageBox(_("You selected images with different sizes.\nApply lens parameter file can result in unwanted results.\nApply settings anyway?"), _("Error"), wxICON_QUESTION |wxYES_NO)==wxID_NO)
662         {
663             return NULL;
664         };
665     };
666     PanoCommand::PanoCommand* cmd=NULL;
667     bool isLoaded=false;
668     if (m_fromDatabase)
669     {
670         isLoaded=ApplyLensDBParameters(parent,&pano,images,cmd);
671     }
672     else
673     {
674         isLoaded=ApplyLensParameters(parent,&pano,images,cmd);
675     };
676     if(isLoaded)
677     {
678         return cmd;
679     }
680     else
681     {
682         return NULL;
683     }
684 };
685 
SaveLensOperation(bool toDatabase)686 SaveLensOperation::SaveLensOperation(bool toDatabase)
687 {
688     m_database = toDatabase;
689 };
690 
GetLabel()691 wxString SaveLensOperation::GetLabel()
692 {
693     if (m_database)
694     {
695         return _("Save lens parameters to lens database");
696     }
697     else
698     {
699         return _("Save lens to ini file");
700     };
701 };
702 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)703 PanoCommand::PanoCommand* SaveLensOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
704 {
705     unsigned int imgNr = *(images.begin());
706     if (m_database)
707     {
708         SaveLensParameters(parent, pano.getImage(imgNr));
709     }
710     else
711     {
712         SaveLensParametersToIni(parent, &pano, images);
713     };
714     return NULL;
715 };
716 
GetLabel()717 wxString RemoveControlPointsOperation::GetLabel()
718 {
719     return _("Remove control points");
720 };
721 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)722 bool RemoveControlPointsOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
723 {
724     return pano.getNrOfImages()>0 && pano.getNrOfCtrlPoints()>0;
725 };
726 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)727 PanoCommand::PanoCommand* RemoveControlPointsOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
728 {
729     HuginBase::UIntSet selImages;
730     if(images.empty())
731     {
732         fill_set(selImages,0,pano.getNrOfCtrlPoints()-1);
733     }
734     else
735     {
736         selImages=images;
737     };
738     HuginBase::UIntSet cpsToDelete;
739     const HuginBase::CPVector & cps = pano.getCtrlPoints();
740     for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
741     {
742         if (set_contains(selImages, (*it).image1Nr) && set_contains(selImages, (*it).image2Nr) )
743         {
744             cpsToDelete.insert(it - cps.begin());
745         }
746     }
747     if(cpsToDelete.empty())
748     {
749         wxMessageBox(_("Selected images have no control points."),
750 #ifdef __WXMSW__
751             wxT("Hugin"),
752 #else
753             wxT(""),
754 #endif
755             wxICON_EXCLAMATION | wxOK);
756         return NULL;
757     };
758     int r =wxMessageBox(wxString::Format(_("Really delete %lu control points?"),
759                                          (unsigned long int) cpsToDelete.size()),
760                         _("Delete Control Points"),
761                         wxICON_QUESTION | wxYES_NO);
762     if (r == wxYES)
763     {
764         return new PanoCommand::RemoveCtrlPointsCmd(pano, cpsToDelete );
765     }
766     else
767     {
768         return NULL;
769     };
770 };
771 
GetLabel()772 wxString CleanControlPointsOperation::GetLabel()
773 {
774     return _("Clean control points");
775 };
776 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)777 bool CleanControlPointsOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
778 {
779     return pano.getNrOfCtrlPoints()>2;
780 };
781 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)782 PanoCommand::PanoCommand* CleanControlPointsOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
783 {
784     deregisterPTWXDlgFcn();
785     ProgressReporterDialog progress(0, _("Cleaning Control points"), _("Checking pairwise"), parent);
786 
787     HuginBase::UIntSet removedCPs=getCPoutsideLimit_pair(pano, progress, 2.0);
788 
789     //create a copy to work with
790     //we copy remaining control points to new pano object for running second step
791     HuginBase::Panorama newPano=pano.duplicate();
792     std::map<size_t,size_t> cpMap;
793     HuginBase::CPVector allCPs=newPano.getCtrlPoints();
794     HuginBase::CPVector firstCleanedCP;
795     size_t j=0;
796     for(size_t i=0;i<allCPs.size();i++)
797     {
798         HuginBase::ControlPoint cp = allCPs[i];
799         if (cp.mode == HuginBase::ControlPoint::X_Y && !set_contains(removedCPs, i))
800         {
801             firstCleanedCP.push_back(cp);
802             cpMap[j++]=i;
803         };
804     };
805     newPano.setCtrlPoints(firstCleanedCP);
806 
807     //check for unconnected images
808     HuginGraph::ImageGraph graph(newPano);
809     if (!progress.updateDisplayValue(_("Checking whole project")))
810     {
811         return NULL;
812     }
813     if (graph.IsConnected())
814     {
815         //now run the second step
816         HuginBase::UIntSet removedCP2=getCPoutsideLimit(newPano, 2.0);
817         if(!removedCP2.empty())
818         {
819             for(HuginBase::UIntSet::const_iterator it=removedCP2.begin();it!=removedCP2.end();++it)
820             {
821                 removedCPs.insert(cpMap[*it]);
822             };
823         };
824     }
825     registerPTWXDlgFcn();
826     if (!progress.updateDisplay(_("Finished cleaning")))
827     {
828         return NULL;
829     }
830     if (!removedCPs.empty())
831     {
832         wxMessageBox(wxString::Format(_("Removed %lu control points"), (unsigned long int)removedCPs.size()), _("Cleaning"), wxOK | wxICON_INFORMATION, parent);
833         return new PanoCommand::RemoveCtrlPointsCmd(pano,removedCPs);
834     };
835     return NULL;
836 };
837 
GetLabel()838 wxString CelesteOperation::GetLabel()
839 {
840     return _("Remove control points on clouds");
841 };
842 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)843 PanoCommand::PanoCommand* CelesteOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
844 {
845     ProgressReporterDialog progress(images.size() + 2, _("Running Celeste"), _("Running Celeste"), parent);
846     progress.updateDisplay(_("Loading model file"));
847 
848     struct celeste::svm_model* model=MainFrame::Get()->GetSVMModel();
849     if (model == NULL || !progress.updateDisplay(_("Loading images")))
850     {
851         return NULL;
852     };
853 
854     // Get Celeste parameters
855     wxConfigBase *cfg = wxConfigBase::Get();
856     // SVM threshold
857     double threshold = HUGIN_CELESTE_THRESHOLD;
858     cfg->Read(wxT("/Celeste/Threshold"), &threshold, HUGIN_CELESTE_THRESHOLD);
859 
860     // Mask resolution - 1 sets it to fine
861     bool t = (cfg->Read(wxT("/Celeste/Filter"), HUGIN_CELESTE_FILTER) == 0);
862     int radius=(t)?10:20;
863     DEBUG_TRACE("Running Celeste");
864 
865     HuginBase::UIntSet cpsToRemove;
866     for (HuginBase::UIntSet::const_iterator it=images.begin(); it!=images.end(); ++it)
867     {
868         // Image to analyse
869         HuginBase::CPointVector cps=pano.getCtrlPointsVectorForImage(*it);
870         if(cps.empty())
871         {
872             if (!progress.updateDisplayValue())
873             {
874                 return NULL;
875             };
876             continue;
877         };
878         HuginBase::ImageCache::EntryPtr img = HuginBase::ImageCache::getInstance().getImage(pano.getImage(*it).getFilename());
879         vigra::UInt16RGBImage in;
880         if(img->image16->width()>0)
881         {
882             in.resize(img->image16->size());
883             vigra::omp::copyImage(srcImageRange(*(img->image16)),destImage(in));
884         }
885         else
886         {
887             HuginBase::ImageCache::ImageCacheRGB8Ptr im8 = img->get8BitImage();
888             in.resize(im8->size());
889             vigra::omp::transformImage(srcImageRange(*im8),destImage(in),vigra::functor::Arg1()*vigra::functor::Param(65535/255));
890         };
891         if (!img->iccProfile->empty())
892         {
893             HuginBase::Color::ApplyICCProfile(in, *(img->iccProfile), TYPE_RGB_16);
894         };
895         if (!progress.updateDisplay(_("Running Celeste")))
896         {
897             return NULL;
898         };
899         HuginBase::UIntSet cloudCP=celeste::getCelesteControlPoints(model,in,cps,radius,threshold,800);
900         in.resize(0,0);
901         if (!progress.updateDisplay())
902         {
903             return NULL;
904         };
905         if(!cloudCP.empty())
906         {
907             for(HuginBase::UIntSet::const_iterator it2=cloudCP.begin();it2!=cloudCP.end(); ++it2)
908             {
909                 cpsToRemove.insert(*it2);
910             };
911         };
912         if (!progress.updateDisplayValue(_("Loading images")))
913         {
914             return NULL;
915         };
916     };
917 
918     if (!progress.updateDisplayValue())
919     {
920         return NULL;
921     }
922     if (!cpsToRemove.empty())
923     {
924         wxMessageBox(wxString::Format(_("Removed %lu control points"), (unsigned long int) cpsToRemove.size()), _("Celeste result"),wxOK|wxICON_INFORMATION);
925         return new PanoCommand::RemoveCtrlPointsCmd(pano,cpsToRemove);
926     }
927     else
928     {
929         return NULL;
930     };
931 };
932 
ResetOperation(ResetMode newResetMode)933 ResetOperation::ResetOperation(ResetMode newResetMode)
934 {
935     m_resetMode=newResetMode;
936     m_resetPos=(m_resetMode==RESET_POSITION);
937     m_resetTranslation=(m_resetMode==RESET_TRANSLATION);
938     m_resetHFOV=(m_resetMode==RESET_LENS);
939     m_resetLens=(m_resetMode==RESET_LENS);
940     m_resetExposure=0;
941     if(m_resetMode==RESET_PHOTOMETRICS)
942     {
943         m_resetExposure=3;
944     };
945     m_resetVignetting=(m_resetMode==RESET_PHOTOMETRICS);
946     m_resetColor = 0;
947     if(m_resetMode==RESET_PHOTOMETRICS)
948     {
949         m_resetColor=1;
950     };
951     m_resetCameraResponse=(m_resetMode==RESET_PHOTOMETRICS);
952 };
953 
GetLabel()954 wxString ResetOperation::GetLabel()
955 {
956     switch(m_resetMode)
957     {
958         case RESET_DIALOG:
959         case RESET_DIALOG_LENS:
960         case RESET_DIALOG_PHOTOMETRICS:
961             return _("Reset user defined...");
962             break;
963         case RESET_POSITION:
964             return _("Reset positions");
965             break;
966         case RESET_TRANSLATION:
967             return _("Reset translation parameters");
968             break;
969         case RESET_LENS:
970             return _("Reset lens parameters");
971             break;
972         case RESET_PHOTOMETRICS:
973             return _("Reset photometric parameters");
974     };
975     return wxEmptyString;
976 };
977 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)978 bool ResetOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
979 {
980     switch(m_resetMode)
981     {
982         case RESET_TRANSLATION:
983             return guiLevel>=GUI_EXPERT && pano.getNrOfImages()>0;
984             break;
985         default:
986             return pano.getNrOfImages()>0;
987     };
988 };
989 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)990 PanoCommand::PanoCommand* ResetOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
991 {
992     if(m_resetMode==RESET_DIALOG || m_resetMode==RESET_DIALOG_LENS || m_resetMode==RESET_DIALOG_PHOTOMETRICS)
993     {
994         if(!ShowDialog(parent))
995         {
996             return NULL;
997         };
998     };
999     if(images.empty())
1000     {
1001         fill_set(images,0,pano.getNrOfImages()-1);
1002     };
1003     double redBalanceAnchor = pano.getImage(pano.getOptions().colorReferenceImage).getExifRedBalance();
1004     double blueBalanceAnchor = pano.getImage(pano.getOptions().colorReferenceImage).getExifBlueBalance();
1005     if(fabs(redBalanceAnchor)<1e-2)
1006     {
1007         redBalanceAnchor=1;
1008     };
1009     if(fabs(blueBalanceAnchor)<1e-2)
1010     {
1011         blueBalanceAnchor=1;
1012     };
1013 
1014     HuginBase::VariableMapVector vars;
1015     for(HuginBase::UIntSet::const_iterator it = images.begin(); it != images.end(); ++it)
1016     {
1017         unsigned int imgNr = *it;
1018         HuginBase::VariableMap ImgVars = pano.getImageVariables(imgNr);
1019         if(m_resetPos)
1020         {
1021             map_get(ImgVars,"y").setValue(0);
1022             map_get(ImgVars,"p").setValue(0);
1023             map_get(ImgVars,"r").setValue(pano.getSrcImage(imgNr).getExifOrientation());
1024             map_get(ImgVars,"TrX").setValue(0);
1025             map_get(ImgVars,"TrY").setValue(0);
1026             map_get(ImgVars,"TrZ").setValue(0);
1027             map_get(ImgVars,"Tpy").setValue(0);
1028             map_get(ImgVars,"Tpp").setValue(0);
1029         };
1030         if(m_resetTranslation)
1031         {
1032             map_get(ImgVars,"TrX").setValue(0);
1033             map_get(ImgVars,"TrY").setValue(0);
1034             map_get(ImgVars,"TrZ").setValue(0);
1035             map_get(ImgVars,"Tpy").setValue(0);
1036             map_get(ImgVars,"Tpp").setValue(0);
1037         };
1038         HuginBase::SrcPanoImage srcImg = pano.getSrcImage(imgNr);
1039         if(m_resetHFOV)
1040         {
1041             double focalLength=srcImg.getExifFocalLength();
1042             double cropFactor=srcImg.getExifCropFactor();
1043             if(focalLength!=0 && cropFactor!=0)
1044             {
1045                 double newHFOV=HuginBase::SrcPanoImage::calcHFOV(srcImg.getProjection(), focalLength, cropFactor, srcImg.getSize());
1046                 if(newHFOV!=0)
1047                 {
1048                     map_get(ImgVars,"v").setValue(newHFOV);
1049                 };
1050             };
1051         };
1052         if(m_resetLens)
1053         {
1054             map_get(ImgVars,"a").setValue(0);
1055             map_get(ImgVars,"b").setValue(0);
1056             map_get(ImgVars,"c").setValue(0);
1057             map_get(ImgVars,"d").setValue(0);
1058             map_get(ImgVars,"e").setValue(0);
1059             map_get(ImgVars,"g").setValue(0);
1060             map_get(ImgVars,"t").setValue(0);
1061         };
1062         if(m_resetExposure>0)
1063         {
1064             if(m_resetExposure==1 || m_resetExposure==3)
1065             {
1066                 //reset to exif value
1067                 double eV=srcImg.calcExifExposureValue();
1068                 if ((m_resetExposure == 1 && eV != 0) || m_resetExposure == 3)
1069                 {
1070                     map_get(ImgVars,"Eev").setValue(eV);
1071                 }
1072             }
1073             else
1074             {
1075                 //reset to zero
1076                 map_get(ImgVars,"Eev").setValue(0);
1077             };
1078         };
1079         if(m_resetColor>0)
1080         {
1081             if(m_resetColor==1)
1082             {
1083                 double redBal=1;
1084                 double blueBal=1;
1085                 const HuginBase::SrcPanoImage& img=pano.getImage(imgNr);
1086                 const HuginBase::SrcPanoImage& anchor=pano.getImage(pano.getOptions().colorReferenceImage);
1087                 // use EXIF Red/BlueBalance data only if image and anchor image are from the same camera
1088                 if(img.getExifMake() == anchor.getExifMake() &&
1089                     img.getExifModel() == anchor.getExifModel())
1090                 {
1091                     redBal=fabs(img.getExifRedBalance()/redBalanceAnchor);
1092                     if(redBal<1e-2)
1093                     {
1094                         redBal=1;
1095                     };
1096                     blueBal=fabs(img.getExifBlueBalance()/blueBalanceAnchor);
1097                     if(blueBal<1e-2)
1098                     {
1099                         blueBal=1;
1100                     };
1101                 };
1102                 map_get(ImgVars,"Er").setValue(redBal);
1103                 map_get(ImgVars,"Eb").setValue(blueBal);
1104             }
1105             else
1106             {
1107                 map_get(ImgVars,"Er").setValue(1);
1108                 map_get(ImgVars,"Eb").setValue(1);
1109             };
1110         };
1111         if(m_resetVignetting)
1112         {
1113             map_get(ImgVars,"Vb").setValue(0);
1114             map_get(ImgVars,"Vc").setValue(0);
1115             map_get(ImgVars,"Vd").setValue(0);
1116             map_get(ImgVars,"Vx").setValue(0);
1117             map_get(ImgVars,"Vy").setValue(0);
1118 
1119         };
1120         if(m_resetCameraResponse)
1121         {
1122             map_get(ImgVars,"Ra").setValue(0);
1123             map_get(ImgVars,"Rb").setValue(0);
1124             map_get(ImgVars,"Rc").setValue(0);
1125             map_get(ImgVars,"Rd").setValue(0);
1126             map_get(ImgVars,"Re").setValue(0);
1127         };
1128         vars.push_back(ImgVars);
1129     };
1130     std::vector<PanoCommand::PanoCommand *> reset_commands;
1131     reset_commands.push_back(
1132                             new PanoCommand::UpdateImagesVariablesCmd(pano, images, vars)
1133                                            );
1134     if(m_resetExposure>0)
1135     {
1136         //reset panorama output exposure value
1137         reset_commands.push_back(new PanoCommand::ResetToMeanExposure(pano));
1138     };
1139     return new PanoCommand::CombinedPanoCommand(pano, reset_commands);
1140 };
1141 
ShowDialog(wxWindow * parent)1142 bool ResetOperation::ShowDialog(wxWindow* parent)
1143 {
1144     ResetDialog reset_dlg(parent, m_guiLevel);
1145     bool checkGeometric;
1146     bool checkPhotometric;
1147     switch(m_resetMode)
1148     {
1149         case RESET_DIALOG_LENS:
1150             reset_dlg.LimitToGeometric();
1151             checkGeometric=true;
1152             checkPhotometric=false;
1153             break;
1154         case RESET_DIALOG_PHOTOMETRICS:
1155             reset_dlg.LimitToPhotometric();
1156             checkGeometric=false;
1157             checkPhotometric=true;
1158             break;
1159         case RESET_DIALOG:
1160         default:
1161             checkGeometric=true;
1162             checkPhotometric=true;
1163             break;
1164     };
1165     if(reset_dlg.ShowModal()==wxID_OK)
1166     {
1167         if(checkGeometric)
1168         {
1169             m_resetPos=reset_dlg.GetResetPos();
1170             m_resetTranslation=reset_dlg.GetResetTranslation();
1171             m_resetHFOV=reset_dlg.GetResetFOV();
1172             m_resetLens=reset_dlg.GetResetLens();
1173         };
1174         if(checkPhotometric)
1175         {
1176             if(reset_dlg.GetResetExposure())
1177             {
1178                 if(reset_dlg.GetResetExposureToExif())
1179                 {
1180                     m_resetExposure=1;
1181                 }
1182                 else
1183                 {
1184                     m_resetExposure=2;
1185                 };
1186             }
1187             else
1188             {
1189                 m_resetExposure=0;
1190             };
1191             m_resetVignetting=reset_dlg.GetResetVignetting();
1192             if(reset_dlg.GetResetColor())
1193             {
1194                 if(reset_dlg.GetResetColorToExif())
1195                 {
1196                     m_resetColor=1;
1197                 }
1198                 else
1199                 {
1200                     m_resetColor=2;
1201                 };
1202             }
1203             else
1204             {
1205                 m_resetColor=0;
1206             };
1207             m_resetCameraResponse=reset_dlg.GetResetResponse();
1208         };
1209         return true;
1210     }
1211     else
1212     {
1213         return false;
1214     };
1215 };
1216 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)1217 bool NewStackOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
1218 {
1219     if(pano.getNrOfImages()==0 || images.empty())
1220     {
1221         return false;
1222     }
1223     else
1224     {
1225         HuginBase::StandardImageVariableGroups variable_groups(pano);
1226         return variable_groups.getStacks().getNumberOfParts()<pano.getNrOfImages();
1227     };
1228 };
1229 
GetLabel()1230 wxString NewStackOperation::GetLabel()
1231 {
1232     return _("New stack");
1233 };
1234 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)1235 PanoCommand::PanoCommand* NewStackOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
1236 {
1237     return new PanoCommand::NewPartCmd(pano, images, HuginBase::StandardImageVariableGroups::getStackVariables());
1238 };
1239 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)1240 bool ChangeStackOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
1241 {
1242     if(pano.getNrOfImages()==0 || images.empty())
1243     {
1244         return false;
1245     }
1246     else
1247     {
1248         //project must have more than 1 stack before you can assign another stack number
1249         HuginBase::StandardImageVariableGroups variableGroups(pano);
1250         return variableGroups.getStacks().getNumberOfParts() > 1;
1251     };
1252 };
1253 
GetLabel()1254 wxString ChangeStackOperation::GetLabel()
1255 {
1256     return _("Change stack...");
1257 };
1258 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)1259 PanoCommand::PanoCommand* ChangeStackOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
1260 {
1261     HuginBase::StandardImageVariableGroups variable_groups(pano);
1262     long nr = wxGetNumberFromUser(
1263                             _("Enter new stack number"),
1264                             _("Stack number"),
1265                             _("Change stack number"), 0, 0,
1266                             variable_groups.getStacks().getNumberOfParts()-1
1267                                  );
1268     if (nr >= 0)
1269     {
1270         // user accepted
1271         // check that image size are the same
1272         const vigra::Size2D stackImgSize = pano.getImage(*variable_groups.getStacks().getPartsSet()[nr].begin()).getSize();
1273         for (const auto& img : images)
1274         {
1275             if (pano.getImage(img).getSize() != stackImgSize)
1276             {
1277                 wxMessageBox(wxString::Format(_("Selected images and selected stack have different sizes. All images of the same stack should have the same size.\n\nImage %d has size %dx%d, while stack %d has images with size of %dx%d pixel."),
1278                     img, pano.getImage(img).getWidth(), pano.getImage(img).getHeight(), nr, stackImgSize.width(), stackImgSize.height()),
1279 #ifdef __WXMSW__
1280                     wxT("Hugin"),
1281 #else
1282                     wxT(""),
1283 #endif
1284                     wxICON_EXCLAMATION | wxOK);
1285                 return NULL;
1286             };
1287         };
1288 
1289         return new PanoCommand::ChangePartNumberCmd(pano, images, nr, HuginBase::StandardImageVariableGroups::getStackVariables());
1290     }
1291     else
1292     {
1293         return NULL;
1294     };
1295 };
1296 
IsEnabled(HuginBase::Panorama & pano,HuginBase::UIntSet images,GuiLevel guiLevel)1297 bool AssignStacksOperation::IsEnabled(HuginBase::Panorama& pano, HuginBase::UIntSet images, GuiLevel guiLevel)
1298 {
1299     return pano.getNrOfImages()>1;
1300 };
1301 
GetLabel()1302 wxString AssignStacksOperation::GetLabel()
1303 {
1304     return _("Set stack size...");
1305 };
1306 
GetInternalCommand(wxWindow * parent,HuginBase::Panorama & pano,HuginBase::UIntSet images)1307 PanoCommand::PanoCommand* AssignStacksOperation::GetInternalCommand(wxWindow* parent, HuginBase::Panorama& pano, HuginBase::UIntSet images)
1308 {
1309     wxConfigBase* cfg = wxConfigBase::Get();
1310     wxDialog dlg;
1311     wxXmlResource::Get()->LoadDialog(&dlg, parent, wxT("stack_size_dialog"));
1312     wxSpinCtrl* stackSpin = XRCCTRL(dlg, "stack_size_spinctrl", wxSpinCtrl);
1313     stackSpin->SetRange(1, pano.getNrOfImages());
1314     size_t oldStackSize = cfg->Read(wxT("/StackDialog/StackSize"), 3);
1315     oldStackSize = std::min(oldStackSize, pano.getNrOfImages());
1316     stackSpin->SetValue(oldStackSize);
1317     wxCheckBox* linkCheckBox = XRCCTRL(dlg, "stack_size_link_checkbox", wxCheckBox);
1318     linkCheckBox->SetValue(cfg->Read(wxT("/StackDialog/LinkPosition"), true) != 0l);
1319     if (dlg.ShowModal() != wxID_OK)
1320     {
1321         // user has canceled dialog
1322         return NULL;
1323     };
1324     long stackSize = stackSpin->GetValue();
1325     bool linkPosition = linkCheckBox->IsChecked();
1326     cfg->Write(wxT("/StackDialog/StackSize"), stackSize);
1327     cfg->Write(wxT("/StackDialog/LinkPosition"), linkPosition);
1328     if(stackSize<0)
1329     {
1330         return NULL;
1331     };
1332     std::vector<PanoCommand::PanoCommand *> commands;
1333     HuginBase::StandardImageVariableGroups variable_groups(pano);
1334     if(variable_groups.getStacks().getNumberOfParts()<pano.getNrOfImages())
1335     {
1336         // first remove all existing stacks
1337         for(size_t i=1; i<pano.getNrOfImages(); i++)
1338         {
1339             HuginBase::UIntSet imgs;
1340             imgs.insert(i);
1341             commands.push_back(new PanoCommand::NewPartCmd(pano, imgs, HuginBase::StandardImageVariableGroups::getStackVariables()));
1342         };
1343     };
1344 
1345     if (stackSize > 1)
1346     {
1347         size_t stackNr=0;
1348         size_t imgNr=0;
1349         while(imgNr<pano.getNrOfImages())
1350         {
1351             HuginBase::UIntSet imgs;
1352             for(size_t i=0; i<stackSize && imgNr<pano.getNrOfImages(); i++)
1353             {
1354                 imgs.insert(imgNr);
1355                 imgNr++;
1356             };
1357             commands.push_back(new PanoCommand::ChangePartNumberCmd(pano, imgs, stackNr, HuginBase::StandardImageVariableGroups::getStackVariables()));
1358             stackNr++;
1359         };
1360     };
1361 
1362     if (!linkPosition && stackSize > 1)
1363     {
1364         // unlink image position
1365         HuginBase::UIntSet imgs;
1366         fill_set(imgs, 0, pano.getNrOfImages() - 1);
1367         std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> variables;
1368         variables.insert(HuginBase::ImageVariableGroup::IVE_Yaw);
1369         variables.insert(HuginBase::ImageVariableGroup::IVE_Pitch);
1370         variables.insert(HuginBase::ImageVariableGroup::IVE_Roll);
1371         variables.insert(HuginBase::ImageVariableGroup::IVE_X);
1372         variables.insert(HuginBase::ImageVariableGroup::IVE_Y);
1373         variables.insert(HuginBase::ImageVariableGroup::IVE_Z);
1374         variables.insert(HuginBase::ImageVariableGroup::IVE_TranslationPlaneYaw);
1375         variables.insert(HuginBase::ImageVariableGroup::IVE_TranslationPlanePitch);
1376         commands.push_back(new PanoCommand::ChangePartImagesLinkingCmd(pano, imgs, variables, false, HuginBase::StandardImageVariableGroups::getStackVariables()));
1377     };
1378     return new PanoCommand::CombinedPanoCommand(pano, commands);
1379 };
1380 
1381 static PanoOperationVector PanoOpImages;
1382 static PanoOperationVector PanoOpLens;
1383 static PanoOperationVector PanoOpStacks;
1384 static PanoOperationVector PanoOpControlPoints;
1385 static PanoOperationVector PanoOpReset;
1386 
GetImagesOperationVector()1387 PanoOperationVector* GetImagesOperationVector()
1388 {
1389     return &PanoOpImages;
1390 };
1391 
GetLensesOperationVector()1392 PanoOperationVector* GetLensesOperationVector()
1393 {
1394     return &PanoOpLens;
1395 };
1396 
GetStacksOperationVector()1397 PanoOperationVector* GetStacksOperationVector()
1398 {
1399     return &PanoOpStacks;
1400 };
1401 
GetControlPointsOperationVector()1402 PanoOperationVector* GetControlPointsOperationVector()
1403 {
1404     return &PanoOpControlPoints;
1405 };
1406 
GetResetOperationVector()1407 PanoOperationVector* GetResetOperationVector()
1408 {
1409     return &PanoOpReset;
1410 };
1411 
GeneratePanoOperationVector()1412 void GeneratePanoOperationVector()
1413 {
1414     PanoOpImages.push_back(new AddImageOperation());
1415     PanoOpImages.push_back(new AddImagesSeriesOperation());
1416     PanoOpImages.push_back(new RemoveImageOperation());
1417     PanoOpImages.push_back(new ChangeAnchorImageOperation());
1418     PanoOpImages.push_back(new ChangeColorAnchorImageOperation());
1419     PanoOpImages.push_back(new ImageVariablesExpressionOperation());
1420 
1421     PanoOpLens.push_back(new NewLensOperation());
1422     PanoOpLens.push_back(new ChangeLensOperation());
1423     PanoOpLens.push_back(new LoadLensOperation(false));
1424     PanoOpLens.push_back(new LoadLensOperation(true));
1425     PanoOpLens.push_back(new SaveLensOperation(false));
1426     PanoOpLens.push_back(new SaveLensOperation(true));
1427 
1428     PanoOpStacks.push_back(new NewStackOperation());
1429     PanoOpStacks.push_back(new ChangeStackOperation());
1430     PanoOpStacks.push_back(new AssignStacksOperation());
1431 
1432     PanoOpControlPoints.push_back(new RemoveControlPointsOperation());
1433     PanoOpControlPoints.push_back(new CelesteOperation());
1434     PanoOpControlPoints.push_back(new CleanControlPointsOperation());
1435 
1436     PanoOpReset.push_back(new ResetOperation(ResetOperation::RESET_POSITION));
1437     PanoOpReset.push_back(new ResetOperation(ResetOperation::RESET_TRANSLATION));
1438     PanoOpReset.push_back(new ResetOperation(ResetOperation::RESET_LENS));
1439     PanoOpReset.push_back(new ResetOperation(ResetOperation::RESET_PHOTOMETRICS));
1440     PanoOpReset.push_back(new ResetOperation(ResetOperation::RESET_DIALOG));
1441 
1442 };
1443 
1444 
_CleanPanoOperationVector(PanoOperationVector & vec)1445 void _CleanPanoOperationVector(PanoOperationVector& vec)
1446 {
1447     for(size_t i=0; i<vec.size(); i++)
1448     {
1449         delete vec[i];
1450     }
1451     vec.clear();
1452 };
1453 
CleanPanoOperationVector()1454 void CleanPanoOperationVector()
1455 {
1456     _CleanPanoOperationVector(PanoOpImages);
1457     _CleanPanoOperationVector(PanoOpLens);
1458     _CleanPanoOperationVector(PanoOpStacks);
1459     _CleanPanoOperationVector(PanoOpControlPoints);
1460     _CleanPanoOperationVector(PanoOpReset);
1461 };
1462 
1463 } //namespace
1464