Hugin trunk 0.1
Loading...
Searching...
No Matches
BrowseDialog.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
11/* This is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public
22 * License along with this software. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 */
26
27#include "hugin/BrowseDialog.h"
28#include "hugin/huginApp.h"
29#include "wx/dir.h"
30#include "wx/busyinfo.h"
32#include "hugin/MainFrame.h"
33#include "base_wx/wxcms.h"
34#include "base_wx/LensTools.h"
35#include "base_wx/platform.h"
36#include "base_wx/wxutils.h"
37
40
42class ThreadImage : public wxObject
43{
44public:
45 // explicit disable default contructor
46 ThreadImage() = delete;
47 // construct wxImage with given vigra image classes
48 ThreadImage(vigra::BRGBImage* thumbnail, vigra::BImage* mask)
49 {
50 // save pointer to raw image data for cleanup in destructor
52 m_mask = mask;
53 // construct wxImage, we keep track of the raw data
54 m_image=new wxImage(m_preview->width(), m_preview->height(), (unsigned char*)m_preview->data(), (unsigned char*)m_mask->data(), true);
55 }
56 // clean up
58 {
59 delete m_image;
60 delete m_preview;
61 delete m_mask;
62 }
63 // return pointer to wxImage
64 wxImage* GetwxImage() const { return m_image; }
65private:
67 vigra::BRGBImage* m_preview;
68 vigra::BImage* m_mask;
69};
70
72class ThumbnailThread : public wxThread
73{
74public:
83 {
85 // the thread is being destroyed; make sure not to leave dangling pointers around
87 }
88
89protected:
90 // main worker thread
91 virtual ExitCode Entry()
92 {
93 for (int i = 0; i < m_files.size(); ++i)
94 {
95 // generate thumbnail
96 vigra::BRGBImage* preview=new vigra::BRGBImage();
97 vigra::BImage* mask = new vigra::BImage();
98 vigra::ImageImportInfo::ICCProfile iccProfile;
99 if (!GenerateThumbnail(m_files[i].ToStdString(), m_thumbnailSize, *preview, *mask, iccProfile))
100 {
101 // generation of thumbnail failed, skip file
102 delete preview;
103 delete mask;
104 continue;
105 };
106 if (TestDestroy())
107 {
108 break;
109 };
110 // fit generated thumbnail into the full thumbnail rectangle
111 if (m_thumbnailSize.GetWidth() != preview->size().width() || m_thumbnailSize.GetHeight() != preview->size().height())
112 {
113 vigra::BRGBImage* scaledPreview = new vigra::BRGBImage(vigra::Diff2D(m_thumbnailSize.GetWidth(), m_thumbnailSize.GetHeight()), vigra::RGBValue<vigra::UInt8>(255, 255, 255));
114 vigra::BImage* scaledMask = new vigra::BImage(scaledPreview->size(), vigra::UInt8(0));
115 vigra::Point2D offset((scaledPreview->width() - preview->width()) / 2, (scaledPreview->height() - preview->height()) / 2);
116 vigra::copyImage(vigra::srcImageRange(*preview), vigra::destImage(*scaledPreview, offset));
117 vigra::copyImage(vigra::srcImageRange(*mask), vigra::destImage(*scaledMask, offset));
118 delete preview;
119 delete mask;
120 preview = scaledPreview;
121 mask = scaledMask;
122 }
123 // convert vigra image to wxImage
124 ThreadImage* thumbnailImageData = new ThreadImage(preview, mask);
125 // notify dialog about new available thumbnail
126 wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE);
127 event->SetInt(i);
128 event->SetEventObject(thumbnailImageData);
129 // check if we need to cancel
130 if (TestDestroy())
131 {
132 // we are destroying the thread
133 // clean up all unneeded pointer
134 delete event;
135 delete thumbnailImageData;
136 break;
137 }
138 else
139 {
140 // all ok, notify dialog to update its image list
142 };
143 };
144 return (wxThread::ExitCode)0;
145 }
146
150};
151
152#define THUMBNAIL_SIZE 128
153
155{
156 return GPSLongitude != DBL_MAX && GPSLatitude != DBL_MAX;
157}
158
160{
161 wxString s;
162 // because we are resuing already existing translation we need to explicit add whitespaces
163 s << _("Number of active images:") << " " << nrActiveImages << "\n";
164 s << _("Projection:") << " " << projectionName << "\n";
165 s << wxString::Format(_("FOV: %.0fx%.0f"), options.getHFOV(), options.getVFOV()) << "\n";
166 s << _("Focal length:") << " " << focalLengthString;
167 return s;
168}
169
171{
172 // load our children. some children might need special
173 // initialization. this will be done later.
174 wxXmlResource::Get()->LoadDialog(this, parent, "browse_pto_dialog");
175
176 m_dirCtrl = XRCCTRL(*this, "browse_dirctrl", wxGenericDirCtrl);
178 m_listCtrl = XRCCTRL(*this, "browse_listctrl", wxListCtrl);
181 m_previewCtrl = XRCCTRL(*this, "browse_preview", wxStaticBitmap);
182 m_splitter1 = XRCCTRL(*this, "browse_splitter1", wxSplitterWindow);
183 m_splitter2 = XRCCTRL(*this, "browse_splitter2", wxSplitterWindow);
184 m_showMap = XRCCTRL(*this, "browse_show_map", wxButton);
185 m_labelControl = XRCCTRL(*this, "browse_statictext", wxStaticText);
186 m_labelControl->SetFont(m_labelControl->GetFont().Larger().Larger());
191 m_listCtrl->InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, 300);
192 m_listCtrl->InsertColumn(1, _("# images"), wxLIST_FORMAT_RIGHT, 50);
193 m_listCtrl->InsertColumn(2, _("# active images"), wxLIST_FORMAT_RIGHT, 50);
194 m_listCtrl->InsertColumn(3, _("Projection"), wxLIST_FORMAT_LEFT, 200);
195 m_listCtrl->InsertColumn(4, _("Field of view"), wxLIST_FORMAT_LEFT, 100);
196 m_listCtrl->InsertColumn(5, _("Canvas size"), wxLIST_FORMAT_LEFT, 100);
197 m_listCtrl->InsertColumn(6, _("Model"), wxLIST_FORMAT_LEFT, 200);
198 m_listCtrl->InsertColumn(7, _("Lens"), wxLIST_FORMAT_LEFT, 250);
199 m_listCtrl->InsertColumn(8, _("Focal length"), wxLIST_FORMAT_LEFT, 150);
200 m_listCtrl->InsertColumn(9, _("Capture date"), wxLIST_FORMAT_LEFT, 150);
201 m_listCtrl->InsertColumn(10, _("Duration"), wxLIST_FORMAT_LEFT, 50);
202 m_listType = XRCCTRL(*this, "browse_list_type", wxChoice);
204 // restore some settings
205 hugin_utils::RestoreFramePosition(this, "BrowsePTODialog");
206 wxConfigBase* config = wxConfigBase::Get();
207 //splitter position
208 int splitter_pos = config->Read("/BrowsePTODialog/splitterPos1", -1l);
209 if (splitter_pos > 0)
210 {
211 m_splitter1->SetSashPosition(splitter_pos);
212 };
213 splitter_pos = config->Read("/BrowsePTODialog/splitterPos2", -1l);
214 if (splitter_pos > 0)
215 {
216 m_splitter2->SetSashPosition(splitter_pos);
217 };
218 //get saved width
219 for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
220 {
221 // -1 is auto
222 int width = config->Read(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), -1);
223 if (width != -1 && width > 5)
224 {
225 m_listCtrl->SetColumnWidth(j, width);
226 };
227 };
228 m_sortCol = config->Read("/BrowsePTODialog/SortColumn", -1);
229 m_sortAscending = config->Read("/BrowsePTODialog/SortAscending", 1) == 1 ? true : false;
230 if (m_sortCol != -1)
231 {
232 m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
233 };
234
235 // fill values for start directory
236 if (!startDirectory.IsEmpty())
237 {
238 m_dirCtrl->SetPath(startDirectory);
239 };
240 long listType = config->Read("/BrowsePTODialog/ListType", 0l);
241 m_listType->SetSelection(listType);
242 wxCommandEvent event;
243 event.SetInt(listType);
246 Bind(wxEVT_BUTTON, &BrowsePTOFilesDialog::OnShowOnMap, this, XRCID("browse_show_map"));
249};
250
252{
253 // stop working thread
255 // save some settings
256 hugin_utils::StoreFramePosition(this, "BrowsePTODialog");
257 wxConfigBase* config = wxConfigBase::Get();
258 config->Write("/BrowsePTODialog/splitterPos1", m_splitter1->GetSashPosition());
259 config->Write("/BrowsePTODialog/splitterPos2", m_splitter2->GetSashPosition());
260 // save width of all columns in wxListCtrl
261 for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
262 {
263 config->Write(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), m_listCtrl->GetColumnWidth(j));
264 };
265 config->Write("/BrowsePTODialog/ListType", m_listType->GetSelection());
266 config->Write("/BrowsePTODialog/SortColumn", m_sortCol);
267 config->Write("/BrowsePTODialog/SortAscending", m_sortAscending ? 1 : 0);
268 config->Flush();
269}
270
272{
273 long index = -1;
274 if (m_listCtrl->GetSelectedItemCount() == 1)
275 {
276 index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
277 };
278 if (index >= 0 && index < m_ptoInfo.size())
279 {
280 return m_ptoInfo[TranslateIndex(index)].ptoFilename.GetFullPath();
281 }
282 else
283 {
284 return wxEmptyString;
285 };
286}
287
292
293void BrowsePTOFilesDialog::OnOk(wxCommandEvent& e)
294{
295 if (m_listCtrl->GetSelectedItemCount() == 1)
296 {
298 }
299 else
300 {
301 wxBell();
302 }
303}
304
306{
307 PanoInfo info;
308 info.ptoFilename = file;
309 // read pto file
311 const std::string input(file.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
313 {
314 return info;
315 };
316 // fill class to store some information about the pano
317 info.nrImages = pano.getNrOfImages();
318 info.nrActiveImages = pano.getActiveImages().size();
319 info.options = pano.getOptions();
320 // translate projection name
321 {
323 wxString s;
325 {
326 wxString str2(proj.name, wxConvLocal);
328 };
329 };
330 // read some EXIF data
331 const HuginBase::UIntSet activeImages = pano.getActiveImages();
332 if (!activeImages.empty())
333 {
334 HuginBase::SrcPanoImage img0 = pano.getSrcImage(*activeImages.begin());
335 if(!hugin_utils::FileExists(img0.getFilename()))
336 {
337 return info;
338 }
339 img0.readEXIF();
340 info.camera = img0.getExifModel();
341 info.lens = img0.getExifLens();
342 info.focalLength35 = img0.getExifFocalLength35();
343 if (info.focalLength35 < 0.01 && img0.getCropFactor()>0)
344 {
345 info.focalLength35 = img0.getExifFocalLength() * img0.getCropFactor();
346 };
347 if (info.focalLength35 < 0.01)
348 {
349 info.focalLength35 = HuginBase::SrcPanoImage::calcFocalLength(img0.getProjection(), img0.getHFOV(), 1.0, img0.getSize());
350 };
352 // read date/time of all active images
353 if (!img0.getExifDate().empty())
354 {
355 struct tm exifdatetime;
356 if (img0.getExifDateTime(&exifdatetime) == 0)
357 {
359 info.end = info.start;
360 };
361 // iterate all images, except the first one, this image is already handled before
362 HuginBase::UIntSet imgs(activeImages);
363 imgs.erase(imgs.begin());
364 for (const auto& img : imgs)
365 {
367 if (!hugin_utils::FileExists(imgSrcImage.getFilename()))
368 {
369 continue;
370 };
372 memset(&exifdatetime, 0, sizeof(exifdatetime));
373 if (!imgSrcImage.getExifDate().empty() && imgSrcImage.getExifDateTime(&exifdatetime) == 0)
374 {
376 if (info.start.IsValid())
377 {
378 if (dateTime.IsEarlierThan(info.start))
379 {
380 info.start = dateTime;
381 }
382 if (dateTime.IsLaterThan(info.end))
383 {
384 info.end = dateTime;
385 }
386 }
387 else
388 {
389 info.start = dateTime;
390 info.end = dateTime;
391 }
392 }
393 }
394 if (info.start.IsValid())
395 {
396 info.duration = info.end.Subtract(info.start);
397 }
398 };
399 const auto& fileMetadata = img0.getFileMetadata();
400 const auto& latitude = fileMetadata.find("latitude");
401 const auto& longitude = fileMetadata.find("longitude");
402 if (latitude != fileMetadata.end())
403 {
405 };
406 if (longitude != fileMetadata.end())
407 {
409 };
410 };
411 return info;
412}
413
414void BrowsePTOFilesDialog::FillPanoInfo(const PanoInfo& info, long index)
415{
416#ifndef __WXMSW__
417 // in generic implementation we can add text to column only in report view
418 if (m_listCtrl->InReportView())
419#endif
420 {
421 m_listCtrl->SetItem(index, 1, wxString::Format("%zu", info.nrImages));
422 m_listCtrl->SetItem(index, 2, wxString::Format("%zu", info.nrActiveImages));
423 m_listCtrl->SetItem(index, 3, info.projectionName);
424 m_listCtrl->SetItem(index, 4, wxString::Format("%.0f x %.0f", info.options.getHFOV(), info.options.getVFOV()));
425 m_listCtrl->SetItem(index, 5, wxString::Format("%u x %u", info.options.getWidth(), info.options.getHeight()));
426 m_listCtrl->SetItem(index, 6, info.camera);
427 m_listCtrl->SetItem(index, 7, info.lens);
428 m_listCtrl->SetItem(index, 8, info.focalLengthString);
429 if (info.start.IsValid())
430 {
431 m_listCtrl->SetItem(index, 9, info.start.Format());
432 };
433 m_listCtrl->SetItem(index, 10, hugin_utils::GetFormattedTimeSpan(info.duration));
434 };
435}
436
438{
439#ifndef __WXMSW__
440 m_listCtrl->DeleteAllItems();
441#endif
442 m_listCtrl->SetWindowStyle(newStyle);
443#ifndef __WXMSW__
444 // changing the window style does invalidate the items, so we delete all items
445 // and add then again
446 for (size_t i = 0; i < m_ptoInfo.size(); ++i)
447 {
448 m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
449 m_listCtrl->SetItemData(i, i);
451 };
452 SortItems();
453#endif
454}
455
457{
458 if (m_dirCtrl && m_listCtrl)
459 {
461 wxBusyInfo busy(wxBusyInfoFlags().Parent(this).Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Reading directory %s"), m_dirCtrl->GetPath().c_str())));
462 // stop running thumbnail thread from last directory if not finished yet
464 // find all pto files in directory
465 m_ptoInfo.clear();
466 m_thumbnails.RemoveAll();
467 SetTitle(wxString::Format(_("Browse project files in %s"), m_dirCtrl->GetPath().c_str()));
468 wxArrayString files;
469 wxDir::GetAllFiles(m_dirCtrl->GetPath(), &files, "*.pto", wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
470 // add to wxListCtrl
471 m_listCtrl->DeleteAllItems();
472 m_ptoInfo.resize(files.size());
473 for (size_t i = 0; i < files.size(); ++i)
474 {
475 const wxFileName filename(files[i]);
476 m_ptoInfo[i] = ParsePTOFile(filename);
477 m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
478 m_listCtrl->SetItemData(i, i);
480 };
481 SortItems();
482 // start background thread for creating all thumbnails
483 m_thumbnailThread = new ThumbnailThread(this, files, m_thumbnails.GetSize());
484 if (!m_listCtrl->InReportView())
485 {
486 m_thumbnailThread->Run();
487 };
488 // clear preview area
489 m_previewCtrl->SetBitmap(wxBitmap());
490 m_previewCtrl->Show(true);
491 m_labelControl->Show(false);
492 m_showMap->Show(false);
493 m_showMap->Enable(false);
494 m_showMap->GetParent()->Layout();
495 };
496}
497
499{
500 if (e.GetIndex() >= 0)
501 {
502 GeneratePreview(TranslateIndex(e.GetIndex()));
503 };
504}
505
507{
508 if (m_listCtrl->GetSelectedItemCount() == 1)
509 {
511 };
512}
513
515{
516 const int newCol = e.GetColumn();
517 if (m_sortCol == newCol)
518 {
520 }
521 else
522 {
524 m_sortAscending = true;
525 };
526 m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
527 SortItems();
528 Refresh();
529}
530
532{
533 // update toolstip only in icon view, not in report view
534 if (!m_listCtrl->InReportView() && !m_ptoInfo.empty())
535 {
536 int flags;
537 long item = m_listCtrl->HitTest(e.GetPosition(), flags);
538 if (item != wxNOT_FOUND && (flags & wxLIST_HITTEST_ONITEM) && item < m_ptoInfo.size())
539 {
540 m_listCtrl->SetToolTip(m_ptoInfo[TranslateIndex(item)].GetToolTip());
541 }
542 else
543 {
544 m_listCtrl->UnsetToolTip();
545 };
546 }
547 e.Skip();
548}
549
551{
552 // we have created a new thumbnail, make the wxListCtrl aware of it
553 const int index = e.GetInt();
554 ThreadImage* thumbnail= wxDynamicCast(e.GetEventObject(), ThreadImage);
555 m_ptoInfo[index].imageIndex = m_thumbnails.Add(*(thumbnail->GetwxImage()));
556 for (size_t i = 0; i < m_listCtrl->GetItemCount(); ++i)
557 {
558 if (m_listCtrl->GetItemData(i) == index)
559 {
560 m_listCtrl->SetItemImage(i, m_ptoInfo[index].imageIndex);
561 break;
562 };
563 };
564 delete thumbnail;
565#ifndef __WXMSW__
566 // a simple Refresh for repainting the control is not working
567 // all thumbnails are drawn on top of each other
568 // only when all done after a size event all is drawn correctly
569 m_listCtrl->SendSizeEvent();
570#endif
571}
572
574{
575 if (e.GetSelection() == 0)
576 {
578 // start creating thumbnail images in background thread
579 if (m_thumbnailThread && !m_thumbnailThread->IsRunning())
580 {
581 m_thumbnailThread->Run();
582 };
583 }
584 else
585 {
587 m_listCtrl->UnsetToolTip();
588 };
589#ifndef __WXMSW__
590 // a simple Refresh for repainting the control is not working
591 // all thumbnails are drawn on top of each other
592 // only when all done after a size event all is drawn correctly
593 m_listCtrl->SendSizeEvent();
594#endif
595}
596
598{
599 long index = -1;
600 if (m_listCtrl->GetSelectedItemCount() == 1)
601 {
602 index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
603 };
604 if (index >= 0 && index < m_ptoInfo.size())
605 {
606 if (m_ptoInfo[index].HasGPS())
607 {
608 const wxString openstreetMapLink = "https://www.openstreetmap.org/?mlat=" + wxString::FromCDouble(m_ptoInfo[index].GPSLatitude) + "&mlon=" + wxString::FromCDouble(m_ptoInfo[index].GPSLongitude);
610 }
611 }
612}
613
615{
616 return m_listCtrl->GetItemData(index);
617}
618
619template <class Type>
620int GreaterComparisonOperator(const Type& a, const Type& b)
621{
622 if (a > b)
623 {
624 return 1;
625 }
626 else
627 {
628 if (a < b)
629 {
630 return -1;
631 }
632 else {
633 return 0;
634 };
635 }
636}
637
638template <class Type>
639int SmallerComparisonOperator(const Type& a, const Type& b)
640{
641 if (a < b)
642 {
643 return 1;
644 }
645 else
646 {
647 if (a > b)
648 {
649 return -1;
650 }
651 else {
652 return 0;
653 };
654 }
655}
656
657#define SORTASCENDING(functionName, var) \
658int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
659{\
660 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
661 return GreaterComparisonOperator(data->at(item1).var, data->at(item2).var);\
662}
663#define SORTDESCENDING(functionName, var) \
664int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
665{\
666 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
667 return SmallerComparisonOperator(data->at(item1).var, data->at(item2).var);\
668}
669
670SORTASCENDING(SortFilenameAscending, ptoFilename.GetFullName())
682
683#undef SORTASCENDING
684#undef SORTDESCENDING
685
686// special variant for wxStrings
687#define SORTASCENDING(functionName, var) \
688int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
689{\
690 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
691 return data->at(item1).var.CmpNoCase(data->at(item2).var);\
692}
693#define SORTDESCENDING(functionName, var) \
694int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
695{\
696 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
697 return -(data->at(item1).var.CmpNoCase(data->at(item2).var));\
698}
703
704#undef SORTASCENDING
705#undef SORTDESCENDING
706
707// sort by field of view
709{
710 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
711 const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
712 const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
714}
715
717{
718 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
719 const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
720 const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
722}
723
724// sort by date
726{
727 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
728 if (data->at(item1).start.IsLaterThan(data->at(item2).start))
729 {
730 return 1;
731 }
732 if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
733 {
734 return -1;
735 }
736 return 0;
737};
738
740{
741 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
742 if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
743 {
744 return 1;
745 }
746 if (data->at(item1).start.IsLaterThan(data->at(item2).start))
747 {
748 return -1;
749 }
750 return 0;
751};
752
753// sort by duration
755{
756 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
757 if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
758 {
759 return 1;
760 }
761 if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
762 {
763 return -1;
764 }
765 return 0;
766};
767
769{
770 std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
771 if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
772 {
773 return 1;
774 }
775 if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
776 {
777 return -1;
778 }
779 return 0;
780};
781
783{
784 if (m_sortCol > -1)
785 {
786 switch (m_sortCol)
787 {
788 default:
789 case 0: // filename
790 if (m_sortAscending)
791 {
793 }
794 else
795 {
797 };
798 break;
799 case 1: // number of images
800 if (m_sortAscending)
801 {
803 }
804 else
805 {
807 };
808 break;
809 case 2: // number of active images
810 if (m_sortAscending)
811 {
813 }
814 else
815 {
817 };
818 break;
819 case 3: // projection
820 if (m_sortAscending)
821 {
823 }
824 else
825 {
827 };
828 break;
829 case 4: // field of view
830 if (m_sortAscending)
831 {
833 }
834 else
835 {
837 };
838 break;
839 case 5: // canvas size
840 if (m_sortAscending)
841 {
843 }
844 else
845 {
847 };
848 break;
849 case 6: // model
850 if (m_sortAscending)
851 {
853 }
854 else
855 {
857 };
858 break;
859 case 7: // lens
860 if (m_sortAscending)
861 {
863 }
864 else
865 {
867 };
868 break;
869 case 8: // focal length
870 if (m_sortAscending)
871 {
873 }
874 else
875 {
877 };
878 break;
879 case 9: // capture date
880 if (m_sortAscending)
881 {
883 }
884 else
885 {
887 };
888 break;
889 case 10: // duration
890 if (m_sortAscending)
891 {
893 }
894 else
895 {
897 };
898 break;
899 };
900 };
901};
902
904{
905 // delete running background thread
907 {
908 bool deleteStoppedThread = false;
909 {
912 {
913 if (m_thumbnailThread->IsRunning())
914 {
915 m_thumbnailThread->Delete();
916 }
917 else
918 {
919 deleteStoppedThread = true;
920 };
921 };
922 };
924 {
925 delete m_thumbnailThread;
926 };
927 };
928 // exit from the critical section to give the thread
929 // the possibility to enter its destructor
930 // (which is guarded with m_ThreadCS critical section!)
931 while (1)
932 {
933 {
936 {
937 break;
938 };
939 };
940 // wait for thread completion
941 wxThread::This()->Sleep(1);
942 };
943}
944
946{
948 {
949 return;
950 };
952 wxBusyInfo busyInfo(wxBusyInfoFlags().Parent(this).Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Generating preview for %s"), m_ptoInfo[index].ptoFilename.GetFullName().c_str())));
953 wxSize previewSize = m_previewCtrl->GetParent()->GetSize();
954 vigra::BRGBImage preview;
955 vigra::BImage mask;
956 vigra::ImageImportInfo::ICCProfile iccProfile;
957 if (GenerateThumbnail(m_ptoInfo[index].ptoFilename.GetFullPath().ToStdString(), previewSize, preview, mask, iccProfile))
958 {
959 wxImage image(preview.width(), preview.height(), (unsigned char*)preview.data(), (unsigned char*)mask.data(), true);
960 // now apply color profile
961 if (!iccProfile.empty() || huginApp::Get()->HasMonitorProfile())
962 {
963 HuginBase::Color::CorrectImage(image, iccProfile, huginApp::Get()->GetMonitorProfile());
964 };
965 // now show in GUI
966 m_previewCtrl->SetBitmap(image);
967 m_previewCtrl->Show(true);
968 m_labelControl->Show(false);
969 m_showMap->Show(m_ptoInfo[index].HasGPS());
970 m_showMap->Enable(m_ptoInfo[index].HasGPS());
971 m_showMap->GetParent()->Layout();
972 }
973 else
974 {
975 // could not create preview, disable control and show error message
976 m_previewCtrl->Show(false);
977 m_labelControl->Show(true);
978 m_showMap->Show(false);
979 m_showMap->Enable(false);
980 m_showMap->GetParent()->Layout();
981 };
982 Update();
983}
int wxCALLBACK SortDateAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
int GreaterComparisonOperator(const Type &a, const Type &b)
int SmallerComparisonOperator(const Type &a, const Type &b)
wxDEFINE_EVENT(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, wxCommandEvent)
int wxCALLBACK SortDateDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
wxDECLARE_EVENT(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, wxCommandEvent)
int wxCALLBACK SortFieldOfViewDescending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
#define SORTDESCENDING(functionName, var)
#define THUMBNAIL_SIZE
const float fieldOfView2
#define SORTASCENDING(functionName, var)
int wxCALLBACK SortDurationAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
options wxIntPtr wxIntPtr sortData
options wxIntPtr item2
options getSize().area()) int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1
const float fieldOfView1
int wxCALLBACK SortDurationDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
Definition of dialog to browse directory with pto files.
bool GenerateThumbnail(const std::string pto_filename, const wxSize size, vigra::BRGBImage &panoImage, vigra::BImage &panoMask, vigra::ImageImportInfo::ICCProfile &iccProfile)
generate thumbnail image for given pto_filename
generate thumbnail from given pto file
some helper classes for graphes
Dialog for browsing pto files.
friend class ThumbnailThread
void OnShowOnMap(wxCommandEvent &e)
show current pano on Openstreetmap
wxString GetSelectedPath()
return last selected path
BrowsePTOFilesDialog(wxWindow *parent, const wxString startDirectory)
Constructor, read from xrc ressource; restore last uses settings and position.
void GeneratePreview(int index)
generate preview for pto file with index
void OnDirectoryChanged(wxTreeEvent &e)
directory changed, load files from new directory
std::vector< PanoInfo > m_ptoInfo
info about the pto file
wxSplitterWindow * m_splitter2
wxStaticText * m_labelControl
void UpdateItemTexts(long newStyle)
update all item texts
ThumbnailThread * m_thumbnailThread
background thumbnail creater thread
void FillPanoInfo(const PanoInfo &info, long index)
add a new item to wxListCtrl and populate all columns
void OnThumbnailUpdate(wxCommandEvent &e)
for notifing from ThumbnailThread about new generated thumbnail generated thumbnail is transfered in ...
wxImageList m_thumbnails
image list with all thumbnails
void OnListMouseMove(wxMouseEvent &e)
mouse move handler to update tooltips
wxListCtrl * m_listCtrl
wxGenericDirCtrl * m_dirCtrl
wxString GetSelectedProject()
return full path of selected project
void OnFileChanged(wxListEvent &e)
new file selected, generate preview for new file
long TranslateIndex(const long index)
translate the index to the index of m_ptoInfo
void EndThumbnailThread()
end background thumbnail creating thread
void SortItems()
sort the items according to selected column
void OnListColClick(wxListEvent &e)
click on header to sort by column
~BrowsePTOFilesDialog()
destructor, saves position
wxStaticBitmap * m_previewCtrl
void OnListTypeChanged(wxCommandEvent &e)
Change display of wxListCtrl.
void OnDblClickListCtrl(wxMouseEvent &e)
double click does open file
wxSplitterWindow * m_splitter1
PanoInfo ParsePTOFile(const wxFileName file)
read the given pto file and add all information to wxListCtrl
wxCriticalSection m_ThreadCS
critical section to synchronize with ThumbnailThread
void OnOk(wxCommandEvent &e)
Saves current expression when closing dialog with Ok.
unsigned int getWidth() const
PanoramaOptions::ProjectionFormat getProjection() const
unsigned int getHeight() const
get panorama height
Model for a panorama.
Definition Panorama.h:153
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
UIntSet getActiveImages() const
get active images
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
static double calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
calcualte focal length, given crop factor and hfov
bool readEXIF()
try to fill out information about the image, by examining the exif data
static MainFrame * Get()
hack.. kind of a pseudo singleton...
class to store some information about a pto file on disc
wxTimeSpan duration
HuginBase::PanoramaOptions options
wxFileName ptoFilename
wxString focalLengthString
size_t nrImages
wxDateTime start
double focalLength35
wxString GetToolTip()
wxDateTime end
wxString lens
wxString camera
wxString projectionName
bool HasGPS()
double GPSLongitude
double GPSLatitude
size_t nrActiveImages
helper class to transfer thumbnail data from worker thread to GUI thread
wxImage * GetwxImage() const
ThreadImage(vigra::BRGBImage *thumbnail, vigra::BImage *mask)
vigra::BImage * m_mask
vigra::BRGBImage * m_preview
ThreadImage()=delete
wxImage * m_image
background thread to generate thumbnails of all pto files
BrowsePTOFilesDialog * m_parentDialog
wxArrayString m_files
ThumbnailThread(BrowsePTOFilesDialog *parentDialog, const wxArrayString &fileList, const wxSize size)
virtual ExitCode Entry()
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
Definition huginApp.h:120
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition huginApp.cpp:651
static uint16_t flags
implementation of huginApp Class
#define HUGIN_CONV_FILENAME
Definition platform.h:40
wxString GetFocalLength(const HuginBase::SrcPanoImage *img)
return focallength and focallength 35 mm as wxString
void CorrectImage(wxImage &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsHPROFILE &monitorProfile)
apply color correction to given image using input iccProfile and monitor profile
Definition wxcms.cpp:218
std::set< unsigned int > UIntSet
WXIMPEX wxString GetFormattedTimeSpan(const wxTimeSpan &timeSpan)
Definition wxutils.cpp:29
bool FileExists(const std::string &filename)
checks if file exists
Definition utils.cpp:362
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition utils.h:114
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition wxutils.cpp:106
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename, const bool ignoreMaximize)
Definition wxutils.cpp:67
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41