Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FindPanoDialog.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
11 /* This is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software. If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  */
26 
27 #include "FindPanoDialog.h"
28 #include "base_wx/wxPlatform.h"
29 #include "panoinc.h"
31 #include "PTBatcherGUI.h"
32 #include "hugin_utils/alphanum.h"
33 #include "hugin/config_defaults.h"
34 #include "wx/mstream.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 #ifdef _WIN32
41 #include <commctrl.h>
42 #endif
43 #include "base_wx/LensTools.h"
45 #include "base_wx/wxutils.h"
46 
47 enum
48 {
49  ID_REMOVE_IMAGE = wxID_HIGHEST + 300,
50  ID_SPLIT_PANOS = wxID_HIGHEST + 301
51 };
52 
54 {
55  return doj::alphanum_comp(img1->getFilename(),img2->getFilename())<0;
56 };
57 
58 // thumbnail size currently set to 80x80
59 #define THUMBSIZE 80
60 
61 // provide some more translations for strings in wxDirPickerCtrl
62 #if 0
63 _("Browse");
64 #endif
65 
66 FindPanoDialog::FindPanoDialog(BatchFrame* batchframe, wxString xrcPrefix)
67 {
68  // load our children. some children might need special
69  // initialization. this will be done later.
70  wxXmlResource::Get()->LoadDialog(this,batchframe,"find_pano_dialog");
71 
72  m_batchframe=batchframe;
73  m_isRunning=false;
74  m_stopped=false;
75 
76  m_button_start=XRCCTRL(*this,"find_pano_start_stop",wxButton);
77  m_button_start->Bind(wxEVT_BUTTON, &FindPanoDialog::OnButtonStart, this);
78  m_button_send=XRCCTRL(*this,"find_pano_add_queue",wxButton);
79  m_button_send->Bind(wxEVT_BUTTON, &FindPanoDialog::OnButtonSend, this);
80  m_button_close=XRCCTRL(*this,"find_pano_close",wxButton);
81  m_button_close->Bind(wxEVT_BUTTON, &FindPanoDialog::OnButtonClose, this);
82  m_dirPicker=XRCCTRL(*this,"find_pano_dir",wxDirPickerCtrl);
83  m_cb_subdir=XRCCTRL(*this,"find_pano_subdir",wxCheckBox);
84  m_statustext=XRCCTRL(*this,"find_pano_label",wxStaticText);
85  m_list_pano=XRCCTRL(*this,"find_pano_list",wxCheckListBox);
86  m_list_pano->Bind(wxEVT_LISTBOX, &FindPanoDialog::OnSelectPossiblePano, this);
87  m_ch_naming=XRCCTRL(*this,"find_pano_naming",wxChoice);
88  m_cb_createLinks=XRCCTRL(*this,"find_pano_create_links",wxCheckBox);
89  m_cb_loadDistortion=XRCCTRL(*this,"find_pano_load_distortion",wxCheckBox);
90  m_cb_loadVignetting=XRCCTRL(*this,"find_pano_load_vignetting",wxCheckBox);
91  m_sc_minNumberImages=XRCCTRL(*this, "find_pano_min_number_images", wxSpinCtrl);
92  m_sc_maxTimeDiff=XRCCTRL(*this, "find_pano_max_time_diff", wxSpinCtrl);
93  m_ch_blender = XRCCTRL(*this, "find_pano_default_blender", wxChoice);
95 
96  //set parameters
97  wxConfigBase* config = wxConfigBase::Get();
98  // restore position and size
99  hugin_utils::RestoreFramePosition(this, "FindPanoDialog");
100  long splitterPos = config->Read("/FindPanoDialog/splitterPos", -1l);
101  if (splitterPos != -1)
102  {
103  XRCCTRL(*this, "find_pano_splitter", wxSplitterWindow)->SetSashPosition(splitterPos);
104  };
105  wxString path=config->Read("/FindPanoDialog/actualPath",wxEmptyString);
106  if(!path.IsEmpty())
107  {
108  m_dirPicker->SetPath(path);
109  }
110  bool val;
111  config->Read("/FindPanoDialog/includeSubDirs",&val,false);
112  m_cb_subdir->SetValue(val);
113  long i=config->Read("/FindPanoDialog/Naming",0l);
114  m_ch_naming->SetSelection(i);
115  config->Read("/FindPanoDialog/linkStacks",&val,true);
116  m_cb_createLinks->SetValue(val);
117  config->Read("/FindPanoDialog/loadDistortion",&val,false);
118  m_cb_loadDistortion->SetValue(val);
119  config->Read("/FindPanoDialog/loadVignetting",&val,false);
120  m_cb_loadVignetting->SetValue(val);
121  i=config->Read("/FindPanoDialog/MinNumberImages", 2l);
122  m_sc_minNumberImages->SetValue(i);
123  i=config->Read("/FindPanoDialog/MaxTimeDiff", 30l);
124  m_sc_maxTimeDiff->SetValue(i);
125  i = config->Read("/FindPanoDialog/DefaultBlender", static_cast<long>(HuginBase::PanoramaOptions::ENBLEND_BLEND));
127  m_button_send->Disable();
128  m_thumbs = new wxImageList(THUMBSIZE, THUMBSIZE, true, 0);
129  m_thumbsList = XRCCTRL(*this, "find_pano_selected_thumbslist", wxListCtrl);
130  m_thumbsList->SetImageList(m_thumbs, wxIMAGE_LIST_NORMAL);
131  m_thumbsList->Bind(wxEVT_MOTION, &FindPanoDialog::OnListMouseMove, this);
132  m_thumbsList->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &FindPanoDialog::OnListItemRightClick, this);
133 #ifdef _WIN32
134  // default image spacing is too big, wxWidgets does not provide direct
135  // access to the spacing, so using the direct API function
136  ListView_SetIconSpacing(m_thumbsList->GetHandle(), THUMBSIZE + 20, THUMBSIZE + 20);
137 #endif
138  Bind(wxEVT_MENU, &FindPanoDialog::OnRemoveImage, this, ID_REMOVE_IMAGE);
139  Bind(wxEVT_MENU, &FindPanoDialog::OnSplitPanos, this, ID_SPLIT_PANOS);
140  Bind(wxEVT_CLOSE_WINDOW, &FindPanoDialog::OnClose, this);
141 };
142 
144 {
145  wxConfigBase* config=wxConfigBase::Get();
146  hugin_utils::StoreFramePosition(this, "FindPanoDialog");
147  config->Write("/FindPanoDialog/splitterPos", XRCCTRL(*this, "find_pano_splitter", wxSplitterWindow)->GetSashPosition());
148  config->Write("/FindPanoDialog/actualPath",m_dirPicker->GetPath());
149  config->Write("/FindPanoDialog/includeSubDirs",m_cb_subdir->GetValue());
150  config->Write("/FindPanoDialog/Naming",m_ch_naming->GetSelection());
151  config->Write("/FindPanoDialog/linkStacks",m_cb_createLinks->GetValue());
152  config->Write("/FindPanoDialog/loadDistortion",m_cb_loadDistortion->GetValue());
153  config->Write("/FindPanoDialog/loadVignetting",m_cb_loadDistortion->GetValue());
154  config->Write("/FindPanoDialog/MinNumberImages", m_sc_minNumberImages->GetValue());
155  config->Write("/FindPanoDialog/MaxTimeDiff", m_sc_maxTimeDiff->GetValue());
156  config->Write("/FindPanoDialog/DefaultBlender", static_cast<long>(GetSelectedValue(m_ch_blender)));
157  CleanUpPanolist();
158  delete m_thumbs;
159 };
160 
162 {
163  while(!m_panos.empty())
164  {
165  delete m_panos.back();
166  m_panos.pop_back();
167  };
168 };
169 
170 //prevent closing window when running detection
171 void FindPanoDialog::OnClose(wxCloseEvent& e)
172 {
173  if(e.CanVeto() && m_isRunning)
174  {
175  wxBell();
176  e.Veto();
177  }
178  else
179  {
180  e.Skip();
181  };
182 };
183 
184 void FindPanoDialog::OnButtonClose(wxCommandEvent& e)
185 {
186  if(!m_panos.empty())
187  {
188  if (hugin_utils::HuginMessageBox(_("The list contains possibly unprocessed panoramas.\nIf you close the dialog, you will lose them.\nContinue anyway?"),
189  _("PTBatcherGUI"), wxYES_NO | wxICON_WARNING, this) == wxNO)
190  {
191  return;
192  };
193  };
194  this->Close();
195 };
196 
197 void FindPanoDialog::OnButtonStart(wxCommandEvent& e)
198 {
199  if(m_isRunning)
200  {
201  //stop detection
202  m_stopped=true;
203  m_button_start->SetLabel(_("Accepted"));
204  }
205  else
206  {
207  //start detection
208  m_start_dir=m_dirPicker->GetPath();
209  if(wxDir::Exists(m_start_dir))
210  {
211  if(!m_panos.empty())
212  {
213  if (hugin_utils::HuginMessageBox(_("The list contains still not yet processed panoramas.\nIf you continue, they will be disregarded.\nDo you still want to continue?"),
214  _("PTBatcherGUI"), wxYES_NO | wxICON_WARNING, this) == wxNO)
215  {
216  return;
217  };
218  };
219  m_isRunning=true;
220  m_stopped=false;
221  //deactivate TIFF warning message boxes
222  m_oldtiffwarning=TIFFSetWarningHandler(NULL);
223  m_button_start->SetLabel(_("Stop"));
224  CleanUpPanolist();
225  m_list_pano->Clear();
226  wxCommandEvent dummy;
227  OnSelectPossiblePano(dummy);
228  EnableButtons(false);
229  SearchInDir(m_start_dir,m_cb_subdir->GetValue(), m_cb_loadDistortion->GetValue(), m_cb_loadVignetting->GetValue(),
230  m_sc_minNumberImages->GetValue(), m_sc_maxTimeDiff->GetValue());
231  }
232  else
233  {
234  hugin_utils::HuginMessageBox(wxString::Format(_("Directory %s does not exist.\nPlease give an existing directory."), m_start_dir),
235  _("PTBatcherGUI"), wxOK | wxICON_EXCLAMATION, this);
236  };
237  };
238 }
239 
240 void FindPanoDialog::OnButtonSend(wxCommandEvent& e)
241 {
242  if(m_panos.empty())
243  {
244  return;
245  }
246  unsigned int nr=0;
247  for(unsigned int i=0; i<m_list_pano->GetCount(); i++)
248  {
249  if(m_list_pano->IsChecked(i))
250  {
251  nr++;
252  };
253  };
254  if(nr==0)
255  {
256  hugin_utils::HuginMessageBox(_("You have not selected a panorama.\nPlease select at least one panorama and try again."), _("PTBatcherGUI"), wxOK | wxICON_EXCLAMATION, this);
257  return;
258  }
259  bool failed=false;
260  bool createLinks=m_cb_createLinks->GetValue();
261  for(unsigned int i=0; i<m_list_pano->GetCount(); i++)
262  {
263  if(m_list_pano->IsChecked(i))
264  {
265  wxString filename=m_panos[i]->GeneratePanorama((PossiblePano::NamingConvention)(m_ch_naming->GetSelection()),createLinks,
266  static_cast<HuginBase::PanoramaOptions::BlendingMechanism>(GetSelectedValue(m_ch_blender)));
267  if(!filename.IsEmpty())
268  {
270  }
271  else
272  {
273  failed=true;
274  };
275  };
276  };
277  if(failed)
278  {
279  hugin_utils::HuginMessageBox(_("Not all project files could be written successfully.\nMaybe you have no write permission for these directories or your disc is full."), _("PTBatcherGUI"), wxOK, this);
280  };
281  this->Close();
282 };
283 
284 void FindPanoDialog::EnableButtons(const bool state)
285 {
286  m_dirPicker->Enable(state);
287  m_cb_subdir->Enable(state);
288  m_ch_naming->Enable(state);
289  m_cb_createLinks->Enable(state);
290  m_button_close->Enable(state);
291  m_button_send->Enable(state);
292 };
293 
295 {
296  int selected = m_list_pano->GetSelection();
297  if (selected != wxNOT_FOUND)
298  {
299  XRCCTRL(*this, "find_pano_selected_cam", wxStaticText)->SetLabel(m_panos[selected]->GetCameraName());
300  XRCCTRL(*this, "find_pano_selected_lens", wxStaticText)->SetLabel(m_panos[selected]->GetLensName());
301  XRCCTRL(*this, "find_pano_selected_focallength", wxStaticText)->SetLabel(m_panos[selected]->GetFocalLength());
302  XRCCTRL(*this, "find_pano_selected_date_time", wxStaticText)->SetLabel(m_panos[selected]->GetStartString() + " ("+ m_panos[selected]->GetDuration() + ")");
303  m_panos[selected]->PopulateListCtrl(m_thumbsList, m_thumbs, m_tooltips);
304  }
305  else
306  {
307  XRCCTRL(*this, "find_pano_selected_cam", wxStaticText)->SetLabel(wxEmptyString);
308  XRCCTRL(*this, "find_pano_selected_lens", wxStaticText)->SetLabel(wxEmptyString);
309  XRCCTRL(*this, "find_pano_selected_focallength", wxStaticText)->SetLabel(wxEmptyString);
310  XRCCTRL(*this, "find_pano_selected_date_time", wxStaticText)->SetLabel(wxEmptyString);
311  m_thumbsList->DeleteAllItems();
312  m_thumbs->RemoveAll();
313  m_tooltips.Clear();
314  };
315 };
316 
318 {
319  // build menu
320  wxMenu contextMenu;
321  const int selectedPano = m_list_pano->GetSelection();
322  if (m_panos[selectedPano]->GetImageCount() > 2)
323  {
324  contextMenu.Append(ID_REMOVE_IMAGE, _("Remove image from project"));
325  };
326  long imageIndex = -1;
327  imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
328  if(imageIndex > 1 && imageIndex <= static_cast<long>(m_panos[selectedPano]->GetImageCount()) - 2)
329  {
330  contextMenu.Append(ID_SPLIT_PANOS, _("Split here into two panoramas"));
331  }
332  // show popup menu
333  if (contextMenu.GetMenuItemCount() > 0)
334  {
335  PopupMenu(&contextMenu);
336  };
337 };
338 
339 void FindPanoDialog::OnRemoveImage(wxCommandEvent &e)
340 {
341  const int selectedPano = m_list_pano->GetSelection();
342  if (selectedPano != wxNOT_FOUND)
343  {
344  if (m_panos[selectedPano]->GetImageCount() > 2)
345  {
346  long imageIndex = -1;
347  imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
348  if (imageIndex != wxNOT_FOUND)
349  {
350  // remove image from possible pano
351  m_panos[selectedPano]->RemoveImage(imageIndex);
352  // now remove from the wxListCtrl
353  m_thumbsList->DeleteItem(imageIndex);
354  // update the pano list
355  m_list_pano->SetString(selectedPano, m_panos[selectedPano]->GetItemString(m_start_dir));
356  // update the labels above
357  wxCommandEvent dummy;
358  OnSelectPossiblePano(dummy);
359  };
360  };
361  };
362 };
363 
364 void FindPanoDialog::OnSplitPanos(wxCommandEvent &e)
365 {
366  const int selectedPano = m_list_pano->GetSelection();
367  if (selectedPano != wxNOT_FOUND)
368  {
369  long imageIndex = -1;
370  imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
371  if (imageIndex != wxNOT_FOUND)
372  {
373  if (imageIndex > 1 && imageIndex <= static_cast<long>(m_panos[selectedPano]->GetImageCount()) - 2)
374  {
375  // do split
376  PossiblePano* newSubPano = m_panos[selectedPano]->SplitPano(imageIndex);
377  if (newSubPano->GetImageCount() > 0)
378  {
379  // insert new pano into internal list
380  m_panos.insert(m_panos.begin() + selectedPano + 1, newSubPano);
381  // update pano list
382  m_list_pano->SetString(selectedPano, m_panos[selectedPano]->GetItemString(m_start_dir));
383  int newItem = m_list_pano->Insert(m_panos[selectedPano + 1]->GetItemString(m_start_dir), selectedPano + 1);
384  m_list_pano->Check(newItem, true);
385  // update display
386  wxCommandEvent dummy;
387  OnSelectPossiblePano(dummy);
388  }
389  else
390  {
391  wxBell();
392  delete newSubPano;
393  };
394  };
395  };
396  };
397 }
398 
399 void FindPanoDialog::OnListMouseMove(wxMouseEvent & e)
400 {
401  if (m_tooltips.IsEmpty())
402  {
403  return;
404  }
405  int flags;
406  long item = m_thumbsList->HitTest(e.GetPosition(), flags);
407  if (item != wxNOT_FOUND && (flags&wxLIST_HITTEST_ONITEM) && item < m_tooltips.GetCount())
408  {
409  m_thumbsList->SetToolTip(m_tooltips[item]);
410  }
411  else
412  {
413  m_thumbsList->UnsetToolTip();
414  };
415  e.Skip();
416 };
417 
418 int SortWxFilenames(const wxString& s1,const wxString& s2)
419 {
420  return doj::alphanum_comp(std::string(s1.mb_str(wxConvLocal)),std::string(s2.mb_str(wxConvLocal)));
421 };
422 
423 void FindPanoDialog::SearchInDir(wxString dirstring, const bool includeSubdir, const bool loadDistortion, const bool loadVignetting, const size_t minNumberImages, const size_t maxTimeDiff)
424 {
425  std::vector<PossiblePano*> newPanos;
426  wxTimeSpan max_diff(0, 0, maxTimeDiff, 0);
427  wxString filename;
428  wxArrayString fileList;
429  wxDir::GetAllFiles(dirstring,&fileList,wxEmptyString,wxDIR_FILES|wxDIR_HIDDEN);
430  fileList.Sort(SortWxFilenames);
431  //map for caching projection information to prevent reading from database for each image
432  for(size_t j=0; j<fileList.size() && !m_stopped; j++)
433  {
434  m_statustext->SetLabel(wxString::Format(_("Reading file %s"),fileList[j].c_str()));
435  wxFileName file(fileList[j]);
436  file.MakeAbsolute();
437  wxString ext=file.GetExt();
438  if(ext.CmpNoCase("jpg")==0 || ext.CmpNoCase("jpeg")==0 ||
439  ext.CmpNoCase("tif")==0 || ext.CmpNoCase("tiff")==0)
440  {
441  std::string filenamestr(file.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
443  img->setFilename(filenamestr);
444  img->readEXIF();
445  // check for black/white images, if so skip
446  const HuginBase::FileMetaData& metadata = img->getFileMetadata();
447  HuginBase::FileMetaData::const_iterator it = metadata.find("pixeltype");
448  if (it != metadata.end())
449  {
450  if (it->second == "BILEVEL")
451  {
452  wxGetApp().Yield(true);
453  continue;
454  };
455  };
456  img->applyEXIFValues();
457  if(!img->getExifMake().empty() && !img->getExifModel().empty() &&
458  img->getExifFocalLength()!=0 && img->getCropFactor()!=0)
459  {
460  img->readProjectionFromDB(false);
461  if(loadDistortion)
462  {
463  img->readDistortionFromDB();
464  };
465  if(loadVignetting)
466  {
467  img->readVignettingFromDB();
468  };
469  bool found=false;
470  for(unsigned int i=0; i<newPanos.size() && !m_stopped && !found; i++)
471  {
472  //compare with all other image groups
473  if(newPanos[i]->BelongsTo(img,max_diff))
474  {
475  newPanos[i]->AddSrcPanoImage(img);
476  found=true;
477  };
478  if(i%10==0)
479  {
480  wxGetApp().Yield(true);
481  };
482  };
483  if(!found)
484  {
485  PossiblePano* newPano=new PossiblePano();
486  newPano->AddSrcPanoImage(img);
487  newPanos.push_back(newPano);
488  };
489  }
490  else
491  {
492  //could not read exif infos, disregard this image
493  delete img;
494  };
495  };
496  //allow processing events
497  wxGetApp().Yield(true);
498  };
499  if(!m_stopped && !newPanos.empty())
500  {
501  for(size_t i=0; i<newPanos.size(); i++)
502  {
503  if(newPanos[i]->GetImageCount()>=minNumberImages)
504  {
505  m_panos.push_back(newPanos[i]);
506  int newItem=m_list_pano->Append(m_panos[m_panos.size()-1]->GetItemString(m_start_dir));
507  m_list_pano->Check(newItem,true);
508  }
509  else
510  {
511  delete newPanos[i];
512  };
513  };
514  };
515 
516  if(includeSubdir && !m_stopped)
517  {
518  //now we go into all directories
519  wxDir dir(dirstring);
520  bool cont=dir.GetFirst(&filename,wxEmptyString,wxDIR_DIRS);
521  while(cont && !m_stopped)
522  {
523  SearchInDir(dir.GetName()+wxFileName::GetPathSeparator()+filename,includeSubdir, loadDistortion, loadVignetting, minNumberImages, maxTimeDiff);
524  cont=dir.GetNext(&filename);
525  }
526  };
527  if(m_start_dir.Cmp(dirstring)==0)
528  {
529  m_stopped=false;
530  m_isRunning=false;
531  m_button_start->SetLabel(_("Start"));
532  EnableButtons(true);
533  //enable send button if at least one panorama found
534  m_button_send->Enable(!m_panos.empty());
535  if(!m_panos.empty())
536  {
537  m_statustext->SetLabel(wxString::Format(_("Found %d possible panoramas"), static_cast<int>(m_panos.size())));
538  }
539  else
540  {
541  m_statustext->SetLabel(_("No possible panoramas found"));
542  };
543  TIFFSetWarningHandler(m_oldtiffwarning);
544  };
545 };
546 
548 {
549  if(!m_images.empty())
550  {
551  for(ImageSet::reverse_iterator it=m_images.rbegin(); it!=m_images.rend(); ++it)
552  {
553  delete (*it);
554  }
555  };
556 };
557 
558 bool PossiblePano::BelongsTo(HuginBase::SrcPanoImage* img, const wxTimeSpan max_time_diff)
559 {
560  if(m_make.compare(img->getExifMake())!=0)
561  {
562  return false;
563  }
564  if(m_camera.compare(img->getExifModel())!=0)
565  {
566  return false;
567  }
568  if(m_lens.compare(img->getExifLens())!=0)
569  {
570  return false;
571  }
572  if(fabs(m_focallength-img->getExifFocalLength())>0.01)
573  {
574  return false;
575  }
576  if (fabs(m_cropfactor - img->getCropFactor()) > 0.01)
577  {
578  return false;
579  }
580  if(m_size!=img->getSize())
581  {
582  return false;
583  }
584  if(!GetDateTime(img).IsBetween(m_dt_start-max_time_diff,m_dt_end+max_time_diff))
585  {
586  return false;
587  };
588  return true;
589 };
590 
592 {
593  struct tm exifdatetime;
594  if(img->getExifDateTime(&exifdatetime)==0)
595  {
596  return wxDateTime(exifdatetime);
597  }
598  else
599  {
600  wxFileName file(wxString(img->getFilename().c_str(),HUGIN_CONV_FILENAME));
601  return file.GetModificationTime();
602  };
603 };
604 
606 {
607  if(m_images.empty())
608  {
609  //fill all values from first image
610  m_make=img->getExifMake();
611  m_camera=img->getExifModel();
612  m_lens=img->getExifLens();
613  m_focallength=img->getExifFocalLength();
614  m_cropfactor = img->getCropFactor();
615  m_size=img->getSize();
616  m_dt_start=GetDateTime(img);
618  }
619  else
620  {
621  wxDateTime dt=GetDateTime(img);
622  if(dt.IsEarlierThan(m_dt_start))
623  {
624  m_dt_start=dt;
625  }
626  if(dt.IsLaterThan(m_dt_end))
627  {
628  m_dt_end=dt;
629  };
630  };
631  m_images.insert(img);
632 };
633 
634 const wxString PossiblePano::GetFilestring(const wxString BasePath, const bool stripExtension) const
635 {
636  ImageSet::const_iterator it=m_images.begin();
637  wxFileName f1(wxString((*it)->getFilename().c_str(),HUGIN_CONV_FILENAME));
638  f1.MakeRelativeTo(BasePath);
639  ImageSet::const_reverse_iterator rit=m_images.rbegin();
640  wxFileName f2(wxString((*rit)->getFilename().c_str(),HUGIN_CONV_FILENAME));
641  if(stripExtension)
642  {
643  return f1.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)+f1.GetName()+"-"+f2.GetName();
644  }
645  else
646  {
647  return f1.GetFullPath()+" - "+f2.GetFullName();
648  };
649 };
650 
651 const wxString PossiblePano::GetItemString(const wxString BasePath) const
652 {
653  return wxString::Format(_("%d images: %s"), static_cast<int>(m_images.size()), GetFilestring(BasePath).c_str());
654 };
655 
656 bool PossiblePano::GetNewProjectFilename(NamingConvention nc,const wxString basePath, wxFileName& projectFile)
657 {
658  wxString mask;
659  unsigned int i=1;
660  projectFile.SetPath(basePath);
661  projectFile.SetName("pano");
662  projectFile.SetExt("pto");
663  if(!projectFile.IsDirWritable())
664  {
665  return false;
666  };
667  switch(nc)
668  {
669  case NAMING_PANO:
670  mask="panorama%d";
671  break;
672  case NAMING_FIRST_LAST:
673  mask=GetFilestring(basePath,true);
674  projectFile.SetName(mask);
675  if(!projectFile.FileExists())
676  {
677  return true;
678  };
679  mask=mask+"_%d";
680  break;
681  case NAMING_FOLDER:
682  {
683  wxArrayString folders=projectFile.GetDirs();
684  if(folders.GetCount()==0)
685  {
686  return false;
687  }
688  mask=folders.Last();
689  projectFile.SetName(mask);
690  if(!projectFile.FileExists())
691  {
692  return true;
693  }
694  mask=mask+"_%d";
695  }
696  break;
697  case NAMING_TEMPLATE:
698  {
699  HuginBase::Panorama tempPano;
700  tempPano.addImage(**m_images.begin());
701  tempPano.addImage(**m_images.rbegin());
702  wxFileName newProject(getDefaultProjectName(tempPano));
703  mask=newProject.GetName();
704  projectFile.SetName(mask);
705  if(!projectFile.FileExists())
706  {
707  return true;
708  }
709  mask=mask+"_%d";
710  };
711  break;
712  default:
713  mask="panorama%d";
714  };
715 
716  projectFile.SetName(wxString::Format(mask,i));
717  while(projectFile.FileExists())
718  {
719  i++;
720  projectFile.SetName(wxString::Format(mask,i));
721  //security fall through
722  if(i>1000)
723  {
724  return false;
725  };
726  }
727  return true;
728 };
729 
731 {
732  if(m_images.empty())
733  {
734  return wxEmptyString;
735  };
736  ImageSet::const_iterator it=m_images.begin();
737  wxFileName firstFile(wxString((*it)->getFilename().c_str(),HUGIN_CONV_FILENAME));
738  firstFile.MakeAbsolute();
739  wxFileName projectFile;
740  if(!GetNewProjectFilename(nc,firstFile.GetPath(),projectFile))
741  {
742  return wxEmptyString;
743  };
744  //generate panorama
745  double redBalanceAnchor = (*m_images.begin())->getExifRedBalance();
746  double blueBalanceAnchor = (*m_images.begin())->getExifBlueBalance();
747  if (fabs(redBalanceAnchor)<1e-2)
748  {
749  redBalanceAnchor = 1;
750  };
751  if (fabs(blueBalanceAnchor)<1e-2)
752  {
753  blueBalanceAnchor = 1;
754  };
755  HuginBase::Panorama pano;
756  for(ImageSet::iterator it=m_images.begin(); it!=m_images.end(); ++it)
757  {
758  (*it)->setWhiteBalanceRed((*it)->getExifRedBalance() / redBalanceAnchor);
759  (*it)->setWhiteBalanceBlue((*it)->getExifBlueBalance() / blueBalanceAnchor);
760  pano.addImage(*(*it));
761  };
762  //assign all images the same lens number
763  HuginBase::StandardImageVariableGroups variable_groups(pano);
764  HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
765  if(pano.getNrOfImages()>1)
766  {
767  for(unsigned int i=1; i<pano.getNrOfImages(); i++)
768  {
769  lenses.switchParts(i, lenses.getPartNumber(0));
770  };
771  };
772  if (pano.hasPossibleStacks())
773  {
774  pano.linkPossibleStacks(createLinks);
775  };
776  // Setup pano with options from preferences
778  //set default exposure value
779  opts.outputExposureValue = pano.getImage(0).getExposureValue();
780  wxConfigBase* config = wxConfigBase::Get();
781  opts.quality = config->Read("/output/jpeg_quality",HUGIN_JPEG_QUALITY);
782  switch(config->Read("/output/tiff_compression", HUGIN_TIFF_COMPRESSION))
783  {
784  case 0:
785  default:
786  opts.outputImageTypeCompression = "NONE";
787  opts.tiffCompression = "NONE";
788  break;
789  case 1:
790  opts.outputImageTypeCompression = "PACKBITS";
791  opts.tiffCompression = "PACKBITS";
792  break;
793  case 2:
794  opts.outputImageTypeCompression = "LZW";
795  opts.tiffCompression = "LZW";
796  break;
797  case 3:
798  opts.outputImageTypeCompression = "DEFLATE";
799  opts.tiffCompression = "DEFLATE";
800  break;
801  }
802  switch (config->Read("/output/ldr_format", HUGIN_LDR_OUTPUT_FORMAT))
803  {
804  case 1:
805  opts.outputImageType ="jpg";
806  break;
807  case 2:
808  opts.outputImageType ="png";
809  break;
810  case 3:
811  opts.outputImageType ="exr";
812  break;
813  default:
814  case 0:
815  opts.outputImageType ="tif";
816  break;
817  }
819  opts.blendMode = defaultBlender;
820  opts.enblendOptions = config->Read("Enblend/Args",HUGIN_ENBLEND_ARGS).mb_str(wxConvLocal);
821  opts.enfuseOptions = config->Read("Enfuse/Args",HUGIN_ENFUSE_ARGS).mb_str(wxConvLocal);
822  opts.interpolator = (vigra_ext::Interpolator)config->Read("Nona/Interpolator",HUGIN_NONA_INTERPOLATOR);
823  opts.tiff_saveROI = config->Read("Nona/CroppedImages",HUGIN_NONA_CROPPEDIMAGES)!=0;
826  opts.verdandiOptions = config->Read("/VerdandiDefaultArgs", wxEmptyString).mb_str(wxConvLocal);
827  pano.setOptions(opts);
828  // set optimizer switches
831 
832  if (pano.WritePTOFile(std::string(projectFile.GetFullPath().mb_str(HUGIN_CONV_FILENAME)),
833  std::string(projectFile.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR).mb_str(HUGIN_CONV_FILENAME))))
834  {
835  return projectFile.GetFullPath();
836  }
837  else
838  {
839  return wxEmptyString;
840  };
841 };
842 
844 {
845  return wxString(m_camera.c_str(), wxConvLocal);
846 }
847 
849 {
850  return wxString(m_lens.c_str(), wxConvLocal);
851 };
852 
854 {
855  if (!m_images.empty())
856  {
857  return FormatString::GetFocalLength(*m_images.begin());
858  };
859  return wxString::Format("%0.1f mm", m_focallength);
860 };
861 
863 {
864  return m_dt_start.Format();
865 };
866 
868 {
870 };
871 
872 void PossiblePano::PopulateListCtrl(wxListCtrl* list, wxImageList* thumbs, wxArrayString& tooltips)
873 {
874  list->DeleteAllItems();
875  thumbs->RemoveAll();
876  tooltips.Clear();
877  wxBusyCursor cursor;
878  for (ImageSet::iterator it = m_images.begin(); it != m_images.end(); ++it)
879  {
880 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
881  Exiv2::Image::UniquePtr image;
882 #else
883  Exiv2::Image::AutoPtr image;
884 #endif
885  bool opened = false;
886  try
887  {
888  image = Exiv2::ImageFactory::open((*it)->getFilename().c_str());
889  opened = true;
890  }
891  catch (...)
892  {
893  std::cerr << __FILE__ << " " << __LINE__ << " Error opening file" << std::endl;
894  continue;
895  }
896  int index = -1;
897  if (opened)
898  {
899  try
900  {
901  image->readMetadata();
902  }
903  catch (const Exiv2::Error& e)
904  {
905  std::cerr << __FILE__ << " " << __LINE__ << " Exiv2: Error reading metadata (" << e.what() << ")" << std::endl;
906  continue;
907  }
908  // read all thumbnails
909  Exiv2::PreviewManager previews(*image);
910  Exiv2::PreviewPropertiesList lists = previews.getPreviewProperties();
911  if (!lists.empty())
912  {
913  // select a preview with matching size
914  int previewIndex = 0;
915  while (previewIndex < lists.size() - 1 && lists[previewIndex].width_ < THUMBSIZE && lists[previewIndex].height_ < THUMBSIZE)
916  {
917  ++previewIndex;
918  };
919  // load preview image to wxImage
920  wxImage rawImage;
921  Exiv2::PreviewImage previewImage = previews.getPreviewImage(lists[previewIndex]);
922  wxMemoryInputStream stream(previewImage.pData(), previewImage.size());
923  rawImage.LoadFile(stream, wxString(previewImage.mimeType().c_str(), wxConvLocal), -1);
924  int x = 0;
925  int y = 0;
926  if (previewImage.width() > previewImage.height())
927  {
928  //landscape format
929  int newHeight = THUMBSIZE*previewImage.height() / previewImage.width();
930  rawImage.Rescale(THUMBSIZE, newHeight);
931  x = 0;
932  y = (THUMBSIZE - newHeight) / 2;
933  }
934  else
935  {
936  //portrait format
937  int newWidth = THUMBSIZE*previewImage.width() / previewImage.height();
938  rawImage.Rescale(newWidth, THUMBSIZE);
939  x = (THUMBSIZE - newWidth) / 2;
940  y = 0;
941  }
942  // rotate according to orientation tag
943  if ((*it)->getRoll() == 90)
944  {
945  rawImage = rawImage.Rotate90();
946  std::swap(x, y);
947  }
948  else
949  {
950  if ((*it)->getRoll() == 270)
951  {
952  rawImage = rawImage.Rotate90(false);
953  std::swap(x, y);
954  }
955  else
956  {
957  if ((*it)->getRoll() == 180)
958  {
959  rawImage = rawImage.Rotate180();
960  };
961  };
962  };
963  // create final bitmap with centered thumbnail
964  wxBitmap bitmap(THUMBSIZE, THUMBSIZE);
965  wxMemoryDC dc(bitmap);
966  dc.SetBackground(list->GetBackgroundColour());
967  dc.Clear();
968  dc.DrawBitmap(rawImage, x, y);
969  dc.SelectObject(wxNullBitmap);
970  // create mask bitmap
971  wxImage mask(THUMBSIZE, THUMBSIZE);
972  mask.SetRGB(wxRect(0, 0, THUMBSIZE, THUMBSIZE), 0, 0, 0);
973  mask.SetRGB(wxRect(x, y, THUMBSIZE - 2 * x, THUMBSIZE - 2 * y), 255, 255, 255);
974  // add to wxImageList
975  index = thumbs->Add(bitmap, wxBitmap(mask, 1));
976  };
977  };
978  // create item in thumb list
979  wxFileName fn(wxString((*it)->getFilename().c_str(), HUGIN_CONV_FILENAME));
980  list->InsertItem(list->GetItemCount(), fn.GetFullName(), index);
981  // build tooltip text
982  tooltips.Add(fn.GetFullName() + "\n" + FormatString::GetAperture(*it) + ", "
983  + FormatString::GetExposureTime(*it) + ", " + _("ISO") + FormatString::GetIso(*it)
984  + "\n" + FormatString::GetExifDateTime(*it));
985  };
986 };
987 
988 void PossiblePano::RemoveImage(const unsigned int index)
989 {
990  // remove image with given index
991  if (index < m_images.size())
992  {
993  ImageSet::iterator item = m_images.begin();
994  std::advance(item, index);
995  delete *item;
996  m_images.erase(item);
997  //update the internal times
998  UpdateDateTimes();
999  };
1000 }
1001 
1002 PossiblePano* PossiblePano::SplitPano(const unsigned int index)
1003 {
1004  PossiblePano* newPano = new PossiblePano();
1005  if (index < m_images.size())
1006  {
1007  // now move all images to right pano
1008  ImageSet allImages = m_images;
1009  m_images.clear();
1010  ImageSet::iterator img = allImages.begin();
1011  while (m_images.size() < index && img != allImages.end())
1012  {
1013  m_images.insert(*img);
1014  ++img;
1015  };
1016  while (img != allImages.end())
1017  {
1018  newPano->AddSrcPanoImage(*img);
1019  ++img;
1020  }
1021  UpdateDateTimes();
1022  };
1023  return newPano;
1024 }
1025 
1027 {
1028  // update internal stored start and end time
1029  m_dt_start = GetDateTime(*m_images.begin());
1030  m_dt_end = m_dt_start;
1031  for (auto& img : m_images)
1032  {
1033  wxDateTime dt = GetDateTime(img);
1034  if (dt.IsEarlierThan(m_dt_start))
1035  {
1036  m_dt_start = dt;
1037  }
1038  if (dt.IsLaterThan(m_dt_end))
1039  {
1040  m_dt_end = dt;
1041  };
1042  };
1043 }
std::string m_lens
wxString GetFocalLength()
returns the focal length as string
int alphanum_comp(const std::string &l, const std::string &r)
Compare l and r with the same semantics as strcmp(), but with the &quot;Alphanum Algorithm&quot; which produces...
Definition: alphanum.cpp:119
static void swap(T &x, T &y)
Definition: svm.cpp:67
#define HUGIN_NONA_CROPPEDIMAGES
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition: wxutils.cpp:67
unsigned int getPartNumber(unsigned int imageNr) const
Get a part number from an image number.
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition: wxutils.cpp:106
wxChoice * m_ch_naming
bool operator()(const HuginBase::SrcPanoImage *img1, const HuginBase::SrcPanoImage *img2) const
wxSpinCtrl * m_sc_maxTimeDiff
wxDateTime m_dt_start
#define HUGIN_CONV_FILENAME
Definition: platform.h:40
void AddSrcPanoImage(HuginBase::SrcPanoImage *img)
adds the given SrcPanoImage to this pano-group
#define HUGIN_JPEG_QUALITY
~PossiblePano()
destructor, cleans up used variables
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Definition: Panorama.cpp:311
Somewhere to specify what variables belong to what.
void AddToList(wxString aFile, Project::Target target=Project::STITCHING, wxString userDefined=wxEmptyString)
Definition: BatchFrame.cpp:481
void RemoveImage(const unsigned int index)
removes the image at given index
some helper classes for graphes
void EnableButtons(const bool state)
wxImageList * m_thumbs
wxButton * m_button_start
include file for the hugin project
wxCheckBox * m_cb_loadDistortion
std::string outputImageTypeCompression
wxString GetCameraName()
returns the camera name
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
Definition: Panorama.cpp:303
wxCheckBox * m_cb_createLinks
WXIMPEX wxString GetFormattedTimeSpan(const wxTimeSpan &timeSpan)
Definition: wxutils.cpp:29
ImageSet m_images
double m_cropfactor
Batch processor for Hugin with GUI.
int SortWxFilenames(const wxString &s1, const wxString &s2)
void OnButtonClose(wxCommandEvent &e)
closes window
wxCheckBox * m_cb_loadVignetting
void UpdateDateTimes()
updates the internal date/time representations
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
Definition: Panorama.cpp:2181
Model for a panorama.
Definition: Panorama.h:152
PossiblePano * SplitPano(const unsigned int index)
split pano into 2 subpanos, index is used as first image of second pano
BatchFrame * m_batchframe
wxArrayString m_tooltips
some definitions to work with optimizer master switches
size_t GetSelectedValue(wxControlWithItems *list)
Returns the client value of the selected item from list.
Definition: LensTools.cpp:103
wxDateTime m_dt_end
const wxString GetFilestring(const wxString BasePath, const bool stripExtension=false) const
returns a string with the filename of the first and last file
wxString GetDuration()
returns the duration as string
void PopulateListCtrl(wxListCtrl *list, wxImageList *thumbs, wxArrayString &tooltips)
add all images to wxListCtrl
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
void SearchInDir(wxString dirstring, const bool includeSubdir, const bool loadDistortion, const bool loadVignetting, const size_t minNumberImages, const size_t maxTimeDiff)
const int getExifDateTime(struct tm *datetime) const
try to convert Exif date time string to struct tm
void OnSplitPanos(wxCommandEvent &e)
event handler to split into 2 panos
vigra_ext::Interpolator interpolator
const unsigned int GetImageCount() const
returns number of images in this group
Definition of dialog to change user defined sequence.
wxString GetStartString()
return the start date/time as string
ImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
#define HUGIN_NONA_INTERPOLATOR
#define HUGIN_TIFF_COMPRESSION
void OnListMouseMove(wxMouseEvent &e)
mouse move handler for tooltips
wxCheckBox * m_cb_subdir
std::map< std::string, std::string > FileMetaData
typedef for general map for storing metadata in files
Definition: SrcPanoImage.h:55
wxString GetLensName()
returns the lens name
std::set< HuginBase::SrcPanoImage *, SortFilename > ImageSet
std::string stripExtension(const std::string &basename2)
remove extension of a filename
Definition: utils.cpp:130
TIFFErrorHandler m_oldtiffwarning
void OnSelectPossiblePano(wxCommandEvent &e)
event to populate information on the right
wxStaticText * m_statustext
void OnListItemRightClick(wxListEvent &e)
event handler for context menu
#define HUGIN_HDRMERGE_ARGS
#define HUGIN_LDR_OUTPUT_FORMAT
bool BelongsTo(HuginBase::SrcPanoImage *img, const wxTimeSpan max_time_diff)
return true, if the image could belong to the given PossiblePano, it checks camera maker and model...
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition: Panorama.cpp:319
wxString GetExposureTime(const HuginBase::SrcPanoImage *img)
returns formatted exposure time
Definition: LensTools.cpp:550
Same as above, but use a non const panorama.
wxButton * m_button_close
NamingConvention
enumeration for different naming conventions, must be match combobox in ressource ...
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
wxString GeneratePanorama(NamingConvention nc, bool createLinks, HuginBase::PanoramaOptions::BlendingMechanism defaultBlender)
generates the panorama file from this set of images
wxString GetExifDateTime(const HuginBase::SrcPanoImage *img)
returns Exif DateTimeOriginal as formatted wxString
Definition: LensTools.cpp:499
#define THUMBSIZE
void SelectListValue(wxControlWithItems *list, size_t newValue)
Selects the given value (stored in the client data) in the given list item.
Definition: LensTools.cpp:90
wxListCtrl * m_thumbsList
vigra::Size2D m_size
double m_focallength
wxString m_start_dir
bool GetNewProjectFilename(NamingConvention nc, const wxString basePath, wxFileName &projectFile)
returns a given filename, which does not already exists
#define HUGIN_ENFUSE_ARGS
bool readEXIF()
try to fill out information about the image, by examining the exif data
const wxDateTime GetDateTime(const HuginBase::SrcPanoImage *img)
does some reformating date/time format
PossiblePano()
constructor, init values
wxDirPickerCtrl * m_dirPicker
void OnClose(wxCloseEvent &e)
prevents closing window when running detection
platform/compiler specific stuff.
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
wxSpinCtrl * m_sc_minNumberImages
~FindPanoDialog()
destructor, saves size and position
std::string m_camera
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
FindPanoDialog(BatchFrame *batchframe, wxString xrcPrefix)
Constructor, read from xrc ressource; restore last uses settings, size and position.
const wxString GetItemString(const wxString BasePath) const
returns a string which contains description of this pano
void OnRemoveImage(wxCommandEvent &e)
event handler to remove selected image from list
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
Definition: Panorama.cpp:1531
wxString GetFocalLength(const HuginBase::SrcPanoImage *img)
return focallength and focallength 35 mm as wxString
Definition: LensTools.cpp:515
Interpolator
enum with all interpolation methods
Definition: Interpolators.h:78
static uint16_t flags
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
wxString GetAperture(const HuginBase::SrcPanoImage *img)
returns formatted aperture value
Definition: LensTools.cpp:536
void switchParts(unsigned int ImageNr, unsigned int partNr)
switch a given image to a different part number.
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
Definition: Panorama.cpp:2137
#define HUGIN_ENBLEND_ARGS
wxChoice * m_ch_blender
void OnButtonSend(wxCommandEvent &e)
add selected projects to queue
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition: wxutils.cpp:176
wxString getDefaultProjectName(const HuginBase::Panorama &pano, const wxString filenameTemplate)
gets the default project name, as defined in the preferences
BlendingMechanism blendMode
std::string m_make
wxCheckListBox * m_list_pano
void FillBlenderList(wxControlWithItems *list)
Fills a wxControlWithItem with all possible blender options, the client data contains the associated ...
Definition: LensTools.cpp:82
wxString GetIso(const HuginBase::SrcPanoImage *img)
returns formatted iso value
Definition: LensTools.cpp:587
wxButton * m_button_send
void OnButtonStart(wxCommandEvent &e)
start/stops detections
std::vector< PossiblePano * > m_panos