1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file LensCalFrame.cpp
4  *
5  *  @brief implementation of LensCal main frame class
6  *
7  *  @author T. Modes
8  *
9  */
10 
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 "panoinc_WX.h"
29 #include "panoinc.h"
30 
31 #include "base_wx/platform.h"
32 #include "base_wx/wxPlatform.h"
33 #include "base_wx/LensTools.h"
34 #include "base_wx/GraphTools.h"
35 #include "huginapp/ImageCache.h"
36 #include "LensCalFrame.h"
37 #include <wx/app.h>
38 #include "LensCalApp.h"
39 #include "hugin/config_defaults.h"
40 #include <algorithms/optimizer/PTOptimizer.h>
41 #include "lensdb/LensDB.h"
42 #include "base_wx/wxLensDB.h"
43 #include "panodata/StandardImageVariableGroups.h"
44 
45 const unsigned int cps_per_line=10;
46 
47 #define DEFAULT_LENSCAL_SCALE 2.0
48 #define DEFAULT_LENSCAL_THRESHOLD 4.0
49 #define DEFAULT_RESIZE_DIMENSION 1600
50 #define DEFAULT_MINLINELENGTH 0.3
51 
52 /** file drag and drop handler method */
OnDropFiles(wxCoord x,wxCoord y,const wxArrayString & filenames)53 bool FileDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
54 {
55     DEBUG_TRACE("OnDropFiles");
56     LensCalFrame* frame=wxGetApp().GetLensCalFrame();
57     if (!frame)
58         return false;
59 
60     // try to add as images
61     wxArrayString files;
62     wxArrayString invalidFiles;
63     for (unsigned int i=0; i< filenames.GetCount(); i++)
64     {
65         wxFileName file(filenames[i]);
66         if (file.GetExt().CmpNoCase(wxT("jpg")) == 0 ||
67             file.GetExt().CmpNoCase(wxT("jpeg")) == 0 ||
68             file.GetExt().CmpNoCase(wxT("tif")) == 0 ||
69             file.GetExt().CmpNoCase(wxT("tiff")) == 0 ||
70             file.GetExt().CmpNoCase(wxT("png")) == 0 ||
71             file.GetExt().CmpNoCase(wxT("bmp")) == 0 ||
72             file.GetExt().CmpNoCase(wxT("gif")) == 0 ||
73             file.GetExt().CmpNoCase(wxT("pnm")) == 0 ||
74             file.GetExt().CmpNoCase(wxT("sun")) == 0 ||
75             file.GetExt().CmpNoCase(wxT("hdr")) == 0 ||
76             file.GetExt().CmpNoCase(wxT("viff")) == 0 )
77         {
78             if(containsInvalidCharacters(file.GetFullPath()))
79             {
80                 invalidFiles.push_back(file.GetFullPath());
81             }
82             else
83             {
84                 files.push_back(file.GetFullPath());
85             };
86         }
87     }
88     if(!invalidFiles.empty())
89     {
90         ShowFilenameWarning(frame, invalidFiles);
91     }
92     // we got some images to add.
93     if (!files.empty())
94     {
95         // use a Command to ensure proper undo and updating of GUI parts
96         frame->AddImages(files);
97     }
98     return true;
99 }
100 
101 // event table. this frame will recieve mostly global commands.
BEGIN_EVENT_TABLE(LensCalFrame,wxFrame)102 BEGIN_EVENT_TABLE(LensCalFrame, wxFrame)
103     EVT_LISTBOX(XRCID("lenscal_images_list"), LensCalFrame::OnImageSelected)
104     EVT_MENU(XRCID("menu_save"), LensCalFrame::OnSaveProject)
105     EVT_MENU(XRCID("menu_quit"), LensCalFrame::OnExit)
106     EVT_BUTTON(XRCID("lenscal_add_image"), LensCalFrame::OnAddImage)
107     EVT_BUTTON(XRCID("lenscal_remove_image"), LensCalFrame::OnRemoveImage)
108     EVT_BUTTON(XRCID("lenscal_find_lines"), LensCalFrame::OnFindLines)
109     EVT_BUTTON(XRCID("lenscal_reset"), LensCalFrame::OnReset)
110     EVT_BUTTON(XRCID("lenscal_opt"), LensCalFrame::OnOptimize)
111     EVT_BUTTON(XRCID("lenscal_show_distortion_graph"), LensCalFrame::OnShowDistortionGraph)
112     EVT_BUTTON(XRCID("lenscal_save_lens"), LensCalFrame::OnSaveLens)
113     EVT_BUTTON(XRCID("lenscal_refresh"), LensCalFrame::OnRefresh)
114     EVT_CHOICE(XRCID("lenscal_preview_content"), LensCalFrame::OnSelectPreviewContent)
115     EVT_CHECKBOX(XRCID("lenscal_show_lines"), LensCalFrame::OnShowLines)
116 END_EVENT_TABLE()
117 
118 LensCalFrame::LensCalFrame(wxWindow* parent)
119 {
120     DEBUG_TRACE("");
121     // load our children. some children might need special
122     // initialization. this will be done later.
123     wxXmlResource::Get()->LoadFrame(this, parent, wxT("lenscal_frame"));
124     DEBUG_TRACE("");
125 
126     // load our menu bar
127 #ifdef __WXMAC__
128     wxApp::s_macExitMenuItemId = XRCID("menu_quit");
129 #endif
130     SetMenuBar(wxXmlResource::Get()->LoadMenuBar(this, wxT("lenscal_menubar")));
131 
132     m_choice_projection=XRCCTRL(*this,"lenscal_proj_choice",wxChoice);
133     FillLensProjectionList(m_choice_projection);
134     m_images_list=XRCCTRL(*this,"lenscal_images_list",wxListBox);
135     m_preview=XRCCTRL(*this,"lenscal_preview",LensCalImageCtrl);
136 
137     wxConfigBase* config = wxConfigBase::Get();
138     config->Read(wxT("/LensCalFrame/EdgeScale"),&m_edge_scale,DEFAULT_LENSCAL_SCALE);
139     config->Read(wxT("/LensCalFrame/EdgeThreshold"),&m_edge_threshold,DEFAULT_LENSCAL_THRESHOLD);
140     m_resize_dimension=config->Read(wxT("/LensCalFrame/ResizeDimension"),DEFAULT_RESIZE_DIMENSION);
141     config->Read(wxT("/LensCalFrame/MinLineLength"),&m_minlinelength,DEFAULT_MINLINELENGTH);
142     ParametersToDisplay();
143 
144     bool selected;
145     config->Read(wxT("/LensCalFrame/Optimize_a"),&selected,false);
146     XRCCTRL(*this,"lenscal_opt_a",wxCheckBox)->SetValue(selected);
147     config->Read(wxT("/LensCalFrame/Optimize_b"),&selected,true);
148     XRCCTRL(*this,"lenscal_opt_b",wxCheckBox)->SetValue(selected);
149     config->Read(wxT("/LensCalFrame/Optimize_c"),&selected,false);
150     XRCCTRL(*this,"lenscal_opt_c",wxCheckBox)->SetValue(selected);
151     config->Read(wxT("/LensCalFrame/Optimize_de"),&selected,false);
152     XRCCTRL(*this,"lenscal_opt_de",wxCheckBox)->SetValue(selected);
153 
154     // set the minimize icon
155 #ifdef __WXMSW__
156     wxIcon myIcon(GetXRCPath() + wxT("data/hugin.ico"),wxBITMAP_TYPE_ICO);
157 #else
158     wxIcon myIcon(GetXRCPath() + wxT("data/hugin.png"),wxBITMAP_TYPE_PNG);
159 #endif
160     SetIcon(myIcon);
161     SetTitle(_("Hugin Lens calibration GUI"));
162 
163     // create a new drop handler. wxwindows deletes the automatically
164     SetDropTarget(new FileDropTarget());
165 
166     // create a status bar
167     const int fields (2);
168     CreateStatusBar(fields);
169     int widths[fields] = {-1, 85};
170     SetStatusWidths( fields, &widths[0]);
171 
172     // Set sizing characteristics
173     //set minumum size
174 #if defined __WXMAC__ || defined __WXMSW__
175     // a minimum nice looking size; smaller than this would clutter the layout.
176     SetSizeHints(900, 675);
177 #else
178     // For ASUS eeePc
179     SetSizeHints(780, 455); //set minumum size
180 #endif
181 
182     // set progress display for image cache.
183     ImageCache::getInstance().setProgressDisplay(this);
184 #if defined __WXMSW__
185     unsigned long long mem = HUGIN_IMGCACHE_UPPERBOUND;
186     unsigned long mem_low = config->Read(wxT("/ImageCache/UpperBound"), HUGIN_IMGCACHE_UPPERBOUND);
187     unsigned long mem_high = config->Read(wxT("/ImageCache/UpperBoundHigh"), (long) 0);
188     if (mem_high > 0) {
189       mem = ((unsigned long long) mem_high << 32) + mem_low;
190     }
191     else {
192       mem = mem_low;
193     }
194     ImageCache::getInstance().SetUpperLimit(mem);
195 #else
196     ImageCache::getInstance().SetUpperLimit(config->Read(wxT("/ImageCache/UpperBound"), HUGIN_IMGCACHE_UPPERBOUND));
197 #endif
198     //disable buttons
199     EnableButtons();
200     XRCCTRL(*this,"lenscal_remove_image",wxButton)->Enable(false);
201 }
202 
~LensCalFrame()203 LensCalFrame::~LensCalFrame()
204 {
205     DEBUG_TRACE("dtor");
206     m_preview->SetEmptyImage();
207     ImageCache::getInstance().setProgressDisplay(0);
208     delete & ImageCache::getInstance();
209     // get the global config object
210     wxConfigBase* config = wxConfigBase::Get();
211     if(ReadInputs(false,true,false))
212     {
213         config->Write(wxT("/LensCalFrame/EdgeScale"),m_edge_scale);
214         config->Write(wxT("/LensCalFrame/EdgeThreshold"),m_edge_threshold);
215         config->Write(wxT("/LensCalFrame/ResizeDimension"),(int)m_resize_dimension);
216         config->Write(wxT("/LensCalFrame/MinLineLength"),m_minlinelength);
217     };
218     config->Write(wxT("/LensCalFrame/Optimize_a"),XRCCTRL(*this,"lenscal_opt_a",wxCheckBox)->GetValue());
219     config->Write(wxT("/LensCalFrame/Optimize_b"),XRCCTRL(*this,"lenscal_opt_b",wxCheckBox)->GetValue());
220     config->Write(wxT("/LensCalFrame/Optimize_c"),XRCCTRL(*this,"lenscal_opt_c",wxCheckBox)->GetValue());
221     config->Write(wxT("/LensCalFrame/Optimize_de"),XRCCTRL(*this,"lenscal_opt_de",wxCheckBox)->GetValue());
222     StoreFramePosition(this, wxT("LensCalFrame"));
223     config->Flush();
224     //cleanup
225     for(unsigned int i=0;i<m_images.size();i++)
226     {
227         delete m_images[i];
228     };
229     m_images.clear();
230     HuginBase::LensDB::LensDB::Clean();
231     DEBUG_TRACE("dtor end");
232 }
233 
ParametersToDisplay()234 void LensCalFrame::ParametersToDisplay()
235 {
236     XRCCTRL(*this,"lenscal_scale",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_edge_scale,2));
237     XRCCTRL(*this, "lenscal_threshold", wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_edge_threshold, 2));
238     XRCCTRL(*this, "lenscal_resizedim", wxTextCtrl)->SetValue(wxString::Format(wxT("%d"), m_resize_dimension));
239     XRCCTRL(*this, "lenscal_minlinelength", wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_minlinelength, 2));
240 };
241 
GetXRCPath()242 const wxString & LensCalFrame::GetXRCPath()
243 {
244      return wxGetApp().GetXRCPath();
245 };
246 
247 /** update the display */
updateProgressDisplay()248 void LensCalFrame::updateProgressDisplay()
249 {
250     wxString msg;
251     if (!m_message.empty())
252     {
253         msg = wxGetTranslation(wxString(m_message.c_str(), wxConvLocal));
254         if (!m_filename.empty())
255         {
256             msg.Append(wxT(" "));
257             msg.Append(wxString(ProgressDisplay::m_filename.c_str(), HUGIN_CONV_FILENAME));
258         };
259     };
260     GetStatusBar()->SetStatusText(msg, 0);
261 
262 #ifdef __WXMSW__
263     UpdateWindow(NULL);
264 #endif
265 }
266 
OnExit(wxCommandEvent & e)267 void LensCalFrame::OnExit(wxCommandEvent &e)
268 {
269     Close();
270 };
271 
AddImages(wxArrayString files)272 void LensCalFrame::AddImages(wxArrayString files)
273 {
274     wxArrayString wrongSize;
275     wxArrayString wrongExif;
276     for (unsigned int i=0; i<files.GetCount(); i++)
277     {
278         ImageLineList* image=new ImageLineList(files[i]);
279         //check input
280         {
281             // check for black/white images
282             const HuginBase::FileMetaData metadata = image->GetPanoImage()->getFileMetadata();
283             HuginBase::FileMetaData::const_iterator it = metadata.find("pixeltype");
284             if (it != metadata.end())
285             {
286                 if (it->second == "BILEVEL")
287                 {
288                     wxMessageBox(wxString::Format(_("File \"%s\" is a black/white image.\nHugin does not support this image type. Skipping this image.\nConvert image to grayscale image and try loading again."), files[i].c_str()),
289                         _("Warning"), wxOK | wxICON_EXCLAMATION, this);
290                     delete image;
291                     continue;
292                 };
293             };
294         };
295         if(!m_images.empty())
296         {
297             const HuginBase::SrcPanoImage* image0=m_images[0]->GetPanoImage();
298             const HuginBase::SrcPanoImage* image1=image->GetPanoImage();
299             if(image0->getSize()!=image1->getSize())
300             {
301                 wrongSize.push_back(files[i]);
302                 delete image;
303                 continue;
304             };
305             if(!image0->getExifMake().empty() && !image1->getExifMake().empty() &&
306                !image0->getExifModel().empty() && !image1->getExifModel().empty() &&
307                image0->getExifFocalLength()>0 && image1->getExifFocalLength()>0 &&
308                image0->getCropFactor()>0 && image1->getCropFactor()>0
309               )
310             {
311                 if(image0->getExifMake()!=image1->getExifMake() ||
312                     image0->getExifModel()!=image1->getExifModel() ||
313                     image0->getExifFocalLength()!=image1->getExifFocalLength() ||
314                     image0->getCropFactor()!=image1->getCropFactor())
315                 {
316                     //only show a warning, but continue processing
317                     wrongExif.push_back(files[i]);
318                 };
319             };
320         };
321         m_images.push_back(image);
322         SetStatusText(wxString::Format(_("Added %s"),image->GetFilename().c_str()));
323         if (image->GetPanoImage()->getExifFocalLength() > 0)
324         {
325             XRCCTRL(*this, "lenscal_focallength", wxTextCtrl)->SetValue(
326                 hugin_utils::doubleTowxString(image->GetPanoImage()->getExifFocalLength(), 2)
327                 );
328         };
329         if (image->GetPanoImage()->getExifCropFactor() > 0)
330         {
331             XRCCTRL(*this, "lenscal_cropfactor", wxTextCtrl)->SetValue(
332                 hugin_utils::doubleTowxString(image->GetPanoImage()->getCropFactor(), 2)
333                 );
334         };
335         SelectListValue(m_choice_projection,image->GetPanoImage()->getProjection());
336     }
337     UpdateList(false);
338     m_images_list->SetSelection(m_images_list->GetCount()-1);
339     wxCommandEvent e;
340     OnImageSelected(e);
341     EnableButtons();
342     if(!wrongSize.empty())
343     {
344         wxString fileText;
345         for(unsigned int i=0;i<wrongSize.size();i++)
346         {
347             wxFileName filename(wrongSize[i]);
348             fileText.Append(filename.GetFullName());
349             if(i<wrongSize.size()-1)
350                 fileText.Append(wxT(", "));
351         };
352         wxMessageBox(wxString::Format(_("The size of the images (%s) does not match the already added image(s)."),fileText.c_str()),
353             _("Error"),wxOK|wxICON_EXCLAMATION,this);
354     };
355     if(!wrongExif.empty())
356     {
357         wxString fileText;
358         for(unsigned int i=0;i<wrongExif.size();i++)
359         {
360             wxFileName filename(wrongExif[i]);
361             fileText.Append(filename.GetFullName());
362             if(i<wrongExif.size()-1)
363                 fileText.Append(wxT(", "));
364         };
365         wxMessageBox(wxString::Format(_("The EXIF information of the added images (%s) is not consistent with the already added image(s).\nPlease check the image again, if you selected the correct images."),fileText.c_str()),
366             _("Warning"),wxOK|wxICON_EXCLAMATION,this);
367     };
368 };
369 
OnAddImage(wxCommandEvent & e)370 void LensCalFrame::OnAddImage(wxCommandEvent &e)
371 {
372     wxConfigBase* config = wxConfigBase::Get();
373     wxString path = config->Read(wxT("/actualPath"), wxT(""));
374     wxFileDialog dlg(this,_("Add images"),
375                      path, wxT(""),
376                      GetFileDialogImageFilters(),
377                      wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST | wxFD_PREVIEW, wxDefaultPosition);
378     dlg.SetDirectory(path);
379 
380     // remember the image extension
381     wxString img_ext;
382     if (config->HasEntry(wxT("lastImageType"))){
383       img_ext = config->Read(wxT("lastImageType")).c_str();
384     }
385     if (img_ext == wxT("all images"))
386       dlg.SetFilterIndex(0);
387     else if (img_ext == wxT("jpg"))
388       dlg.SetFilterIndex(1);
389     else if (img_ext == wxT("tiff"))
390       dlg.SetFilterIndex(2);
391     else if (img_ext == wxT("png"))
392       dlg.SetFilterIndex(3);
393     else if (img_ext == wxT("hdr"))
394       dlg.SetFilterIndex(4);
395     else if (img_ext == wxT("exr"))
396       dlg.SetFilterIndex(5);
397     else if (img_ext == wxT("all files"))
398       dlg.SetFilterIndex(6);
399     DEBUG_INFO ( "Image extention: " << img_ext.mb_str(wxConvLocal) )
400 
401     // call the file dialog
402     if (dlg.ShowModal() == wxID_OK)
403     {
404         // get the selections
405         wxArrayString Pathnames;
406         dlg.GetPaths(Pathnames);
407 
408         // save the current path to config
409 #ifdef __WXGTK__
410         //workaround a bug in GTK, see https://bugzilla.redhat.com/show_bug.cgi?id=849692 and http://trac.wxwidgets.org/ticket/14525
411         config->Write(wxT("/actualPath"), wxPathOnly(Pathnames[0]));
412 #else
413         config->Write(wxT("/actualPath"), dlg.GetDirectory());
414 #endif
415 
416         wxArrayString invalidFiles;
417         for(unsigned int i=0;i<Pathnames.GetCount(); i++)
418         {
419             if(containsInvalidCharacters(Pathnames[i]))
420             {
421                 invalidFiles.push_back(Pathnames[i]);
422             };
423         };
424         if(!invalidFiles.empty())
425         {
426             ShowFilenameWarning(this, invalidFiles);
427         }
428         else
429         {
430             AddImages(Pathnames);
431         };
432         DEBUG_INFO ( wxString::Format(wxT("img_ext: %d"), dlg.GetFilterIndex()).mb_str(wxConvLocal) )
433         // save the image extension
434         switch ( dlg.GetFilterIndex() )
435         {
436             case 0: config->Write(wxT("lastImageType"), wxT("all images")); break;
437             case 1: config->Write(wxT("lastImageType"), wxT("jpg")); break;
438             case 2: config->Write(wxT("lastImageType"), wxT("tiff")); break;
439             case 3: config->Write(wxT("lastImageType"), wxT("png")); break;
440             case 4: config->Write(wxT("lastImageType"), wxT("hdr")); break;
441             case 5: config->Write(wxT("lastImageType"), wxT("exr")); break;
442             case 6: config->Write(wxT("lastImageType"), wxT("all files")); break;
443         }
444     }
445     else
446     {
447         // nothing to open
448         SetStatusText( _("Add Image: cancel"));
449     }
450     EnableButtons();
451 };
452 
UpdateListString(unsigned int index)453 void LensCalFrame::UpdateListString(unsigned int index)
454 {
455     wxFileName file(m_images[index]->GetFilename());
456     m_images_list->SetString(index,wxString::Format(_("%s (%d lines)"),file.GetFullName().c_str(),m_images[index]->GetNrOfValidLines()));
457 };
458 
UpdateList(bool restoreSelection)459 void LensCalFrame::UpdateList(bool restoreSelection)
460 {
461     int oldSelection=m_images_list->GetSelection();
462     m_images_list->Clear();
463     for(unsigned int i=0;i<m_images.size();i++)
464     {
465         wxFileName file(m_images[i]->GetFilename());
466         wxString text=wxString::Format(_("%s (%d lines)"),file.GetFullName().c_str(),m_images[i]->GetNrOfValidLines());
467         m_images_list->Append(text);
468     };
469     if(oldSelection!=wxNOT_FOUND && restoreSelection)
470     {
471         m_images_list->SetSelection(oldSelection);
472     };
473     wxCommandEvent e;
474     OnImageSelected(e);
475 };
476 
OnRemoveImage(wxCommandEvent & e)477 void LensCalFrame::OnRemoveImage(wxCommandEvent &e)
478 {
479     int selection=m_images_list->GetSelection();
480     if(selection!=wxNOT_FOUND)
481     {
482         delete m_images[selection];
483         m_images.erase(m_images.begin()+selection);
484         ImageCache::getInstance().softFlush();
485         UpdateList(false);
486     }
487     else
488     {
489         wxBell();
490     };
491     EnableButtons();
492 };
493 
EnableButtons()494 void LensCalFrame::EnableButtons()
495 {
496     const bool enabling = !m_images.empty();
497     XRCCTRL(*this,"lenscal_find_lines",wxButton)->Enable(enabling);
498     XRCCTRL(*this,"lenscal_opt",wxButton)->Enable(enabling);
499     XRCCTRL(*this, "lenscal_show_distortion_graph", wxButton)->Enable(enabling);
500     XRCCTRL(*this,"lenscal_save_lens",wxButton)->Enable(enabling);
501     GetMenuBar()->Enable(XRCID("menu_save"),enabling);
502 };
503 
ReadInputs(bool readFocalLength,bool readOptions,bool readLensParameter)504 bool LensCalFrame::ReadInputs(bool readFocalLength,bool readOptions,bool readLensParameter)
505 {
506     if(readFocalLength)
507     {
508         m_projection = (HuginBase::SrcPanoImage::Projection)GetSelectedValue(m_choice_projection);
509         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_focallength", wxTextCtrl)->GetValue(), m_focallength))
510             return false;
511         if(m_focallength<1)
512             return false;
513         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_cropfactor", wxTextCtrl)->GetValue(), m_cropfactor))
514             return false;
515         if(m_cropfactor<0.1)
516             return false;
517     }
518     if(readOptions)
519     {
520         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_scale", wxTextCtrl)->GetValue(), m_edge_scale))
521             return false;
522         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_threshold", wxTextCtrl)->GetValue(), m_edge_threshold))
523             return false;
524         double resize_dim;
525         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_resizedim", wxTextCtrl)->GetValue(), resize_dim))
526             return false;
527         if(resize_dim<100)
528             return false;
529         m_resize_dimension=(unsigned int)resize_dim;
530         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_minlinelength", wxTextCtrl)->GetValue(), m_minlinelength))
531             return false;
532         if(m_minlinelength<=0 || m_minlinelength>1)
533             return false;
534     };
535     if(readLensParameter)
536     {
537         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_a", wxTextCtrl)->GetValue(), m_a))
538             return false;
539         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_b", wxTextCtrl)->GetValue(), m_b))
540             return false;
541         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_c", wxTextCtrl)->GetValue(), m_c))
542             return false;
543         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_d", wxTextCtrl)->GetValue(), m_d))
544             return false;
545         if (!hugin_utils::str2double(XRCCTRL(*this, "lenscal_e", wxTextCtrl)->GetValue(), m_e))
546             return false;
547     }
548     return true;
549 };
550 
OnFindLines(wxCommandEvent & e)551 void LensCalFrame::OnFindLines(wxCommandEvent &e)
552 {
553     if(!ReadInputs(true,true,false))
554     {
555         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."),_("Warning"),wxOK | wxICON_INFORMATION, this);
556         return;
557     }
558     m_preview->SetLens(m_projection,m_focallength,m_cropfactor);
559     for(unsigned int i=0;i<m_images.size();i++)
560     {
561         std::string filename(m_images[i]->GetFilename().mb_str(HUGIN_CONV_FILENAME));
562         ImageCache::EntryPtr img=ImageCache::getInstance().getImage(filename);
563         double scale;
564         SetStatusText(_("Detecting edges..."));
565         m_images[i]->SetEdgeImage(HuginLines::detectEdges(*(img->get8BitImage()),m_edge_scale,m_edge_threshold,m_resize_dimension,scale));
566         SetStatusText(_("Finding lines..."));
567         m_images[i]->SetLines(HuginLines::findLines(*(m_images[i]->GetEdgeImage()),m_minlinelength,m_focallength,m_cropfactor));
568         m_images[i]->ScaleLines(scale);
569     };
570     SetStatusText(_("Finished"));
571     UpdateList(true);
572 };
573 
OnOptimize(wxCommandEvent & e)574 void LensCalFrame::OnOptimize(wxCommandEvent &e)
575 {
576     if(!ReadInputs(true,false,true))
577     {
578         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."),_("Warning"),wxOK | wxICON_INFORMATION, this);
579         return;
580     }
581     unsigned int count=0;
582     for(unsigned int i=0;i<m_images.size();i++)
583         count+=m_images[i]->GetNrOfValidLines();
584     if(count==0)
585     {
586         wxMessageBox(_("There are no detected lines.\nPlease run \"Find lines\" first. If there are no lines found, change the parameters."),_("Warning"),wxOK  | wxICON_INFORMATION, this);
587         return;
588     };
589     Optimize();
590 };
591 
OnShowDistortionGraph(wxCommandEvent & e)592 void LensCalFrame::OnShowDistortionGraph(wxCommandEvent &e)
593 {
594     if (m_images.empty())
595     {
596         return;
597     };
598     if (!ReadInputs(true, false, true))
599     {
600         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."), _("Warning"), wxOK | wxICON_INFORMATION, this);
601         return;
602     };
603     delete m_popup;
604     HuginBase::SrcPanoImage image(*(m_images[0]->GetPanoImage()));
605     image.setProjection(m_projection);
606     image.setExifFocalLength(m_focallength);
607     image.setCropFactor(m_cropfactor);
608     image.setVar("a", m_a);
609     image.setVar("b", m_b);
610     image.setVar("c", m_c);
611     image.setVar("d", m_d);
612     image.setVar("e", m_e);
613     image.setHFOV(HuginBase::SrcPanoImage::calcHFOV(image.getProjection(), image.getExifFocalLength(), image.getCropFactor(), image.getSize()));
614 
615     m_popup=new wxGraphTools::GraphPopupWindow(this, wxGraphTools::GetDistortionGraph(image));
616     wxWindow *button = (wxWindow*)e.GetEventObject();
617     wxPoint pos = button->ClientToScreen(wxPoint(0, 0));
618     m_popup->Position(pos, button->GetSize());
619     m_popup->Popup();
620 };
621 
GetPanorama()622 HuginBase::Panorama LensCalFrame::GetPanorama()
623 {
624     HuginBase::Panorama pano;
625     HuginBase::OptimizeVector optvec;
626     unsigned int line_number=3;
627     for(unsigned int i=0;i<m_images.size();i++)
628     {
629         HuginBase::SrcPanoImage image(*(m_images[i]->GetPanoImage()));
630         image.setProjection(m_projection);
631         image.setExifFocalLength(m_focallength);
632         image.setCropFactor(m_cropfactor);
633         image.setVar("a",m_a);
634         image.setVar("b",m_b);
635         image.setVar("c",m_c);
636         image.setVar("d",m_d);
637         image.setVar("e",m_e);
638         double hfov = HuginBase::SrcPanoImage::calcHFOV(image.getProjection(), image.getExifFocalLength(), image.getCropFactor(), image.getSize());
639         image.setHFOV(hfov);
640         pano.addImage(image);
641         std::set<std::string> imgopt;
642         if(i==0)
643         {
644             if(XRCCTRL(*this,"lenscal_opt_a",wxCheckBox)->GetValue())
645                 imgopt.insert("a");
646             if(XRCCTRL(*this,"lenscal_opt_b",wxCheckBox)->GetValue())
647                 imgopt.insert("b");
648             if(XRCCTRL(*this,"lenscal_opt_c",wxCheckBox)->GetValue())
649                 imgopt.insert("c");
650             if(XRCCTRL(*this,"lenscal_opt_de",wxCheckBox)->GetValue())
651             {
652                 imgopt.insert("d");
653                 imgopt.insert("e");
654             }
655         };
656         optvec.push_back(imgopt);
657         //now generate control points from lines
658         HuginLines::Lines lines=m_images[i]->GetLines();
659         for(unsigned j=0;j<lines.size();j++)
660         {
661             if(lines[j].status==HuginLines::valid_line)
662             {
663                 HuginBase::CPVector cpv=GetControlPoints(lines[j],i,line_number,cps_per_line);
664                 for(unsigned int k=0;k<cpv.size();k++)
665                     pano.addCtrlPoint(cpv[k]);
666                 line_number++;
667             };
668         };
669     };
670     //assign all images the same lens number
671     HuginBase::StandardImageVariableGroups variable_groups(pano);
672     HuginBase::ImageVariableGroup & lenses = variable_groups.getLenses();
673     if(pano.getNrOfImages()>1)
674     {
675         for(unsigned int i=1;i<pano.getNrOfImages();i++)
676         {
677             HuginBase::SrcPanoImage img = pano.getSrcImage(i);
678             lenses.switchParts(i,lenses.getPartNumber(0));
679             img.setExposureValue(0);
680             img.setWhiteBalanceRed(1);
681             img.setWhiteBalanceBlue(1);
682             pano.setSrcImage(i, img);
683         };
684     };
685     //set default exposure value
686     HuginBase::PanoramaOptions opts = pano.getOptions();
687     opts.outputExposureValue = 0;
688     opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
689     pano.setOptions(opts);
690 
691     pano.setOptimizeVector(optvec);
692     return pano;
693 };
694 
Optimize()695 void LensCalFrame::Optimize()
696 {
697     SetStatusText(_("Optimizing lens distortion parameters..."));
698     HuginBase::Panorama pano = GetPanorama();
699     HuginBase::PTools::optimize(pano);
700 
701     const HuginBase::SrcPanoImage img = pano.getImage(0);
702     m_a=img.getVar("a");
703     m_b=img.getVar("b");
704     m_c=img.getVar("c");
705     m_d=img.getVar("d");
706     m_e=img.getVar("e");
707     XRCCTRL(*this,"lenscal_a",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_a,5));
708     XRCCTRL(*this,"lenscal_b",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_b,5));
709     XRCCTRL(*this,"lenscal_c",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_c,5));
710     XRCCTRL(*this,"lenscal_d",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_d,3));
711     XRCCTRL(*this,"lenscal_e",wxTextCtrl)->SetValue(hugin_utils::doubleTowxString(m_e,3));
712     m_preview->SetLensDistortions(m_a,m_b,m_c,m_d,m_e);
713     SetStatusText(_("Finished"));
714 };
715 
SaveLensToIni()716 void LensCalFrame::SaveLensToIni()
717 {
718     wxFileDialog dlg(this,
719                         _("Save lens parameters file"),
720                         wxConfigBase::Get()->Read(wxT("/lensPath"),wxT("")), wxT(""),
721                         _("Lens Project Files (*.ini)|*.ini|All files (*)|*"),
722                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT, wxDefaultPosition);
723     dlg.SetDirectory(wxConfigBase::Get()->Read(wxT("/lensPath"),wxT("")));
724     if (dlg.ShowModal() == wxID_OK)
725     {
726         wxFileName filename(dlg.GetPath());
727         if(!filename.HasExt())
728             filename.SetExt(wxT("ini"));
729         wxConfig::Get()->Write(wxT("/lensPath"), dlg.GetDirectory());  // remember for later
730         if (filename.FileExists())
731         {
732             int d = wxMessageBox(wxString::Format(_("File %s exists. Overwrite?"), filename.GetFullPath().c_str()),
733                                  _("Save project"), wxYES_NO | wxICON_QUESTION);
734             if (d != wxYES)
735             {
736                 return;
737             }
738         }
739         HuginBase::Panorama pano = GetPanorama();
740         SaveLensParameters(filename.GetFullPath(),&pano,0);
741     }
742 };
743 
OnSaveLens(wxCommandEvent & e)744 void LensCalFrame::OnSaveLens(wxCommandEvent &e)
745 {
746     if(!ReadInputs(true,false,true))
747     {
748         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."),_("Warning"),wxOK | wxICON_INFORMATION, this);
749         return;
750     }
751     unsigned int count=0;
752     for(unsigned int i=0;i<m_images.size();i++)
753         count+=m_images[i]->GetNrOfValidLines();
754     if(count==0)
755     {
756         wxMessageBox(_("There are no detected lines.\nPlease run \"Find lines\" and \"Optimize\" before saving the lens data. If there are no lines found, change the parameters."),_("Warning"),wxOK  | wxICON_INFORMATION, this);
757         return;
758     };
759 
760     wxArrayString choices;
761     choices.push_back(_("Save lens parameters to ini file"));
762     choices.push_back(_("Save lens parameters to lens database"));
763     wxSingleChoiceDialog save_dlg(this,_("Saving lens data"),_("Save lens"),choices);
764     if(save_dlg.ShowModal()==wxID_OK)
765     {
766         if(save_dlg.GetSelection()==0)
767         {
768             SaveLensToIni();
769         }
770         else
771         {
772             HuginBase::Panorama pano = GetPanorama();
773             SaveLensParameters(this,pano.getImage(0),false);
774         };
775     };
776 };
777 
OnSaveProject(wxCommandEvent & e)778 void LensCalFrame::OnSaveProject(wxCommandEvent &e)
779 {
780     if(!ReadInputs(true,false,true))
781     {
782         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."),_("Warning"),wxOK | wxICON_INFORMATION, this);
783         return;
784     }
785 
786     wxFileDialog dlg(this,_("Save project file"),wxEmptyString,wxEmptyString,
787                      _("Project files (*.pto)|*.pto|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, wxDefaultPosition);
788     dlg.SetDirectory(wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")));
789     if (dlg.ShowModal() == wxID_OK)
790     {
791         wxConfig::Get()->Write(wxT("/actualPath"), dlg.GetDirectory());  // remember for later
792         wxFileName filename(dlg.GetPath());
793         if(!filename.HasExt())
794             filename.SetExt(wxT("pto"));
795         if (filename.FileExists())
796         {
797             int d = wxMessageBox(wxString::Format(_("File %s exists. Overwrite?"), filename.GetFullPath().c_str()),
798                                  _("Save project"), wxYES_NO | wxICON_QUESTION);
799             if (d != wxYES)
800             {
801                 return;
802             }
803         }
804         HuginBase::Panorama pano = GetPanorama();
805         std::string path = hugin_utils::getPathPrefix(std::string(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME)));
806         std::ofstream script(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
807         script.exceptions ( std::ofstream::eofbit | std::ofstream::failbit | std::ofstream::badbit );
808         HuginBase::UIntSet all;
809         fill_set(all, 0, pano.getNrOfImages()-1);
810         pano.printPanoramaScript(script, pano.getOptimizeVector(), pano.getOptions(), all, false, path);
811         script.close();
812     };
813 };
814 
OnImageSelected(wxCommandEvent & e)815 void LensCalFrame::OnImageSelected(wxCommandEvent &e)
816 {
817     bool selected=m_images_list->GetSelection()!=wxNOT_FOUND;
818     XRCCTRL(*this,"lenscal_remove_image",wxButton)->Enable(selected);
819     if(selected)
820     {
821         m_preview->SetImage(m_images[m_images_list->GetSelection()],m_images_list->GetSelection());
822     }
823     else
824     {
825         m_preview->SetEmptyImage();
826     };
827 };
828 
OnSelectPreviewContent(wxCommandEvent & e)829 void LensCalFrame::OnSelectPreviewContent(wxCommandEvent &e)
830 {
831     m_preview->SetMode((LensCalImageCtrl::LensCalPreviewMode)e.GetSelection());
832     XRCCTRL(*this,"lenscal_refresh",wxButton)->Enable(m_preview->GetMode()==LensCalImageCtrl::mode_corrected);
833 };
834 
OnShowLines(wxCommandEvent & e)835 void LensCalFrame::OnShowLines(wxCommandEvent &e)
836 {
837     m_preview->SetShowLines(XRCCTRL(*this,"lenscal_show_lines",wxCheckBox)->GetValue());
838     m_preview->Refresh(true);
839 };
840 
OnReset(wxCommandEvent & e)841 void LensCalFrame::OnReset(wxCommandEvent &e)
842 {
843     m_edge_scale=DEFAULT_LENSCAL_SCALE;
844     m_edge_threshold=DEFAULT_LENSCAL_THRESHOLD;
845     m_resize_dimension=DEFAULT_RESIZE_DIMENSION;
846     m_minlinelength=DEFAULT_MINLINELENGTH;
847     ParametersToDisplay();
848 };
849 
OnRefresh(wxCommandEvent & e)850 void LensCalFrame::OnRefresh(wxCommandEvent &e)
851 {
852     if(!ReadInputs(true,false,true))
853     {
854         wxMessageBox(_("There are invalid values in the input boxes.\nPlease check your inputs."),_("Warning"),wxOK | wxICON_INFORMATION, this);
855         return;
856     }
857     m_preview->SetLens(m_projection,m_focallength,m_cropfactor);
858     m_preview->SetLensDistortions(m_a,m_b,m_c,m_d,m_e);
859 };
860 
861