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