1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file ImagesPanel.cpp
4  *
5  *  @brief implementation of ImagesPanel Class
6  *
7  *  @author Kai-Uwe Behrmann <web@tiscali.de> and
8  *          Pablo d'Angelo <pablo.dangelo@web.de>
9  *
10  *  $Id$
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU General Public
14  *  License as published by the Free Software Foundation; either
15  *  version 2 of the License, or (at your option) any later version.
16  *
17  *  This software is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  *  General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public
23  *  License along with this software. If not, see
24  *  <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #include "hugin_config.h"
29 #include "panoinc_WX.h"
30 #include "panoinc.h"
31 #include <time.h>
32 
33 #include "base_wx/platform.h"
34 #include "base_wx/wxPlatform.h"
35 #include "base_wx/wxcms.h"
36 #include <vector>
37 #include <map>
38 #include <functional>    // std::bind
39 
40 #include "hugin/ImagesPanel.h"
41 #include "base_wx/CommandHistory.h"
42 #include "hugin/TextKillFocusHandler.h"
43 #include "hugin/CPEditorPanel.h"
44 #include "hugin/ImagesList.h"
45 #include "hugin/MainFrame.h"
46 #include "hugin/huginApp.h"
47 #include "icpfind/AutoCtrlPointCreator.h"
48 #include "hugin/config_defaults.h"
49 #include "base_wx/PTWXDlg.h"
50 #include "base_wx/LensTools.h"
51 #include "hugin/ImagesTree.h"
52 #include "panodata/OptimizerSwitches.h"
53 #include "base_wx/PanoCommand.h"
54 
55 #include <panodata/StandardImageVariableGroups.h>
56 
BEGIN_EVENT_TABLE(ImagesPanel,wxPanel)57 BEGIN_EVENT_TABLE(ImagesPanel, wxPanel)
58     EVT_TREE_SEL_CHANGED(XRCID("images_tree_ctrl"), ImagesPanel::OnSelectionChanged )
59     EVT_CHOICE     ( XRCID("images_lens_type"), ImagesPanel::OnLensTypeChanged)
60     EVT_CHOICE     ( XRCID("images_group_mode"), ImagesPanel::OnGroupModeChanged)
61     EVT_RADIOBOX   ( XRCID("images_column_radiobox"), ImagesPanel::OnDisplayModeChanged)
62     EVT_CHOICE     ( XRCID("images_optimize_mode"), ImagesPanel::OnOptimizerSwitchChanged)
63     EVT_CHOICE     ( XRCID("images_photo_optimize_mode"), ImagesPanel::OnPhotometricOptimizerSwitchChanged)
64     EVT_TEXT_ENTER ( XRCID("images_focal_length"), ImagesPanel::OnFocalLengthChanged)
65     EVT_TEXT_ENTER ( XRCID("images_crop_factor"),  ImagesPanel::OnCropFactorChanged)
66     EVT_TEXT_ENTER ( XRCID("images_overlap"), ImagesPanel::OnMinimumOverlapChanged)
67     EVT_TEXT_ENTER ( XRCID("images_maxev"), ImagesPanel::OnMaxEvDiffChanged)
68     EVT_BUTTON     ( XRCID("images_feature_matching"), ImagesPanel::CPGenerate)
69     EVT_BUTTON     ( XRCID("images_optimize"), ImagesPanel::OnOptimizeButton)
70     EVT_BUTTON     ( XRCID("images_photo_optimize"), ImagesPanel::OnPhotometricOptimizeButton)
71 END_EVENT_TABLE()
72 
73 ImagesPanel::ImagesPanel()
74 {
75     m_pano = 0;
76     m_guiLevel=GUI_SIMPLE;
77 }
78 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)79 bool ImagesPanel::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
80                       long style, const wxString& name)
81 {
82     if (! wxPanel::Create(parent, id, pos, size, style, name)) {
83         return false;
84     }
85 
86     wxXmlResource::Get()->LoadPanel(this, wxT("images_panel"));
87     wxPanel * panel = XRCCTRL(*this, "images_panel", wxPanel);
88     wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
89     topsizer->Add(panel, 1, wxEXPAND, 0);
90     SetSizer(topsizer);
91 
92     m_images_tree = XRCCTRL(*this, "images_tree_ctrl", ImagesTreeCtrl);
93     DEBUG_ASSERT(m_images_tree);
94 
95     m_showImgNr = INT_MAX;
96 
97     m_matchingButton = XRCCTRL(*this, "images_feature_matching", wxButton);
98     DEBUG_ASSERT(m_matchingButton);
99 
100     m_CPDetectorChoice = XRCCTRL(*this, "cpdetector_settings", wxChoice);
101 
102     // Image Preview
103     m_smallImgCtrl = XRCCTRL(*this, "images_selected_image", wxStaticBitmap);
104     DEBUG_ASSERT(m_smallImgCtrl);
105 
106     // empty bitmap with size (0,0) is not valid for wxStaticBitmap
107     // so we create a bitmap with the background color of the static bitmap control
108     wxImage image(1, 1, true);
109     const wxColour imageBackgroundColor = m_smallImgCtrl->GetBackgroundColour();
110     image.SetRGB(0, 0, imageBackgroundColor.Red(), imageBackgroundColor.Green(), imageBackgroundColor.Blue());
111     m_empty = wxBitmap(image);
112     m_smallImgCtrl->SetBitmap(m_empty);
113 
114     m_lenstype = XRCCTRL(*this, "images_lens_type", wxChoice);
115     DEBUG_ASSERT(m_lenstype);
116     FillLensProjectionList(m_lenstype);
117     m_lenstype->SetSelection(0);
118 
119     m_focallength = XRCCTRL(*this, "images_focal_length", wxTextCtrl);
120     DEBUG_ASSERT(m_focallength);
121     m_focallength->PushEventHandler(new TextKillFocusHandler(this));
122 
123     m_cropfactor = XRCCTRL(*this, "images_crop_factor", wxTextCtrl);
124     DEBUG_ASSERT(m_cropfactor);
125     m_cropfactor->PushEventHandler(new TextKillFocusHandler(this));
126 
127     m_overlap = XRCCTRL(*this, "images_overlap", wxTextCtrl);
128     DEBUG_ASSERT(m_overlap);
129     m_overlap->PushEventHandler(new TextKillFocusHandler(this));
130 
131     m_maxEv = XRCCTRL(*this, "images_maxev", wxTextCtrl);
132     DEBUG_ASSERT(m_maxEv);
133     m_maxEv->PushEventHandler(new TextKillFocusHandler(this));
134 
135     FillGroupChoice();
136 
137     wxTreeEvent ev;
138     OnSelectionChanged(ev);
139     DEBUG_TRACE("end");
140 
141     m_optChoice = XRCCTRL(*this, "images_optimize_mode", wxChoice);
142     DEBUG_ASSERT(m_optChoice);
143 
144     m_optPhotoChoice = XRCCTRL(*this, "images_photo_optimize_mode", wxChoice);
145     DEBUG_ASSERT(m_optPhotoChoice);
146 
147     FillOptimizerChoice();
148 
149     wxConfigBase* config=wxConfigBase::Get();
150     m_degDigits = config->Read(wxT("/General/DegreeFractionalDigitsEdit"),3);
151     //read autopano generator settings
152     cpdetector_config.Read(config,huginApp::Get()->GetDataPath()+wxT("default.setting"));
153     //write current autopano generator settings
154     cpdetector_config.Write(config);
155     config->Flush();
156     cpdetector_config.FillControl(m_CPDetectorChoice,true);
157     Layout();
158 #ifdef __WXGTK__
159     // explicitly set focus to propogate correctly key presses/shortcuts
160     m_images_tree->SetFocus();
161 #endif
162     return true;
163 }
164 
Init(HuginBase::Panorama * panorama)165 void ImagesPanel::Init(HuginBase::Panorama * panorama)
166 {
167     m_pano = panorama;
168     m_images_tree->Init(m_pano);
169     // observe the panorama
170     m_pano->addObserver(this);
171 }
172 
DeleteClientData(wxChoice * cb)173 void DeleteClientData(wxChoice* cb)
174 {
175     if(cb->HasClientUntypedData())
176     {
177         for(size_t i = 0; i < cb->GetCount(); i++)
178         {
179             delete static_cast<int*>(cb->GetClientData(i));
180         };
181     };
182 };
183 
~ImagesPanel()184 ImagesPanel::~ImagesPanel()
185 {
186     DEBUG_TRACE("dtor");
187     m_focallength->PopEventHandler(true);
188     m_cropfactor->PopEventHandler(true);
189     m_overlap->PopEventHandler(true);
190     m_maxEv->PopEventHandler(true);
191     m_pano->removeObserver(this);
192     wxChoice* group=XRCCTRL(*this,"images_group_mode", wxChoice);
193     DeleteClientData(group);
194     DeleteClientData(m_optChoice);
195     DeleteClientData(m_optPhotoChoice);
196     DEBUG_TRACE("dtor end");
197 }
198 
199 // We need to override the default handling of size events because the
200 // sizers set the virtual size but not the actual size. We reverse
201 // the standard handling and fit the child to the parent rather than
202 // fitting the parent around the child
203 
OnSize(wxSizeEvent & e)204 void ImagesPanel::OnSize( wxSizeEvent & e )
205 {
206     int winWidth, winHeight;
207     GetClientSize(&winWidth, &winHeight);
208     DEBUG_INFO( "image panel: " << winWidth <<"x"<< winHeight );
209     UpdatePreviewImage();
210 
211     e.Skip();
212 }
213 
panoramaChanged(HuginBase::Panorama & pano)214 void ImagesPanel::panoramaChanged(HuginBase::Panorama & pano)
215 {
216     //update optimizer choice selection
217     int optSwitch=m_pano->getOptimizerSwitch();
218     int found=wxNOT_FOUND;
219     for(size_t i=0;i<m_optChoice->GetCount();i++)
220     {
221         if(optSwitch==*static_cast<int*>(m_optChoice->GetClientData(i)))
222         {
223             found=i;
224             break;
225         };
226     };
227     if(found==wxNOT_FOUND)
228     {
229         PanoCommand::GlobalCmdHist::getInstance().addCommand(
230             new PanoCommand::UpdateOptimizerSwitchCmd(*m_pano, 0)
231         );
232     }
233     else
234     {
235         m_optChoice->SetSelection(found);
236     };
237 
238     //update photometric optimizer choice selection
239     optSwitch=m_pano->getPhotometricOptimizerSwitch();
240     found=wxNOT_FOUND;
241     for(size_t i=0;i<m_optPhotoChoice->GetCount();i++)
242     {
243         if(optSwitch==*static_cast<int*>(m_optPhotoChoice->GetClientData(i)))
244         {
245             found=i;
246             break;
247         };
248     };
249     if(found==wxNOT_FOUND)
250     {
251         PanoCommand::GlobalCmdHist::getInstance().addCommand(
252             new PanoCommand::UpdatePhotometricOptimizerSwitchCmd(*m_pano, 0)
253         );
254     }
255     else
256     {
257         m_optPhotoChoice->SetSelection(found);
258     };
259     const HuginBase::PanoramaOptions opts = m_pano->getOptions();
260     m_overlap->ChangeValue(hugin_utils::doubleTowxString(opts.outputStacksMinOverlap,3));
261     m_maxEv->ChangeValue(hugin_utils::doubleTowxString(opts.outputLayersExposureDiff,2));
262 }
263 
panoramaImagesChanged(HuginBase::Panorama & pano,const HuginBase::UIntSet & _imgNr)264 void ImagesPanel::panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet & _imgNr)
265 {
266     DEBUG_TRACE("");
267 
268     // update text field if selected
269     const HuginBase::UIntSet & selected = m_images_tree->GetSelectedImages();
270     DEBUG_DEBUG("nr of sel Images: " << selected.size());
271     if (pano.getNrOfImages() == 0)
272     {
273         DisableImageCtrls();
274         m_matchingButton->Disable();
275     }
276     else
277     {
278         m_matchingButton->Enable();
279         wxTreeEvent ev;
280         OnSelectionChanged(ev);
281     };
282     //enable/disable optimize buttons
283     XRCCTRL(*this, "images_optimize", wxButton)->Enable(pano.getNrOfImages()>0);
284     XRCCTRL(*this, "images_photo_optimize", wxButton)->Enable(pano.getNrOfImages()>1);
285 }
286 
287 // #####  Here start the eventhandlers  #####
288 
289 /** run control point detector on selected images, and add control points */
CPGenerate(wxCommandEvent & e)290 void ImagesPanel::CPGenerate(wxCommandEvent & e)
291 {
292     HuginBase::UIntSet selImg = m_images_tree->GetSelectedImages();
293     //if only one image is selected, run detector on all images, except for linefind
294     wxString progName = cpdetector_config.settings[m_CPDetectorChoice->GetSelection()].GetProg().Lower();
295     if ((selImg.empty()) || (selImg.size() == 1 && progName.Find(wxT("linefind")) == wxNOT_FOUND))
296     {
297         // add all images.
298         selImg.clear();
299         fill_set(selImg, 0, m_pano->getNrOfImages() - 1);
300     }
301 
302     if (selImg.empty())
303     {
304         return;
305     }
306     RunCPGenerator(cpdetector_config.settings[m_CPDetectorChoice->GetSelection()], selImg);
307 };
308 
RunCPGenerator(const HuginBase::UIntSet & img)309 void ImagesPanel::RunCPGenerator(const HuginBase::UIntSet& img)
310 {
311     RunCPGenerator(cpdetector_config.settings[m_CPDetectorChoice->GetSelection()], img);
312 }
313 
RunCPGenerator(CPDetectorSetting & setting,const HuginBase::UIntSet & img)314 void ImagesPanel::RunCPGenerator(CPDetectorSetting &setting, const HuginBase::UIntSet& img)
315 {
316     wxConfigBase* config=wxConfigBase::Get();
317     long nFeatures = HUGIN_ASS_NCONTROLPOINTS;
318     if(wxGetKeyState(WXK_COMMAND))
319     {
320         nFeatures = config->Read(wxT("/MainFrame/nControlPoints"), HUGIN_ASS_NCONTROLPOINTS);
321         nFeatures = wxGetNumberFromUser(
322                             _("Enter maximal number of control points per image pair"),
323                             _("Points per Overlap"),
324                             _("Control point detector option"),
325                             nFeatures, 1, 10000
326                                  );
327         if(nFeatures<1)
328         {
329             return;
330         };
331         config->Write(wxT("/MainFrame/nControlPoints"), nFeatures);
332     }
333     else
334     {
335         nFeatures = config->Read(wxT("/Assistant/nControlPoints"), HUGIN_ASS_NCONTROLPOINTS);
336     };
337 
338     AutoCtrlPointCreator matcher;
339     HuginBase::CPVector cps = matcher.automatch(setting, *m_pano, img, nFeatures, this);
340     wxString msg;
341     wxMessageBox(wxString::Format(_("Added %lu control points"), (unsigned long) cps.size()), _("Control point detector result"),wxOK|wxICON_INFORMATION,this);
342     PanoCommand::GlobalCmdHist::getInstance().addCommand(
343             new PanoCommand::AddCtrlPointsCmd(*m_pano, cps)
344                                            );
345 
346 };
347 
GetSelectedCPGenerator()348 const wxString ImagesPanel::GetSelectedCPGenerator()
349 {
350     return cpdetector_config.settings[m_CPDetectorChoice->GetSelection()].GetCPDetectorDesc();
351 };
352 
OnSelectionChanged(wxTreeEvent & e)353 void ImagesPanel::OnSelectionChanged(wxTreeEvent & e)
354 {
355     const HuginBase::UIntSet & sel = m_images_tree->GetSelectedImages();
356     DEBUG_DEBUG("selected Images: " << sel.size());
357     if (sel.empty())
358     {
359         // nothing to edit
360         DisableImageCtrls();
361     }
362     else
363     {
364         // enable edit
365         EnableImageCtrls();
366         const HuginBase::SrcPanoImage& img = m_pano->getImage(*sel.begin());
367         bool identical_projection=true;
368         HuginBase::SrcPanoImage::Projection proj = img.getProjection();
369         double focallength = HuginBase::SrcPanoImage::calcFocalLength(img.getProjection(), img.getHFOV(),
370                 img.getCropFactor(),img.getSize());;
371         double cropFactor=img.getCropFactor();
372         for (HuginBase::UIntSet::const_iterator it = sel.begin(); it != sel.end(); ++it)
373         {
374             const HuginBase::SrcPanoImage& img2 = m_pano->getImage(*it);
375             if(proj!=img2.getProjection())
376             {
377                 identical_projection=false;
378             };
379             double focallength2 = HuginBase::SrcPanoImage::calcFocalLength(img2.getProjection(), img2.getHFOV(),
380                 img2.getCropFactor(),img2.getSize());
381             if(focallength>0 && fabs(focallength-focallength2)>0.05)
382             {
383                 focallength=-1;
384             };
385             if(fabs(cropFactor-img2.getCropFactor())>0.1)
386             {
387                 cropFactor=-1;
388             };
389         };
390 
391         if(identical_projection)
392         {
393             SelectListValue(m_lenstype, proj);
394         }
395         else
396         {
397             m_lenstype->Select(wxNOT_FOUND);
398         };
399         if(focallength>0)
400         {
401             // use ChangeValue explicit, SetValue would create EVT_TEXT event which collides with our TextKillFocusHandler
402             m_focallength->ChangeValue(hugin_utils::doubleTowxString(focallength,m_degDigits));
403         }
404         else
405         {
406             m_focallength->Clear();
407         };
408         if(cropFactor>0)
409         {
410             m_cropfactor->ChangeValue(hugin_utils::doubleTowxString(cropFactor,m_degDigits));
411         }
412         else
413         {
414             m_cropfactor->Clear();
415         };
416 
417         if (sel.size() == 1)
418         {
419             ShowImage(*(sel.begin()));
420         }
421         else
422         {
423             m_smallImgCtrl->SetBitmap(m_empty);
424             m_smallImgCtrl->GetParent()->Layout();
425             m_smallImgCtrl->Refresh();
426         };
427     }
428 }
429 
DisableImageCtrls()430 void ImagesPanel::DisableImageCtrls()
431 {
432     // disable controls
433     m_lenstype->Disable();
434     m_focallength->Disable();
435     m_cropfactor->Disable();
436     m_smallImgCtrl->SetBitmap(m_empty);
437     m_smallImgCtrl->GetParent()->Layout();
438     m_smallImgCtrl->Refresh();
439 }
440 
EnableImageCtrls()441 void ImagesPanel::EnableImageCtrls()
442 {
443     // enable control if not already enabled
444     m_lenstype->Enable();
445     m_focallength->Enable();
446     m_cropfactor->Enable();
447 }
448 
ShowImage(unsigned int imgNr)449 void ImagesPanel::ShowImage(unsigned int imgNr)
450 {
451     m_showImgNr = imgNr;
452     UpdatePreviewImage();
453 }
454 
UpdatePreviewImage()455 void ImagesPanel::UpdatePreviewImage()
456 {
457     if (m_showImgNr < 0 || m_showImgNr >= m_pano->getNrOfImages())
458     {
459         return;
460     }
461     ImageCache::EntryPtr cacheEntry = ImageCache::getInstance().getSmallImageIfAvailable(
462             m_pano->getImage(m_showImgNr).getFilename());
463     if (!cacheEntry.get())
464     {
465         // image currently isn't loaded.
466         // Instead of loading and displaying the image now, request it for
467         // later. Then the user can switch between images in the list quickly,
468         // even when not all images previews are in the cache.
469         thumbnail_request = ImageCache::getInstance().requestAsyncSmallImage(
470                 m_pano->getImage(m_showImgNr).getFilename());
471         // When the image is ready, try this function again.
472         thumbnail_request->ready.push_back(
473             std::bind(&ImagesPanel::UpdatePreviewImage, this)
474             );
475     } else {
476         // forget any request now the image has loaded.
477         thumbnail_request = ImageCache::RequestPtr();
478         wxImage img = imageCacheEntry2wxImage(cacheEntry);
479 
480         double iRatio = img.GetWidth() / (double) img.GetHeight();
481 
482         wxSize sz;
483         // estimate image size
484 
485         sz = m_smallImgCtrl->GetContainingSizer()->GetSize();
486         double sRatio = (double)sz.GetWidth() / sz.GetHeight();
487         if (iRatio > sRatio) {
488             // image is wider than screen, display landscape
489             sz.SetHeight((int) (sz.GetWidth() / iRatio));
490         } else {
491             // portrait
492             sz.SetWidth((int) (sz.GetHeight() * iRatio));
493         }
494         // Make sure the size is positive:
495         // on a small window, m_smallImgCtrl can have 0 width.
496         sz.IncTo(wxSize(1,1));
497         wxImageResizeQuality resizeQuality = wxIMAGE_QUALITY_NORMAL;
498         if (std::max(img.GetWidth(), img.GetHeight()) > (ULONG_MAX >> 16))
499         {
500             // wxIMAGE_QUALITY_NORMAL resizes the image with ResampleNearest
501             // this algorithm works only if image dimensions are smaller then
502             // ULONG_MAX >> 16 (actual size of unsigned long differ from system
503             // to system)
504             resizeQuality = wxIMAGE_QUALITY_BOX_AVERAGE;
505         };
506         wxImage scaled = img.Scale(sz.GetWidth(),sz.GetHeight(), resizeQuality);
507         // now apply color profile
508         if (!cacheEntry->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
509         {
510             HuginBase::Color::CorrectImage(scaled, *(cacheEntry->iccProfile), huginApp::Get()->GetMonitorProfile());
511         };
512         m_smallImgCtrl->SetBitmap(wxBitmap(scaled));
513         m_smallImgCtrl->GetParent()->Layout();
514         m_smallImgCtrl->Refresh();
515     }
516 }
517 
ReloadCPDetectorSettings()518 void ImagesPanel::ReloadCPDetectorSettings()
519 {
520     cpdetector_config.Read();
521     cpdetector_config.FillControl(m_CPDetectorChoice,true);
522     m_CPDetectorChoice->InvalidateBestSize();
523     m_CPDetectorChoice->GetParent()->Layout();
524     Refresh();
525 };
526 
OnLensTypeChanged(wxCommandEvent & e)527 void ImagesPanel::OnLensTypeChanged (wxCommandEvent & e)
528 {
529     size_t var = GetSelectedValue(m_lenstype);
530     HuginBase::UIntSet images = m_images_tree->GetSelectedImages();
531     if(!images.empty())
532     {
533         const HuginBase::SrcPanoImage & img = m_pano->getImage(*(images.begin()));
534         double focal_length = HuginBase::SrcPanoImage::calcFocalLength(img.getProjection(), img.getHFOV(), img.getCropFactor(), img.getSize());
535         std::vector<PanoCommand::PanoCommand*> commands;
536         commands.push_back(new PanoCommand::ChangeImageProjectionCmd(*m_pano, images,(HuginBase::SrcPanoImage::Projection) var));
537         commands.push_back(new PanoCommand::UpdateFocalLengthCmd(*m_pano, images, focal_length));
538         PanoCommand::GlobalCmdHist::getInstance().addCommand(
539             new PanoCommand::CombinedPanoCommand(*m_pano, commands)
540         );
541         // check if fisheye projections is selected
542         if (wxConfig::Get()->Read(wxT("/ShowFisheyeCropHint"), 1l) == 1 &&
543             !(var == HuginBase::BaseSrcPanoImage::RECTILINEAR ||
544                 var == HuginBase::BaseSrcPanoImage::PANORAMIC ||
545                 var == HuginBase::BaseSrcPanoImage::EQUIRECTANGULAR ||
546                 var == HuginBase::BaseSrcPanoImage::FULL_FRAME_FISHEYE))
547         {
548             // if so, show hint about crop and open tab when requested
549             wxDialog dlg;
550             wxXmlResource::Get()->LoadDialog(&dlg, NULL, wxT("fisheye_show_crop_dlg"));
551             if (dlg.ShowModal() == wxID_OK)
552             {
553                 MainFrame::Get()->ShowMaskEditor(*(images.begin()), true);
554             };
555             if (XRCCTRL(dlg, "fisheye_crop_dont_ask_checkbox", wxCheckBox)->IsChecked())
556             {
557                 wxConfig::Get()->Write(wxT("/ShowFisheyeCropHint"), 0l);
558             };
559         };
560     };
561 };
562 
OnFocalLengthChanged(wxCommandEvent & e)563 void ImagesPanel::OnFocalLengthChanged(wxCommandEvent & e)
564 {
565     if (m_pano->getNrOfImages() == 0)
566     {
567         return;
568     };
569 
570     wxString text = m_focallength->GetValue();
571     if(text.IsEmpty())
572     {
573         return;
574     };
575     double val;
576     if (!hugin_utils::str2double(text, val))
577     {
578         return;
579     }
580     //no negative values, no zero input please
581     if (val<0.1)
582     {
583         wxBell();
584         return;
585     };
586 
587     HuginBase::UIntSet images = m_images_tree->GetSelectedImages();
588     const HuginBase::SrcPanoImage& srcImg = m_pano->getImage(*(images.begin()));
589     if (srcImg.getProjection() == HuginBase::SrcPanoImage::FISHEYE_ORTHOGRAPHIC)
590     {
591         double hfov=srcImg.calcHFOV(srcImg.getProjection(), val, srcImg.getCropFactor(), srcImg.getSize());
592         if(hfov>190)
593         {
594             if(wxMessageBox(
595                 wxString::Format(_("You have given a field of view of %.2f degrees.\n But the orthographic projection is limited to a field of view of 180 degress.\nDo you want still use that high value?"), hfov),
596 #ifdef __WXMSW__
597                 _("Hugin"),
598 #else
599                 wxT(""),
600 #endif
601                 wxICON_EXCLAMATION | wxYES_NO)==wxNO)
602             {
603                 wxTreeEvent dummy;
604                 OnSelectionChanged(dummy);
605                 return;
606             };
607         };
608     };
609     PanoCommand::GlobalCmdHist::getInstance().addCommand(
610         new PanoCommand::UpdateFocalLengthCmd(*m_pano, images, val)
611     );
612 }
613 
OnCropFactorChanged(wxCommandEvent & e)614 void ImagesPanel::OnCropFactorChanged(wxCommandEvent & e)
615 {
616     if (m_pano->getNrOfImages() == 0)
617     {
618         return;
619     };
620 
621     wxString text = m_cropfactor->GetValue();
622     if(text.IsEmpty())
623     {
624         return;
625     };
626     double val;
627     if (!hugin_utils::str2double(text, val))
628     {
629         return;
630     }
631     //no negative values, no zero input please
632     if (val<0.1)
633     {
634         wxBell();
635         return;
636     };
637 
638     HuginBase::UIntSet images = m_images_tree->GetSelectedImages();
639     PanoCommand::GlobalCmdHist::getInstance().addCommand(
640         new PanoCommand::UpdateCropFactorCmd(*m_pano,images,val)
641     );
642 }
643 
OnMinimumOverlapChanged(wxCommandEvent & e)644 void ImagesPanel::OnMinimumOverlapChanged(wxCommandEvent & e)
645 {
646     wxString text = m_overlap->GetValue();
647     if(text.IsEmpty())
648     {
649         return;
650     };
651     double val;
652     if (!hugin_utils::str2double(text, val))
653     {
654         return;
655     }
656     if(fabs(val)<0.001 || val>1)
657     {
658         wxMessageBox(_("The minimum overlap has to be greater than 0 and smaller than 1."),
659 #ifdef _WIN32
660             _("Hugin"),
661 #else
662             wxT(""),
663 #endif
664             wxOK | wxICON_INFORMATION, this);
665         return;
666     };
667     if (val < 0)
668     {
669         val = -1;
670     };
671     HuginBase::PanoramaOptions opt = m_pano->getOptions();
672     opt.outputStacksMinOverlap=val;
673     PanoCommand::GlobalCmdHist::getInstance().addCommand(
674         new PanoCommand::SetPanoOptionsCmd(*m_pano,opt)
675     );
676 };
677 
OnMaxEvDiffChanged(wxCommandEvent & e)678 void ImagesPanel::OnMaxEvDiffChanged(wxCommandEvent& e)
679 {
680     wxString text = m_maxEv->GetValue();
681     if(text.IsEmpty())
682     {
683         return;
684     };
685     double val;
686     if (!hugin_utils::str2double(text, val))
687     {
688         return;
689     }
690     if(val<0)
691     {
692         wxMessageBox(_("The maximum Ev difference has to be greater than 0."),
693 #ifdef _WIN32
694             _("Hugin"),
695 #else
696             wxT(""),
697 #endif
698             wxOK | wxICON_INFORMATION, this);
699         return;
700     };
701     HuginBase::PanoramaOptions opt = m_pano->getOptions();
702     opt.outputLayersExposureDiff=val;
703     PanoCommand::GlobalCmdHist::getInstance().addCommand(
704         new PanoCommand::SetPanoOptionsCmd(*m_pano,opt)
705     );
706 };
707 
FillGroupChoice()708 void ImagesPanel::FillGroupChoice()
709 {
710     wxChoice* group=XRCCTRL(*this,"images_group_mode", wxChoice);
711     size_t sel=group->GetSelection();
712     DeleteClientData(group);
713     group->Clear();
714     int* i=new int;
715     *i=ImagesTreeCtrl::GROUP_NONE;
716     group->Append(_("None"), i);
717     i=new int;
718     *i=ImagesTreeCtrl::GROUP_LENS;
719     group->Append(_("Lens"), i);
720     if(m_guiLevel>GUI_SIMPLE)
721     {
722         i=new int;
723         *i=ImagesTreeCtrl::GROUP_STACK;
724         group->Append(_("Stacks"), i);
725         if(m_guiLevel==GUI_EXPERT)
726         {
727             i=new int;
728             *i=ImagesTreeCtrl::GROUP_OUTPUTLAYERS;
729             group->Append(_("Output layers"), i);
730             i=new int;
731             *i=ImagesTreeCtrl::GROUP_OUTPUTSTACK;
732             group->Append(_("Output stacks"), i);
733         };
734     };
735     if((m_guiLevel==GUI_ADVANCED && sel>2) || (m_guiLevel==GUI_SIMPLE && sel>1))
736     {
737         sel=0;
738     };
739     group->SetSelection(sel);
740     wxCommandEvent dummy;
741     OnGroupModeChanged(dummy);
742 };
743 
FillOptimizerChoice()744 void ImagesPanel::FillOptimizerChoice()
745 {
746     DeleteClientData(m_optChoice);
747     m_optChoice->Clear();
748     int* i=new int;
749     *i=HuginBase::OPT_PAIR;
750     m_optChoice->Append(_("Positions (incremental, starting from anchor)"), i);
751     i=new int;
752     *i=HuginBase::OPT_POSITION;
753     m_optChoice->Append(_("Positions (y,p,r)"), i);
754     i=new int;
755     *i=(HuginBase::OPT_POSITION | HuginBase::OPT_VIEW);
756     m_optChoice->Append(_("Positions and View (y,p,r,v)"), i);
757     i=new int;
758     *i=(HuginBase::OPT_POSITION | HuginBase::OPT_BARREL);
759     m_optChoice->Append(_("Positions and Barrel Distortion (y,p,r,b)"), i);
760     i=new int;
761     *i=(HuginBase::OPT_POSITION | HuginBase::OPT_VIEW | HuginBase::OPT_BARREL);
762     m_optChoice->Append(_("Positions, View and Barrel (y,p,r,v,b)"), i);
763     i=new int;
764     *i=HuginBase::OPT_ALL;
765     if(m_guiLevel==GUI_EXPERT)
766     {
767         m_optChoice->Append(_("Everything without translation"), i);
768     }
769     else
770     {
771         m_optChoice->Append(_("Everything"), i);
772     };
773     if(m_guiLevel==GUI_EXPERT)
774     {
775         i=new int;
776         *i=(HuginBase::OPT_POSITION | HuginBase::OPT_TRANSLATION);
777         m_optChoice->Append(_("Positions and Translation (y,p,r,x,y,z)"), i);
778         i=new int;
779         *i=(HuginBase::OPT_POSITION | HuginBase::OPT_TRANSLATION | HuginBase::OPT_VIEW);
780         m_optChoice->Append(_("Positions, Translation and View (y,p,r,x,y,z,v)"), i);
781         i=new int;
782         *i=(HuginBase::OPT_POSITION | HuginBase::OPT_TRANSLATION | HuginBase::OPT_BARREL);
783         m_optChoice->Append(_("Positions, Translation and Barrel (y,p,r,x,y,z,b)"), i);
784         i=new int;
785         *i=(HuginBase::OPT_POSITION | HuginBase::OPT_TRANSLATION | HuginBase::OPT_VIEW | HuginBase::OPT_BARREL);
786         m_optChoice->Append(_("Positions, Translation, View and Barrel (y,p,r,x,y,z,v,b)"), i);
787     };
788     i=new int;
789     *i=0;
790     m_optChoice->Append(_("Custom parameters"), i);
791 
792     DeleteClientData(m_optPhotoChoice);
793     m_optPhotoChoice->Clear();
794     i=new int;
795     *i=(HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);
796     m_optPhotoChoice->Append(_("Low dynamic range"), i);
797     i=new int;
798     *i=(HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE | HuginBase::OPT_WHITEBALANCE);
799     m_optPhotoChoice->Append(_("Low dynamic range, variable white balance"), i);
800     if(m_guiLevel>GUI_SIMPLE)
801     {
802         i=new int;
803         *i=(HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);
804         m_optPhotoChoice->Append(_("High dynamic range, fixed exposure"), i);
805         i=new int;
806         *i=(HuginBase::OPT_WHITEBALANCE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);
807         m_optPhotoChoice->Append(_("High dynamic range, variable white balance, fixed exposure"), i);
808     };
809     i=new int;
810     *i=0;
811     m_optPhotoChoice->Append(_("Custom parameters"), i);
812     m_optChoice->GetParent()->Layout();
813     Refresh();
814 };
815 
GetCurrentOptimizerString()816 wxString ImagesPanel::GetCurrentOptimizerString()
817 {
818     return m_optChoice->GetString(m_optChoice->GetSelection());
819 };
820 
OnGroupModeChanged(wxCommandEvent & e)821 void ImagesPanel::OnGroupModeChanged(wxCommandEvent & e)
822 {
823     wxChoice* group=XRCCTRL(*this,"images_group_mode", wxChoice);
824     ImagesTreeCtrl::GroupMode mode=ImagesTreeCtrl::GroupMode(*static_cast<int*>(group->GetClientData(group->GetSelection())));
825     m_images_tree->SetGroupMode(mode);
826     XRCCTRL(*this, "images_text_overlap", wxStaticText)->Show(mode==ImagesTreeCtrl::GROUP_OUTPUTSTACK);
827     m_overlap->Show(mode==ImagesTreeCtrl::GROUP_OUTPUTSTACK);
828     m_overlap->Enable(mode==ImagesTreeCtrl::GROUP_OUTPUTSTACK);
829     XRCCTRL(*this, "images_text_maxev", wxStaticText)->Show(mode==ImagesTreeCtrl::GROUP_OUTPUTLAYERS);
830     m_maxEv->Show(mode==ImagesTreeCtrl::GROUP_OUTPUTLAYERS);
831     m_maxEv->Enable(mode==ImagesTreeCtrl::GROUP_OUTPUTLAYERS);
832     Layout();
833     Refresh();
834 };
835 
OnDisplayModeChanged(wxCommandEvent & e)836 void ImagesPanel::OnDisplayModeChanged(wxCommandEvent & e)
837 {
838     wxRadioBox* display=XRCCTRL(*this,"images_column_radiobox", wxRadioBox);
839     m_images_tree->SetDisplayMode((ImagesTreeCtrl::DisplayMode)display->GetSelection());
840 };
841 
OnOptimizerSwitchChanged(wxCommandEvent & e)842 void ImagesPanel::OnOptimizerSwitchChanged(wxCommandEvent &e)
843 {
844     int optSwitch=*static_cast<int*>(m_optChoice->GetClientData(m_optChoice->GetSelection()));
845     if(optSwitch!=m_pano->getOptimizerSwitch())
846     {
847         PanoCommand::GlobalCmdHist::getInstance().addCommand(
848             new PanoCommand::UpdateOptimizerSwitchCmd(*m_pano,optSwitch)
849         );
850     };
851 };
852 
OnPhotometricOptimizerSwitchChanged(wxCommandEvent & e)853 void ImagesPanel::OnPhotometricOptimizerSwitchChanged(wxCommandEvent &e)
854 {
855     int optSwitch=*static_cast<int*>(m_optPhotoChoice->GetClientData(m_optPhotoChoice->GetSelection()));
856     if(optSwitch!=m_pano->getPhotometricOptimizerSwitch())
857     {
858         PanoCommand::GlobalCmdHist::getInstance().addCommand(
859             new PanoCommand::UpdatePhotometricOptimizerSwitchCmd(*m_pano,optSwitch)
860         );
861     };
862 };
863 
SetGuiLevel(GuiLevel newGuiLevel)864 void ImagesPanel::SetGuiLevel(GuiLevel newGuiLevel)
865 {
866     m_guiLevel=newGuiLevel;
867     m_images_tree->SetGuiLevel(newGuiLevel);
868     FillGroupChoice();
869     FillOptimizerChoice();
870     wxStaticText* textlabel=XRCCTRL(*this, "images_mode_text", wxStaticText);
871     switch(m_guiLevel)
872     {
873         case GUI_SIMPLE:
874             textlabel->SetLabel(_("Simple interface"));
875             break;
876         case GUI_ADVANCED:
877             textlabel->SetLabel(_("Advanced interface"));
878             break;
879         case GUI_EXPERT:
880             textlabel->SetLabel(_("Expert interface"));
881             break;
882     };
883     textlabel->GetParent()->Layout();
884     textlabel->Refresh();
885     panoramaChanged(*m_pano);
886 };
887 
OnOptimizeButton(wxCommandEvent & e)888 void ImagesPanel::OnOptimizeButton(wxCommandEvent &e)
889 {
890     MainFrame::Get()->OnOptimize(e);
891 };
892 
OnPhotometricOptimizeButton(wxCommandEvent & e)893 void ImagesPanel::OnPhotometricOptimizeButton(wxCommandEvent &e)
894 {
895     MainFrame::Get()->OnPhotometricOptimize(e);
896 };
897 
IMPLEMENT_DYNAMIC_CLASS(ImagesPanel,wxPanel)898 IMPLEMENT_DYNAMIC_CLASS(ImagesPanel, wxPanel)
899 
900 ImagesPanelXmlHandler::ImagesPanelXmlHandler()
901                 : wxXmlResourceHandler()
902 {
903     AddWindowStyles();
904 }
905 
DoCreateResource()906 wxObject *ImagesPanelXmlHandler::DoCreateResource()
907 {
908     XRC_MAKE_INSTANCE(cp, ImagesPanel)
909 
910     cp->Create(m_parentAsWindow,
911                    GetID(),
912                    GetPosition(), GetSize(),
913                    GetStyle(wxT("style")),
914                    GetName());
915 
916     SetupWindow( cp);
917     return cp;
918 }
919 
CanHandle(wxXmlNode * node)920 bool ImagesPanelXmlHandler::CanHandle(wxXmlNode *node)
921 {
922     return IsOfClass(node, wxT("ImagesPanel"));
923 }
924 
925 IMPLEMENT_DYNAMIC_CLASS(ImagesPanelXmlHandler, wxXmlResourceHandler)
926