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