Hugin trunk 0.1
Loading...
Searching...
No Matches
CPEditorPanel.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
27#include "hugin_config.h"
28
29// often necessary before panoinc.h
30#include "panoinc_WX.h"
31// standard hugin include
32#include "panoinc.h"
33// both includes above need to come before other wx includes on OSX
34
35// hugin's
36#include "hugin/huginApp.h"
40#include "hugin/CPImageCtrl.h"
42#include "hugin/CPEditorPanel.h"
47#include "base_wx/PTWXDlg.h"
48#include "base_wx/wxPlatform.h"
49#include "base_wx/wxutils.h"
50
51// more standard includes if needed
52#include <algorithm>
53#include <float.h>
54#include <vector>
55
56// more vigra include if needed
57#include "vigra/cornerdetection.hxx"
58#include "vigra/localminmax.hxx"
61#include "vigra_ext/cms.h"
62
63// Celeste header
64#include "Celeste.h"
65
67{
68 DEBUG_TRACE("**********************");
69 m_pano = 0;
70 m_countCP = 0;
71}
72
74 const wxPoint& pos,
75 const wxSize& size,
76 long style,
77 const wxString& name)
78{
79 DEBUG_TRACE(" Create called *************");
80 if (! wxPanel::Create(parent, id, pos, size, style, name) ) {
81 return false;
82 }
83
93
94 DEBUG_TRACE("");
95 wxXmlResource::Get()->LoadPanel(this, "cp_editor_panel");
96 wxPanel * panel = XRCCTRL(*this, "cp_editor_panel", wxPanel);
97
99 topsizer->Add(panel, 1, wxEXPAND, 0);
100
101 m_leftChoice = XRCCTRL(*this, "cp_editor_left_choice", CPImagesComboBox);
103 m_leftImg = XRCCTRL(*this, "cp_editor_left_img", CPImageCtrl);
105 m_leftImg->Init(this);
108
109 // right image
110 m_rightChoice = XRCCTRL(*this, "cp_editor_right_choice", CPImagesComboBox);
112 m_rightImg = XRCCTRL(*this, "cp_editor_right_img", CPImageCtrl);
114 m_rightImg->Init(this);
117
118 // setup list view
119 m_cpList = XRCCTRL(*this, "cp_editor_cp_list", wxListCtrl);
120 m_cpList->InsertColumn( 0, _("#"), wxLIST_FORMAT_RIGHT, 35);
121 m_cpList->InsertColumn( 1, _("left x"), wxLIST_FORMAT_RIGHT, 65);
122 m_cpList->InsertColumn( 2, _("left y"), wxLIST_FORMAT_RIGHT, 65);
123 m_cpList->InsertColumn( 3, _("right x"), wxLIST_FORMAT_RIGHT, 65);
124 m_cpList->InsertColumn( 4, _("right y"), wxLIST_FORMAT_RIGHT, 65);
125 m_cpList->InsertColumn( 5, _("Alignment"), wxLIST_FORMAT_LEFT,110 );
126 m_cpList->InsertColumn( 6, _("Distance"), wxLIST_FORMAT_RIGHT, 110);
132
133 //get saved width
134 wxConfigBase* config = wxConfig::Get();
135 for ( int j=0; j < m_cpList->GetColumnCount() ; j++ )
136 {
137 // -1 is auto
138 int width = config->Read(wxString::Format( "/CPEditorPanel/ColumnWidth%d", j ), -1);
139 if(width != -1)
140 m_cpList->SetColumnWidth(j, width);
141 }
142 m_sortCol = config->Read("/CPEditorPanel/SortColumn", -1);
143 m_sortAscending = config->Read("/CPEditorPanel/SortAscending", 1) == 1 ? true : false;
144 if (m_sortCol != -1)
145 {
146 m_cpList->ShowSortIndicator(m_sortCol, m_sortAscending);
147 };
148
149 // other controls
150 m_x1Text = XRCCTRL(*this,"cp_editor_x1", wxTextCtrl);
151 m_x1Text->PushEventHandler(new TextKillFocusHandler(this));
153 m_y1Text = XRCCTRL(*this,"cp_editor_y1", wxTextCtrl);
154 m_y1Text->PushEventHandler(new TextKillFocusHandler(this));
156 m_x2Text = XRCCTRL(*this,"cp_editor_x2", wxTextCtrl);
157 m_x2Text->PushEventHandler(new TextKillFocusHandler(this));
159 m_y2Text = XRCCTRL(*this,"cp_editor_y2", wxTextCtrl);
160 m_y2Text->PushEventHandler(new TextKillFocusHandler(this));
162
163 m_cpModeChoice = XRCCTRL(*this, "cp_editor_mode", wxChoice);
165 m_addButton = XRCCTRL(*this, "cp_editor_add", wxButton);
167 m_delButton = XRCCTRL(*this, "cp_editor_delete", wxButton);
169
170 m_autoAddCB = XRCCTRL(*this,"cp_editor_auto_add", wxCheckBox);
172 m_fineTuneCB = XRCCTRL(*this,"cp_editor_fine_tune_check",wxCheckBox);
174
175 m_estimateCB = XRCCTRL(*this,"cp_editor_auto_estimate", wxCheckBox);
177
178 m_showLinesCB = XRCCTRL(*this, "cp_editor_show_lines", wxCheckBox);
181
182 m_finetuneButton = XRCCTRL(*this, "cp_editor_finetune_button", wxButton);
184 m_prevImgButton = XRCCTRL(*this, "cp_editor_previous_img", wxButton);
186 m_nextImgButton = XRCCTRL(*this, "cp_editor_next_img", wxButton);
188
189 m_actionButton = XRCCTRL(*this, "cp_editor_action_button", wxButton);
192 m_cpActionContextMenu = wxXmlResource::Get()->LoadMenu("cp_menu_action");
193 // setup scroll window for the controls under the images
194 m_cp_ctrls = XRCCTRL(*this, "cp_controls_panel", wxPanel);
196
197 m_zoomChoice = XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice);
199
200 m_autoAddCB->SetValue(config->Read("/CPEditorPanel/autoAdd",0l) != 0 );
201 m_fineTuneCB->SetValue(config->Read("/CPEditorPanel/autoFineTune",1l) != 0 );
202 m_estimateCB->SetValue(config->Read("/CPEditorPanel/autoEstimate",1l) != 0 );
203 m_showLinesCB->SetValue(config->Read("/CPEditorPanel/showLines", 1l) != 0);
204 m_leftImg->ShowLines(m_showLinesCB->IsChecked());
205 m_rightImg->ShowLines(m_showLinesCB->IsChecked());
206
207 // disable controls by default
208 m_cpModeChoice->Disable();
209 m_addButton->Disable();
210 m_delButton->Disable();
211 m_autoAddCB->Disable();
212 m_fineTuneCB->Disable();
213 m_estimateCB->Disable();
214 m_showLinesCB->Disable();
215 m_finetuneButton->Disable();
216 m_actionButton->Disable();
217 m_zoomChoice->Disable();
218 m_prevImgButton->Disable();
219 m_nextImgButton->Disable();
220 m_leftChoice->Disable();
221 m_rightChoice->Disable();
222
223 // apply zoom specified in xrc file
224 wxCommandEvent dummy;
225 dummy.SetInt(m_zoomChoice->GetSelection());
226 OnZoom(dummy);
227
229 // read last used action setting
230 m_cpActionButtonMode = static_cast<CPTabActionButtonMode>(config->Read("/CPEditorPanel/ActionMode", 1l));
231 switch (m_cpActionButtonMode)
232 {
234 m_cpActionContextMenu->Check(XRCID("cp_menu_create_cp"), true);
235 {
236 wxCommandEvent e;
238 };
239 break;
241 m_cpActionContextMenu->Check(XRCID("cp_menu_clean_cp"), true);
242 {
243 wxCommandEvent e;
245 };
246 break;
248 default:
249 m_cpActionContextMenu->Check(XRCID("cp_menu_celeste"), true);
250 {
251 wxCommandEvent e;
253 };
254 break;
255 };
257 Bind(wxEVT_CHAR, &CPEditorPanel::OnKey, this);
258 Bind(wxEVT_MENU, &CPEditorPanel::OnActionSelectCreate, this, XRCID("cp_menu_create_cp"));
259 Bind(wxEVT_MENU, &CPEditorPanel::OnActionSelectCeleste, this, XRCID("cp_menu_celeste"));
260 Bind(wxEVT_MENU, &CPEditorPanel::OnActionSelectCleanCP, this, XRCID("cp_menu_clean_cp"));
261
262 return true;
263}
264
266{
267 m_pano=pano;
268 // observe the panorama
269 m_pano->addObserver(this);
270}
271
273{
274 DEBUG_TRACE("dtor");
275
276 m_x1Text->PopEventHandler(true);
277 m_y1Text->PopEventHandler(true);
278 m_x2Text->PopEventHandler(true);
279 m_y2Text->PopEventHandler(true);
280
281 wxConfigBase* config = wxConfig::Get();
282 config->Write("/CPEditorPanel/autoAdd", m_autoAddCB->IsChecked() ? 1 : 0);
283 config->Write("/CPEditorPanel/autoFineTune", m_fineTuneCB->IsChecked() ? 1 : 0);
284 config->Write("/CPEditorPanel/autoEstimate", m_estimateCB->IsChecked() ? 1 : 0);
285 config->Write("/CPEditorPanel/showLines", m_showLinesCB->IsChecked() ? 1 : 0);
286 config->Write("/CPEditorPanel/SortColumn", m_sortCol);
287 config->Write("/CPEditorPanel/SortAscending", m_sortAscending ? 1 : 0);
288 config->Flush();
289
290 m_pano->removeObserver(this);
291 DEBUG_TRACE("dtor end");
292}
293
294void CPEditorPanel::setLeftImage(unsigned int imgNr)
295{
296 DEBUG_TRACE("image " << imgNr);
297 if (imgNr == UINT_MAX) {
299 m_leftImageNr = imgNr;
300 m_leftFile = "";
302 UpdateDisplay(true);
303 } else if (m_leftImageNr != imgNr) {
304 double yaw = const_map_get(m_pano->getImageVariables(imgNr),"y").getValue();
305 double pitch = const_map_get(m_pano->getImageVariables(imgNr),"p").getValue();
306 double roll = const_map_get(m_pano->getImageVariables(imgNr),"r").getValue();
307 m_leftRot = GetRot(yaw, pitch, roll);
308 m_leftImg->setImage(m_pano->getImage(imgNr).getFilename(), m_leftRot);
309 m_leftImageNr = imgNr;
310 if (m_leftChoice->GetSelection() != (int) imgNr) {
311 m_leftChoice->SetSelection(imgNr);
312 }
314 m_rightChoice->Refresh();
315 m_leftFile = m_pano->getImage(imgNr).getFilename();
317 UpdateDisplay(true);
318 }
320 // FIXME: lets hope that nobody holds references to these images..
321 ImageCache::getInstance().softFlush();
323}
324
325
326void CPEditorPanel::setRightImage(unsigned int imgNr)
327{
328 DEBUG_TRACE("image " << imgNr);
329 if (imgNr == UINT_MAX) {
331 m_rightImageNr = imgNr;
332 m_rightFile = "";
335 UpdateDisplay(true);
336 } else if (m_rightImageNr != imgNr) {
337 // set the new image
338 double yaw = const_map_get(m_pano->getImageVariables(imgNr),"y").getValue();
339 double pitch = const_map_get(m_pano->getImageVariables(imgNr),"p").getValue();
340 double roll = const_map_get(m_pano->getImageVariables(imgNr),"r").getValue();
341 m_rightRot = GetRot(yaw, pitch, roll);
342 m_rightImg->setImage(m_pano->getImage(imgNr).getFilename(), m_rightRot);
343 // select tab
344 m_rightImageNr = imgNr;
345 if (m_rightChoice->GetSelection() != (int) imgNr) {
346 m_rightChoice->SetSelection(imgNr);
347 }
349 m_leftChoice->Refresh();
350 m_rightFile = m_pano->getImage(imgNr).getFilename();
351 // update the rest of the display (new control points etc)
353 UpdateDisplay(true);
354 }
356
357 // FIXME: lets hope that nobody holds references to these images..
358 ImageCache::getInstance().softFlush();
360}
361
363{
364 // these transformations are passed to the CPImageCtrl where they are used for
365 // a) generating the lines, use the user selected output projection
366 // b) generating the warped magnifier, use a equirectangular projection
369
370 if(m_leftImageNr<m_pano->getNrOfImages())
371 {
375 // reset all positions
376 img.setYaw(0);
377 img.setPitch(0);
378 img.setRoll(0);
379 img.setX(0);
380 img.setY(0);
381 img.setZ(0);
382 img.setTranslationPlaneYaw(0);
383 img.setTranslationPlanePitch(0);
384 // calculate optimal scale factor
385 const double scale = 2.0 * HuginBase::CalculateOptimalScale::calcOptimalPanoScale(img, opts);
387 scaledOpts.setWidth(scale * scaledOpts.getWidth());
390 };
391 if(m_rightImageNr<m_pano->getNrOfImages())
392 {
396 // reset all positions
397 img.setYaw(0);
398 img.setPitch(0);
399 img.setRoll(0);
400 img.setX(0);
401 img.setY(0);
402 img.setZ(0);
403 img.setTranslationPlaneYaw(0);
404 img.setTranslationPlanePitch(0);
405 // calculate optimal scale factor
406 const double scale = 2.0 * HuginBase::CalculateOptimalScale::calcOptimalPanoScale(img, opts);
408 scaledOpts.setWidth(scale * scaledOpts.getWidth());
411 };
412}
413
414// define sorting callbacks
416{
417 if (item1 < item2)
418 return 1;
419 if (item1 > item2)
420 return -1;
421 return 0;
422}
423
425{
426 if (item1 > item2)
427 return 1;
428 if (item1 < item2)
429 return -1;
430 return 0;
431}
432
433#define SORTASCENDING(functionName, var) \
434int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
435{\
436 HuginBase::CPointVector* data = (HuginBase::CPointVector*)(sortData);\
437 if (data->at(item1).second.var > data->at(item2).second.var)\
438 return 1;\
439 if (data->at(item1).second.var < data->at(item2).second.var)\
440 return -1;\
441 return 0;\
442}
443
444#define SORTDESCENDING(functionName, var) \
445int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
446{\
447 HuginBase::CPointVector* data = (HuginBase::CPointVector*)(sortData); \
448 if (data->at(item1).second.var < data->at(item2).second.var)\
449 return 1; \
450 if (data->at(item1).second.var > data->at(item2).second.var)\
451 return -1; \
452 return 0; \
453}
454
467
468#undef SORTASCENDING
469#undef SORTDESCENDING
470
472{
473 if (m_sortCol > -1)
474 {
475 switch (m_sortCol)
476 {
477 case 0: // index
478 if (m_sortAscending)
479 {
480 m_cpList->SortItems(SortIndexAscending, 0);
481 }
482 else
483 {
484 m_cpList->SortItems(SortIndexDescending, 0);
485 };
486 break;
487 case 1: // x1
488 if (m_sortAscending)
489 {
491 }
492 else
493 {
495 };
496 break;
497 case 2: // y1
498 if (m_sortAscending)
499 {
501 }
502 else
503 {
505 };
506 break;
507 case 3: // x2
508 if (m_sortAscending)
509 {
511 }
512 else
513 {
515 };
516 break;
517 case 4: // y2
518 if (m_sortAscending)
519 {
521 }
522 else
523 {
525 };
526 break;
527 case 5: // mode
528 if (m_sortAscending)
529 {
531 }
532 else
533 {
535 };
536 break;
537 case 6: // error
538 if (m_sortAscending)
539 {
541 }
542 else
543 {
545 };
546 break;
547 };
548 };
549}
550
552{
553 DEBUG_TRACE("");
555 unsigned int nr = ev.getPointNr();
556 hugin_utils::FDiff2D point = ev.getPoint();
557 bool left (TRUE);
558 if (ev.GetEventObject() == m_leftImg) {
559 left = true;
560 } else if (ev.GetEventObject() == m_rightImg){
561 left = false;
562 } else {
563 DEBUG_FATAL("UNKNOWN SOURCE OF CPEvent");
564 }
565
566 switch (ev.getMode()) {
567 case CPEvent::NONE:
568 text = "NONE";
569 break;
571 NewPointChange(ev.getPoint(),left);
572 break;
574 // need to reset cpEditState
575 DEBUG_DEBUG("selected point " << nr);
576 {
577 // user clicked a cp in one windows
578 // scroll only the other window, the scrolling of the originating
579 // window is done in the CPImageCtrl::mouseReleaseLMBEvent or in
580 // CPEvent POINT_CHANGED
581 // we can't use SelectLocalPoint here because this interferes with
582 // selection in wxListCtrl
583 if (left)
584 {
586 }
587 else
588 {
590 };
591 const long selectedPoint = m_cpList->FindItem(-1, nr);
592 // SetItemState will send the EVT_LIST_ITEM_SELECTED event,
593 // the work is done there
595 };
597 break;
599 {
600 float vertBias = getVerticalCPBias();
603 cp.image2Nr=m_rightImageNr;
604 bool hor = std::abs(cp.x1 - cp.x2) > (std::abs(cp.y1 - cp.y2) * vertBias);
605 switch (m_leftRot)
606 {
609 if (hor)
611 else
613 break;
614 default:
615 if (hor)
617 else
619 break;
620 }
622 // create points
624 // select new control Point
625 unsigned int lPoint = m_pano->getNrOfCtrlPoints()-1;
628 MainFrame::Get()->SetStatusText(_("new control point added"));
631 break;
632 };
634 {
635 DEBUG_DEBUG("move point("<< nr << ")");
636 if (nr >= currentPoints.size()) {
637 DEBUG_ERROR("invalid point number while moving point")
638 return;
639 }
642 DEBUG_DEBUG("changing point to: " << cp.x1 << "," << cp.y1
643 << " " << cp.x2 << "," << cp.y2);
644
647 );
648
649 break;
650 }
652 {
654 DEBUG_DEBUG("right click -> adding point");
656 } else {
657 DEBUG_DEBUG("right click without two points..");
659 }
660 break;
661 }
663 {
665 {
667 };
668 break;
669 };
671 {
677 // after scrolling the canvas is redrawn, no need to
678 // force a redraw at the end of OnCPEvent
679 return;
680 }
681 break;
683 {
685 if(!currentPoints.empty())
686 {
687 wxRect rect=ev.getRect();
688 for(unsigned int i=0;i<currentPoints.size();i++)
689 {
692 {
693 //checking only normal control points
694 if(left)
695 {
696 if (rect.Contains(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1)))
697 {
699 };
700 }
701 else
702 {
703 if (rect.Contains(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2)))
704 {
706 };
707 };
708 };
709 };
710 };
712 if(!cpToRemove.empty())
713 {
715 };
716 break;
717 }
718 } //end switch
719 m_leftImg->Refresh();
720 m_rightImg->Refresh();
721}
722
723
725{
726 DEBUG_TRACE("");
730 point.image1Nr = m_leftImageNr;
731 point.x1 = p1.x;
732 point.y1 = p1.y;
733 point.image2Nr = m_rightImageNr;
734 point.x2 = p2.x;
735 point.y2 = p2.y;
736 if (point.image1Nr == point.image2Nr) {
737 if (m_cpModeChoice->GetSelection()>=3) {
738 // keep line until user chooses new mode
739 point.mode = m_cpModeChoice->GetSelection();
740 } else {
741 // Most projections will have a bias to creating vertical
742 // constraints.
743 float vertBias = getVerticalCPBias();
744 bool hor = std::abs(p1.x - p2.x) > (std::abs(p1.y - p2.y) * vertBias);
745 switch (m_leftRot) {
748 if (hor)
750 else
752 break;
753 default:
754 if (hor)
756 else
758 break;
759 }
760 }
761 } else {
763 }
764
766
767 // create points
770 );
771
772
773 // select new control Point
774 unsigned int lPoint = m_pano->getNrOfCtrlPoints() -1;
777 MainFrame::Get()->SetStatusText(_("new control point added"));
780}
781
782
784{
787 float bias;
788 switch (projFormat)
789 {
791 bias = 1.0;
792 break;
793 default:
794 bias = 2.0;
795 break;
796 }
797 return bias;
798}
799
800
802{
803 if (m_selectedPoint == UINT_MAX) {
804 // no point selected, no need to select one.
805 return;
806 }
807 const long selectedPoint = m_cpList->FindItem(-1, m_selectedPoint);
809 {
811 };
812
817 UpdateDisplay(false);
818}
819
821{
822 DEBUG_TRACE("selectLocalPoint(" << LVpointNr << ")");
823
824 if ( m_selectedPoint == LVpointNr) {
825 DEBUG_DEBUG("already selected");
828 return;
829 }
831
833 m_x1Text->SetValue(wxString::Format("%.2f",p.x1));
834 m_y1Text->SetValue(wxString::Format("%.2f",p.y1));
835 m_x2Text->SetValue(wxString::Format("%.2f",p.x2));
836 m_y2Text->SetValue(wxString::Format("%.2f",p.y2));
837 m_cpModeChoice->SetSelection(p.mode);
840 const long selectedPoint = m_cpList->FindItem(-1, LVpointNr);
841 if (scrollLeft && scrollRight)
842 {
843 // SetItemState will generate a EVT_LIST_ITEM_SELECTED event
844 // do so only if both images are scrolled
846 };
847 m_cpList->EnsureVisible(selectedPoint);
848
849 EnablePointEdit(true);
850}
851
853{
854 unsigned int localNr;
856 DEBUG_DEBUG("CPEditor::setGlobalPoint(" << globalNr << ") found local point " << localNr);
858 } else {
859 DEBUG_ERROR("CPEditor::setGlobalPoint: point " << globalNr << " not found in currentPoints");
860 }
861}
862
863bool CPEditorPanel::globalPNr2LocalPNr(unsigned int & localNr, unsigned int globalNr) const
864{
865 HuginBase::CPointVector::const_iterator it = currentPoints.begin();
866
867 while(it != currentPoints.end() && (*it).first != globalNr) {
868 ++it;
869 }
870
871 if (it != currentPoints.end()) {
872 localNr = it - currentPoints.begin();
873 return true;
874 } else {
875 return false;
876 }
877}
878
879unsigned int CPEditorPanel::localPNr2GlobalPNr(unsigned int localNr) const
880{
881 assert(localNr < currentPoints.size());
882 return currentPoints[localNr].first;
883}
884
885
887 bool left,
889 unsigned int thisImgNr,
893 unsigned int otherImgNr,
896{
898 op = EstimatePoint(hugin_utils::FDiff2D(p.x, p.y), left);
899 // check if point is in image.
901 if (op.x < (int) pImg.getSize().width() && op.x >= 0
902 && op.y < (int) pImg.getSize().height() && op.y >= 0)
903 {
904 otherImg->setNewPoint(op);
905 // if fine-tune is checked, run a fine-tune session as well.
906 // hmm probably there should be another separate function for this..
907 if (m_fineTuneCB->IsChecked()) {
908 MainFrame::Get()->SetStatusText(_("searching similar points..."),0);
909 hugin_utils::FDiff2D newPoint = otherImg->getNewPoint();
910
911 long templWidth = wxConfigBase::Get()->Read("/Finetune/TemplateSize", HUGIN_FT_TEMPLATE_SIZE);
913 double sAreaPercent = wxConfigBase::Get()->Read("/Finetune/SearchAreaPercent",HUGIN_FT_SEARCH_AREA_PERCENT);
914 int sWidth = std::min((int)(img.getWidth() * sAreaPercent / 100.0), 500);
916 bool corrOk=false;
917 vigra::Diff2D roundp(p.toDiff2D());
918 try {
920 roundp,
923 newPoint,
924 sWidth,
925 corrPoint);
926 } catch (std::exception & e) {
927 hugin_utils::HuginMessageBox(wxString::Format(_("Error during Fine-tune (%s)"), wxString(e.what(), wxConvLocal)), _("Hugin"), wxOK, this);
928 }
929 if (! corrOk) {
930 // just set point, PointFineTune already complained
931 if (corrPoint.corrPos.x >= 0 && corrPoint.corrPos.y >= 0 && corrPoint.maxpos.x > 0 && corrPoint.maxpos.y > 0)
932 {
933 otherImg->setScale(m_detailZoomFactor);
934 otherImg->setNewPoint(corrPoint.maxpos);
935 thisImg->setNewPoint(corrPoint.corrPos);
937 };
938 } else {
939 // show point & zoom in if auto add is not set
940 if (!m_autoAddCB->IsChecked()) {
941 otherImg->setScale(m_detailZoomFactor);
942 otherImg->setNewPoint(corrPoint.maxpos);
943 thisImg->setNewPoint(corrPoint.corrPos);
945 wxString s1;
946 s1.Printf(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f"),
947 corrPoint.maxAngle, corrPoint.maxi, corrPoint.curv.x, corrPoint.curv.y );
948
949 wxString s2 = s1 + " -- " + wxString(_("change points, or press right mouse button to add the pair"));
950 MainFrame::Get()->SetStatusText(s2,0);
951 } else {
952 // add point
953 otherImg->setNewPoint(corrPoint.maxpos);
954 thisImg->setNewPoint(corrPoint.corrPos);
957 }
958 }
959 } else {
960 // no fine-tune, set 100% scale and set both points to selected
961 otherImg->setScale(m_detailZoomFactor);
962 otherImg->showPosition(op);
964 }
965
966 } else {
967 // estimate was outside of image
968 // do nothing special
969 wxBell();
970 MainFrame::Get()->SetStatusText(_("Estimated point outside image"),0);
971 }
972}
973
975{
976 DEBUG_TRACE("");
977
979
981 unsigned int thisImgNr = m_leftImageNr;
983 unsigned int otherImgNr = m_rightImageNr;
988
989 bool estimate = m_estimateCB->IsChecked();
990
991 if (!left) {
1000 }
1001
1002
1003 if (cpCreationState == NO_POINT) {
1004 //case NO_POINT
1006 // zoom into our window
1007 if (thisImg->getScale() < 1) {
1008 thisImg->setScale(m_detailZoomFactor);
1009 thisImg->showPosition(p);
1010 } else {
1011 // run auto-estimate procedure?
1012 bool hasNormalCP = false;
1013 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end() && !hasNormalCP; ++it)
1014 {
1015 hasNormalCP = (it->second.mode == HuginBase::ControlPoint::X_Y);
1016 };
1017 if (estimate && (thisImgNr != otherImgNr) && hasNormalCP) {
1021 };
1022 }
1023
1024 } else if (cpCreationState == OTHER_POINT_RETRY) {
1025 thisImg->showPosition(p);
1026 } else if (cpCreationState == THIS_POINT) {
1027 thisImg->showPosition(p);
1028
1029 bool hasNormalCP = false;
1030 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end() && !hasNormalCP; ++it)
1031 {
1032 hasNormalCP = (it->second.mode == HuginBase::ControlPoint::X_Y);
1033 };
1034 if (estimate && (thisImgNr != otherImgNr) && hasNormalCP) {
1038 }
1040 // the try for the second point.
1042 // other point already selected, finalize point.
1043
1044 // TODO: option to ignore the auto fine tune button when multiple images are selected.
1045 if (m_fineTuneCB->IsChecked() ) {
1047
1048 hugin_utils::FDiff2D newPoint = otherImg->getNewPoint();
1049
1050 long templWidth = wxConfigBase::Get()->Read("/Finetune/TemplateSize",HUGIN_FT_TEMPLATE_SIZE);
1052 double sAreaPercent = wxConfigBase::Get()->Read("/Finetune/SearchAreaPercent",
1054 int sWidth = std::min((int) (img.getWidth() * sAreaPercent / 100.0), 500);
1055 bool corrOk = false;
1056 // corr point
1057 vigra::Diff2D newPoint_round = newPoint.toDiff2D();
1058 try {
1061 templWidth,
1062 thisImgNr,
1063 p,
1064 sWidth,
1065 corrRes);
1066 } catch (std::exception & e) {
1067 hugin_utils::HuginMessageBox(wxString::Format(_("Error during Fine-tune (%s)"), wxString(e.what(), wxConvLocal)), _("Hugin"), wxOK, this);
1068 }
1069
1070 if (! corrOk) {
1071 // low xcorr
1072 // zoom to 100 percent. & set second stage
1073 // to abandon finetune this time.
1074 if (corrRes.corrPos.x >= 0 && corrRes.corrPos.y >= 0 && corrRes.maxpos.x >= 0 && corrRes.maxpos.y >= 0)
1075 {
1076 thisImg->setScale(m_detailZoomFactor);
1077 thisImg->setNewPoint(corrRes.maxi > -1 ? corrRes.maxpos : p);
1078 thisImg->Refresh();
1079 otherImg->setNewPoint(corrRes.maxi > -1 ? corrRes.corrPos : newPoint);
1081 };
1082 } else {
1083 // show point & zoom in if auto add is not set
1085 if (!m_autoAddCB->IsChecked()) {
1086 thisImg->setScale(m_detailZoomFactor);
1087 }
1088 thisImg->setNewPoint(corrRes.maxpos);
1089 otherImg->setNewPoint(corrRes.corrPos);
1090 wxString s1;
1091 s1.Printf(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f"),
1092 corrRes.maxAngle, corrRes.maxi, corrRes.curv.x, corrRes.curv.y );
1093
1094 corrMsg = s1 + " -- " + wxString(_("change points, or press right mouse button to add the pair"));
1095 MainFrame::Get()->SetStatusText(corrMsg,0);
1096
1097 }
1098 } else {
1099 // no finetune. but zoom into picture, when we where zoomed out
1100 if (thisImg->getScale() < 1) {
1101 // zoom to 100 percent. & set second stage
1102 // to abandon finetune this time.
1103 thisImg->setScale(m_detailZoomFactor);
1104 thisImg->clearNewPoint();
1105 thisImg->showPosition(p);
1106 //thisImg->setNewPoint(p.x, p.y);
1108 return;
1109 } else {
1110 // point is already set. no need to move.
1111 // setNewPoint(p);
1113 }
1114 }
1115 } else {
1116 // selection retry
1117 // nothing special, no second stage fine-tune yet.
1118 }
1119
1120 // ok, we have determined the other point.. apply if auto add is on
1121 if (m_autoAddCB->IsChecked()) {
1123 } else {
1124 // keep both point floating around, until they are
1125 // added with a right mouse click or the add button
1127 if (corrMsg != wxEmptyString) {
1128 MainFrame::Get()->SetStatusText(corrMsg,0);
1129 }
1130 }
1131
1132 } else if (cpCreationState == BOTH_POINTS_SELECTED) {
1133 // nothing to do.. maybe a special fine-tune with
1134 // a small search region
1135
1136 } else {
1137 // should never reach this, else state machine is broken.
1138 DEBUG_ASSERT(0);
1139 }
1140}
1141
1142// return a SrcPanoImage so that the given point is in the center
1143HuginBase::SrcPanoImage GetImageRotatedTo(const HuginBase::SrcPanoImage& img, const vigra::Diff2D& point, int testWidth, double& neededHFOV)
1144{
1145 // copy only necessary information into temporary SrcPanoImage
1147 imgMod.setSize(img.getSize());
1148 imgMod.setProjection(img.getProjection());
1149 imgMod.setHFOV(img.getHFOV());
1150 // calculate, where the interest point lies
1153 opt.setHFOV(360);
1154 opt.setWidth(360);
1155 opt.setHeight(180);
1156
1158 transform.createInvTransform(imgMod, opt);
1159 double x1, y1;
1160 if (!transform.transformImgCoord(x1, y1, point.x, point.y))
1161 {
1162 neededHFOV = -1;
1163 return imgMod;
1164 }
1165 // equirect image coordinates -> equirectangular coordinates
1166 // transformImgCoord places the origin at the upper left corner and negate
1167 Matrix3 rotY;
1168 rotY.SetRotationPT(DEG_TO_RAD(180 - (x1 + 0.5)), 0, 0);
1169 Matrix3 rotP;
1170 rotP.SetRotationPT(0, DEG_TO_RAD((y1 + 0.5) - 90), 0);
1171 double y, p, r;
1172 // calculate the necessary rotation angles and remember
1173 Matrix3 rot = rotP * rotY;
1174 rot.GetRotationPT(y, p, r);
1175 imgMod.setYaw(RAD_TO_DEG(y));
1176 imgMod.setPitch(RAD_TO_DEG(p));
1177 imgMod.setRoll(RAD_TO_DEG(r));
1178
1179 // new we calculate the needed HFOV for template/search area width
1180 double x2, y2;
1181 // check a point left from our interest point
1182 if (transform.transformImgCoord(x2, y2, point.x - testWidth / 2.0, point.y))
1183 {
1184 if (x2 < x1)
1185 {
1186 neededHFOV = 2.0 * (x1 - x2);
1187 }
1188 else
1189 {
1190 // we crossed the 360 deg border
1191 neededHFOV = 2.0 * (360 - x2 + x1);
1192 };
1193 // limit maximum HFOV for remapping to stereographic projection done as next step
1194 if (neededHFOV > 90)
1195 {
1196 neededHFOV = 90;
1197 };
1198 return imgMod;
1199 };
1200 // this goes wrong, maybe the tested point is outside the image area of a fisheye image
1201 // now test the right point
1202 if (transform.transformImgCoord(x2, y2, point.x + testWidth / 2.0, point.y))
1203 {
1204 if (x1 < x2)
1205 {
1206 neededHFOV = 2.0 * (x2 - x1);
1207 }
1208 else
1209 {
1210 // we crossed the 360 deg border
1211 neededHFOV = 2.0 * (360 + x2 - x1);
1212 };
1213 // limit maximum HFOV for remapping to stereographic projection done as next step
1214 if (neededHFOV > 90)
1215 {
1216 neededHFOV = 90;
1217 };
1218 return imgMod;
1219 };
1220 // we can't calculate the needed HFOV, return -1
1221 neededHFOV = -1;
1222 return imgMod;
1223};
1224
1226 vigra::Diff2D templPos, int templSize,
1227 const HuginBase::SrcPanoImage& search, const vigra::UInt8RGBImage& searchImg,
1228 vigra::Diff2D searchPos, int sWidth)
1229{
1231 // read settings
1232 wxConfigBase *cfg = wxConfigBase::Get();
1233 bool rotatingFinetune = cfg->Read("/Finetune/RotationSearch", HUGIN_FT_ROTATION_SEARCH) == 1;
1235 cfg->Read("/Finetune/RotationStartAngle", &startAngle, HUGIN_FT_ROTATION_START_ANGLE);
1238 cfg->Read("/Finetune/RotationStopAngle", &stopAngle, HUGIN_FT_ROTATION_STOP_ANGLE);
1240 int nSteps = cfg->Read("/Finetune/RotationSteps", HUGIN_FT_ROTATION_STEPS);
1241 // if both images have the same projection and the angle does not differ to much use normal point fine-tune
1242 if (templ.getProjection() == search.getProjection()
1243 && templ.getHFOV() < 65 && search.getHFOV() < 65
1244 && fabs(templ.getHFOV() - search.getHFOV()) < 5)
1245 {
1247 if (rotatingFinetune)
1248 {
1251 }
1252 else
1253 {
1254 res = vigra_ext::PointFineTune(templImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), templPos, templSize,
1255 searchImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), searchPos, sWidth);
1256 };
1257 res.corrPos = templPos;
1258 return res;
1259 };
1260 // images have different projections or the HFOV is different
1261 // so we reproject the image to stereographic projection and fine tune point there
1262 // rotate image so that interest point is in the center
1263 double templHFOV = 0;
1264 double searchHFOV = 0;
1268 res.maxpos = hugin_utils::FDiff2D(-1, -1);
1269 res.corrPos = hugin_utils::FDiff2D(-1, -1);
1270 if (templHFOV < 0 || searchHFOV < 0)
1271 {
1272 //something went wrong, e.g. image outside of projection circle for fisheye lenses
1273 return res;
1274 }
1275 // populate PanoramaOptions
1278 opts.setHFOV(std::max(templHFOV, searchHFOV));
1279 // calculate a sensible scale factor
1282 opts.setWidth(std::max<unsigned int>(opts.getWidth()*std::min(scaleTempl, scaleSearch), 3 * templSize));
1283 opts.setHeight(opts.getWidth());
1284 // transform coordinates to transform system
1286 transform.createInvTransform(templMod, opts);
1287 double templX, templY, searchX, searchY;
1289 transform.createInvTransform(searchMod, opts);
1291 // now transform the images
1294 transform.createTransform(searchMod, opts);
1295 vigra::UInt8RGBImage searchImgMod(opts.getSize());
1296 vigra::BImage alpha(opts.getSize());
1297 vigra_ext::transformImage(srcImageRange(searchImg), destImageRange(searchImgMod), destImage(alpha),
1298 vigra::Diff2D(0, 0), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy);
1299 // now remap template, we need to remap a little bigger area to have enough information when the template
1300 // is rotated in PointFineTuneRotSearch
1302 vigra::Rect2D rect(templPointInt.x - templSize - 2, templPointInt.y - templSize - 2,
1304 rect &= vigra::Rect2D(opts.getSize());
1305 transform.createTransform(templMod, opts);
1306 vigra::UInt8RGBImage templImgMod(opts.getSize());
1307 vigra_ext::transformImage(srcImageRange(templImg), destImageRange(templImgMod, rect), destImage(alpha),
1308 vigra::Diff2D(rect.left(), rect.top()), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy);
1309#if defined DEBUG_EXPORT_FINE_TUNE_REMAPPING
1310 {
1311 vigra::ImageExportInfo templExport("template_remapped.tif");
1312 vigra::exportImage(srcImageRange(templImgMod), templExport.setPixelType("UINT8"));
1313 vigra::ImageExportInfo searchExport("search_remapped.tif");
1314 vigra::exportImage(srcImageRange(searchImgMod), searchExport.setPixelType("UINT8"));
1315 }
1316#endif
1317 // now we can finetune the point in stereographic projection
1318 // we are always using the rotate fine-tune algorithm, because for this case
1319 // often a rotation is involved
1322 // we transfer also the new found template position back to the original image
1323 transform.createTransform(templMod, opts);
1324 transform.transformImgCoord(res.corrPos.x, res.corrPos.y, templPointInt.x + 0.00001, templPointInt.y + 0.00001);
1325 // we need to move the finetune point back to position in original image
1326 transform.createTransform(searchMod, opts);
1327 transform.transformImgCoord(res.maxpos.x, res.maxpos.y, res.maxpos.x, res.maxpos.y);
1328 return res;
1329};
1330
1332 const vigra::Diff2D & tmplPoint,
1333 int templSize,
1334 unsigned int subjImgNr,
1336 int sWidth,
1338{
1339 DEBUG_TRACE("tmpl img nr: " << tmplImgNr << " corr src: "
1340 << subjImgNr);
1341 if (tmplImgNr == subjImgNr)
1342 {
1343 // for line control points (assumed when both points are in the same image)
1344 // check the distance before running fine-tune
1345 // if both points are in the search area decrease search area
1346 const double distance = (tmplPoint - o_subjPoint.toDiff2D()).magnitude();
1347 if (distance < sWidth)
1348 {
1349 sWidth = 0.9 * distance;
1350 };
1351 if (sWidth < 1.1 * templSize)
1352 {
1353 MainFrame::Get()->SetStatusText(_("Distance between line control points too short, skipping fine-tune."));
1354 wxBell();
1355 return false;
1356 };
1357 };
1358 MainFrame::Get()->SetStatusText(_("searching similar points..."),0);
1359
1360 double corrThresh=HUGIN_FT_CORR_THRESHOLD;
1361 wxConfigBase::Get()->Read("/Finetune/CorrThreshold",&corrThresh,
1363
1365 // use default curvature threshold for line control points
1366 if (tmplImgNr != subjImgNr)
1367 {
1368 wxConfigBase::Get()->Read("/Finetune/CurvThreshold", &curvThresh, HUGIN_FT_CURV_THRESHOLD);
1369 };
1370
1371 // fixme: just cutout suitable gray
1372 ImageCache::ImageCacheRGB8Ptr subjImg = ImageCache::getInstance().getImage(m_pano->getImage(subjImgNr).getFilename())->get8BitImage();
1373 ImageCache::ImageCacheRGB8Ptr tmplImg = ImageCache::getInstance().getImage(m_pano->getImage(tmplImgNr).getFilename())->get8BitImage();
1374
1376 m_pano->getImage(subjImgNr), *(subjImg), o_subjPoint.toDiff2D(), sWidth);
1377
1378 // invert curvature. we always assume its a maxima, the curvature there is negative
1379 // however, we allow the user to specify a positive threshold, so we need to
1380 // invert it
1381 res.curv.x = - res.curv.x;
1382 res.curv.y = - res.curv.y;
1383
1384 MainFrame::Get()->SetStatusText(wxString::Format(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f"),
1385 res.maxAngle, res.maxi, res.curv.x, res.curv.y ),0);
1386 if (res.corrPos.x < 0 || res.corrPos.y < 0 || res.maxpos.x < 0 || res.maxpos.y < 0)
1387 {
1388 // invalid transformation in fine tune
1389 hugin_utils::MessageDialog dlg=hugin_utils::GetMessageDialog(_("No similar point found."), _("Hugin"), wxICON_ERROR | wxOK, this);
1390 dlg->SetExtendedMessage(_("An internal transformation went wrong.\nCheck that the point is inside the image."));
1391 dlg->ShowModal();
1392 return false;
1393 }
1394 if (res.maxi < corrThresh || res.curv.x < curvThresh || res.curv.y < curvThresh )
1395 {
1396 // Bad correlation result.
1397 hugin_utils::MessageDialog dlg=hugin_utils::GetMessageDialog(_("No similar point found."), _("Hugin"), wxICON_ERROR | wxOK, this);
1398 dlg->SetExtendedMessage(wxString::Format(_("Check the similarity visually.\nCorrelation coefficient (%.3f) is lower than the threshold set in the preferences."),
1399 res.maxi));
1400 dlg->ShowModal();
1401 return false;
1402 }
1403
1404 return true;
1405}
1406
1408{
1409 int nGui = m_cpModeChoice->GetCount();
1410 int nPano = pano.getNextCPTypeLineNumber()+1;
1411 DEBUG_DEBUG("mode choice: " << nGui << " entries, required: " << nPano);
1412
1413 if (nGui > nPano)
1414 {
1415 m_cpModeChoice->Freeze();
1416 // remove some items.
1417 for (int i = nGui-1; i > nPano-1; --i) {
1418 m_cpModeChoice->Delete(i);
1419 }
1420 if (nPano > 3) {
1421 m_cpModeChoice->SetString(nPano-1, _("Add new Line"));
1422 }
1423 m_cpModeChoice->Thaw();
1424 } else if (nGui < nPano) {
1425 m_cpModeChoice->Freeze();
1426 if (nGui > 3) {
1427 m_cpModeChoice->SetString(nGui-1, wxString::Format(_("Line %d"), nGui-1));
1428 }
1429 for (int i = nGui; i < nPano-1; i++) {
1430 m_cpModeChoice->Append(wxString::Format(_("Line %d"), i));
1431 }
1432 m_cpModeChoice->Append(_("Add new Line"));
1433 m_cpModeChoice->Thaw();
1434 }
1436 // check if number of control points has changed, if so we need to update our variables
1437 if (pano.getNrOfCtrlPoints() != m_countCP)
1438 {
1440 UpdateDisplay(false);
1441 };
1442 // update header if necessary
1444 if (m_cpList->GetColumn(6, item))
1445 {
1446 if (MainFrame::Get()->IsShowingCorrelation())
1447 {
1448 item.SetText(_("Correlation"));
1449 }
1450 else
1451 {
1452 item.SetText(_("Distance"));
1453 }
1454 m_cpList->SetColumn(6, item);
1455 };
1457 m_leftChoice->Refresh();
1459 m_rightChoice->Refresh();
1460
1461 DEBUG_TRACE("");
1462}
1463
1465{
1466 unsigned int nrImages = pano.getNrOfImages();
1467 unsigned int nrTabs = m_leftChoice->GetCount();
1468 DEBUG_TRACE("nrImages:" << nrImages << " nrTabs:" << nrTabs);
1469
1470#ifdef __WXMSW__
1471 int oldLeftSelection = m_leftChoice->GetSelection();
1472 int oldRightSelection = m_rightChoice->GetSelection();
1473#endif
1474
1475 if (nrImages == 0)
1476 {
1477 // disable controls
1478 m_cpModeChoice->Disable();
1479 m_addButton->Disable();
1480 m_delButton->Disable();
1481 m_autoAddCB->Disable();
1482 m_fineTuneCB->Disable();
1483 m_estimateCB->Disable();
1484 m_showLinesCB->Disable();
1485 m_finetuneButton->Disable();
1486 m_actionButton->Disable();
1487 m_zoomChoice->Disable();
1488 m_prevImgButton->Disable();
1489 m_nextImgButton->Disable();
1490 m_leftChoice->Disable();
1491 m_rightChoice->Disable();
1492 }
1493 else
1494 {
1495 // enable controls
1496 m_cpModeChoice->Enable();
1497 m_autoAddCB->Enable();
1498 m_fineTuneCB->Enable();
1499 m_estimateCB->Enable();
1500 m_showLinesCB->Enable();
1501 m_finetuneButton->Enable();
1502 m_actionButton->Enable();
1503 m_zoomChoice->Enable();
1504 m_prevImgButton->Enable();
1505 m_nextImgButton->Enable();
1506 m_leftChoice->Enable();
1507 m_rightChoice->Enable();
1508
1509 ImageCache::getInstance().softFlush();
1510
1511 m_leftChoice->Freeze();
1512 m_rightChoice->Freeze();
1513 for (unsigned int i=0; i < ((nrTabs < nrImages)? nrTabs: nrImages); i++) {
1514 wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
1515 m_leftChoice->SetString(i, wxString::Format("%d", i) + ". - " + fileName.GetFullName());
1516 m_rightChoice->SetString(i, wxString::Format("%d", i) + ". - " + fileName.GetFullName());
1517 }
1518 // wxChoice on windows looses the selection when setting new labels. Restore selection
1519#ifdef __WXMSW__
1520 m_leftChoice->SetSelection(oldLeftSelection);
1521 m_rightChoice->SetSelection(oldRightSelection);
1522#endif
1523 // add tab bar entries, if needed
1524 if (nrTabs < nrImages)
1525 {
1526 for (unsigned int i=nrTabs; i < nrImages; i++)
1527 {
1528 wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
1529 m_leftChoice->Append(wxString::Format("%d", i) + ". - " + fileName.GetFullName());
1530 m_rightChoice->Append(wxString::Format("%d", i) + ". - " + fileName.GetFullName());
1531 }
1532 }
1533 m_leftChoice->Thaw();
1534 m_rightChoice->Thaw();
1535 }
1536 if (nrTabs > nrImages)
1537 {
1538 // remove tab bar entries if needed
1539 // we have to disable listening to notebook selection events,
1540 // else we might update to a noexisting image
1541 m_listenToPageChange = false;
1542 m_leftChoice->Freeze();
1543 m_rightChoice->Freeze();
1544 for (int i=nrTabs-1; i >= (int)nrImages; i--) {
1545 m_leftChoice->Delete(i);
1546 m_rightChoice->Delete(i);
1547 }
1548 m_leftChoice->Thaw();
1549 m_rightChoice->Thaw();
1550 m_listenToPageChange = true;
1551 if (nrImages > 0) {
1552 // select some other image if we deleted the current image
1553 if (m_leftImageNr >= nrImages) {
1554 setLeftImage(nrImages -1);
1555 }
1556 if (m_rightImageNr >= nrImages) {
1557 setRightImage(nrImages -1);
1558 }
1559 } else {
1560 DEBUG_DEBUG("setting no images");
1562 m_leftFile = "";
1564 m_rightFile = "";
1565 // no image anymore..
1568 }
1569 }
1570
1571 // update changed images
1572 bool update(false);
1573 for(HuginBase::UIntSet::const_iterator it = changed.begin(); it != changed.end(); ++it) {
1574 unsigned int imgNr = *it;
1575 // we only need to update the view if the currently
1576 // selected images were changed.
1577 // changing the images via the tabbar will always
1578 // take the current state directly from the pano
1579 // object
1580 DEBUG_DEBUG("image changed "<< imgNr);
1581 double yaw = const_map_get(m_pano->getImageVariables(imgNr), "y").getValue();
1582 double pitch = const_map_get(m_pano->getImageVariables(imgNr), "p").getValue();
1583 double roll = const_map_get(m_pano->getImageVariables(imgNr), "r").getValue();
1584 CPImageCtrl::ImageRotation rot = GetRot(yaw, pitch, roll);
1585 if (m_leftImageNr == imgNr) {
1586 DEBUG_DEBUG("left image dirty "<< imgNr);
1587 if (m_leftFile != pano.getImage(imgNr).getFilename()
1588 || m_leftRot != rot )
1589 {
1590 m_leftRot = rot;
1591 m_leftFile = pano.getImage(imgNr).getFilename();
1593 }
1594 update=true;
1595 }
1596
1597 if (m_rightImageNr == imgNr) {
1598 DEBUG_DEBUG("right image dirty "<< imgNr);
1599 if (m_rightFile != pano.getImage(imgNr).getFilename()
1600 || m_rightRot != rot )
1601 {
1602 m_rightRot = rot;
1603 m_rightFile = pano.getImage(imgNr).getFilename();
1605 }
1606 update=true;
1607 }
1608 }
1609 // check if number of control points has changed, if so we need to update our variables
1610 if (pano.getNrOfCtrlPoints() != m_countCP)
1611 {
1613 update = true;
1614 };
1615
1616 // if there is no selection, select the first one.
1617 m_leftChoice->Freeze();
1618 m_rightChoice->Freeze();
1619 if (m_rightImageNr == UINT_MAX && nrImages > 0) {
1620 setRightImage(0);
1621 }
1622 if (m_leftImageNr == UINT_MAX && nrImages > 0) {
1623 setLeftImage(0);
1624 }
1625 m_leftChoice->Thaw();
1626 m_rightChoice->Thaw();
1627
1628 if (update || nrImages == 0) {
1629 UpdateDisplay(false);
1630 }
1633}
1634
1636{
1637 DEBUG_DEBUG("")
1638 int fI = m_leftChoice->GetSelection();
1639 int sI = m_rightChoice->GetSelection();
1640
1641 // valid selection and already set left image
1642 if (fI >= 0 && m_leftImageNr != UINT_MAX)
1643 {
1644 // set image number to selection
1645 m_leftImageNr = (unsigned int) fI;
1646 }
1647 // valid selection and already set right image
1648 if (sI >= 0 && m_rightImageNr != UINT_MAX)
1649 {
1650 // set image number to selection
1651 m_rightImageNr = (unsigned int) sI;
1652 }
1653 // reset selection
1654 m_x1Text->Clear();
1655 m_y1Text->Clear();
1656 m_x2Text->Clear();
1657 m_y2Text->Clear();
1658 if (m_cpModeChoice->GetSelection() < 3) {
1659 m_cpModeChoice->SetSelection(0);
1660 }
1661
1664
1665 // update control points
1667 const size_t oldCurrentPointSize = currentPoints.size();
1668 currentPoints.clear();
1669 mirroredPoints.clear();
1670
1671 // create a list of all control points
1672 HuginBase::CPVector::size_type i = 0;
1675 for (HuginBase::CPVector::size_type index = 0; index < controlPoints.size(); ++index)
1676 {
1678 if ((point.image1Nr == m_leftImageNr) && (point.image2Nr == m_rightImageNr)){
1679 m_leftImg->setCtrlPoint(point, false);
1680 m_rightImg->setCtrlPoint(point, true);
1681 currentPoints.push_back(std::make_pair(index, point));
1682 i++;
1683 } else if ((point.image2Nr == m_leftImageNr) && (point.image1Nr == m_rightImageNr)){
1684 m_leftImg->setCtrlPoint(point, true);
1685 m_rightImg->setCtrlPoint(point, false);
1686 point.mirror();
1687 mirroredPoints.insert(i);
1688 currentPoints.push_back(std::make_pair(index, point));
1689 i++;
1690 }
1691 }
1692 m_leftImg->Refresh();
1693 m_rightImg->Refresh();
1694
1695 // put these control points into our listview.
1696 unsigned int selectedItem = UINT_MAX;
1697 long selectedCP = -1;
1698 for (int i = 0; i < m_cpList->GetItemCount(); i++)
1699 {
1700 if ( m_cpList->GetItemState( i, wxLIST_STATE_SELECTED))
1701 {
1702 selectedItem = (unsigned int) i; // remembers the old selection, only one can be selected
1703 selectedCP = m_cpList->GetItemData(i);
1704 break;
1705 }
1706 }
1707 m_cpList->Freeze();
1708 m_cpList->DeleteAllItems();
1709
1710 for (unsigned int i=0; i < currentPoints.size(); ++i) {
1712 DEBUG_DEBUG("inserting LVItem " << i);
1713 long item = m_cpList->InsertItem(i, wxString::Format("%d", i), -1);
1714 // store index in list data field
1715 m_cpList->SetItemData(item, i);
1716 m_cpList->SetItem(i,1,wxString::Format("%.2f",p.x1));
1717 m_cpList->SetItem(i,2,wxString::Format("%.2f",p.y1));
1718 m_cpList->SetItem(i,3,wxString::Format("%.2f",p.x2));
1719 m_cpList->SetItem(i,4,wxString::Format("%.2f",p.y2));
1720 wxString mode;
1721 switch (p.mode) {
1723 mode = _("normal");
1724 break;
1726 mode = _("vert. Line");
1727 break;
1729 mode = _("horiz. Line");
1730 break;
1731 default:
1732 mode = wxString::Format(_("Line %d"), p.mode);
1733 break;
1734 }
1735 m_cpList->SetItem(i,5,mode);
1736 m_cpList->SetItem(i,6,wxString::Format("%.2f",p.error));
1737 }
1738 SortList();
1739
1740 if (selectedItem <= (unsigned int)m_cpList->GetItemCount() && !newPair && m_cpList->GetItemCount() > 0)
1741 {
1742 // sets an old selection again, only if the images have not changed
1743 if (oldCurrentPointSize == currentPoints.size())
1744 {
1745 // number of control points is not changed, so select the same cp
1746 // as before
1747 long selected = m_cpList->FindItem(-1, selectedCP);
1749 m_cpList->EnsureVisible(selected);
1751 }
1752 else
1753 {
1754 // number of cp has change, so keep the next item seleteced
1755 if (selectedItem == (unsigned int)m_cpList->GetItemCount())
1756 {
1757 // previously last item was selected, move selected to now last item
1758 selectedItem = m_cpList->GetItemCount() - 1;
1759 };
1761 m_cpList->EnsureVisible(selectedItem);
1762 m_selectedPoint = m_cpList->GetItemData(selectedItem);
1763 };
1764 EnablePointEdit(true);
1765
1767 m_x1Text->SetValue(wxString::Format("%.2f",p.x1));
1768 m_y1Text->SetValue(wxString::Format("%.2f",p.y1));
1769 m_x2Text->SetValue(wxString::Format("%.2f",p.x2));
1770 m_y2Text->SetValue(wxString::Format("%.2f",p.y2));
1771 m_cpModeChoice->SetSelection(p.mode);
1774
1775 } else {
1777 EnablePointEdit(false);
1778 }
1779
1780 for ( int j=0; j < m_cpList->GetColumnCount() ; j++ )
1781 {
1782 //get saved width
1783 // -1 is auto
1784 int width = wxConfigBase::Get()->Read(wxString::Format( "/CPEditorPanel/ColumnWidth%d", j ), -1);
1785 if(width != -1)
1786 m_cpList->SetColumnWidth(j, width);
1787 }
1788
1789 m_cpList->Thaw();
1790}
1791
1793{
1794 m_delButton->Enable(state);
1795 m_finetuneButton->Enable(state);
1796 m_x1Text->Enable(state);
1797 m_y1Text->Enable(state);
1798 m_x2Text->Enable(state);
1799 m_y2Text->Enable(state);
1800 m_cpModeChoice->Enable(state);
1801}
1802
1804{
1805 DEBUG_TRACE("");
1806 // find selected point
1807 long item = -1;
1808 item = m_cpList->GetNextItem(item,
1811 // no selected item.
1812 if (item == -1) {
1813 return;
1814 }
1815 const unsigned int nr = m_cpList->GetItemData(item);
1816 assert(nr < currentPoints.size());
1818
1819 // update point state
1820 double oldValue=cp.x1;
1821 bool valid_input=hugin_utils::str2double(m_x1Text->GetValue(), cp.x1);
1822 if(valid_input)
1823 valid_input=(cp.x1>=0) && (cp.x1<=m_pano->getSrcImage(cp.image1Nr).getWidth());
1824 if (!valid_input) {
1825 m_x1Text->Clear();
1826 *m_x1Text << oldValue;
1827 return;
1828 }
1829 oldValue=cp.y1;
1831 if(valid_input)
1832 valid_input=(cp.y1>=0) && (cp.y1<=m_pano->getSrcImage(cp.image1Nr).getHeight());
1833 if (!valid_input) {
1834 m_y1Text->Clear();
1835 *m_y1Text << oldValue;
1836 return;
1837 }
1838 oldValue=cp.x2;
1840 if(valid_input)
1841 valid_input=(cp.x2>=0) && (cp.x2<=m_pano->getSrcImage(cp.image2Nr).getWidth());
1842 if (!valid_input) {
1843 m_x2Text->Clear();
1844 *m_x2Text << oldValue;
1845 return;
1846 }
1847 oldValue=cp.y2;
1849 if(valid_input)
1850 valid_input=(cp.y2>=0) && (cp.y2<=m_pano->getSrcImage(cp.image1Nr).getHeight());
1851 if (!valid_input) {
1852 m_y2Text->Clear();
1853 *m_y2Text << oldValue;
1854 return;
1855 }
1856
1857 cp.mode = m_cpModeChoice->GetSelection();
1858 // if point was mirrored, reverse before setting it.
1860 cp.mirror();
1861 }
1864 );
1865
1866}
1867
1869{
1870 DEBUG_TRACE("OnLeftChoiceChange() to " << e.GetSelection());
1871 if (m_listenToPageChange && e.GetSelection() >= 0) {
1872 setLeftImage((unsigned int) e.GetSelection());
1873 }
1874}
1875
1877{
1878 DEBUG_TRACE("OnRightChoiceChange() to " << e.GetSelection());
1879 if (m_listenToPageChange && e.GetSelection() >= 0) {
1880 setRightImage((unsigned int) e.GetSelection());
1881 }
1882}
1884{
1885 int t = ev.GetIndex();
1886 DEBUG_TRACE("selected: " << t);
1887 if (t >=0) {
1889 {
1890 // default behaviour, scroll both CPImageCtrls
1891 SelectLocalPoint(m_cpList->GetItemData(t));
1892 }
1893 else
1894 {
1895 // special case: scroll only one image
1897 // reset scroll status
1899 };
1901 }
1902 EnablePointEdit(true);
1903}
1904
1906{
1907 // disable controls
1908 // when doing changes to this procedure do also check
1909 // interaction with control point table
1910 // e.g. m_selectedPoint=UINT_MAX will result in a endless loop and crash
1912 EnablePointEdit(false);
1915}
1916
1917void CPEditorPanel::OnZoom(wxCommandEvent & e)
1918{
1919 int leftX = 0;
1920 int leftY = 0;
1921 int rightX = 0;
1922 int rightY = 0;
1923 const wxSize leftSize = m_leftImg->GetClientSize();
1924 const wxSize rightSize = m_rightImg->GetClientSize();
1925 if (m_leftImg->getScale() > 0)
1926 {
1927 // remember old scroll position
1928 leftX = (m_leftImg->GetScrollPos(wxHORIZONTAL) + leftSize.GetWidth() / 2 )/ m_leftImg->getScale();
1929 leftY = (m_leftImg->GetScrollPos(wxVERTICAL) + leftSize.GetHeight() / 2) / m_leftImg->getScale();
1930 rightX = (m_rightImg->GetScrollPos(wxHORIZONTAL) + rightSize.GetWidth() / 2) / m_rightImg->getScale();
1931 rightY = (m_rightImg->GetScrollPos(wxVERTICAL) + rightSize.GetHeight() / 2) / m_rightImg->getScale();
1932 };
1933 double factor;
1934 switch (e.GetSelection()) {
1935 case 0:
1936 factor = 1;
1937 m_detailZoomFactor = factor;
1938 break;
1939 case 1:
1940 // fit to window
1941 factor = 0;
1942 break;
1943 case 2:
1944 factor = 2;
1945 m_detailZoomFactor = factor;
1946 break;
1947 case 3:
1948 factor = 1.5;
1949 m_detailZoomFactor = factor;
1950 break;
1951 case 4:
1952 factor = 0.75;
1953 break;
1954 case 5:
1955 factor = 0.5;
1956 break;
1957 case 6:
1958 factor = 0.25;
1959 break;
1960 default:
1961 DEBUG_ERROR("unknown scale factor");
1962 factor = 1;
1963 }
1964 m_leftImg->setScale(factor);
1965 m_rightImg->setScale(factor);
1966 // if a point is selected, keep it in view
1967 if (m_selectedPoint < UINT_MAX) {
1969 }
1970 else
1971 {
1972 if (factor > 0)
1973 {
1974 // scroll to keep old position in view
1975 m_leftImg->Scroll(leftX*factor - leftSize.GetWidth() / 2, leftY*factor - leftSize.GetHeight() / 2);
1976 m_rightImg->Scroll(rightX*factor - rightSize.GetWidth() / 2, rightY*factor - rightSize.GetHeight() / 2);
1977 };
1978 }
1979}
1980
1982{
1983 DEBUG_DEBUG("key " << e.GetKeyCode()
1984 << " origin: id:" << e.GetId() << " obj: "
1985 << e.GetEventObject());
1986
1987 if (e.m_keyCode == WXK_DELETE){
1988 DEBUG_DEBUG("Delete pressed");
1989 // remove working points..
1990 if (cpCreationState != NO_POINT) {
1992 } else {
1993 // remove selected point
1994 // find selected point
1995 long item = -1;
1996 item = m_cpList->GetNextItem(item,
1999 // no selected item.
2000 if (item == -1) {
2001 wxBell();
2002 return;
2003 }
2004 unsigned int pNr = localPNr2GlobalPNr(m_cpList->GetItemData(item));
2005 DEBUG_DEBUG("about to delete point " << pNr);
2008 );
2009 }
2010 } else if (e.m_keyCode == '0') {
2011 wxCommandEvent dummy;
2012 dummy.SetInt(1);
2013 OnZoom(dummy);
2014 m_zoomChoice->SetSelection(1);
2015 } else if (e.m_keyCode == '1') {
2016 wxCommandEvent dummy;
2017 dummy.SetInt(0);
2018 OnZoom(dummy);
2019 m_zoomChoice->SetSelection(0);
2020 } else if (e.m_keyCode == '2') {
2021 wxCommandEvent dummy;
2022 dummy.SetInt(2);
2023 OnZoom(dummy);
2024 m_zoomChoice->SetSelection(2);
2025 } else if (e.CmdDown() && e.GetKeyCode() == WXK_LEFT) {
2026 // move to previous
2027 wxCommandEvent dummy;
2029 } else if (e.CmdDown() && e.GetKeyCode() == WXK_RIGHT) {
2030 // move to next
2031 wxCommandEvent dummy;
2033 } else if (e.GetKeyCode() == 'f') {
2034 bool left = e.GetEventObject() == m_leftImg;
2035 if (cpCreationState == NO_POINT) {
2037 } else if (cpCreationState == BOTH_POINTS_SELECTED) {
2038 FineTuneNewPoint(left);
2039 }
2040 } else if (e.GetKeyCode() == 'g') {
2041 // generate keypoints
2042 long th = wxGetNumberFromUser(_("Create control points.\nTo create less points,\nenter a higher number."), _("Corner Detection threshold"), _("Create control points"), 400, 0, 32000);
2043 if (th == -1) {
2044 return;
2045 }
2046 long scale = wxGetNumberFromUser(_("Create control points"), _("Corner Detection scale"), _("Create control points"), 2);
2047 if (scale == -1) {
2048 return;
2049 }
2050
2051 try {
2053 DEBUG_DEBUG("corner threshold: " << th << " scale: " << scale);
2056 );
2057 } catch (std::exception & e) {
2058 wxLogError(_("Error during control point creation:\n") + wxString(e.what(), wxConvLocal));
2059 }
2060 } else {
2061 e.Skip();
2062 }
2063}
2064
2065void CPEditorPanel::OnAddButton(wxCommandEvent & e)
2066{
2067 // check if the point can be created..
2070 }
2071}
2072
2073void CPEditorPanel::OnDeleteButton(wxCommandEvent & e)
2074{
2075 DEBUG_TRACE("");
2076 // check if a point has been selected, but not added.
2077 if (cpCreationState != NO_POINT) {
2079 } else {
2080 // find selected point
2081 long item = -1;
2082 item = m_cpList->GetNextItem(item,
2085 // no selected item.
2086 if (item == -1) {
2087 wxBell();
2088 return;
2089 }
2090 // get the global point number
2091 unsigned int pNr = localPNr2GlobalPNr(m_cpList->GetItemData(item));
2092
2095 );
2098 }
2099}
2100
2101// show a global control point
2103{
2107 // FIXME reset display state
2109
2111}
2112
2114{
2115 DEBUG_TRACE(cpCreationState << " --> " << newState);
2116 // handle global state changes.
2117 bool fineTune = m_fineTuneCB->IsChecked() && (m_leftImageNr != m_rightImageNr);
2118 switch(newState) {
2119 case NO_POINT:
2120 // disable all drawing search boxes.
2121 m_leftImg->showSearchArea(false);
2122 m_rightImg->showSearchArea(false);
2123 // but draw template size, if fine-tune enabled
2126 m_addButton->Enable(false);
2127 if (m_selectedPoint < UINT_MAX) {
2128 m_delButton->Enable(true);
2129 } else {
2130 m_delButton->Enable(false);
2131 }
2132 if (cpCreationState != NO_POINT) {
2133 // reset zoom to previous setting
2134 wxCommandEvent tmpEvt;
2135 tmpEvt.SetInt(m_zoomChoice->GetSelection());
2136 OnZoom(tmpEvt);
2139 }
2140 break;
2141 case LEFT_POINT:
2142 // disable search area on left window
2143 m_leftImg->showSearchArea(false);
2144 // show search area on right window
2146
2147 // show template area
2150
2151 // unselect point
2153 m_addButton->Enable(false);
2154 m_delButton->Enable(false);
2155 MainFrame::Get()->SetStatusText(_("Select point in right image"),0);
2156 break;
2157 case RIGHT_POINT:
2159 m_rightImg->showSearchArea(false);
2160
2163
2165 m_addButton->Enable(false);
2166 m_delButton->Enable(false);
2167 MainFrame::Get()->SetStatusText(_("Select point in left image"),0);
2168 break;
2169 case LEFT_POINT_RETRY:
2170 case RIGHT_POINT_RETRY:
2171 m_leftImg->showSearchArea(false);
2172 m_rightImg->showSearchArea(false);
2173 // but draw template size, if fine-tune enabled
2176 m_addButton->Enable(false);
2177 m_delButton->Enable(false);
2178 break;
2182 m_leftImg->showSearchArea(false);
2183 m_rightImg->showSearchArea(false);
2184 m_addButton->Enable(true);
2185 m_delButton->Enable(false);
2186 }
2187 // apply the change
2189}
2190
2191void CPEditorPanel::OnPrevImg(wxCommandEvent & e)
2192{
2193 if (m_pano->getNrOfImages() < 2) return;
2194 int nImgs = m_pano->getNrOfImages();
2195 int left = m_leftImageNr -1;
2196 int right = m_rightImageNr -1;
2197 if (left < 0) {
2198 left += nImgs;
2199 } else if (left >= nImgs) {
2200 left -= nImgs;
2201 }
2202
2203 if (right < 0) {
2204 right += nImgs;
2205 } else if (right >= nImgs) {
2206 right -= nImgs;
2207 }
2208 setLeftImage((unsigned int) left);
2209 setRightImage((unsigned int) right);
2210}
2211
2212void CPEditorPanel::OnNextImg(wxCommandEvent & e)
2213{
2214 if (m_pano->getNrOfImages() < 2) return;
2215 int nImgs = m_pano->getNrOfImages();
2216 int left = m_leftImageNr + 1;
2217 int right = m_rightImageNr + 1;
2218 if (left < 0) {
2219 left += nImgs;
2220 } else if (left >= nImgs) {
2221 left -= nImgs;
2222 }
2223
2224 if (right < 0) {
2225 right += nImgs;
2226 } else if (right >= nImgs) {
2227 right -= nImgs;
2228 }
2229 setLeftImage((unsigned int) left);
2230 setRightImage((unsigned int) right);
2231}
2232
2233void CPEditorPanel::OnFineTuneButton(wxCommandEvent & e)
2234{
2235 if (cpCreationState == NO_POINT) {
2236 FineTuneSelectedPoint(false);
2237 } else if (cpCreationState == BOTH_POINTS_SELECTED) {
2238 FineTuneNewPoint(false);
2239 }
2240}
2241
2243{
2244 m_cpActionContextMenu->SetLabel(XRCID("cp_menu_create_cp"), wxString::Format(_("Create cp (Current setting: %s)"), MainFrame::Get()->GetSelectedCPGenerator().c_str()));
2246};
2247
2248void CPEditorPanel::OnActionButton(wxCommandEvent& e)
2249{
2250 switch (m_cpActionButtonMode)
2251 {
2254 break;
2256 OnCleanCPButton(e);
2257 break;
2259 default:
2260 OnCelesteButton(e);
2261 break;
2262 };
2263};
2264
2265void CPEditorPanel::OnCreateCPButton(wxCommandEvent& e)
2266{
2268 {
2269 // when the same image is selected left and right we are running linefind
2270 // with default parameters
2272#ifdef __WXMSW__
2273 linefindSetting.SetProg("linefind.exe");
2274#else
2275 linefindSetting.SetProg("linefind");
2276#endif
2277 linefindSetting.SetArgs("-o %o %s");
2278 HuginBase::UIntSet imgs;
2279 imgs.insert(m_leftImageNr);
2281 }
2282 else
2283 {
2284 HuginBase::UIntSet imgs;
2285 imgs.insert(m_leftImageNr);
2286 imgs.insert(m_rightImageNr);
2288 };
2289};
2290
2291void CPEditorPanel::OnCelesteButton(wxCommandEvent & e)
2292{
2293 if (currentPoints.empty())
2294 {
2295 hugin_utils::HuginMessageBox(_("Cannot run celeste without at least one control point connecting the two images"), _("Hugin"), wxOK | wxICON_INFORMATION, this);
2296 std::cout << "Cannot run celeste without at least one control point connecting the two images" << std::endl;
2297 }
2298 else
2299 {
2300 ProgressReporterDialog progress(4, _("Running Celeste"), _("Running Celeste"), this);
2301 progress.updateDisplayValue(_("Loading model file"));
2302
2304 if(model==NULL)
2305 {
2306 return;
2307 };
2308
2309 // Get Celeste parameters
2310 wxConfigBase *cfg = wxConfigBase::Get();
2311 // SVM threshold
2313 cfg->Read("/Celeste/Threshold", &threshold, HUGIN_CELESTE_THRESHOLD);
2314
2315 // Mask resolution - 1 sets it to fine
2316 bool t = (cfg->Read("/Celeste/Filter", HUGIN_CELESTE_FILTER) == 0);
2317 int radius=(t)?10:20;
2318 DEBUG_TRACE("Running Celeste");
2319
2320 if (!progress.updateDisplayValue(_("Running Celeste")))
2321 {
2322 return;
2323 }
2324 // Image to analyse
2325 ImageCache::EntryPtr img=ImageCache::getInstance().getImage(m_pano->getImage(m_leftImageNr).getFilename());
2326 vigra::UInt16RGBImage in;
2327 if(img->image16->width()>0)
2328 {
2329 in.resize(img->image16->size());
2330 vigra::omp::copyImage(srcImageRange(*(img->image16)),destImage(in));
2331 }
2332 else
2333 {
2334 ImageCache::ImageCacheRGB8Ptr im8=img->get8BitImage();
2335 in.resize(im8->size());
2336 vigra::omp::transformImage(srcImageRange(*im8),destImage(in),vigra::functor::Arg1()*vigra::functor::Param(65535/255));
2337 };
2338 // convert to sRGB if icc profile found in file
2339 if (!img->iccProfile->empty())
2340 {
2341 HuginBase::Color::ApplyICCProfile(in, *(img->iccProfile), TYPE_RGB_16);
2342 };
2343 if (!progress.updateDisplayValue())
2344 {
2345 return;
2346 };
2348 in.resize(0,0);
2349 if (!progress.updateDisplay())
2350 {
2351 return;
2352 }
2353
2354 if(!cloudCP.empty())
2355 {
2358 );
2359 };
2360
2361 progress.updateDisplayValue();
2362 hugin_utils::HuginMessageBox(wxString::Format(_("Removed %lu control points"), static_cast<unsigned long int>(cloudCP.size())), _("Hugin"), wxOK | wxICON_INFORMATION, this);
2363 DEBUG_TRACE("Finished running Celeste");
2364 }
2365}
2366
2367void CPEditorPanel::OnCleanCPButton(wxCommandEvent& e)
2368{
2369 if (currentPoints.size() < 2)
2370 {
2371 wxBell();
2372 return;
2373 };
2374 // calculate mean and variance only for currently active cp
2375 double mean = 0;
2376 double var = 0;
2377 size_t n = 0;
2378 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
2379 {
2380 n++;
2381 double x = it->second.error;
2382 double delta = x - mean;
2383 mean += delta / n;
2384 var += delta*(x - mean);
2385 }
2386 var = var / (n - 1);
2387 const double limit = (sqrt(var) > mean) ? mean : (mean + sqrt(var));
2389 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
2390 {
2391 if (it->second.error > limit)
2392 {
2393 removedCPs.insert(it->first);
2394 };
2395 };
2396 if (!removedCPs.empty())
2397 {
2398 hugin_utils::HuginMessageBox(wxString::Format(_("Removed %lu control points"), (unsigned long int)removedCPs.size()), _("Hugin"), wxOK | wxICON_INFORMATION, this);
2400 }
2401 else
2402 {
2403 wxBell();
2404 }
2405};
2406
2408{
2410 wxString s(_("Create cp"));
2411 s.Append(wxUniChar(0x25bc));
2412 m_actionButton->SetLabel(s);
2413 m_actionButton->SetToolTip(_("Create control points for image pair with currently selected control point detector on photos tab."));
2414 Layout();
2415 wxConfig::Get()->Write("/CPEditorPanel/ActionMode", static_cast<long>(m_cpActionButtonMode));
2416};
2417
2419{
2421 wxString s(_("Celeste"));
2422 s.Append(wxUniChar(0x25bc));
2423 m_actionButton->SetLabel(s);
2424 m_actionButton->SetToolTip(_("Tries to remove control points from clouds"));
2425 Layout();
2426 wxConfig::Get()->Write("/CPEditorPanel/ActionMode", static_cast<long>(m_cpActionButtonMode));
2427};
2428
2430{
2432 wxString s(_("Clean cp"));
2433 s.Append(wxUniChar(0x25bc));
2434 m_actionButton->SetLabel(s);
2435 m_actionButton->SetToolTip(_("Remove outlying control points by statistical method"));
2436 Layout();
2437 wxConfig::Get()->Write("/CPEditorPanel/ActionMode", static_cast<long>(m_cpActionButtonMode));
2438};
2439
2441 const vigra::Diff2D & srcPnt,
2443 unsigned int moveNr,
2445{
2446 long templWidth = wxConfigBase::Get()->Read("/Finetune/TemplateSize",HUGIN_FT_TEMPLATE_SIZE);
2447 long sWidth = templWidth + wxConfigBase::Get()->Read("/Finetune/LocalSearchWidth",HUGIN_FT_LOCAL_SEARCH_WIDTH);
2450 {
2451 return hugin_utils::FDiff2D(-1, -1);
2452 };
2453 movedSrcPnt = result.corrPos;
2454 if (result.corrPos.x < 0 || result.corrPos.y < 0 || result.maxpos.x < 0 || result.maxpos.y < 0)
2455 {
2456 return hugin_utils::FDiff2D(-1, -1);
2457 }
2458 return result.maxpos;
2459}
2460
2462{
2463 DEBUG_DEBUG(" selected Point: " << m_selectedPoint);
2464 if (m_selectedPoint == UINT_MAX) return;
2466
2468
2469 unsigned int srcNr = cp.image1Nr;
2470 unsigned int moveNr = cp.image2Nr;
2471 vigra::Diff2D srcPnt(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
2472 vigra::Diff2D movePnt(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
2473 if (left) {
2474 srcNr = cp.image2Nr;
2475 moveNr = cp.image1Nr;
2476 srcPnt = vigra::Diff2D(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
2477 movePnt = vigra::Diff2D(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
2478 }
2479
2482
2483 if (result.x < 0 || result.y < 0)
2484 {
2485 wxBell();
2486 return;
2487 };
2488
2489 if (left) {
2490 cp.x1 = result.x;
2491 cp.y1 = result.y;
2492 cp.x2 = movedSrcPnt.x;
2493 cp.y2 = movedSrcPnt.y;
2494 } else {
2495 cp.x2 = result.x;
2496 cp.y2 = result.y;
2497 cp.x1 = movedSrcPnt.x;
2498 cp.y1 = movedSrcPnt.y;
2499 }
2500
2501 // if point was mirrored, reverse before setting it.
2503 cp.mirror();
2504 }
2507 );
2508}
2509
2510
2512{
2516 {
2517 return;
2518 }
2519
2522
2523 unsigned int srcNr = m_leftImageNr;
2524 vigra::Diff2D srcPnt(leftP.toDiff2D());
2525 unsigned int moveNr = m_rightImageNr;
2526 vigra::Diff2D movePnt(rightP.toDiff2D());
2527 if (left) {
2529 srcPnt = rightP.toDiff2D();
2531 movePnt = leftP.toDiff2D();
2532 }
2533
2536
2537 if (result.x < 0 || result.y < 0)
2538 {
2539 wxBell();
2540 return;
2541 };
2542 if (left) {
2543 m_leftImg->setNewPoint(result);
2544 m_leftImg->Refresh();
2546 m_rightImg->Refresh();
2547
2548 } else {
2549 m_rightImg->setNewPoint(result);
2550 m_rightImg->Refresh();
2552 m_leftImg->Refresh();
2553 }
2554}
2555
2557{
2558 size_t nrNormalCp = 0;
2559 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
2560 {
2561 if (it->second.mode == HuginBase::ControlPoint::X_Y)
2562 {
2563 ++nrNormalCp;
2564 };
2565 };
2566 if (nrNormalCp==0)
2567 {
2568 DEBUG_WARN("Cannot estimate position without at least one point");
2569 return hugin_utils::FDiff2D(0, 0);
2570 }
2571
2572 // get copy of SrcPanoImage and reset position
2574 leftImg.setYaw(0);
2575 leftImg.setPitch(0);
2576 leftImg.setRoll(0);
2577 leftImg.setX(0);
2578 leftImg.setY(0);
2579 leftImg.setZ(0);
2581 rightImg.setYaw(0);
2582 rightImg.setPitch(0);
2583 rightImg.setRoll(0);
2584 rightImg.setX(0);
2585 rightImg.setY(0);
2586 rightImg.setZ(0);
2587 // generate a temporary pano
2590 optPano.addImage(rightImg);
2591 // construct OptimizeVector
2593 std::set<std::string> opt;
2594 optVec.push_back(opt);
2595 opt.insert("y");
2596 opt.insert("p");
2597 if (nrNormalCp > 1)
2598 {
2599 opt.insert("r");
2600 };
2601 optVec.push_back(opt);
2602 optPano.setOptimizeVector(optVec);
2603 // now add control points, need to check image numbers
2605 for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
2606 {
2607 HuginBase::ControlPoint cp(it->second);
2608 if (cp.mode == HuginBase::ControlPoint::X_Y)
2609 {
2610 cp.image1Nr = left ? 0 : 1;
2611 cp.image2Nr = left ? 1 : 0;
2612 cps.push_back(cp);
2613 };
2614 };
2615 optPano.setCtrlPoints(cps);
2619
2620 // now transform the wanted point p to other image
2622 transformBackward.createInvTransform(optPano.getImage(0), optPano.getOptions());
2624 transformForward.createTransform(optPano.getImage(1), optPano.getOptions());
2626 if (transformBackward.transformImgCoord(t, p))
2627 {
2628 if (transformForward.transformImgCoord(t, t))
2629 {
2630 // clip to fit to
2631 if (t.x < 0) t.x = 0;
2632 if (t.y < 0) t.y = 0;
2633 if (t.x > optPano.getImage(1).getWidth()) t.x = optPano.getImage(1).getWidth();
2634 if (t.y > optPano.getImage(1).getHeight()) t.y = optPano.getImage(1).getHeight();
2635 DEBUG_DEBUG("estimated point " << t.x << "," << t.y);
2636 return t;
2637 };
2638 };
2639 wxBell();
2640 return hugin_utils::FDiff2D(0, 0);
2641}
2642
2644{
2645 int colNum = e.GetColumn();
2646 wxConfigBase::Get()->Write( wxString::Format("/CPEditorPanel/ColumnWidth%d",colNum), m_cpList->GetColumnWidth(colNum) );
2647}
2648
2650{
2651 const int newCol = e.GetColumn();
2652 if (m_sortCol == newCol)
2653 {
2655 }
2656 else
2657 {
2658 m_sortCol = newCol;
2659 m_sortAscending = true;
2660 };
2661 m_cpList->ShowSortIndicator(m_sortCol, m_sortAscending);
2662 SortList();
2663 Refresh();
2664}
2665
2667{
2668 m_leftImg->ShowLines(e.IsChecked());
2669 m_rightImg->ShowLines(e.IsChecked());
2670 Refresh();
2671}
2672
2673CPImageCtrl::ImageRotation CPEditorPanel::GetRot(double yaw, double pitch, double roll)
2674{
2676 // normalize roll angle
2677 while (roll > 360) roll-= 360;
2678 while (roll < 0) roll += 360;
2679
2680 while (pitch > 180) pitch -= 360;
2681 while (pitch < -180) pitch += 360;
2682 bool headOver = (pitch > 90 || pitch < -90);
2683
2684 if (wxConfig::Get()->Read("/CPEditorPanel/AutoRot",1L)) {
2685 if (roll >= 315 || roll < 45) {
2687 } else if (roll >= 45 && roll < 135) {
2689 } else if (roll >= 135 && roll < 225) {
2691 } else {
2693 }
2694 }
2695 return rot;
2696}
2697
2698IMPLEMENT_DYNAMIC_CLASS(CPEditorPanel, wxPanel)
2699
2701 : wxXmlResourceHandler()
2702{
2704}
2705
2707{
2709
2710 cp->Create(m_parentAsWindow,
2711 GetID(),
2712 GetPosition(), GetSize(),
2713 GetStyle("style"),
2714 GetName());
2715
2716 SetupWindow( cp);
2717
2718 return cp;
2719}
2720
2722{
2723 return IsOfClass(node, "CPEditorPanel");
2724}
2725
2726IMPLEMENT_DYNAMIC_CLASS(CPEditorPanelXmlHandler, wxXmlResourceHandler)
2727
options wxIntPtr wxIntPtr sortData
options wxIntPtr item2
int wxCALLBACK SortIndexAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr WXUNUSED(sortData))
#define SORTDESCENDING(functionName, var)
int wxCALLBACK SortIndexDescending(wxIntPtr item1, wxIntPtr item2, wxIntPtr WXUNUSED(sortData))
#define SORTASCENDING(functionName, var)
HuginBase::SrcPanoImage GetImageRotatedTo(const HuginBase::SrcPanoImage &img, const vigra::Diff2D &point, int testWidth, double &neededHFOV)
vigra_ext::CorrelationResult PointFineTuneProjectionAware(const HuginBase::SrcPanoImage &templ, const vigra::UInt8RGBImage &templImg, vigra::Diff2D templPos, int templSize, const HuginBase::SrcPanoImage &search, const vigra::UInt8RGBImage &searchImg, vigra::Diff2D searchPos, int sWidth)
function for fine-tune with remapping to stereographic projection
vigra_ext::CorrelationResult PointFineTuneProjectionAware(const HuginBase::SrcPanoImage &templ, const vigra::UInt8RGBImage &templImg, vigra::Diff2D templPos, int templSize, const HuginBase::SrcPanoImage &search, const vigra::UInt8RGBImage &searchImg, vigra::Diff2D searchPos, int sWidth)
function for fine-tune with remapping to stereographic projection
!! from PTOptimise.h 1951
void registerPTWXDlgFcn()
Definition PTWXDlg.cpp:178
void deregisterPTWXDlgFcn()
Definition PTWXDlg.cpp:185
Dummy progress display, without output.
class, which stores all settings of one cp detector
void SetProg(wxString new_prog)
sets program for one step detector or feature descriptor
virtual bool CanHandle(wxXmlNode *node)
virtual wxObject * DoCreateResource()
control point editor panel.
CPImageCtrl::ImageRotation m_leftRot
HuginBase::PTools::Transform m_leftTransform
void SelectGlobalPoint(unsigned int globalNr)
Select a point.
wxChoice * m_cpModeChoice
bool globalPNr2LocalPNr(unsigned int &localNr, unsigned int globalNr) const
map a global point nr to a local one, if possible
wxPanel * m_cp_ctrls
wxCheckBox * m_showLinesCB
void OnPrevImg(wxCommandEvent &e)
void OnDeleteButton(wxCommandEvent &e)
void OnZoom(wxCommandEvent &e)
hugin_utils::FDiff2D LocalFineTunePoint(unsigned int srcNr, const vigra::Diff2D &srcPnt, hugin_utils::FDiff2D &movedSrcPnt, unsigned int moveNr, const hugin_utils::FDiff2D &movePnt)
void estimateAndAddOtherPoint(const hugin_utils::FDiff2D &p, bool left, CPImageCtrl *thisImg, unsigned int thisImgNr, CPCreationState THIS_POINT, CPCreationState THIS_POINT_RETRY, CPImageCtrl *otherImg, unsigned int otherImgNr, CPCreationState OTHER_POINT, CPCreationState OTHER_POINT_RETRY)
estimate and set point in other image
void OnActionSelectCleanCP(wxCommandEvent &e)
void CreateNewPoint()
this is used to finally create the point in the panorama model
wxListCtrl * m_cpList
void OnFineTuneButton(wxCommandEvent &e)
HuginBase::PTools::Transform m_leftInvTransform
CPTabActionButtonMode m_cpActionButtonMode
CPImagesComboBox * m_leftChoice
wxCheckBox * m_fineTuneCB
void OnActionButton(wxCommandEvent &e)
wxCheckBox * m_autoAddCB
void OnShowLinesCheckbox(wxCommandEvent &e)
void OnKey(wxKeyEvent &e)
wxButton * m_delButton
void OnCPListSelect(wxListEvent &e)
CPCreationState
the state machine for point selection: it is set to the current selection
@ BOTH_POINTS_SELECTED
left and right point selected, waiting for add point.
@ LEFT_POINT
point in left image selected
@ LEFT_POINT_RETRY
right point, finetune for left point failed
@ RIGHT_POINT
selected point in right image
@ RIGHT_POINT_RETRY
point in left image selected, finetune failed in right image
@ NO_POINT
no point selected
void UpdateTransforms()
updated the internal transform object for drawing line in controls
void OnCreateCPButton(wxCommandEvent &e)
virtual ~CPEditorPanel()
dtor.
hugin_utils::FDiff2D EstimatePoint(const hugin_utils::FDiff2D &p, bool left)
Estimate position of point in the other image.
void FineTuneNewPoint(bool left)
void setRightImage(unsigned int imgNr)
set right image
void panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet &imgNr)
notifies about changes to images
wxChoice * m_zoomChoice
HuginBase::PTools::Transform m_rightTransform
wxButton * m_addButton
wxTextCtrl * m_x1Text
void UpdateDisplay(bool newImages)
updates the display after another image has been selected.
void OnColumnWidthChange(wxListEvent &e)
unsigned int m_rightImageNr
wxCheckBox * m_estimateCB
CPScrollHint m_scrollHint
void Init(HuginBase::Panorama *pano)
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name="panel")
std::string m_rightFile
void SortList()
sorting functions
void OnNextImg(wxCommandEvent &e)
void FineTuneSelectedPoint(bool left)
void OnActionContextMenu(wxContextMenuEvent &e)
std::string m_leftFile
HuginBase::Panorama * m_pano
HuginBase::PTools::Transform m_leftInvTransformMag
HuginBase::PTools::Transform m_rightInvTransform
void OnActionSelectCeleste(wxCommandEvent &e)
void OnActionSelectCreate(wxCommandEvent &e)
unsigned int m_selectedPoint
void panoramaChanged(HuginBase::Panorama &pano)
called when the panorama changes and we should update our display
HuginBase::PTools::Transform m_rightInvTransformMag
bool PointFineTune(unsigned int tmplImgNr, const vigra::Diff2D &tmplPoint, int tmplWidth, unsigned int subjImgNr, const hugin_utils::FDiff2D &subjPoint, int searchWidth, vigra_ext::CorrelationResult &tunedPos)
search for region in destImg
void SelectLocalPoint(unsigned int LVpointNr, bool scrollLeft=true, bool scrollRight=true)
select a local point.
wxButton * m_prevImgButton
void OnLeftChoiceChange(wxCommandEvent &e)
CPImageCtrl * m_leftImg
CPImageCtrl * m_rightImg
const float getVerticalCPBias()
CPCreationState cpCreationState
void OnCelesteButton(wxCommandEvent &e)
void OnColumnHeaderClick(wxListEvent &e)
void EnablePointEdit(bool state)
enable or disable controls for editing other points
wxMenu * m_cpActionContextMenu
void setLeftImage(unsigned int imgNr)
set left image
void changeState(CPCreationState newState)
void OnCleanCPButton(wxCommandEvent &e)
wxTextCtrl * m_y1Text
wxButton * m_finetuneButton
std::set< unsigned int > mirroredPoints
void OnTextPointChange(wxCommandEvent &e)
void OnRightChoiceChange(wxCommandEvent &e)
wxButton * m_nextImgButton
CPImageCtrl::ImageRotation GetRot(double yaw, double roll, double pitch)
calculate rotation required for upright image display from roll, pitch and yaw angles
unsigned int m_leftImageNr
wxTextCtrl * m_x2Text
bool m_listenToPageChange
CPImageCtrl::ImageRotation m_rightRot
void NewPointChange(hugin_utils::FDiff2D p, bool left)
wxButton * m_actionButton
void ClearSelection()
unselect a point
double m_detailZoomFactor
wxTextCtrl * m_y2Text
HuginBase::PTools::Transform m_rightTransformMag
void OnCPListDeselect(wxListEvent &e)
void OnCPEvent(CPEvent &ev)
unsigned int localPNr2GlobalPNr(unsigned int localNr) const
find a local point
void OnAddButton(wxCommandEvent &e)
CPImagesComboBox * m_rightChoice
void ShowControlPoint(unsigned int cpNr)
show a control point
HuginBase::PTools::Transform m_leftTransformMag
HuginBase::CPointVector currentPoints
Events to notify about new point / region / point change.
Definition CPImageCtrl.h:41
const hugin_utils::FDiff2D & getPoint()
Definition CPImageCtrl.h:75
unsigned int getPointNr()
Definition CPImageCtrl.h:78
const wxRect & getRect()
Definition CPImageCtrl.h:72
@ POINT_CHANGED
Definition CPImageCtrl.h:44
@ DELETE_REGION_SELECTED
Definition CPImageCtrl.h:44
@ RIGHT_CLICK
Definition CPImageCtrl.h:44
@ NEW_LINE_ADDED
Definition CPImageCtrl.h:44
@ NEW_POINT_CHANGED
Definition CPImageCtrl.h:44
@ POINT_SELECTED
Definition CPImageCtrl.h:44
const HuginBase::ControlPoint & getControlPoint()
Definition CPImageCtrl.h:81
CPEventMode getMode()
accessor functions (they could check mode to see if a getXYZ() is allowed
Definition CPImageCtrl.h:69
brief description.
void showSearchArea(bool show=true)
show/hide the search area rectangle
ImageRotation
image rotation.
void clearNewPoint()
clear new point
void setTransforms(HuginBase::PTools::Transform *firstTrans, HuginBase::PTools::Transform *firstInvTrans, HuginBase::PTools::Transform *secondInvTrans)
void clearCtrlPointList()
clear internal control point list
wxPoint MaxScrollDelta(wxPoint delta)
calculate maximum delta that is allowed when scrolling
void setSameImage(bool sameImage)
void Init(CPEditorPanel *parent)
void setImage(const std::string &filename, ImageRotation rot)
display img.
double getScale()
return scale factor, 0 for autoscale
hugin_utils::FDiff2D getNewPoint()
get the new point
void setCtrlPoint(const HuginBase::ControlPoint &cp, const bool mirrored)
add control piont to internal cp list
void deselect()
remove selection.
void setNewPoint(const hugin_utils::FDiff2D &p)
set new point to a specific point
void showTemplateArea(bool show=true)
void ShowLines(bool isShown)
void setScale(double factor)
set the scaling factor for cp display.
void setMagTransforms(HuginBase::PTools::Transform *magTrans, HuginBase::PTools::Transform *magInvTrans)
void ScrollDelta(const wxPoint &delta)
scroll the window by delta pixels
void selectPoint(unsigned int, bool scrollTo=true)
select a point for usage
Owner Drawn ComboBox for showing connected images on CP tab.
void SetRefImage(HuginBase::Panorama *pano, unsigned int newRefImg)
Set new reference image.
void CalcCPDistance(HuginBase::Panorama *pano)
Get maximum CP distance for all images pairs containing the reference image
static double calcOptimalPanoScale(const SrcPanoImage &src, const PanoramaOptions &dest)
function to calculate the scaling factor so that the distances in the input image and panorama image ...
represents a control point
void mirror()
swap (image1Nr,x1,y1) with (image2Nr,x2,y2)
@ X
evaluate x, points are on a vertical line
@ Y
evaluate y, points are on a horizontal line
Holds transformations for Image -> Pano and the other way.
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
void createTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
initialize pano->image transformation
void createInvTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
create image->pano transformation
Panorama image options.
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
void setHeight(unsigned int h)
set panorama height
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
unsigned int getWidth() const
PanoramaOptions::ProjectionFormat getProjection() const
ProjectionFormat
Projection of final panorama.
vigra::Size2D getSize() const
get size of output image
Model for a panorama.
Definition Panorama.h:153
const VariableMap getImageVariables(unsigned int imgNr) const
Get the variables of an image.
Definition Panorama.cpp:128
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
int getNextCPTypeLineNumber() const
get the number of a control point
void addObserver(PanoramaObserver *o)
add a panorama observer.
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
const ControlPoint & getCtrlPoint(std::size_t nr) const
get a control point, counting starts with 0
Definition Panorama.h:312
std::size_t getNrOfCtrlPoints() const
number of control points
Definition Panorama.h:306
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition Panorama.cpp:319
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition Panorama.h:319
bool removeObserver(PanoramaObserver *observer)
remove a panorama observer.
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
int getWidth() const
Get the width of the image in pixels.
void setSize(vigra::Size2D val)
Set the image size in pixels.
int getHeight() const
Get the height of the image in pixels.
static MainFrame * Get()
hack.. kind of a pseudo singleton...
void RunCPGenerator(CPDetectorSetting &setting, const HuginBase::UIntSet &img)
run the cp generator with the given setting on selected images
struct celeste::svm_model * GetSVMModel()
general : Matrix3 is a class for handling 3x3 Matrix manipulation.
Definition Matrix3.h:38
void SetRotationPT(double yaw, double pitch, double roll)
set rotation in panotools style, code adapted from Panotools-Script by Bruno Postle
Definition Matrix3.cpp:110
void GetRotationPT(double &Yaw, double &Pitch, double &Roll)
GetRotation in panotools style.
Definition Matrix3.cpp:157
add a control point
change a control point
void addCommand(PanoCommand *command, bool execute=true)
Adds a command to the history.
static GlobalCmdHist & getInstance()
remove a control point
remove several control points
Handle EVT_KILL_FOCUS and convert it to a EVT_TEXT_ENTER event.
functions to handle icc profiles in images
#define HUGIN_FT_TEMPLATE_SIZE
#define HUGIN_FT_ROTATION_START_ANGLE
#define HUGIN_FT_ROTATION_STEPS
#define HUGIN_FT_ROTATION_SEARCH
#define HUGIN_FT_CURV_THRESHOLD
#define HUGIN_FT_LOCAL_SEARCH_WIDTH
#define HUGIN_CELESTE_FILTER
#define HUGIN_FT_ROTATION_STOP_ANGLE
#define HUGIN_CELESTE_THRESHOLD
#define HUGIN_FT_SEARCH_AREA_PERCENT
#define HUGIN_FT_CORR_THRESHOLD
#define HUGIN_CONV_FILENAME
Definition platform.h:40
#define RAD_TO_DEG(x)
Definition hugin_math.h:45
#define DEG_TO_RAD(x)
Definition hugin_math.h:44
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_FATAL(msg)
Definition utils.h:78
#define DEBUG_TRACE(msg)
Definition utils.h:67
#define DEBUG_WARN(msg)
Definition utils.h:74
void ApplyICCProfile(ImageType &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsUInt32Number imageFormat)
converts given image with iccProfile to sRGB/gray space, need to give pixel type in lcms2 format work...
Definition cms.h:37
unsigned int optimize(PanoramaData &pano, const char *userScript)
optimize the images imgs, for variables optvec, using vars as start.
std::vector< ControlPoint > CPVector
std::vector< std::set< std::string > > OptimizeVector
std::set< unsigned int > UIntSet
HuginBase::UIntSet getCelesteControlPoints(struct svm_model *model, vigra::UInt16RGBImage &input, HuginBase::CPointVector cps, int radius, float threshold, int resize_dimension, bool verbose)
Definition Celeste.cpp:363
bool str2double(const wxString &s, double &d)
TDiff2D< double > FDiff2D
Definition hugin_math.h:162
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:176
std::unique_ptr< wxMessageDialogBase > MessageDialog
Definition wxutils.h:90
int roundi(T x)
Definition hugin_math.h:73
MessageDialog GetMessageDialog(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:212
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
void transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
CorrelationResult PointFineTuneRotSearch(const IMAGET &templImg, vigra::Diff2D templPos, int templSize, const IMAGES &searchImg, vigra::Diff2D searchPos, int sWidth, double startAngle, double stopAngle, int angleSteps)
fine tune a point with normalized cross correlation, searches x,y and phi (rotation around z)
CorrelationResult PointFineTune(const IMAGET &templImg, ACCESSORT access_t, vigra::Diff2D templPos, int templSize, const IMAGES &searchImg, ACCESSORS access_s, vigra::Diff2D searchPos, int sWidth)
fine tune a point with normalized cross correlation
include file for the hugin project
include file for the hugin project
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
const Map::mapped_type & const_map_get(const Map &m, const typename Map::key_type &key)
Definition stl_utils.h:110
vigra::Diff2D toDiff2D() const
Definition hugin_math.h:141
Maximum of correlation, position and value.
Definition Correlation.h:57
hugin_utils::FDiff2D corrPos
Definition Correlation.h:66
hugin_utils::FDiff2D curv
Definition Correlation.h:68
hugin_utils::FDiff2D maxpos
Definition Correlation.h:64
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
wxwindows specific panorama commands
platform/compiler specific stuff.