Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
StackerPanel.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
10 /* This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This software is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public
21  * License along with this software. If not, see
22  * <http://www.gnu.org/licenses/>.
23  *
24  */
25 
26 #include "StackerPanel.h"
27 #include <wx/stdpaths.h>
28 #include <wx/propgrid/advprops.h>
29 #include "base_wx/platform.h"
30 #include "base_wx/wxPlatform.h"
32 #include "base_wx/Executor.h"
33 #include <wx/fileconf.h>
34 #include <wx/wfstream.h>
35 #include <wx/sstream.h>
36 #include <wx/filepicker.h>
37 #include <wx/dir.h>
38 #include "hugin_utils/utils.h"
40 #include "base_wx/LensTools.h"
41 #include "base_wx/wxutils.h"
42 #include "CreateBrightImgDlg.h"
43 
45 class SelectDirectoryFileDialog :public wxDialog
46 {
47 public:
49  SelectDirectoryFileDialog(wxWindow* parent)
50  {
51  wxXmlResource::Get()->LoadDialog(this, parent, "select_dir_dialog");
52  m_dirPicker = XRCCTRL(*this, "select_dir_picker", wxDirPickerCtrl);
53  wxConfigBase* config = wxConfigBase::Get();
54  wxString path = config->Read("/actualPath", "");
55  if (!path.IsEmpty())
56  {
57  m_dirPicker->SetPath(path);
58  }
59  m_recursiveCheck = XRCCTRL(*this, "select_dir_recursive", wxCheckBox);
60  if (config->HasEntry("/ToolboxFrame/Stacker/AddDirRecursive"))
61  {
62  m_recursiveCheck->SetValue(config->ReadBool("/ToolboxFrame/Stacker/AddDirRecursive", false));
63  };
64  m_filenameCtrl = XRCCTRL(*this, "select_dir_filename", wxTextCtrl);
65  if (config->HasEntry("/ToolboxFrame/Stacker/AddDirFilenames"))
66  {
67  m_filenameCtrl->SetValue(config->Read("/ToolboxFrame/Stacker/AddDirFilenames", "*"));
68  };
69  m_filetypeChoice = XRCCTRL(*this, "select_dir_filetype", wxChoice);
71  if (config->HasEntry("/ToolboxFrame/Stacker/AddDirFileType"))
72  {
73  m_filetypeChoice->SetSelection(config->ReadLong("/ToolboxFrame/Stacker/AddDirFileType", 0l));
74  };
75  Bind(wxEVT_BUTTON, &SelectDirectoryFileDialog::OnOk, this, wxID_OK);
76  hugin_utils::RestoreFramePosition(this, "ToolboxFrame/Stacker/AddDirDialog");
77  }
78  // return list which matches the given criterium
79  wxArrayString GetFilenames()
80  {
81  return m_filenames;
82  }
83 protected:
84  void OnOk(wxCommandEvent& e)
85  {
86  // save positions and settings
87  wxConfigBase* config = wxConfigBase::Get();
88  config->Write("/actualPath", m_dirPicker->GetPath());
89  config->Write("/ToolboxFrame/Stacker/AddDirRecursive", m_recursiveCheck->GetValue());
90  config->Write("/ToolboxFrame/Stacker/AddDirFilenames", m_filenameCtrl->GetValue());
91  config->Write("/ToolboxFrame/Stacker/AddDirFileType", m_filetypeChoice->GetSelection());
92  hugin_utils::StoreFramePosition(this, "ToolboxFrame/Stacker/AddDirDialog");
93  // now try to find all images
94  wxString filename = m_filenameCtrl->GetValue();
95  if (filename.IsEmpty())
96  {
97  filename = "*";
98  };
99  m_filenames.Clear();
100  int flags = wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW;
101  if (m_recursiveCheck->GetValue())
102  {
103  flags = flags | wxDIR_DIRS;
104  };
105  wxArrayString ext = wxSplit(m_filetypeExtensions[m_filetypeChoice->GetSelection()], ';');
106  for (auto& currExt : ext)
107  {
108  wxString currFilename(currExt);
109  currFilename.Replace("*", filename, true);
110  wxDir::GetAllFiles(m_dirPicker->GetPath(), &m_filenames, currFilename, flags);
111  };
112  hugin_utils::HuginMessageBox(wxString::Format("Found %zu images", m_filenames.size()), _("Hugin_toolbox"), wxOK | wxICON_INFORMATION, this);
113  if (!m_filenames.IsEmpty())
114  {
115  EndModal(wxID_OK);
116  };
117  }
118 private:
120  {
121  wxArrayString fileTypes = wxSplit(GetMainImageFilters(), '|');
122  size_t i = 0;
123  while (i < fileTypes.size())
124  {
125  if (i + 1 >= fileTypes.size())
126  {
127  break;
128  };
129  m_filetypeChoice->AppendString(fileTypes[i]);
130  ++i;
131  m_filetypeExtensions.Add(fileTypes[i]);
132  ++i;
133  };
134  m_filetypeChoice->SetSelection(0);
135  }
136  wxDirPickerCtrl* m_dirPicker;
137  wxCheckBox* m_recursiveCheck;
138  wxTextCtrl* m_filenameCtrl;
139  wxChoice* m_filetypeChoice;
140  wxArrayString m_filenames;
141  wxArrayString m_filetypeExtensions;
142 };
143 
145 class StackerDropTarget : public wxFileDropTarget
146 {
147 public:
148  StackerDropTarget(StackerPanel* parent) : wxFileDropTarget()
149  {
150  m_stackerPanel = parent;
151  }
152 
153  bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
154  {
155  // try to add as images
156  wxArrayString files;
157  for (unsigned int i = 0; i < filenames.GetCount(); i++)
158  {
159  wxFileName file(filenames[i]);
160  if (file.GetExt().CmpNoCase("jpg") == 0 ||
161  file.GetExt().CmpNoCase("jpeg") == 0 ||
162  file.GetExt().CmpNoCase("tif") == 0 ||
163  file.GetExt().CmpNoCase("tiff") == 0 ||
164  file.GetExt().CmpNoCase("png") == 0 ||
165  file.GetExt().CmpNoCase("bmp") == 0 ||
166  file.GetExt().CmpNoCase("gif") == 0 ||
167  file.GetExt().CmpNoCase("pnm") == 0 ||
168  file.GetExt().CmpNoCase("sun") == 0 ||
169  file.GetExt().CmpNoCase("hdr") == 0 ||
170  file.GetExt().CmpNoCase("viff") == 0)
171  {
172  files.push_back(file.GetFullPath());
173  };
174  };
175  // we got some images to add.
176  if (!files.empty())
177  {
178  for (auto& f : files)
179  {
181  };
182  };
183  return true;
184  }
185 private:
187 };
188 
189 // destructor, save settings
191 {
192  wxConfigBase* config = wxConfigBase::Get();
193  config->Write("/ToolboxFrame/Stacker/Splitter", XRCCTRL(*this, "stacker_splitter", wxSplitterWindow)->GetSashPosition());
194 
195  config->Write("/ToolboxFrame/Stacker/Mode", m_modeChoice->GetSelection());
196  config->Write("/ToolboxFrame/Stacker/WinsorTrim", m_winsorTrim->GetValue());
197  config->Write("/ToolboxFrame/Stacker/Sigma", m_sigma->GetValue());
198  config->Write("/ToolboxFrame/Stacker/SigmaIter", m_sigmaMaxIter->GetValue());
199  for (int j = 0; j < m_fileListCtrl->GetColumnCount(); j++)
200  {
201  config->Write(wxString::Format("/ToolboxFrame/Stacker/FileListColumnWidth%d", j), m_fileListCtrl->GetColumnWidth(j));
202  };
203  config->Write("/ToolboxFrame/Stacker/FileListSortColumn", m_sortCol);
204  config->Write("/ToolboxFrame/Stacker/FileListSortAscending", m_sortAscending);
205 
206  config->Flush();
208 }
209 
210 bool StackerPanel::Create(wxWindow* parent, MyExecPanel* logPanel)
211 {
212  if (!wxPanel::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, "panel"))
213  {
214  return false;
215  }
216  // create preview window
217  m_preview = new PreviewWindow();
218  m_preview->Create(this);
219 
220  m_logWindow = logPanel;
221  // load from xrc file and attach unknown controls
222  wxXmlResource::Get()->LoadPanel(this, "stacker_panel");
223  wxXmlResource::Get()->AttachUnknownControl("stacker_preview_window", m_preview, this);
224  // add to sizer
225  wxPanel* mainPanel = XRCCTRL(*this, "stacker_panel", wxPanel);
226  wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
227  topsizer->Add(mainPanel, wxSizerFlags(1).Expand());
228  // create columns in wxListCtrl
229  m_fileListCtrl = XRCCTRL(*this, "stacker_files", wxListCtrl);
230  m_fileListCtrl->InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, 200);
231  m_fileListCtrl->InsertColumn(1, _("Width"), wxLIST_FORMAT_LEFT, 50);
232  m_fileListCtrl->InsertColumn(2, _("Height"), wxLIST_FORMAT_LEFT, 50);
233  m_fileListCtrl->InsertColumn(3, _("Path"), wxLIST_FORMAT_LEFT, 200);
234  m_fileListCtrl->EnableCheckBoxes(true);
235  SetSizer(topsizer);
236  // store some controls for easier access
237  m_modeChoice = XRCCTRL(*this, "stacker_mode_choice", wxChoice);
238  m_winsorTrim = XRCCTRL(*this, "stacker_winsor_trim_spinctrl", wxSpinCtrlDouble);
239  m_sigma = XRCCTRL(*this, "stacker_sigma_spinctrl", wxSpinCtrlDouble);
240  m_sigmaMaxIter = XRCCTRL(*this, "stacker_sigma_iterations_spinctrl", wxSpinCtrl);
241 
242  SetDropTarget(new StackerDropTarget(this));
243 
244  wxConfigBase* config = wxConfigBase::Get();
245  // restore splitter position
246  if (config->HasEntry("/ToolboxFrame/Stacker/Splitter"))
247  {
248  XRCCTRL(*this, "stacker_splitter", wxSplitterWindow)->SetSashPosition(config->ReadLong("/ToolboxFrame/Stacker/Splitter", 300));
249  }
250  m_modeChoice->SetSelection(config->ReadLong("/ToolboxFrame/Stacker/Mode", 0));
251  m_winsorTrim->SetValue(config->ReadDouble("/ToolboxFrame/Stacker/WinsorTrim", 0.2));
252  m_sigma->SetValue(config->ReadDouble("/ToolboxFrame/Stacker/Sigma", 2));
253  m_sigmaMaxIter->SetValue(config->ReadLong("/ToolboxFrame/Stacker/SigmaIter", 5));
254  //get saved width of list ctrl column width
255  for (int j = 0; j < m_fileListCtrl->GetColumnCount(); j++)
256  {
257  // -1 is auto
258  int width = config->Read(wxString::Format("/ToolboxFrame/Stacker/FileListColumnWidth%d", j), -1);
259  if (width != -1)
260  {
261  m_fileListCtrl->SetColumnWidth(j, width);
262  };
263  };
264  m_sortCol = config->Read("/ToolboxFrame/Stacker/FileListSortColumn", -1);
265  m_sortAscending = config->Read("/ToolboxFrame/Stacker/FileListSortAscending", 1) == 1 ? true : false;
266  if (m_sortCol != -1)
267  {
268  m_fileListCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
269  };
270 
271  // bind event handler
272  Bind(wxEVT_BUTTON, &StackerPanel::OnAddFiles, this, XRCID("stacker_add_file"));
273  Bind(wxEVT_BUTTON, &StackerPanel::OnAddDirectory, this, XRCID("stacker_add_files_dir"));
274  Bind(wxEVT_BUTTON, &StackerPanel::OnRemoveFile, this, XRCID("stacker_remove_file"));
275  m_fileListCtrl->Bind(wxEVT_CHAR, &StackerPanel::OnFileListChar, this);
276  m_fileListCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, &StackerPanel::OnFileSelectionChanged, this);
277  m_fileListCtrl->Bind(wxEVT_LIST_ITEM_DESELECTED, &StackerPanel::OnFileSelectionChanged, this);
278  m_fileListCtrl->Bind(wxEVT_LIST_ITEM_CHECKED, &StackerPanel::OnFileCheckStateChanged, this);
279  m_fileListCtrl->Bind(wxEVT_LIST_ITEM_UNCHECKED, &StackerPanel::OnFileCheckStateChanged, this);
280  m_fileListCtrl->Bind(wxEVT_LIST_COL_CLICK, &StackerPanel::OnFileColumnHeaderClick, this);
281  Bind(wxEVT_CHOICE, &StackerPanel::OnModeChanged, this, XRCID("stacker_mode_choice"));
282  Bind(wxEVT_BUTTON, &StackerPanel::OnGeneratePreview, this, XRCID("stacker_preview"));
283  Bind(wxEVT_BUTTON, &StackerPanel::OnGenerateOutput, this, XRCID("stacker_output"));
284  Bind(wxEVT_CHOICE, &StackerPanel::OnZoom, this, XRCID("stacker_choice_zoom"));
285 
287  EnableOutputButtons(false);
288  wxCommandEvent e;
289  OnModeChanged(e);
290  return true;
291 }
292 
293 void StackerPanel::AddFile(const wxString& filename)
294 {
295  wxFileName fullFilename(filename);
296  // add image dimensions
297  vigra::ImageImportInfo info(fullFilename.GetFullPath().c_str());
298  vigra::Size2D imageSize = info.getCanvasSize();
299  if (imageSize.area() == 0)
300  {
301  imageSize = info.size();
302  };
303  // add to array
304  m_files.push_back(FileInfo(fullFilename.GetFullPath(), fullFilename.GetFullName(), fullFilename.GetPath(), imageSize.width(), imageSize.height()));
305  AddFileInfo(m_files.back());
307 }
308 
309 void StackerPanel::OnAddFiles(wxCommandEvent& e)
310 {
311  wxConfigBase* config = wxConfigBase::Get();
312  wxString path = config->Read("/actualPath", "");
313  wxFileDialog dlg(this, _("Add images"), path, wxEmptyString,
314  GetFileDialogImageFilters(), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST | wxFD_PREVIEW, wxDefaultPosition);
315  dlg.SetDirectory(path);
316 
317  // remember the image extension
318  wxString img_ext;
319  if (config->HasEntry("lastImageType"))
320  {
321  img_ext = config->Read("lastImageType").c_str();
322  }
323  if (img_ext == "all images")
324  dlg.SetFilterIndex(0);
325  else if (img_ext == "jpg")
326  dlg.SetFilterIndex(1);
327  else if (img_ext == "tiff")
328  dlg.SetFilterIndex(2);
329  else if (img_ext == "png")
330  dlg.SetFilterIndex(3);
331  else if (img_ext == "hdr")
332  dlg.SetFilterIndex(4);
333  else if (img_ext == "exr")
334  dlg.SetFilterIndex(5);
335  else if (img_ext == "all files")
336  dlg.SetFilterIndex(6);
337 
338  // call the file dialog
339  if (dlg.ShowModal() == wxID_OK)
340  {
341  // get the selections
342  wxArrayString Pathnames;
343  dlg.GetPaths(Pathnames);
344  // save the current path to config
345  config->Write("/actualPath", dlg.GetDirectory());
346  // save the image extension
347  switch (dlg.GetFilterIndex())
348  {
349  case 0: config->Write("lastImageType", "all images"); break;
350  case 1: config->Write("lastImageType", "jpg"); break;
351  case 2: config->Write("lastImageType", "tiff"); break;
352  case 3: config->Write("lastImageType", "png"); break;
353  case 4: config->Write("lastImageType", "hdr"); break;
354  case 5: config->Write("lastImageType", "exr"); break;
355  case 6: config->Write("lastImageType", "all files"); break;
356  };
357  m_fileListCtrl->Freeze();
358  for (auto& f : Pathnames)
359  {
360  AddFile(f);
361  };
362  SortList();
363  m_fileListCtrl->Thaw();
364  }
365 }
366 
367 void StackerPanel::OnAddDirectory(wxCommandEvent& e)
368 {
369  SelectDirectoryFileDialog dlg(this);
370  if (dlg.ShowModal() == wxID_OK)
371  {
372  const wxArrayString filenames = dlg.GetFilenames();
373  wxBusyCursor cur;
374  m_fileListCtrl->Freeze();
375  for (auto& f : filenames)
376  {
377  AddFile(f);
378  };
379  SortList();
380  m_fileListCtrl->Thaw();
381  };
382 }
383 
384 void StackerPanel::OnRemoveFile(wxCommandEvent& e)
385 {
386  HuginBase::UIntSet selected;
387  if (m_fileListCtrl->GetSelectedItemCount() >= 1)
388  {
389  for (int i = 0; i < m_fileListCtrl->GetItemCount(); ++i)
390  {
391  if (m_fileListCtrl->GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
392  {
393  selected.insert(m_fileListCtrl->GetItemData(i));
394  };
395  };
396  };
397  if (!selected.empty())
398  {
399  // remove selected file from internal file list
400  for (auto it = selected.rbegin(); it != selected.rend(); ++it)
401  {
402  m_files.erase(m_files.begin() + (*it));
403  };
404  // now refill list again
405  m_fileListCtrl->DeleteAllItems();
406  FillFileList();
409  }
410  else
411  {
412  wxBell();
413  };
414 }
415 
416 void StackerPanel::OnFileListChar(wxKeyEvent& e)
417 {
418  // ctrl + a, select all files
419  if (e.GetKeyCode() == 1 && e.CmdDown())
420  {
421  // select all
422  for (int i = 0; i < m_fileListCtrl->GetItemCount(); i++)
423  {
424  m_fileListCtrl->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
425  };
426  };
427  // insert key, add file
428  if ((e.GetKeyCode() == WXK_INSERT || e.GetKeyCode() == WXK_NUMPAD_INSERT) && !e.HasAnyModifiers())
429  {
430  wxCommandEvent e(wxEVT_BUTTON, XRCID("stacker_add_file"));
431  this->GetEventHandler()->AddPendingEvent(e);
432  };
433  // delete key, delete selected file
434  if ((e.GetKeyCode() == WXK_DELETE || e.GetKeyCode() == WXK_NUMPAD_DELETE) && !e.HasAnyModifiers())
435  {
436  if (m_fileListCtrl->GetSelectedItemCount() > 0)
437  {
438  wxCommandEvent e(wxEVT_BUTTON, XRCID("stacker_remove_file"));
439  this->GetEventHandler()->AddPendingEvent(e);
440  }
441  else
442  {
443  wxBell();
444  };
445  };
446  e.Skip();
447 }
448 
450 {
451  // enable/disable button according to selected files
453 }
454 
456 {
457  const long index = m_fileListCtrl->GetItemData(e.GetItem());
458  m_files[index].isChecked = m_fileListCtrl->IsItemChecked(e.GetItem());
460 }
461 
463 {
464  const int newCol = e.GetColumn();
465  if (m_sortCol == newCol)
466  {
468  }
469  else
470  {
471  m_sortCol = newCol;
472  m_sortAscending = true;
473  };
474  m_fileListCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
475  SortList();
476  Refresh();
477 }
478 
479 void StackerPanel::OnModeChanged(wxCommandEvent& e)
480 {
481  const int mode = m_modeChoice->GetSelection();
482  // hide controls which are not necessary for current mode
483  XRCCTRL(*this, "stacker_winsor_trim_label", wxStaticText)->Show(mode == 2);
484  m_winsorTrim->Show(mode == 2);
485  XRCCTRL(*this, "stacker_sigma_label", wxStaticText)->Show(mode == 3);
486  m_sigma->Show(mode == 3);
487  XRCCTRL(*this, "stacker_sigma_iterations_label", wxStaticText)->Show(mode == 3);
488  m_sigmaMaxIter->Show(mode == 3);
489  m_modeChoice->GetParent()->Layout();
490 }
491 
492 void StackerPanel::OnGeneratePreview(wxCommandEvent& e)
493 {
494  // create temp file name, set extension to tif
495  m_outputFilenames.resize(2);
496  wxFileName tempfile(wxFileName::CreateTempFileName(HuginQueue::GetConfigTempDir(wxConfig::Get()) + "he"));
497  m_outputFilenames[1] = tempfile.GetFullPath();
498  tempfile.SetExt("tif");
499  m_outputFilenames[0] = tempfile.GetFullPath();
500  m_cleanupOutput = true;
501  // get command line and execute
502  ExecuteStacker();
503 }
504 
506 {
507  wxString cmd = GetStackerCommandLine();
508  if (!cmd.IsEmpty())
509  {
510  EnableOutputButtons(false);
512  cmd = HuginQueue::wxEscapeFilename(HuginQueue::GetInternalProgram(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), "hugin_stacker")) + cmd;
513  m_logWindow->AddString(cmd);
515  }
516 }
517 
519 {
520  wxString cmd = GetStackerCommandLine();
521  if (!cmd.IsEmpty())
522  {
523  EnableOutputButtons(false);
526  wxString exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
527  queue->push_back(new HuginQueue::NormalCommand(HuginQueue::GetInternalProgram(exePath, "hugin_stacker"), cmd, _("Stacking images") + " (" + cmd + ")"));
528  // get first image
529  long index = -1;
530  for (long i = 0; i < m_fileListCtrl->GetItemCount(); ++i)
531  {
532  if (m_fileListCtrl->IsItemChecked(i))
533  {
534  index = m_fileListCtrl->GetItemData(i);
535  break;
536  };
537  };
538  wxString exiftoolArgs(" -overwrite_original -tagsfromfile " + HuginQueue::wxEscapeFilename(m_files[index].fullFilename));
539  exiftoolArgs.Append(" -all:all --thumbnail --thumbnailimage --xposition --yposition --imagefullwidth --imagefullheight ");
540  exiftoolArgs.Append(HuginQueue::wxEscapeFilename(m_outputFilenames[0]));
541  queue->push_back(new HuginQueue::OptionalCommand(HuginQueue::GetExternalProgram(wxConfig::Get(), exePath, "exiftool"), exiftoolArgs, _("Updating EXIF")));
542  m_logWindow->ExecQueue(queue);
543  };
544 }
545 
546 void StackerPanel::OnGenerateOutput(wxCommandEvent& e)
547 {
548  if (m_files.empty())
549  {
550  wxBell();
551  return;
552  };
553  wxConfigBase* config = wxConfigBase::Get();
554  wxString path = config->Read("/actualPath", "");
555  wxFileDialog dlg(this, _("Save output"), path, wxEmptyString, GetMainImageFilters(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, wxDefaultPosition);
556  dlg.SetDirectory(path);
557 
558  // remember the image extension
559  wxString img_ext;
560  if (config->HasEntry("lastImageType"))
561  {
562  img_ext = config->Read("lastImageType").c_str();
563  }
564  if (img_ext == "jpg")
565  {
566  dlg.SetFilterIndex(0);
567  }
568  else
569  {
570  if (img_ext == "tiff")
571  {
572  dlg.SetFilterIndex(1);
573  }
574  else
575  {
576  if (img_ext == "png")
577  {
578  dlg.SetFilterIndex(2);
579  };
580  };
581  };
582  wxFileName outputfilename(m_files[0].fullFilename);
583  outputfilename.SetName(outputfilename.GetName() + "_stacked");
584  dlg.SetFilename(outputfilename.GetFullPath());
585  // call the file dialog
586  if (dlg.ShowModal() == wxID_OK)
587  {
588  m_outputFilenames.resize(1);
589  m_outputFilenames[0] = dlg.GetPath();
590  // save the current path to config
591  config->Write("/actualPath", dlg.GetDirectory());
592  // save the image extension
593  switch (dlg.GetFilterIndex())
594  {
595  case 0:
596  config->Write("lastImageType", "jpg");
597  break;
598  case 1:
599  config->Write("lastImageType", "tiff");
600  break;
601  case 2:
602  config->Write("lastImageType", "png");
603  break;
604  };
605  m_cleanupOutput = false;
607  };
608 }
609 
610 void StackerPanel::OnZoom(wxCommandEvent& e)
611 {
612  double factor;
613  switch (e.GetSelection())
614  {
615  case 0:
616  factor = 1;
617  break;
618  case 1:
619  // fit to window
620  factor = 0;
621  break;
622  case 2:
623  factor = 2;
624  break;
625  case 3:
626  factor = 1.5;
627  break;
628  case 4:
629  factor = 0.75;
630  break;
631  case 5:
632  factor = 0.5;
633  break;
634  case 6:
635  factor = 0.25;
636  break;
637  default:
638  DEBUG_ERROR("unknown scale factor");
639  factor = 1;
640  }
641  m_preview->setScale(factor);
642 }
643 
644 void StackerPanel::OnProcessFinished(wxCommandEvent& e)
645 {
646  wxFileName output(m_outputFilenames[0]);
647  if (output.FileExists() && output.GetSize()>0)
648  {
649  m_preview->setImage(output.GetFullPath());
650  };
651  EnableOutputButtons(true);
652  if (m_cleanupOutput && output.FileExists())
653  {
654  // cleanup up preview files
655  for (const auto& f : m_outputFilenames)
656  {
657  wxRemoveFile(f);
658  };
659  };
661 }
662 
664 {
665  wxString commandline;
666  switch (XRCCTRL(*this, "stacker_mode_choice", wxChoice)->GetSelection())
667  {
668  case 0:
669  // mean or average
670  commandline.Append(" --mode=mean ");
671  break;
672  case 1:
673  // Median
674  commandline.Append(" --mode=median ");
675  break;
676  case 2:
677  // Winsor trimmed mean
678  commandline.Append(" --mode=winsor ");
679  commandline.Append("--winsor-trim=" + wxString::FromCDouble(m_winsorTrim->GetValue()) + " ");
680  break;
681  case 3:
682  // Sigma clipped mean
683  commandline.Append(" --mode=sigma ");
684  commandline.Append("--max-sigma=" + wxString::FromCDouble(m_sigma->GetValue()) + wxString::Format(" --max-iterations=%d ", m_sigmaMaxIter->GetValue()));
685  break;
686  case 4:
687  //Minimum
688  commandline.Append(" --mode=minimum ");
689  break;
690  case 5:
691  //Maximum
692  commandline.Append(" --mode=maximum ");
693  break;
694  }
695  return commandline;
696 }
697 
699 {
700  wxString commandline(" --output=" + hugin_utils::wxQuoteFilename(m_outputFilenames[0]));
701  // add options from control
702  commandline.Append(" ");
703  commandline.Append(GetStackerOptions());
704  // now build image list
705  wxArrayInt selectedFiles;
706  for (int i = 0; i < m_fileListCtrl->GetItemCount(); ++i)
707  {
708  if (m_fileListCtrl->IsItemChecked(i))
709  {
710  selectedFiles.push_back(m_fileListCtrl->GetItemData(i));
711  };
712  };
713  if (selectedFiles.IsEmpty())
714  {
715  hugin_utils::HuginMessageBox(_("Please activate at least one file."), _("Hugin_toolbox"), wxOK | wxOK_DEFAULT | wxICON_WARNING, this);
716  return wxEmptyString;
717  }
718  if (selectedFiles.size() > 5)
719  {
720  // for more than 5 files use response file
721  // first create a temp file
722  wxString tempFilename = wxFileName::CreateTempFileName(HuginQueue::GetConfigTempDir(wxConfig::Get()) + "hsfl");
723  wxTextFile textfile(tempFilename);
724  textfile.Open();
725  if (textfile.IsOpened())
726  {
727  m_tempFiles.push_back(tempFilename);
728  // write filenames to response file
729  for (int i = 0; i < selectedFiles.size(); ++i)
730  {
731  textfile.AddLine(m_files[selectedFiles[i]].fullFilename);
732  };
733  // write file and do some checking for erros
734  if (textfile.Write(wxTextFileType_None, wxConvLocal))
735  {
736  textfile.Close();
737  // add response file to command line
738  commandline.Append(" @ ");
739  commandline.Append(hugin_utils::wxQuoteFilename(tempFilename));
740  return commandline;
741  }
742  else
743  {
744  textfile.Close();
745  hugin_utils::HuginMessageBox(wxString::Format(_("Could not write to temporary file (%s)."), tempFilename), _("Hugin_toolbox"), wxOK | wxOK_DEFAULT | wxICON_WARNING, this);
746  return wxEmptyString;
747 
748  };
749  }
750  else
751  {
752  hugin_utils::HuginMessageBox(wxString::Format(_("Could not write to temporary file (%s)."), tempFilename), _("Hugin_toolbox"), wxOK | wxOK_DEFAULT | wxICON_WARNING, this);
753  return wxEmptyString;
754  };
755  }
756  else
757  {
758  // use filename direct on command line, if there are only up to 5 files
759  for (int i = 0; i < selectedFiles.size(); ++i)
760  {
761  commandline.Append(" ");
762  commandline.Append(hugin_utils::wxQuoteFilename(m_files[selectedFiles[i]].fullFilename));
763  }
764  return commandline;
765  };
766 }
767 
769 {
770  // enable/disable button according to selected files
771  XRCCTRL(*this, "stacker_remove_file", wxButton)->Enable(m_fileListCtrl->GetSelectedItemCount() >= 1);
772 }
773 
775 {
776  // count checked items
777  long count = 0;
778  for (int i = 0; i < m_fileListCtrl->GetItemCount(); ++i)
779  {
780  if (m_fileListCtrl->IsItemChecked(i))
781  {
782  ++count;
783  };
784  };
785  return count;
786 }
787 
789 {
790  XRCCTRL(*this, "stacker_preview", wxButton)->Enable(enable);
791  XRCCTRL(*this, "stacker_output", wxButton)->Enable(enable);
792 }
793 
795 {
796  for (const auto& f : m_tempFiles)
797  {
798  wxRemoveFile(f);
799  };
800  m_tempFiles.clear();
801 }
802 
804 {
805  m_fileListCtrl->Freeze();
806  for (auto& info : m_files)
807  {
808  AddFileInfo(info);
809  };
810  SortList();
811  m_fileListCtrl->Thaw();
812 }
813 
815 {
816  const long index = m_fileListCtrl->InsertItem(m_fileListCtrl->GetItemCount(), info.name);
817  // store index as client data for sorting
818  m_fileListCtrl->SetItemData(index, index);
819  // add image dimensions
820  m_fileListCtrl->SetItem(index, 1, wxString::Format("%zu", info.width));
821  m_fileListCtrl->SetItem(index, 2, wxString::Format("%zu", info.height));
822  m_fileListCtrl->SetItem(index, 3, info.path);
823  // set checkbox
824  m_fileListCtrl->CheckItem(index, info.isChecked);
825 }
826 
827 #define SORTASCENDING(functionName, var) \
828 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
829 {\
830  StackerPanel::FileInfoVector* data = (StackerPanel::FileInfoVector*)(sortData);\
831  if (data->at(item1).var > data->at(item2).var)\
832  return 1;\
833  if (data->at(item1).var < data->at(item2).var)\
834  return -1;\
835  return 0;\
836 }
837 
838 #define SORTDESCENDING(functionName, var) \
839 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
840 {\
841  StackerPanel::FileInfoVector* data = (StackerPanel::FileInfoVector*)(sortData); \
842  if (data->at(item1).var < data->at(item2).var)\
843  return 1; \
844  if (data->at(item1).var > data->at(item2).var)\
845  return -1; \
846  return 0; \
847 }
848 
849 SORTASCENDING(SortFilenameAscending, name)
850 SORTDESCENDING(SortFilenameDescending, name)
851 SORTASCENDING(SortImageWidthAscending, width)
852 SORTDESCENDING(SortImageWidthDescending, width)
853 SORTASCENDING(SortImageHeightAscending, height)
854 SORTDESCENDING(SortImageHeightDescending, height)
855 SORTASCENDING(SortPathAscending, path)
856 SORTDESCENDING(SortPathDescending, path)
857 
858 #undef SORTASCENDING
859 #undef SORTDESCENDING
860 
862 {
863  if (m_sortCol > -1)
864  {
865  switch (m_sortCol)
866  {
867  case 0: // filename
868  if (m_sortAscending)
869  {
870  m_fileListCtrl->SortItems(SortFilenameAscending, wxIntPtr(&m_files));
871  }
872  else
873  {
874  m_fileListCtrl->SortItems(SortFilenameDescending, wxIntPtr(&m_files));
875  };
876  break;
877  case 1: // image width
878  if (m_sortAscending)
879  {
880  m_fileListCtrl->SortItems(SortImageWidthAscending, wxIntPtr(&m_files));
881  }
882  else
883  {
884  m_fileListCtrl->SortItems(SortImageWidthDescending, wxIntPtr(&m_files));
885  };
886  break;
887  case 2: // image height
888  if (m_sortAscending)
889  {
890  m_fileListCtrl->SortItems(SortImageHeightAscending, wxIntPtr(&m_files));
891  }
892  else
893  {
894  m_fileListCtrl->SortItems(SortImageHeightDescending, wxIntPtr(&m_files));
895  };
896  break;
897  case 3: // path
898  if (m_sortAscending)
899  {
900  m_fileListCtrl->SortItems(SortPathAscending, wxIntPtr(&m_files));
901  }
902  else
903  {
904  m_fileListCtrl->SortItems(SortPathDescending, wxIntPtr(&m_files));
905  };
906  break;
907  };
908  };
909 }
910 
int ExecWithRedirect(wxString command)
normal command for queue, processing is stopped if an error occurred in program
Definition: Executor.h:37
implementation of huginApp Class
const wxString GetConfigTempDir(const wxConfigBase *config)
return the temp dir from the preferences, ensure that it ends with path separator ...
Definition: Executor.cpp:302
declaration of panel for stacker GUI
void OnModeChanged(wxCommandEvent &e)
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition: wxutils.cpp:67
void OnFileSelectionChanged(wxCommandEvent &e)
StackerDropTarget(StackerPanel *parent)
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition: wxutils.cpp:106
void OnProcessFinished(wxCommandEvent &e)
call at the end of the enfuse command, to load result back and enable buttons again ...
void AddFile(const wxString &filename)
adds the given file to the list
void OnGeneratePreview(wxCommandEvent &e)
optional command for queue, processing of queue is always continued, also if an error occurred ...
Definition: Executor.h:53
void EnableOutputButtons(bool enable)
enable/disable output buttons
wxSpinCtrlDouble * m_sigma
Definition: StackerPanel.h:108
void OnFileListChar(wxKeyEvent &e)
int ExecQueue(HuginQueue::CommandQueue *queue)
some helper classes for graphes
void ClearOutput()
clear the output
wxString GetInternalProgram(const wxString &bindir, const wxString &name)
return path and name of external program, which comes bundled with Hugin
Definition: Executor.cpp:129
#define SORTASCENDING(functionName, var)
file drag and drop handler method
StackerPanel * m_stackerPanel
wxArrayString m_filetypeExtensions
void OnOk(wxCommandEvent &e)
void ExecuteStacker()
build the command line and execute enfuse command
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
bool m_cleanupOutput
Definition: StackerPanel.h:115
basic classes and function for queuing commands in wxWidgets
void OnAddFiles(wxCommandEvent &e)
event handler
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
PreviewWindow * m_preview
Definition: StackerPanel.h:111
void EnableFileButtons()
enable/disable the file(s) remove and create buttons depending on selection of wxListCtrl ...
void setImage(const wxString &filename)
set the current image and mask list, this loads also the image from cache
wxString GetStackerCommandLine()
get the full commandline for enfuse without program, but with files and output options, the output filename is read from m_outputFilename
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name="panel")
create the control
wxString GetStackerOptions()
build string with all stacker options from values in wxPropertyGrid this are the mainly the fusion op...
bool Create(wxWindow *parent, MyExecPanel *logPanel)
creates the control and populates all controls with their settings
IMPLEMENT_DYNAMIC_CLASS(wxTreeListHeaderWindow, wxWindow)
wxString GetMainImageFilters()
return a filter for the main image files (JPG/TIFF/PNG) only
Definition: platform.cpp:80
Definition of dialog class to create brighter/darker versions of the image.
wxListCtrl * m_fileListCtrl
Definition: StackerPanel.h:105
void AddString(const wxString &s)
display the string in the panel
#define SORTDESCENDING(functionName, var)
~StackerPanel()
destructor, save state and settings
void OnGenerateOutput(wxCommandEvent &e)
wxSpinCtrlDouble * m_winsorTrim
Definition: StackerPanel.h:107
long GetCheckedItemCount() const
return the number of check images
wxArrayString m_tempFiles
Definition: StackerPanel.h:114
#define DEBUG_ERROR(msg)
Definition: utils.h:76
FileInfoVector m_files
Definition: StackerPanel.h:110
void OnFileCheckStateChanged(wxListEvent &e)
void setScale(double factor)
set the scaling factor f.
void FillFileList()
fill the list from internal file list
wxChoice * m_modeChoice
Definition: StackerPanel.h:106
str wxEscapeFilename(const str &arg)
special escaping routine for CommandQueues
Definition: Executor.h:79
void OnRemoveFile(wxCommandEvent &e)
MyExecPanel * m_logWindow
Definition: StackerPanel.h:112
preview window
Definition: PreviewWindow.h:38
platform/compiler specific stuff.
static void info(const char *fmt,...)
Definition: svm.cpp:95
void SortList()
sort filename list
wxSpinCtrl * m_sigmaMaxIter
Definition: StackerPanel.h:109
wxArrayString GetFilenames()
void OnFileColumnHeaderClick(wxListEvent &e)
static uint16_t flags
void OnAddDirectory(wxCommandEvent &e)
str wxQuoteFilename(const str &arg)
Quote a filename, so that it is surrounded by &quot;&quot;.
Definition: wxPlatform.h:82
panel for stacker GUI
Definition: StackerPanel.h:37
wxString GetFileDialogImageFilters()
return filter for image files, needed by file open dialog it contains all image format vigra can read...
Definition: platform.cpp:69
void OnZoom(wxCommandEvent &e)
dialog for selection of directory and file name/type
void AddFileInfo(const FileInfo &info)
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition: wxutils.cpp:176
wxString GetExternalProgram(wxConfigBase *config, const wxString &bindir, const wxString &name)
return path and name of external program, which can be overwritten by the user
Definition: Executor.cpp:148
wxDirPickerCtrl * m_dirPicker
void ExecuteStackerExiftool()
build the command line and execute enfuse and exiftool afterwards
std::vector< NormalCommand * > CommandQueue
Definition: Executor.h:61
SelectDirectoryFileDialog(wxWindow *parent)
Constructor, read from xrc ressource; restore last uses settings, size and position.
bool m_sortAscending
Definition: StackerPanel.h:118
wxArrayString m_outputFilenames
Definition: StackerPanel.h:113
void CleanUpTempFiles()
delete all temporary files, to be called mainly at end