Hugin trunk 0.1
Loading...
Searching...
No Matches
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"
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
47enum
48{
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
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
73 m_isRunning=false;
74 m_stopped=false;
75
76 m_button_start=XRCCTRL(*this,"find_pano_start_stop",wxButton);
78 m_button_send=XRCCTRL(*this,"find_pano_add_queue",wxButton);
80 m_button_close=XRCCTRL(*this,"find_pano_close",wxButton);
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);
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);
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
137#endif
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)));
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
172{
173 if(e.CanVeto() && m_isRunning)
174 {
175 wxBell();
176 e.Veto();
177 }
178 else
179 {
180 e.Skip();
181 };
182};
183
184void 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
197void 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
223 m_button_start->SetLabel(_("Stop"));
225 m_list_pano->Clear();
226 wxCommandEvent 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
240void 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,
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
284void 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
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 {
336 };
337};
338
339void FindPanoDialog::OnRemoveImage(wxCommandEvent &e)
340{
341 const int selectedPano = m_list_pano->GetSelection();
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;
359 };
360 };
361 };
362};
363
364void FindPanoDialog::OnSplitPanos(wxCommandEvent &e)
365{
366 const int selectedPano = m_list_pano->GetSelection();
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
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;
388 }
389 else
390 {
391 wxBell();
392 delete newSubPano;
393 };
394 };
395 };
396 };
397}
398
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
419{
420 return doj::alphanum_comp(std::string(s1.mb_str(wxConvLocal)),std::string(s2.mb_str(wxConvLocal)));
421};
422
423void 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;
427 wxString filename;
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 {
464 };
466 {
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
517 {
518 //now we go into all directories
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 };
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
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 }
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();
618 }
619 else
620 {
622 if(dt.IsEarlierThan(m_dt_start))
623 {
625 }
626 if(dt.IsLaterThan(m_dt_end))
627 {
628 m_dt_end=dt;
629 };
630 };
631 m_images.insert(img);
632};
633
634const 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
652{
653 return wxString::Format(_("%d images: %s"), static_cast<int>(m_images.size()), GetFilestring(BasePath).c_str());
654};
655
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;
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 {
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 {
700 tempPano.addImage(**m_images.begin());
701 tempPano.addImage(**m_images.rbegin());
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();
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 {
750 };
751 if (fabs(blueBalanceAnchor)<1e-2)
752 {
754 };
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
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 {
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 }
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)),
834 {
835 return projectFile.GetFullPath();
836 }
837 else
838 {
839 return wxEmptyString;
840 };
841};
842
847
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
871
873{
874 list->DeleteAllItems();
875 thumbs->RemoveAll();
876 tooltips.Clear();
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
921 Exiv2::PreviewImage previewImage = previews.getPreviewImage(lists[previewIndex]);
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
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
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) + ", "
985 };
986};
987
988void 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
999 };
1000}
1001
1002PossiblePano* 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
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 }
1022 };
1023 return newPano;
1024}
1025
1027{
1028 // update internal stored start and end time
1029 m_dt_start = GetDateTime(*m_images.begin());
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}
#define THUMBSIZE
int SortWxFilenames(const wxString &s1, const wxString &s2)
@ ID_SPLIT_PANOS
@ ID_REMOVE_IMAGE
Definition of dialog to change user defined sequence.
std::set< HuginBase::SrcPanoImage *, SortFilename > ImageSet
void FillBlenderList(wxControlWithItems *list)
Fills a wxControlWithItem with all possible blender options, the client data contains the associated ...
Definition LensTools.cpp:82
size_t GetSelectedValue(wxControlWithItems *list)
Returns the client value of the selected item from list.
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
some helper classes for graphes
some definitions to work with optimizer master switches
Batch processor for Hugin with GUI.
Somewhere to specify what variables belong to what.
void AddToList(wxString aFile, Project::Target target=Project::STITCHING, wxString userDefined=wxEmptyString)
wxArrayString m_tooltips
void OnSelectPossiblePano(wxCommandEvent &e)
event to populate information on the right
wxCheckBox * m_cb_subdir
wxSpinCtrl * m_sc_minNumberImages
void SearchInDir(wxString dirstring, const bool includeSubdir, const bool loadDistortion, const bool loadVignetting, const size_t minNumberImages, const size_t maxTimeDiff)
wxStaticText * m_statustext
BatchFrame * m_batchframe
wxDirPickerCtrl * m_dirPicker
FindPanoDialog(BatchFrame *batchframe, wxString xrcPrefix)
Constructor, read from xrc ressource; restore last uses settings, size and position.
TIFFErrorHandler m_oldtiffwarning
wxChoice * m_ch_naming
void OnClose(wxCloseEvent &e)
prevents closing window when running detection
wxSpinCtrl * m_sc_maxTimeDiff
void OnButtonStart(wxCommandEvent &e)
start/stops detections
wxCheckListBox * m_list_pano
wxImageList * m_thumbs
wxCheckBox * m_cb_createLinks
wxButton * m_button_start
void OnSplitPanos(wxCommandEvent &e)
event handler to split into 2 panos
void OnListMouseMove(wxMouseEvent &e)
mouse move handler for tooltips
std::vector< PossiblePano * > m_panos
void OnRemoveImage(wxCommandEvent &e)
event handler to remove selected image from list
void EnableButtons(const bool state)
wxListCtrl * m_thumbsList
wxCheckBox * m_cb_loadVignetting
wxButton * m_button_close
void OnButtonClose(wxCommandEvent &e)
closes window
wxButton * m_button_send
void OnButtonSend(wxCommandEvent &e)
add selected projects to queue
wxCheckBox * m_cb_loadDistortion
void OnListItemRightClick(wxListEvent &e)
event handler for context menu
~FindPanoDialog()
destructor, saves size and position
wxChoice * m_ch_blender
Same as above, but use a non const panorama.
void switchParts(unsigned int ImageNr, unsigned int partNr)
switch a given image to a different part number.
Panorama image options.
vigra_ext::Interpolator interpolator
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition Panorama.cpp:319
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Definition Panorama.cpp:311
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
Definition Panorama.cpp:303
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
bool readDistortionFromDB()
tries to read distortion data from lens database you need to call SrcPanoImage::readEXIF before to fi...
bool readVignettingFromDB()
tries to read vignetting data from lens database you need to call SrcPanoImage::readEXIF before to fi...
const int getExifDateTime(struct tm *datetime) const
try to convert Exif date time string to struct tm
bool readProjectionFromDB(const bool ignoreFovRectilinear=true)
tries to read projection and crop area from lens database you need to call SrcPanoImage::readEXIF bef...
bool readEXIF()
try to fill out information about the image, by examining the exif data
void UpdateDateTimes()
updates the internal date/time representations
wxString GeneratePanorama(NamingConvention nc, bool createLinks, HuginBase::PanoramaOptions::BlendingMechanism defaultBlender)
generates the panorama file from this set of images
PossiblePano * SplitPano(const unsigned int index)
split pano into 2 subpanos, index is used as first image of second pano
wxString GetStartString()
return the start date/time as string
NamingConvention
enumeration for different naming conventions, must be match combobox in ressource
wxString GetFocalLength()
returns the focal length as string
std::string m_make
std::string m_lens
wxDateTime m_dt_start
bool GetNewProjectFilename(NamingConvention nc, const wxString basePath, wxFileName &projectFile)
returns a given filename, which does not already exists
const wxString GetFilestring(const wxString BasePath, const bool stripExtension=false) const
returns a string with the filename of the first and last file
wxString GetCameraName()
returns the camera name
void PopulateListCtrl(wxListCtrl *list, wxImageList *thumbs, wxArrayString &tooltips)
add all images to wxListCtrl
void RemoveImage(const unsigned int index)
removes the image at given index
~PossiblePano()
destructor, cleans up used variables
wxString GetDuration()
returns the duration as string
wxString GetLensName()
returns the lens name
std::string m_camera
void AddSrcPanoImage(HuginBase::SrcPanoImage *img)
adds the given SrcPanoImage to this pano-group
vigra::Size2D m_size
PossiblePano()
constructor, init values
wxDateTime m_dt_end
ImageSet m_images
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,...
const wxDateTime GetDateTime(const HuginBase::SrcPanoImage *img)
does some reformating date/time format
const wxString GetItemString(const wxString BasePath) const
returns a string which contains description of this pano
#define HUGIN_JPEG_QUALITY
#define HUGIN_ENBLEND_ARGS
#define HUGIN_NONA_CROPPEDIMAGES
#define HUGIN_TIFF_COMPRESSION
#define HUGIN_ENFUSE_ARGS
#define HUGIN_NONA_INTERPOLATOR
#define HUGIN_HDRMERGE_ARGS
#define HUGIN_LDR_OUTPUT_FORMAT
static uint16_t flags
#define HUGIN_CONV_FILENAME
Definition platform.h:40
wxString getDefaultProjectName(const HuginBase::Panorama &pano, const wxString filenameTemplate)
gets the default project name, as defined in the preferences
wxString GetIso(const HuginBase::SrcPanoImage *img)
returns formatted iso value
wxString GetFocalLength(const HuginBase::SrcPanoImage *img)
return focallength and focallength 35 mm as wxString
wxString GetExifDateTime(const HuginBase::SrcPanoImage *img)
returns Exif DateTimeOriginal as formatted wxString
wxString GetExposureTime(const HuginBase::SrcPanoImage *img)
returns formatted exposure time
wxString GetAperture(const HuginBase::SrcPanoImage *img)
returns formatted aperture value
std::map< std::string, std::string > FileMetaData
typedef for general map for storing metadata in files
int alphanum_comp(const std::string &l, const std::string &r)
Compare l and r with the same semantics as strcmp(), but with the "Alphanum Algorithm" which produces...
Definition alphanum.cpp:119
WXIMPEX wxString GetFormattedTimeSpan(const wxTimeSpan &timeSpan)
Definition wxutils.cpp:29
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:176
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition wxutils.cpp:106
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition wxutils.cpp:67
Interpolator
enum with all interpolation methods
include file for the hugin project
bool operator()(const HuginBase::SrcPanoImage *img1, const HuginBase::SrcPanoImage *img2) const
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
platform/compiler specific stuff.