CPEditorPanel.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include <config.h>
00028 
00029 // often necessary before panoinc.h
00030 #ifdef __APPLE__
00031 #include "panoinc_WX.h"
00032 #endif
00033 // standard hugin include
00034 #include "panoinc.h"
00035 // both includes above need to come before other wx includes on OSX
00036 
00037 // hugin's
00038 #include "hugin/huginApp.h"
00039 #include "hugin/config_defaults.h"
00040 #include "base_wx/CommandHistory.h"
00041 #include "base_wx/wxImageCache.h"
00042 #include "hugin/CPImageCtrl.h"
00043 #include "hugin/TextKillFocusHandler.h"
00044 #include "hugin/CPEditorPanel.h"
00045 #include "base_wx/wxPanoCommand.h"
00046 #include "base_wx/MyProgressDialog.h"
00047 #include "algorithms/optimizer/PTOptimizer.h"
00048 #include "algorithms/basic/CalculateOptimalScale.h"
00049 #include "base_wx/PTWXDlg.h"
00050 #include "base_wx/wxPlatform.h"
00051 
00052 // more standard includes if needed
00053 #include <algorithm>
00054 #include <float.h>
00055 #include <vector>
00056 
00057 // more vigra include if needed
00058 #include "vigra/cornerdetection.hxx"
00059 #include "vigra/localminmax.hxx"
00060 #include "vigra_ext/openmp_vigra.h"
00061 #include "vigra_ext/Correlation.h"
00062 #include "vigra_ext/cms.h"
00063 
00064 // Celeste header
00065 #include "Celeste.h"
00066 
00067 BEGIN_EVENT_TABLE(CPEditorPanel, wxPanel)
00068     EVT_CPEVENT(CPEditorPanel::OnCPEvent)
00069     EVT_COMBOBOX(XRCID("cp_editor_left_choice"), CPEditorPanel::OnLeftChoiceChange )
00070     EVT_COMBOBOX(XRCID("cp_editor_right_choice"), CPEditorPanel::OnRightChoiceChange )
00071     EVT_LIST_ITEM_SELECTED(XRCID("cp_editor_cp_list"), CPEditorPanel::OnCPListSelect)
00072     EVT_LIST_ITEM_DESELECTED(XRCID("cp_editor_cp_list"), CPEditorPanel::OnCPListDeselect)
00073     EVT_LIST_COL_END_DRAG(XRCID("cp_editor_cp_list"), CPEditorPanel::OnColumnWidthChange)
00074     EVT_CHOICE(XRCID("cp_editor_choice_zoom"), CPEditorPanel::OnZoom)
00075     EVT_TEXT_ENTER(XRCID("cp_editor_x1"), CPEditorPanel::OnTextPointChange )
00076     EVT_TEXT_ENTER(XRCID("cp_editor_y1"), CPEditorPanel::OnTextPointChange )
00077     EVT_TEXT_ENTER(XRCID("cp_editor_x2"), CPEditorPanel::OnTextPointChange )
00078     EVT_TEXT_ENTER(XRCID("cp_editor_y2"), CPEditorPanel::OnTextPointChange )
00079     EVT_CHOICE(XRCID("cp_editor_mode"), CPEditorPanel::OnTextPointChange )
00080     EVT_CHAR(CPEditorPanel::OnKey)
00081     EVT_BUTTON(XRCID("cp_editor_delete"), CPEditorPanel::OnDeleteButton)
00082     EVT_BUTTON(XRCID("cp_editor_add"), CPEditorPanel::OnAddButton)
00083     EVT_BUTTON(XRCID("cp_editor_previous_img"), CPEditorPanel::OnPrevImg)
00084     EVT_BUTTON(XRCID("cp_editor_next_img"), CPEditorPanel::OnNextImg)
00085     EVT_BUTTON(XRCID("cp_editor_finetune_button"), CPEditorPanel::OnFineTuneButton)
00086     EVT_BUTTON(XRCID("cp_editor_action_button"), CPEditorPanel::OnActionButton)
00087     EVT_MENU(XRCID("cp_menu_create_cp"), CPEditorPanel::OnActionSelectCreate)
00088     EVT_MENU(XRCID("cp_menu_celeste"), CPEditorPanel::OnActionSelectCeleste)
00089     EVT_MENU(XRCID("cp_menu_clean_cp"), CPEditorPanel::OnActionSelectCleanCP)
00090 END_EVENT_TABLE()
00091 
00092 CPEditorPanel::CPEditorPanel()
00093 {
00094     DEBUG_TRACE("**********************");
00095     m_pano = 0;
00096     m_countCP = 0;
00097 }
00098 
00099 bool CPEditorPanel::Create(wxWindow* parent, wxWindowID id,
00100                     const wxPoint& pos,
00101                     const wxSize& size,
00102                     long style,
00103                     const wxString& name)
00104 {
00105     DEBUG_TRACE(" Create called *************");
00106     if (! wxPanel::Create(parent, id, pos, size, style, name) ) {
00107         return false;
00108     }
00109 
00110     cpCreationState = NO_POINT;
00111     m_leftImageNr=UINT_MAX;
00112     m_rightImageNr=UINT_MAX;
00113     m_listenToPageChange=true;
00114     m_detailZoomFactor=1;
00115     m_selectedPoint=UINT_MAX;
00116     m_leftRot=CPImageCtrl::ROT0;
00117     m_rightRot=CPImageCtrl::ROT0;
00118 
00119     DEBUG_TRACE("");
00120     wxXmlResource::Get()->LoadPanel(this, wxT("cp_editor_panel"));
00121     wxPanel * panel = XRCCTRL(*this, "cp_editor_panel", wxPanel);
00122 
00123     wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
00124     topsizer->Add(panel, 1, wxEXPAND, 0);
00125 
00126     m_leftChoice = XRCCTRL(*this, "cp_editor_left_choice", CPImagesComboBox); 
00127     m_leftImg = XRCCTRL(*this, "cp_editor_left_img", CPImageCtrl);
00128     assert(m_leftImg);
00129     m_leftImg->Init(this);
00130     m_leftImg->setTransforms(&m_leftTransform, &m_leftInvTransform, &m_rightInvTransform);
00131 
00132     // right image
00133     m_rightChoice = XRCCTRL(*this, "cp_editor_right_choice", CPImagesComboBox);
00134     m_rightImg = XRCCTRL(*this, "cp_editor_right_img", CPImageCtrl);
00135     assert(m_rightImg);
00136     m_rightImg->Init(this);
00137     m_rightImg->setTransforms(&m_rightTransform, &m_rightInvTransform, &m_leftInvTransform);
00138 
00139     // setup list view
00140     m_cpList = XRCCTRL(*this, "cp_editor_cp_list", wxListCtrl);
00141     m_cpList->Connect(wxEVT_CHAR,wxKeyEventHandler(CPEditorPanel::OnKey),NULL,this);
00142     m_cpList->InsertColumn( 0, _("#"), wxLIST_FORMAT_RIGHT, 35);
00143     m_cpList->InsertColumn( 1, _("left x"), wxLIST_FORMAT_RIGHT, 65);
00144     m_cpList->InsertColumn( 2, _("left y"), wxLIST_FORMAT_RIGHT, 65);
00145     m_cpList->InsertColumn( 3, _("right x"), wxLIST_FORMAT_RIGHT, 65);
00146     m_cpList->InsertColumn( 4, _("right y"), wxLIST_FORMAT_RIGHT, 65);
00147     m_cpList->InsertColumn( 5, _("Alignment"), wxLIST_FORMAT_LEFT,110 );
00148     m_cpList->InsertColumn( 6, _("Distance"), wxLIST_FORMAT_RIGHT, 110);
00149 
00150     //get saved width
00151     wxConfigBase* config = wxConfig::Get();
00152     for ( int j=0; j < m_cpList->GetColumnCount() ; j++ )
00153     {
00154         // -1 is auto
00155         int width = config->Read(wxString::Format( wxT("/CPEditorPanel/ColumnWidth%d"), j ), -1);
00156         if(width != -1)
00157             m_cpList->SetColumnWidth(j, width);
00158     }
00159 
00160     // other controls
00161     m_x1Text = XRCCTRL(*this,"cp_editor_x1", wxTextCtrl);
00162     m_x1Text->PushEventHandler(new TextKillFocusHandler(this));
00163     m_y1Text = XRCCTRL(*this,"cp_editor_y1", wxTextCtrl);
00164     m_y1Text->PushEventHandler(new TextKillFocusHandler(this));
00165     m_x2Text = XRCCTRL(*this,"cp_editor_x2", wxTextCtrl);
00166     m_x2Text->PushEventHandler(new TextKillFocusHandler(this));
00167     m_y2Text = XRCCTRL(*this,"cp_editor_y2", wxTextCtrl);
00168     m_y2Text->PushEventHandler(new TextKillFocusHandler(this));
00169 
00170     m_cpModeChoice = XRCCTRL(*this, "cp_editor_mode", wxChoice);
00171     m_addButton = XRCCTRL(*this, "cp_editor_add", wxButton);
00172     m_delButton = XRCCTRL(*this, "cp_editor_delete", wxButton);
00173 
00174     m_autoAddCB = XRCCTRL(*this,"cp_editor_auto_add", wxCheckBox);
00175     DEBUG_ASSERT(m_autoAddCB);
00176     m_fineTuneCB = XRCCTRL(*this,"cp_editor_fine_tune_check",wxCheckBox);
00177     DEBUG_ASSERT(m_fineTuneCB);
00178 
00179     m_estimateCB = XRCCTRL(*this,"cp_editor_auto_estimate", wxCheckBox);
00180     DEBUG_ASSERT(m_estimateCB);
00181 
00182     m_actionButton = XRCCTRL(*this, "cp_editor_action_button", wxButton);
00183     m_actionButton->Connect(wxEVT_CONTEXT_MENU, wxContextMenuEventHandler(CPEditorPanel::OnActionContextMenu), NULL, this);
00184     m_cpActionContextMenu = wxXmlResource::Get()->LoadMenu(wxT("cp_menu_action"));
00185     // setup scroll window for the controls under the images
00186     m_cp_ctrls = XRCCTRL(*this, "cp_controls_panel", wxPanel);
00187     DEBUG_ASSERT(m_cp_ctrls);
00188 
00189     m_autoAddCB->SetValue(config->Read(wxT("/CPEditorPanel/autoAdd"),0l) != 0 );
00190     m_fineTuneCB->SetValue(config->Read(wxT("/CPEditorPanel/autoFineTune"),1l) != 0 );
00191     m_estimateCB->SetValue(config->Read(wxT("/CPEditorPanel/autoEstimate"),1l) != 0 );
00192 
00193     // disable controls by default
00194     m_cpModeChoice->Disable();
00195     m_addButton->Disable();
00196     m_delButton->Disable();
00197     m_autoAddCB->Disable();
00198     m_fineTuneCB->Disable();
00199     m_estimateCB->Disable();
00200     XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Disable();
00201     m_actionButton->Disable();
00202     XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice)->Disable();
00203     XRCCTRL(*this, "cp_editor_previous_img", wxButton)->Disable();
00204     XRCCTRL(*this, "cp_editor_next_img", wxButton)->Disable();
00205     m_leftChoice->Disable();
00206     m_rightChoice->Disable();
00207 
00208     // apply zoom specified in xrc file
00209     wxCommandEvent dummy;
00210     dummy.SetInt(XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->GetSelection());
00211     OnZoom(dummy);
00212 
00213     SetSizer( topsizer );
00214     // read last used action setting
00215     m_cpActionButtonMode = static_cast<CPTabActionButtonMode>(config->Read(wxT("/CPEditorPanel/ActionMode"), 1l));
00216     switch (m_cpActionButtonMode)
00217     {
00218         case CPTAB_ACTION_CREATE_CP:
00219             m_cpActionContextMenu->Check(XRCID("cp_menu_create_cp"), true);
00220             {
00221                 wxCommandEvent e;
00222                 OnActionSelectCreate(e);
00223             };
00224             break;
00225         case CPTAB_ACTION_CLEAN_CP:
00226             m_cpActionContextMenu->Check(XRCID("cp_menu_clean_cp"), true);
00227             {
00228                 wxCommandEvent e;
00229                 OnActionSelectCleanCP(e);
00230             };
00231             break;
00232         case CPTAB_ACTION_CELESTE:
00233         default:
00234             m_cpActionContextMenu->Check(XRCID("cp_menu_celeste"), true);
00235             {
00236                 wxCommandEvent e;
00237                 OnActionSelectCeleste(e);
00238             };
00239             break;
00240     };
00241 
00242     return true;
00243 }
00244 
00245 void CPEditorPanel::Init(HuginBase::Panorama * pano)
00246 {
00247     m_pano=pano;
00248     // observe the panorama
00249     m_pano->addObserver(this);
00250 }
00251 
00252 CPEditorPanel::~CPEditorPanel()
00253 {
00254     DEBUG_TRACE("dtor");
00255 
00256     m_x1Text->PopEventHandler(true);
00257     m_y1Text->PopEventHandler(true);
00258     m_x2Text->PopEventHandler(true);
00259     m_y2Text->PopEventHandler(true);
00260 
00261     wxConfigBase::Get()->Write(wxT("/CPEditorPanel/autoAdd"), m_autoAddCB->IsChecked() ? 1 : 0);
00262     wxConfigBase::Get()->Write(wxT("/CPEditorPanel/autoFineTune"), m_fineTuneCB->IsChecked() ? 1 : 0);
00263     wxConfigBase::Get()->Write(wxT("/CPEditorPanel/autoEstimate"), m_estimateCB->IsChecked() ? 1 : 0);
00264 
00265     m_pano->removeObserver(this);
00266     DEBUG_TRACE("dtor end");
00267 }
00268 
00269 void CPEditorPanel::setLeftImage(unsigned int imgNr)
00270 {
00271     DEBUG_TRACE("image " << imgNr);
00272     if (imgNr == UINT_MAX) {
00273         m_leftImg->setImage("", CPImageCtrl::ROT0);
00274         m_leftImageNr = imgNr;
00275         m_leftFile = "";
00276         changeState(NO_POINT);
00277         UpdateDisplay(true);
00278     } else if (m_leftImageNr != imgNr) {
00279         double yaw = const_map_get(m_pano->getImageVariables(imgNr),"y").getValue();
00280         double pitch = const_map_get(m_pano->getImageVariables(imgNr),"p").getValue();
00281         double roll = const_map_get(m_pano->getImageVariables(imgNr),"r").getValue();
00282         m_leftRot = GetRot(yaw, pitch, roll);
00283         m_leftImg->setImage(m_pano->getImage(imgNr).getFilename(), m_leftRot);
00284         m_leftImageNr = imgNr;
00285         if (m_leftChoice->GetSelection() != (int) imgNr) {
00286             m_leftChoice->SetSelection(imgNr);
00287         }
00288         m_rightChoice->SetRefImage(m_pano,m_leftImageNr);
00289         m_rightChoice->Refresh();
00290         m_leftFile = m_pano->getImage(imgNr).getFilename();
00291         changeState(NO_POINT);
00292         UpdateDisplay(true);
00293     }
00294     m_selectedPoint = UINT_MAX;
00295     // FIXME: lets hope that nobody holds references to these images..
00296     ImageCache::getInstance().softFlush();
00297     UpdateTransforms();
00298 }
00299 
00300 
00301 void CPEditorPanel::setRightImage(unsigned int imgNr)
00302 {
00303     DEBUG_TRACE("image " << imgNr);
00304     if (imgNr == UINT_MAX) {
00305         m_rightImg->setImage("", CPImageCtrl::ROT0);
00306         m_rightImageNr = imgNr;
00307         m_rightFile = "";
00308         m_rightRot = CPImageCtrl::ROT0;
00309         changeState(NO_POINT);
00310         UpdateDisplay(true);
00311     } else if (m_rightImageNr != imgNr) {
00312         // set the new image
00313         double yaw = const_map_get(m_pano->getImageVariables(imgNr),"y").getValue();
00314         double pitch = const_map_get(m_pano->getImageVariables(imgNr),"p").getValue();
00315         double roll = const_map_get(m_pano->getImageVariables(imgNr),"r").getValue();
00316         m_rightRot = GetRot(yaw, pitch, roll);
00317         m_rightImg->setImage(m_pano->getImage(imgNr).getFilename(), m_rightRot);
00318         // select tab
00319         m_rightImageNr = imgNr;
00320         if (m_rightChoice->GetSelection() != (int) imgNr) {
00321             m_rightChoice->SetSelection(imgNr);
00322         }
00323         m_leftChoice->SetRefImage(m_pano,m_rightImageNr);
00324         m_leftChoice->Refresh();
00325         m_rightFile = m_pano->getImage(imgNr).getFilename();
00326         // update the rest of the display (new control points etc)
00327         changeState(NO_POINT);
00328         UpdateDisplay(true);
00329     }
00330     m_selectedPoint = UINT_MAX;
00331 
00332     // FIXME: lets hope that nobody holds references to these images..
00333     ImageCache::getInstance().softFlush();
00334     UpdateTransforms();
00335 }
00336 
00337 void CPEditorPanel::UpdateTransforms()
00338 {
00339     if(m_leftImageNr<m_pano->getNrOfImages())
00340     {
00341         m_leftTransform.createTransform(m_pano->getImage(m_leftImageNr), m_pano->getOptions());
00342         m_leftInvTransform.createInvTransform(m_pano->getImage(m_leftImageNr), m_pano->getOptions());
00343     };
00344     if(m_rightImageNr<m_pano->getNrOfImages())
00345     {
00346         m_rightTransform.createTransform(m_pano->getImage(m_rightImageNr), m_pano->getOptions());
00347         m_rightInvTransform.createInvTransform(m_pano->getImage(m_rightImageNr), m_pano->getOptions());
00348     };
00349 };
00350 
00351 void CPEditorPanel::OnCPEvent( CPEvent&  ev)
00352 {
00353     DEBUG_TRACE("");
00354     wxString text;
00355     unsigned int nr = ev.getPointNr();
00356     hugin_utils::FDiff2D point = ev.getPoint();
00357     bool left (TRUE);
00358     if (ev.GetEventObject() == m_leftImg) {
00359         left = true;
00360     } else  if (ev.GetEventObject() == m_rightImg){
00361         left = false;
00362     } else {
00363         DEBUG_FATAL("UNKNOWN SOURCE OF CPEvent");
00364     }
00365 
00366     switch (ev.getMode()) {
00367     case CPEvent::NONE:
00368         text = wxT("NONE");
00369         break;
00370     case CPEvent::NEW_POINT_CHANGED:
00371         NewPointChange(ev.getPoint(),left);
00372         break;
00373     case CPEvent::POINT_SELECTED:
00374         // need to reset cpEditState
00375         DEBUG_DEBUG("selected point " << nr);
00376         SelectLocalPoint(nr);
00377         changeState(NO_POINT);
00378         break;
00379     case CPEvent::NEW_LINE_ADDED:
00380         {
00381             float vertBias = getVerticalCPBias();
00382             HuginBase::ControlPoint cp = ev.getControlPoint();
00383             cp.image1Nr=m_leftImageNr;
00384             cp.image2Nr=m_rightImageNr;
00385             bool  hor = abs(cp.x1 - cp.x2) > (abs(cp.y1 - cp.y2) * vertBias);
00386             switch (m_leftRot)
00387             {
00388                 case CPImageCtrl::ROT0:
00389                 case CPImageCtrl::ROT180:
00390                     if (hor)
00391                         cp.mode = HuginBase::ControlPoint::Y;
00392                     else
00393                         cp.mode = HuginBase::ControlPoint::X;
00394                     break;
00395                 default:
00396                     if (hor)
00397                         cp.mode = HuginBase::ControlPoint::X;
00398                     else
00399                         cp.mode = HuginBase::ControlPoint::Y;
00400                     break;
00401             }
00402             changeState(NO_POINT);
00403             // create points
00404             PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::AddCtrlPointCmd(*m_pano, cp));
00405             // select new control Point
00406             unsigned int lPoint = m_pano->getNrOfCtrlPoints()-1;
00407             SelectGlobalPoint(lPoint);
00408             changeState(NO_POINT);
00409             MainFrame::Get()->SetStatusText(_("new control point added"));
00410             m_leftChoice->CalcCPDistance(m_pano);
00411             m_rightChoice->CalcCPDistance(m_pano);
00412             break;
00413         };
00414     case CPEvent::POINT_CHANGED:
00415         {
00416             DEBUG_DEBUG("move point("<< nr << ")");
00417             if (nr >= currentPoints.size()) {
00418                 DEBUG_ERROR("invalid point number while moving point")
00419                 return;
00420             }
00421             HuginBase::ControlPoint cp = ev.getControlPoint();
00422             changeState(NO_POINT);
00423             DEBUG_DEBUG("changing point to: " << cp.x1 << "," << cp.y1
00424                         << "  " << cp.x2 << "," << cp.y2);
00425 
00426             PanoCommand::GlobalCmdHist::getInstance().addCommand(
00427                 new PanoCommand::ChangeCtrlPointCmd(*m_pano, currentPoints[nr].first, cp)
00428                 );
00429 
00430             break;
00431         }
00432     case CPEvent::RIGHT_CLICK:
00433         {
00434             if (cpCreationState == BOTH_POINTS_SELECTED) {
00435                 DEBUG_DEBUG("right click -> adding point");
00436                 CreateNewPoint();
00437             } else {
00438                 DEBUG_DEBUG("right click without two points..");
00439                 changeState(NO_POINT);
00440             }
00441             break;
00442         }
00443     case CPEvent::SCROLLED:
00444         {
00445             wxPoint d(hugin_utils::roundi(point.x), hugin_utils::roundi(point.y));
00446             d = m_rightImg->MaxScrollDelta(d);
00447             d = m_leftImg->MaxScrollDelta(d);
00448             m_rightImg->ScrollDelta(d);
00449             m_leftImg->ScrollDelta(d);
00450         }
00451         break;
00452     case CPEvent::DELETE_REGION_SELECTED:
00453         {
00454             HuginBase::UIntSet cpToRemove;
00455             if(!currentPoints.empty())
00456             {
00457                 wxRect rect=ev.getRect();
00458                 for(unsigned int i=0;i<currentPoints.size();i++)
00459                 {
00460                     HuginBase::ControlPoint cp = currentPoints[i].second;
00461                     if (cp.mode == HuginBase::ControlPoint::X_Y)
00462                     {
00463                         //checking only normal control points
00464                         if(left)
00465                         {
00466                             if (rect.Contains(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1)))
00467                             {
00468                                 cpToRemove.insert(localPNr2GlobalPNr(i));
00469                             };
00470                         }
00471                         else
00472                         {
00473                             if (rect.Contains(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2)))
00474                             {
00475                                 cpToRemove.insert(localPNr2GlobalPNr(i));
00476                             };
00477                         };
00478                     };
00479                 };
00480             };
00481             changeState(NO_POINT);
00482             if(cpToRemove.size()>0)
00483             {
00484                 PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::RemoveCtrlPointsCmd(*m_pano, cpToRemove));
00485             };
00486             break;
00487         }
00488     } //end switch
00489     m_leftImg->update();
00490     m_rightImg->update();
00491 }
00492 
00493 
00494 void CPEditorPanel::CreateNewPoint()
00495 {
00496     DEBUG_TRACE("");
00497     hugin_utils::FDiff2D p1 = m_leftImg->getNewPoint();
00498     hugin_utils::FDiff2D p2 = m_rightImg->getNewPoint();
00499     HuginBase::ControlPoint point;
00500     point.image1Nr = m_leftImageNr;
00501     point.x1 = p1.x;
00502     point.y1 = p1.y;
00503     point.image2Nr = m_rightImageNr;
00504     point.x2 = p2.x;
00505     point.y2 = p2.y;
00506     if (point.image1Nr == point.image2Nr) {
00507         if (m_cpModeChoice->GetSelection()>=3) {
00508             // keep line until user chooses new mode
00509             point.mode = m_cpModeChoice->GetSelection();
00510         } else {
00511             // Most projections will have a bias to creating vertical
00512             // constraints.
00513             float vertBias = getVerticalCPBias();
00514             bool  hor = abs(p1.x - p2.x) > (abs(p1.y - p2.y) * vertBias);
00515             switch (m_leftRot) {
00516                 case CPImageCtrl::ROT0:
00517                 case CPImageCtrl::ROT180:
00518                     if (hor)
00519                         point.mode = HuginBase::ControlPoint::Y;
00520                     else
00521                         point.mode = HuginBase::ControlPoint::X;
00522                     break;
00523                 default:
00524                     if (hor)
00525                         point.mode = HuginBase::ControlPoint::X;
00526                     else
00527                         point.mode = HuginBase::ControlPoint::Y;
00528                     break;
00529             }
00530         }
00531     } else {
00532         point.mode = HuginBase::ControlPoint::X_Y;
00533     }
00534 
00535     changeState(NO_POINT);
00536 
00537     // create points
00538     PanoCommand::GlobalCmdHist::getInstance().addCommand(
00539         new PanoCommand::AddCtrlPointCmd(*m_pano, point)
00540         );
00541 
00542 
00543     // select new control Point
00544     unsigned int lPoint = m_pano->getNrOfCtrlPoints() -1;
00545     SelectGlobalPoint(lPoint);
00546     changeState(NO_POINT);
00547     MainFrame::Get()->SetStatusText(_("new control point added"));
00548     m_leftChoice->CalcCPDistance(m_pano);
00549     m_rightChoice->CalcCPDistance(m_pano);
00550 }
00551 
00552 
00553 const float CPEditorPanel::getVerticalCPBias()
00554 {
00555     HuginBase::PanoramaOptions opts = m_pano->getOptions();
00556     HuginBase::PanoramaOptions::ProjectionFormat projFormat = opts.getProjection();
00557     float bias;
00558     switch (projFormat)
00559     {
00560         case HuginBase::PanoramaOptions::RECTILINEAR:
00561             bias = 1.0;
00562             break;
00563         default:
00564             bias = 2.0;
00565             break;
00566     }
00567     return bias;
00568 }
00569 
00570 
00571 void CPEditorPanel::ClearSelection()
00572 {
00573     if (m_selectedPoint == UINT_MAX) {
00574         // no point selected, no need to select one.
00575         return;
00576     }
00577     m_cpList->SetItemState(m_selectedPoint, 0, wxLIST_STATE_SELECTED);
00578 
00579     m_selectedPoint=UINT_MAX;
00580     changeState(NO_POINT);
00581     m_leftImg->deselect();
00582     m_rightImg->deselect();
00583     UpdateDisplay(false);
00584 }
00585 
00586 void CPEditorPanel::SelectLocalPoint(unsigned int LVpointNr)
00587 {
00588     DEBUG_TRACE("selectLocalPoint(" << LVpointNr << ")");
00589 
00590     if ( m_selectedPoint == LVpointNr) {
00591         DEBUG_DEBUG("already selected");
00592         m_leftImg->selectPoint(LVpointNr);
00593         m_rightImg->selectPoint(LVpointNr);
00594         return;
00595     }
00596     m_selectedPoint = LVpointNr;
00597 
00598     const HuginBase::ControlPoint & p = currentPoints[LVpointNr].second;
00599     m_x1Text->SetValue(wxString::Format(wxT("%.2f"),p.x1));
00600     m_y1Text->SetValue(wxString::Format(wxT("%.2f"),p.y1));
00601     m_x2Text->SetValue(wxString::Format(wxT("%.2f"),p.x2));
00602     m_y2Text->SetValue(wxString::Format(wxT("%.2f"),p.y2));
00603     m_cpModeChoice->SetSelection(p.mode);
00604     m_leftImg->selectPoint(LVpointNr);
00605     m_rightImg->selectPoint(LVpointNr);
00606     m_cpList->SetItemState(LVpointNr, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
00607     m_cpList->EnsureVisible(LVpointNr);
00608 
00609     EnablePointEdit(true);
00610 }
00611 
00612 void CPEditorPanel::SelectGlobalPoint(unsigned int globalNr)
00613 {
00614     unsigned int localNr;
00615     if (globalPNr2LocalPNr(localNr,globalNr)) {
00616         DEBUG_DEBUG("CPEditor::setGlobalPoint(" << globalNr << ") found local point " << localNr);
00617         SelectLocalPoint(localNr);
00618     } else {
00619         DEBUG_ERROR("CPEditor::setGlobalPoint: point " << globalNr << " not found in currentPoints");
00620     }
00621 }
00622 
00623 bool CPEditorPanel::globalPNr2LocalPNr(unsigned int & localNr, unsigned int globalNr) const
00624 {
00625     HuginBase::CPointVector::const_iterator it = currentPoints.begin();
00626 
00627     while(it != currentPoints.end() && (*it).first != globalNr) {
00628         ++it;
00629     }
00630 
00631     if (it != currentPoints.end()) {
00632         localNr = it - currentPoints.begin();
00633         return true;
00634     } else {
00635         return false;
00636     }
00637 }
00638 
00639 unsigned int CPEditorPanel::localPNr2GlobalPNr(unsigned int localNr) const
00640 {
00641     assert(localNr < currentPoints.size());
00642     return currentPoints[localNr].first;
00643 }
00644 
00645 
00646 void CPEditorPanel::estimateAndAddOtherPoint(const hugin_utils::FDiff2D & p,
00647                                              bool left,
00648                                              CPImageCtrl * thisImg,
00649                                              unsigned int thisImgNr,
00650                                              CPCreationState THIS_POINT,
00651                                              CPCreationState THIS_POINT_RETRY,
00652                                              CPImageCtrl * otherImg,
00653                                              unsigned int otherImgNr,
00654                                              CPCreationState OTHER_POINT,
00655                                              CPCreationState OTHER_POINT_RETRY)
00656 {
00657     hugin_utils::FDiff2D op;
00658     op = EstimatePoint(hugin_utils::FDiff2D(p.x, p.y), left);
00659     // check if point is in image.
00660     const HuginBase::SrcPanoImage & pImg = m_pano->getImage(otherImgNr);
00661     if (op.x < (int) pImg.getSize().width() && op.x >= 0
00662         && op.y < (int) pImg.getSize().height() && op.y >= 0)
00663     {
00664         otherImg->setNewPoint(op);
00665         // if fine-tune is checked, run a fine-tune session as well.
00666         // hmm probably there should be another separate function for this..
00667         if (m_fineTuneCB->IsChecked()) {
00668             MainFrame::Get()->SetStatusText(_("searching similar points..."),0);
00669             hugin_utils::FDiff2D newPoint = otherImg->getNewPoint();
00670 
00671             long templWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"), HUGIN_FT_TEMPLATE_SIZE);
00672             const HuginBase::SrcPanoImage & img = m_pano->getImage(thisImgNr);
00673             double sAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"),HUGIN_FT_SEARCH_AREA_PERCENT);
00674             int sWidth = std::min((int)(img.getWidth() * sAreaPercent / 100.0), 500);
00675             vigra_ext::CorrelationResult corrPoint;
00676             bool corrOk=false;
00677             vigra::Diff2D roundp(p.toDiff2D());
00678             try {
00679                 corrOk = PointFineTune(thisImgNr,
00680                                       roundp,
00681                                       templWidth,
00682                                       otherImgNr,
00683                                       newPoint,
00684                                       sWidth,
00685                                       corrPoint);
00686             } catch (std::exception & e) {
00687                 wxMessageBox(wxString (e.what(), wxConvLocal), _("Error during Fine-tune"));
00688             }
00689             if (! corrOk) {
00690                 // just set point, PointFineTune already complained
00691                 if (corrPoint.corrPos.x >= 0 && corrPoint.corrPos.y >= 0 && corrPoint.maxpos.x > 0 && corrPoint.maxpos.y > 0)
00692                 {
00693                     otherImg->setScale(m_detailZoomFactor);
00694                     otherImg->setNewPoint(corrPoint.maxpos);
00695                     thisImg->setNewPoint(corrPoint.corrPos);
00696                     changeState(BOTH_POINTS_SELECTED);
00697                 };
00698             } else {
00699                 // show point & zoom in if auto add is not set
00700                 if (!m_autoAddCB->IsChecked()) {
00701                     otherImg->setScale(m_detailZoomFactor);
00702                     otherImg->setNewPoint(corrPoint.maxpos);
00703                     thisImg->setNewPoint(corrPoint.corrPos);
00704                     changeState(BOTH_POINTS_SELECTED);
00705                     wxString s1;
00706                     s1.Printf(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f "),
00707                               corrPoint.maxAngle, corrPoint.maxi, corrPoint.curv.x, corrPoint.curv.y );
00708                     
00709                     wxString s2 = s1 + wxT(" -- ") + wxString(_("change points, or press right mouse button to add the pair"));
00710                     MainFrame::Get()->SetStatusText(s2,0);
00711                 } else {
00712                     // add point
00713                     otherImg->setNewPoint(corrPoint.maxpos);
00714                     thisImg->setNewPoint(corrPoint.corrPos);
00715                     changeState(BOTH_POINTS_SELECTED);
00716                     CreateNewPoint();
00717                 }
00718             }
00719         } else {
00720             // no fine-tune, set 100% scale and set both points to selected
00721             otherImg->setScale(m_detailZoomFactor);
00722             otherImg->showPosition(op);
00723             changeState(BOTH_POINTS_SELECTED);
00724         }
00725 
00726     } else {
00727         // estimate was outside of image
00728         // do nothing special
00729         wxBell();
00730         MainFrame::Get()->SetStatusText(_("Estimated point outside image"),0);
00731     }
00732 }
00733 
00734 void CPEditorPanel::NewPointChange(hugin_utils::FDiff2D p, bool left)
00735 {
00736     DEBUG_TRACE("");
00737 
00738     wxString corrMsg;
00739 
00740     CPImageCtrl * thisImg = m_leftImg;
00741     unsigned int thisImgNr = m_leftImageNr;
00742     CPImageCtrl * otherImg = m_rightImg;
00743     unsigned int otherImgNr = m_rightImageNr;
00744     CPCreationState THIS_POINT = LEFT_POINT;
00745     CPCreationState THIS_POINT_RETRY = LEFT_POINT_RETRY;
00746     CPCreationState OTHER_POINT = RIGHT_POINT;
00747     CPCreationState OTHER_POINT_RETRY = RIGHT_POINT_RETRY;
00748 
00749     bool estimate = m_estimateCB->IsChecked();
00750 
00751     if (!left) {
00752         thisImg = m_rightImg;
00753         thisImgNr = m_rightImageNr;
00754         otherImg = m_leftImg;
00755         otherImgNr = m_leftImageNr;
00756         THIS_POINT = RIGHT_POINT;
00757         THIS_POINT_RETRY = RIGHT_POINT_RETRY;
00758         OTHER_POINT = LEFT_POINT;
00759         OTHER_POINT_RETRY = LEFT_POINT_RETRY;
00760     }
00761 
00762 
00763     if (cpCreationState == NO_POINT) {
00764         //case NO_POINT
00765         changeState(THIS_POINT);
00766         // zoom into our window
00767         if (thisImg->getScale() < 1) {
00768             thisImg->setScale(m_detailZoomFactor);
00769             thisImg->showPosition(p);
00770         } else {
00771             // run auto-estimate procedure?
00772             bool hasNormalCP = false;
00773             for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end() && !hasNormalCP; ++it)
00774             {
00775                 hasNormalCP = (it->second.mode == HuginBase::ControlPoint::X_Y);
00776             };
00777             if (estimate && (thisImgNr != otherImgNr) && hasNormalCP) {
00778                 estimateAndAddOtherPoint(p, left,
00779                                          thisImg, thisImgNr, THIS_POINT, THIS_POINT_RETRY,
00780                                          otherImg, otherImgNr, OTHER_POINT, OTHER_POINT_RETRY);
00781             };
00782         }
00783 
00784     } else if (cpCreationState == OTHER_POINT_RETRY) {
00785         thisImg->showPosition(p);
00786     } else if (cpCreationState == THIS_POINT) {
00787         thisImg->showPosition(p);
00788 
00789         bool hasNormalCP = false;
00790         for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end() && !hasNormalCP; ++it)
00791         {
00792             hasNormalCP = (it->second.mode == HuginBase::ControlPoint::X_Y);
00793         };
00794         if (estimate && (thisImgNr != otherImgNr) && hasNormalCP) {
00795             estimateAndAddOtherPoint(p, left,
00796                                      thisImg, thisImgNr, THIS_POINT, THIS_POINT_RETRY,
00797                                      otherImg, otherImgNr, OTHER_POINT, OTHER_POINT_RETRY);
00798         }
00799     } else if (cpCreationState == OTHER_POINT || cpCreationState == THIS_POINT_RETRY) {
00800         // the try for the second point.
00801         if (cpCreationState == OTHER_POINT) {
00802             // other point already selected, finalize point.
00803 
00804             // TODO: option to ignore the auto fine tune button when multiple images are selected.
00805             if (m_fineTuneCB->IsChecked() ) {
00806                 vigra_ext::CorrelationResult corrRes;
00807 
00808                 hugin_utils::FDiff2D newPoint = otherImg->getNewPoint();
00809 
00810                 long templWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE);
00811                 const HuginBase::SrcPanoImage & img = m_pano->getImage(thisImgNr);
00812                 double sAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"),
00813                                                                 HUGIN_FT_SEARCH_AREA_PERCENT);
00814                 int sWidth = std::min((int) (img.getWidth() * sAreaPercent / 100.0), 500);
00815                 bool corrOk = false;
00816                 // corr point
00817                 vigra::Diff2D newPoint_round = newPoint.toDiff2D();
00818                 try {
00819                     corrOk = PointFineTune(otherImgNr,
00820                                            newPoint_round,
00821                                            templWidth,
00822                                            thisImgNr,
00823                                            p,
00824                                            sWidth,
00825                                            corrRes);
00826                 } catch (std::exception & e) {
00827                     wxMessageBox(wxString (e.what(), wxConvLocal), _("Error during Fine-tune"));
00828                 }
00829 
00830                 if (! corrOk) {
00831                     // low xcorr
00832                     // zoom to 100 percent. & set second stage
00833                     // to abandon finetune this time.
00834                     if (corrRes.corrPos.x >= 0 && corrRes.corrPos.y >= 0 && corrRes.maxpos.x >= 0 && corrRes.maxpos.y >= 0)
00835                     {
00836                         thisImg->setScale(m_detailZoomFactor);
00837                         thisImg->setNewPoint(corrRes.maxpos);
00838                         thisImg->update();
00839                         otherImg->setNewPoint(corrRes.corrPos);
00840                         changeState(BOTH_POINTS_SELECTED);
00841                     };
00842                 } else {
00843                     // show point & zoom in if auto add is not set
00844                     changeState(BOTH_POINTS_SELECTED);
00845                     if (!m_autoAddCB->IsChecked()) {
00846                         thisImg->setScale(m_detailZoomFactor);
00847                     }
00848                     thisImg->setNewPoint(corrRes.maxpos);
00849                     otherImg->setNewPoint(corrRes.corrPos);
00850                     wxString s1;
00851                     s1.Printf(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f "),
00852                               corrRes.maxAngle, corrRes.maxi, corrRes.curv.x, corrRes.curv.y );
00853                     
00854                     corrMsg = s1 + wxT(" -- ") +  wxString(_("change points, or press right mouse button to add the pair"));
00855                     MainFrame::Get()->SetStatusText(corrMsg,0);
00856                     
00857                 }
00858             } else {
00859                 // no finetune. but zoom into picture, when we where zoomed out
00860                 if (thisImg->getScale() < 1) {
00861                     // zoom to 100 percent. & set second stage
00862                     // to abandon finetune this time.
00863                     thisImg->setScale(m_detailZoomFactor);
00864                     thisImg->clearNewPoint();
00865                     thisImg->showPosition(p);
00866                     //thisImg->setNewPoint(p.x, p.y);
00867                     changeState(THIS_POINT_RETRY);
00868                     return;
00869                 } else {
00870                     // point is already set. no need to move.
00871                     // setNewPoint(p);
00872                     changeState(BOTH_POINTS_SELECTED);
00873                 }
00874             }
00875         } else {
00876             // selection retry
00877             // nothing special, no second stage fine-tune yet.
00878         }
00879 
00880         // ok, we have determined the other point.. apply if auto add is on
00881         if (m_autoAddCB->IsChecked()) {
00882             CreateNewPoint();
00883         } else {
00884             // keep both point floating around, until they are
00885             // added with a right mouse click or the add button
00886             changeState(BOTH_POINTS_SELECTED);
00887             if (corrMsg != wxT("")) {
00888                 MainFrame::Get()->SetStatusText(corrMsg,0);
00889             }
00890         }
00891 
00892     } else if (cpCreationState == BOTH_POINTS_SELECTED) {
00893         // nothing to do.. maybe a special fine-tune with
00894         // a small search region
00895 
00896     } else {
00897         // should never reach this, else state machine is broken.
00898         DEBUG_ASSERT(0);
00899     }
00900 }
00901 
00902 // return a SrcPanoImage so that the given point is in the center
00903 HuginBase::SrcPanoImage GetImageRotatedTo(const HuginBase::SrcPanoImage& img, const vigra::Diff2D& point, int testWidth, double& neededHFOV)
00904 {
00905     // copy only necessary information into temporary SrcPanoImage
00906     HuginBase::SrcPanoImage imgMod;
00907     imgMod.setSize(img.getSize());
00908     imgMod.setProjection(img.getProjection());
00909     imgMod.setHFOV(img.getHFOV());
00910     // calculate, where the interest point lies
00911     HuginBase::PanoramaOptions opt;
00912     opt.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
00913     opt.setHFOV(360);
00914     opt.setWidth(360);
00915     opt.setHeight(180);
00916 
00917     HuginBase::PTools::Transform transform;
00918     transform.createInvTransform(imgMod, opt);
00919     double x1, y1;
00920     if (!transform.transformImgCoord(x1, y1, point.x, point.y))
00921     {
00922         neededHFOV = -1;
00923         return imgMod;
00924     }
00925     // equirect image coordinates -> equirectangular coordinates
00926     // transformImgCoord places the origin at the upper left corner and negate
00927     Matrix3 rotY;
00928     rotY.SetRotationPT(DEG_TO_RAD(180 - (x1 + 0.5)), 0, 0);
00929     Matrix3 rotP;
00930     rotP.SetRotationPT(0, DEG_TO_RAD((y1 + 0.5) - 90), 0);
00931     double y, p, r;
00932     // calculate the necessary rotation angles and remember
00933     Matrix3 rot = rotP * rotY;
00934     rot.GetRotationPT(y, p, r);
00935     imgMod.setYaw(RAD_TO_DEG(y));
00936     imgMod.setPitch(RAD_TO_DEG(p));
00937     imgMod.setRoll(RAD_TO_DEG(r));
00938 
00939     // new we calculate the needed HFOV for template/search area width
00940     double x2, y2;
00941     // check a point left from our interest point
00942     if (transform.transformImgCoord(x2, y2, point.x - testWidth / 2.0, point.y))
00943     {
00944         if (x2 < x1)
00945         {
00946             neededHFOV = 2.0 * (x1 - x2);
00947         }
00948         else
00949         {
00950             // we crossed the 360 deg border
00951             neededHFOV = 2.0 * (360 - x2 + x1);
00952         };
00953         // limit maximum HFOV for remapping to stereographic projection done as next step
00954         if (neededHFOV > 90)
00955         {
00956             neededHFOV = 90;
00957         };
00958         return imgMod;
00959     };
00960     // this goes wrong, maybe the tested point is outside the image area of a fisheye image
00961     // now test the right point
00962     if (transform.transformImgCoord(x2, y2, point.x + testWidth / 2.0, point.y))
00963     {
00964         if (x1 < x2)
00965         {
00966             neededHFOV = 2.0 * (x2 - x1);
00967         }
00968         else
00969         {
00970             // we crossed the 360 deg border
00971             neededHFOV = 2.0 * (360 + x2 - x1);
00972         };
00973         // limit maximum HFOV for remapping to stereographic projection done as next step
00974         if (neededHFOV > 90)
00975         {
00976             neededHFOV = 90;
00977         };
00978         return imgMod;
00979     };
00980     // we can't calculate the needed HFOV, return -1
00981     neededHFOV = -1;
00982     return imgMod;
00983 };
00984 
00985 vigra_ext::CorrelationResult PointFineTuneProjectionAware(const HuginBase::SrcPanoImage& templ, const vigra::UInt8RGBImage& templImg,
00986     vigra::Diff2D templPos, int templSize,
00987     const HuginBase::SrcPanoImage& search, const vigra::UInt8RGBImage& searchImg,
00988     vigra::Diff2D searchPos, int sWidth)
00989 {
00990     wxBusyCursor busy;
00991     // read settings
00992     wxConfigBase *cfg = wxConfigBase::Get();
00993     bool rotatingFinetune = cfg->Read(wxT("/Finetune/RotationSearch"), HUGIN_FT_ROTATION_SEARCH) == 1;
00994     double startAngle = HUGIN_FT_ROTATION_START_ANGLE;
00995     cfg->Read(wxT("/Finetune/RotationStartAngle"), &startAngle, HUGIN_FT_ROTATION_START_ANGLE);
00996     startAngle = DEG_TO_RAD(startAngle);
00997     double stopAngle = HUGIN_FT_ROTATION_STOP_ANGLE;
00998     cfg->Read(wxT("/Finetune/RotationStopAngle"), &stopAngle, HUGIN_FT_ROTATION_STOP_ANGLE);
00999     stopAngle = DEG_TO_RAD(stopAngle);
01000     int nSteps = cfg->Read(wxT("/Finetune/RotationSteps"), HUGIN_FT_ROTATION_STEPS);
01001     // if both images have the same projection and the angle does not differ to much use normal point fine-tune
01002     if (templ.getProjection() == search.getProjection()
01003         && templ.getHFOV() < 65 && search.getHFOV() < 65
01004         && fabs(templ.getHFOV() - search.getHFOV()) < 5)
01005     {
01006         vigra_ext::CorrelationResult res;
01007         if (rotatingFinetune)
01008         {
01009             res = vigra_ext::PointFineTuneRotSearch(templImg, templPos, templSize,
01010                 searchImg, searchPos, sWidth, startAngle, stopAngle, nSteps);
01011         }
01012         else
01013         {
01014             res = vigra_ext::PointFineTune(templImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), templPos, templSize,
01015                 searchImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), searchPos, sWidth);
01016         };
01017         res.corrPos = templPos;
01018         return res;
01019     };
01020     // images have different projections or the HFOV is different
01021     // so we reproject the image to stereographic projection and fine tune point there
01022     // rotate image so that interest point is in the center
01023     double templHFOV = 0;
01024     double searchHFOV = 0;
01025     HuginBase::SrcPanoImage templMod = GetImageRotatedTo(templ, templPos, templSize, templHFOV);
01026     HuginBase::SrcPanoImage searchMod = GetImageRotatedTo(search, searchPos, sWidth + templSize + 5, searchHFOV);
01027     vigra_ext::CorrelationResult res;
01028     res.maxpos = hugin_utils::FDiff2D(-1, -1);
01029     res.corrPos = hugin_utils::FDiff2D(-1, -1);
01030     if (templHFOV < 0 || searchHFOV < 0)
01031     {
01032         //something went wrong, e.g. image outside of projection circle for fisheye lenses
01033         return res;
01034     }
01035     // populate PanoramaOptions
01036     HuginBase::PanoramaOptions opts;
01037     opts.setProjection(HuginBase::PanoramaOptions::STEREOGRAPHIC);
01038     opts.setHFOV(std::max(templHFOV, searchHFOV));
01039     // calculate a sensible scale factor
01040     double scaleTempl = HuginBase::CalculateOptimalScale::calcOptimalPanoScale(templMod, opts);
01041     double scaleSearch = HuginBase::CalculateOptimalScale::calcOptimalPanoScale(searchMod, opts);
01042     opts.setWidth(std::max<unsigned int>(opts.getWidth()*std::min(scaleTempl, scaleSearch), 3 * templSize));
01043     opts.setHeight(opts.getWidth());
01044     // transform coordinates to transform system
01045     HuginBase::PTools::Transform transform;
01046     transform.createInvTransform(templMod, opts);
01047     double templX, templY, searchX, searchY;
01048     transform.transformImgCoord(templX, templY, templPos.x, templPos.y);
01049     transform.createInvTransform(searchMod, opts);
01050     transform.transformImgCoord(searchX, searchY, searchPos.x, searchPos.y);
01051     // now transform the images
01052     vigra_ext::PassThroughFunctor<vigra::UInt8> ptf;
01053     AppBase::DummyProgressDisplay dummy;
01054     transform.createTransform(searchMod, opts);
01055     vigra::UInt8RGBImage searchImgMod(opts.getSize());
01056     vigra::BImage alpha(opts.getSize());
01057     vigra_ext::transformImage(srcImageRange(searchImg), destImageRange(searchImgMod), destImage(alpha),
01058         vigra::Diff2D(0, 0), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy);
01059     // now remap template, we need to remap a little bigger area to have enough information when the template
01060     // is rotated in PointFineTuneRotSearch
01061     vigra::Diff2D templPointInt(hugin_utils::roundi(templX), hugin_utils::roundi(templY));
01062     vigra::Rect2D rect(templPointInt.x - templSize - 2, templPointInt.y - templSize - 2,
01063         templPointInt.x + templSize + 2, templPointInt.y + templSize + 2);
01064     rect &= vigra::Rect2D(opts.getSize());
01065     transform.createTransform(templMod, opts);
01066     vigra::UInt8RGBImage templImgMod(opts.getSize());
01067     vigra_ext::transformImage(srcImageRange(templImg), destImageRange(templImgMod, rect), destImage(alpha),
01068         vigra::Diff2D(rect.left(), rect.top()), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy);
01069 #if defined DEBUG_EXPORT_FINE_TUNE_REMAPPING
01070     {
01071         vigra::ImageExportInfo templExport("template_remapped.tif");
01072         vigra::exportImage(srcImageRange(templImgMod), templExport.setPixelType("UINT8"));
01073         vigra::ImageExportInfo searchExport("search_remapped.tif");
01074         vigra::exportImage(srcImageRange(searchImgMod), searchExport.setPixelType("UINT8"));
01075     }
01076 #endif
01077     // now we can finetune the point in stereographic projection
01078     // we are always using the rotate fine-tune algorithm, because for this case
01079     // often a rotation is involved
01080     res = vigra_ext::PointFineTuneRotSearch(templImgMod, templPointInt, templSize,
01081         searchImgMod, vigra::Diff2D(hugin_utils::roundi(searchX), hugin_utils::roundi(searchY)), sWidth, startAngle, stopAngle, nSteps);
01082     // we transfer also the new found template position back to the original image
01083     transform.createTransform(templMod, opts);
01084     transform.transformImgCoord(res.corrPos.x, res.corrPos.y, templPointInt.x + 0.00001, templPointInt.y + 0.00001);
01085     // we need to move the finetune point back to position in original image
01086     transform.createTransform(searchMod, opts);
01087     transform.transformImgCoord(res.maxpos.x, res.maxpos.y, res.maxpos.x, res.maxpos.y);
01088     return res;
01089 };
01090 
01091 bool CPEditorPanel::PointFineTune(unsigned int tmplImgNr,
01092                                   const vigra::Diff2D & tmplPoint,
01093                                   int templSize,
01094                                   unsigned int subjImgNr,
01095                                   const hugin_utils::FDiff2D & o_subjPoint,
01096                                   int sWidth,
01097                                   vigra_ext::CorrelationResult & res)
01098 {
01099     DEBUG_TRACE("tmpl img nr: " << tmplImgNr << " corr src: "
01100                 << subjImgNr);
01101 
01102     MainFrame::Get()->SetStatusText(_("searching similar points..."),0);
01103 
01104     double corrThresh=HUGIN_FT_CORR_THRESHOLD;
01105     wxConfigBase::Get()->Read(wxT("/Finetune/CorrThreshold"),&corrThresh,
01106                               HUGIN_FT_CORR_THRESHOLD);
01107 
01108     double curvThresh = HUGIN_FT_CURV_THRESHOLD;
01109     wxConfigBase::Get()->Read(wxT("/Finetune/CurvThreshold"),&curvThresh,
01110                               HUGIN_FT_CURV_THRESHOLD);
01111 
01112     // fixme: just cutout suitable gray 
01113     ImageCache::ImageCacheRGB8Ptr subjImg = ImageCache::getInstance().getImage(m_pano->getImage(subjImgNr).getFilename())->get8BitImage();
01114     ImageCache::ImageCacheRGB8Ptr tmplImg = ImageCache::getInstance().getImage(m_pano->getImage(tmplImgNr).getFilename())->get8BitImage();
01115 
01116     res = PointFineTuneProjectionAware(m_pano->getImage(tmplImgNr), *(tmplImg), tmplPoint, templSize,
01117         m_pano->getImage(subjImgNr), *(subjImg), o_subjPoint.toDiff2D(), sWidth);
01118 
01119     // invert curvature. we always assume its a maxima, the curvature there is negative
01120     // however, we allow the user to specify a positive threshold, so we need to
01121     // invert it
01122     res.curv.x = - res.curv.x;
01123     res.curv.y = - res.curv.y;
01124 
01125     MainFrame::Get()->SetStatusText(wxString::Format(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f "),
01126                                     res.maxAngle, res.maxi, res.curv.x, res.curv.y ),0);
01127     if (res.corrPos.x < 0 || res.corrPos.y < 0 || res.maxpos.x < 0 || res.maxpos.y < 0)
01128     {
01129         // invalid transformation in fine tune
01130 #if wxCHECK_VERSION(2, 9, 0)
01131         wxMessageDialog dlg(this,
01132             _("No similar point found."),
01133 #ifdef _WIN32
01134             _("Hugin"),
01135 #else
01136             wxT(""),
01137 #endif
01138             wxICON_ERROR | wxOK);
01139         dlg.SetExtendedMessage(_("An internal transformation went wrong.\nCheck that the point is inside the image."));
01140         dlg.ShowModal();
01141 #else
01142         wxMessageBox(_("An internal transformation went wrong.\nCheck that the point is inside the image."),
01143 #ifdef _WIN32
01144             _("Hugin"),
01145 #else
01146             wxT(""),
01147 #endif
01148             wxICON_ERROR | wxOK, this);
01149 #endif
01150         return false;
01151     }
01152     if (res.maxi < corrThresh || res.curv.x < curvThresh || res.curv.y < curvThresh )
01153     {
01154         // Bad correlation result.
01155 #if wxCHECK_VERSION(2, 9, 0)
01156         wxMessageDialog dlg(this,
01157             _("No similar point found."),
01158 #ifdef _WIN32
01159             _("Hugin"),
01160 #else
01161             wxT(""),
01162 #endif
01163             wxICON_ERROR | wxOK);
01164         dlg.SetExtendedMessage(wxString::Format(_("Check the similarity visually.\nCorrelation coefficient (%.3f) is lower than the threshold set in the preferences."),
01165                              res.maxi));
01166         dlg.ShowModal();
01167 #else
01168         wxMessageBox(
01169             wxString::Format(_("No similar point found. Check the similarity visually.\nCorrelation coefficient (%.3f) is lower than the threshold set in the preferences."),
01170                              res.maxi),
01171 #ifdef _WIN32
01172             _("Hugin"),
01173 #else
01174             wxT(""),
01175 #endif
01176             wxICON_ERROR | wxOK, this);
01177 #endif
01178         return false;
01179     }
01180 
01181     return true;
01182 }
01183 
01184 void CPEditorPanel::panoramaChanged(HuginBase::Panorama &pano)
01185 {
01186     int nGui = m_cpModeChoice->GetCount();
01187     int nPano = pano.getNextCPTypeLineNumber()+1;
01188     DEBUG_DEBUG("mode choice: " << nGui << " entries, required: " << nPano);
01189 
01190     if (nGui > nPano)
01191     {
01192         m_cpModeChoice->Freeze();
01193         // remove some items.
01194         for (int i = nGui-1; i > nPano-1; --i) {
01195             m_cpModeChoice->Delete(i);
01196         }
01197         if (nPano > 3) {
01198             m_cpModeChoice->SetString(nPano-1, _("Add new Line"));
01199         }
01200         m_cpModeChoice->Thaw();
01201     } else if (nGui < nPano) {
01202         m_cpModeChoice->Freeze();
01203         if (nGui > 3) {
01204             m_cpModeChoice->SetString(nGui-1, wxString::Format(_("Line %d"), nGui-1));
01205         }
01206         for (int i = nGui; i < nPano-1; i++) {
01207             m_cpModeChoice->Append(wxString::Format(_("Line %d"), i));
01208         }
01209         m_cpModeChoice->Append(_("Add new Line"));
01210         m_cpModeChoice->Thaw();
01211     }
01212     UpdateTransforms();
01213     // check if number of control points has changed, if so we need to update our variables
01214     if (pano.getNrOfCtrlPoints() != m_countCP)
01215     {
01216         m_countCP = pano.getNrOfCtrlPoints();
01217         UpdateDisplay(false);
01218     };
01219     DEBUG_TRACE("");
01220 }
01221 
01222 void CPEditorPanel::panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet &changed)
01223 {
01224     unsigned int nrImages = pano.getNrOfImages();
01225     unsigned int nrTabs = m_leftChoice->GetCount();
01226     DEBUG_TRACE("nrImages:" << nrImages << " nrTabs:" << nrTabs);
01227 
01228 #ifdef __WXMSW__
01229     int oldLeftSelection = m_leftChoice->GetSelection();
01230     int oldRightSelection = m_rightChoice->GetSelection();
01231 #endif
01232 
01233     if (nrImages == 0)
01234     {
01235         // disable controls
01236         m_cpModeChoice->Disable();
01237         m_addButton->Disable();
01238         m_delButton->Disable();
01239         m_autoAddCB->Disable();
01240         m_fineTuneCB->Disable();
01241         m_estimateCB->Disable();
01242         XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Disable();
01243         m_actionButton->Disable();
01244         XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice)->Disable();
01245         XRCCTRL(*this, "cp_editor_previous_img", wxButton)->Disable();
01246         XRCCTRL(*this, "cp_editor_next_img", wxButton)->Disable();
01247         m_leftChoice->Disable();
01248         m_rightChoice->Disable();
01249     }
01250     else
01251     {
01252         // enable controls
01253         m_cpModeChoice->Enable();
01254         m_autoAddCB->Enable();
01255         m_fineTuneCB->Enable();
01256         m_estimateCB->Enable();
01257         XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Enable();
01258         m_actionButton->Enable();
01259         XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice)->Enable();
01260         XRCCTRL(*this, "cp_editor_previous_img", wxButton)->Enable();
01261         XRCCTRL(*this, "cp_editor_next_img", wxButton)->Enable();
01262         m_leftChoice->Enable();
01263         m_rightChoice->Enable();
01264 
01265         ImageCache::getInstance().softFlush();
01266 
01267         for (unsigned int i=0; i < ((nrTabs < nrImages)? nrTabs: nrImages); i++) {
01268             wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
01269             m_leftChoice->SetString(i, wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01270             m_rightChoice->SetString(i, wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01271         }
01272         // wxChoice on windows looses the selection when setting new labels. Restore selection
01273 #ifdef __WXMSW__
01274         m_leftChoice->SetSelection(oldLeftSelection);
01275         m_rightChoice->SetSelection(oldRightSelection);
01276 #endif
01277         // add tab bar entries, if needed
01278         if (nrTabs < nrImages)
01279         {
01280             for (unsigned int i=nrTabs; i < nrImages; i++)
01281             {
01282                 wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
01283                 m_leftChoice->Append(wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01284                 m_rightChoice->Append(wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01285             }
01286         }
01287     }
01288     if (nrTabs > nrImages)
01289     {
01290         // remove tab bar entries if needed
01291         // we have to disable listening to notebook selection events,
01292         // else we might update to a noexisting image
01293         m_listenToPageChange = false;
01294         for (int i=nrTabs-1; i >= (int)nrImages; i--) {
01295             m_leftChoice->Delete(i);
01296             m_rightChoice->Delete(i);
01297         }
01298         m_listenToPageChange = true;
01299         if (nrImages > 0) {
01300             // select some other image if we deleted the current image
01301             if (m_leftImageNr >= nrImages) {
01302                 setLeftImage(nrImages -1);
01303             }
01304             if (m_rightImageNr >= nrImages) {
01305                 setRightImage(nrImages -1);
01306             }
01307         } else {
01308             DEBUG_DEBUG("setting no images");
01309             m_leftImageNr = UINT_MAX;
01310             m_leftFile = "";
01311             m_rightImageNr = UINT_MAX;
01312             m_rightFile = "";
01313             // no image anymore..
01314             m_leftImg->setImage(m_leftFile, CPImageCtrl::ROT0);
01315             m_rightImg->setImage(m_rightFile, CPImageCtrl::ROT0);
01316         }
01317     }
01318 
01319     // update changed images
01320     bool update(false);
01321     for(HuginBase::UIntSet::const_iterator it = changed.begin(); it != changed.end(); ++it) {
01322         unsigned int imgNr = *it;
01323         // we only need to update the view if the currently
01324         // selected images were changed.
01325         // changing the images via the tabbar will always
01326         // take the current state directly from the pano
01327         // object
01328         DEBUG_DEBUG("image changed "<< imgNr);
01329         double yaw = const_map_get(m_pano->getImageVariables(imgNr), "y").getValue();
01330         double pitch = const_map_get(m_pano->getImageVariables(imgNr), "p").getValue();
01331         double roll = const_map_get(m_pano->getImageVariables(imgNr), "r").getValue();
01332         CPImageCtrl::ImageRotation rot = GetRot(yaw, pitch, roll);
01333         if (m_leftImageNr == imgNr) {
01334             DEBUG_DEBUG("left image dirty "<< imgNr);
01335             if (m_leftFile != pano.getImage(imgNr).getFilename()
01336                 || m_leftRot != rot ) 
01337             {
01338                 m_leftRot = rot;
01339                 m_leftFile = pano.getImage(imgNr).getFilename();
01340                 m_leftImg->setImage(m_leftFile, m_leftRot);
01341             }
01342             update=true;
01343         }
01344 
01345         if (m_rightImageNr == imgNr) {
01346             DEBUG_DEBUG("right image dirty "<< imgNr);
01347             if (m_rightFile != pano.getImage(imgNr).getFilename()
01348                  || m_rightRot != rot ) 
01349             {
01350                 m_rightRot = rot;
01351                 m_rightFile = pano.getImage(imgNr).getFilename();
01352                 m_rightImg->setImage(m_rightFile, m_rightRot);
01353             }
01354             update=true;
01355         }
01356     }
01357     // check if number of control points has changed, if so we need to update our variables
01358     if (pano.getNrOfCtrlPoints() != m_countCP)
01359     {
01360         m_countCP = pano.getNrOfCtrlPoints();
01361         update = true;
01362     };
01363 
01364     // if there is no selection, select the first one.
01365     if (m_rightImageNr == UINT_MAX && nrImages > 0) {
01366         setRightImage(0);
01367     }
01368     if (m_leftImageNr == UINT_MAX && nrImages > 0) {
01369         setLeftImage(0);
01370     }
01371 
01372     if (update || nrImages == 0) {
01373         UpdateDisplay(false);
01374     }
01375     m_leftChoice->CalcCPDistance(m_pano);
01376     m_rightChoice->CalcCPDistance(m_pano);
01377 }
01378 
01379 void CPEditorPanel::UpdateDisplay(bool newPair)
01380 {
01381     DEBUG_DEBUG("")
01382     int fI = m_leftChoice->GetSelection();
01383     int sI = m_rightChoice->GetSelection();
01384 
01385     // valid selection and already set left image
01386     if (fI >= 0 && m_leftImageNr != UINT_MAX)
01387     {
01388         // set image number to selection
01389         m_leftImageNr = (unsigned int) fI;
01390     }
01391     // valid selection and already set right image
01392     if (sI >= 0 && m_rightImageNr != UINT_MAX)
01393     {
01394         // set image number to selection
01395         m_rightImageNr = (unsigned int) sI;
01396     }
01397     // reset selection
01398     m_x1Text->Clear();
01399     m_y1Text->Clear();
01400     m_x2Text->Clear();
01401     m_y2Text->Clear();
01402     if (m_cpModeChoice->GetSelection() < 3) {
01403         m_cpModeChoice->SetSelection(0);
01404     }
01405 
01406     m_leftImg->setSameImage(m_leftImageNr==m_rightImageNr);
01407     m_rightImg->setSameImage(m_leftImageNr==m_rightImageNr);
01408 
01409     // update control points
01410     const HuginBase::CPVector & controlPoints = m_pano->getCtrlPoints();
01411     currentPoints.clear();
01412     mirroredPoints.clear();
01413 
01414     // create a list of all control points
01415     HuginBase::CPVector::size_type i = 0;
01416     m_leftImg->clearCtrlPointList();
01417     m_rightImg->clearCtrlPointList();
01418     for (HuginBase::CPVector::size_type index = 0; index < controlPoints.size(); ++index)
01419     {
01420         HuginBase::ControlPoint point(controlPoints[index]);
01421         if ((point.image1Nr == m_leftImageNr) && (point.image2Nr == m_rightImageNr)){
01422             m_leftImg->setCtrlPoint(point, false);
01423             m_rightImg->setCtrlPoint(point, true);
01424             currentPoints.push_back(std::make_pair(index, point));
01425             i++;
01426         } else if ((point.image2Nr == m_leftImageNr) && (point.image1Nr == m_rightImageNr)){
01427             m_leftImg->setCtrlPoint(point, true);
01428             m_rightImg->setCtrlPoint(point, false);
01429             point.mirror();
01430             mirroredPoints.insert(i);
01431             currentPoints.push_back(std::make_pair(index, point));
01432             i++;
01433         }
01434     }
01435     m_leftImg->update();
01436     m_rightImg->update();
01437 
01438     // put these control points into our listview.
01439     unsigned int selectedCP = UINT_MAX;
01440     for ( int i=0; i < m_cpList->GetItemCount() ; i++ ) {
01441       if ( m_cpList->GetItemState( i, wxLIST_STATE_SELECTED ) ) {
01442         selectedCP = i;            // remembers the old selection
01443       }
01444     }
01445     m_cpList->Freeze();
01446     m_cpList->DeleteAllItems();
01447 
01448     for (unsigned int i=0; i < currentPoints.size(); ++i) {
01449         const HuginBase::ControlPoint & p(currentPoints[i].second);
01450         DEBUG_DEBUG("inserting LVItem " << i);
01451         m_cpList->InsertItem(i,wxString::Format(wxT("%d"),i));
01452         m_cpList->SetItem(i,1,wxString::Format(wxT("%.2f"),p.x1));
01453         m_cpList->SetItem(i,2,wxString::Format(wxT("%.2f"),p.y1));
01454         m_cpList->SetItem(i,3,wxString::Format(wxT("%.2f"),p.x2));
01455         m_cpList->SetItem(i,4,wxString::Format(wxT("%.2f"),p.y2));
01456         wxString mode;
01457         switch (p.mode) {
01458         case HuginBase::ControlPoint::X_Y:
01459             mode = _("normal");
01460             break;
01461         case HuginBase::ControlPoint::X:
01462             mode = _("vert. Line");
01463             break;
01464         case HuginBase::ControlPoint::Y:
01465             mode = _("horiz. Line");
01466             break;
01467         default:
01468             mode = wxString::Format(_("Line %d"), p.mode);
01469             break;
01470         }
01471         m_cpList->SetItem(i,5,mode);
01472         m_cpList->SetItem(i,6,wxString::Format(wxT("%.2f"),p.error));
01473     }
01474 
01475     if ( selectedCP < (unsigned int) m_cpList->GetItemCount() && ! newPair) {
01476         // sets an old selection again, only if the images have not changed
01477         m_cpList->SetItemState( selectedCP,
01478                                 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
01479         m_cpList->EnsureVisible(selectedCP);
01480         m_selectedPoint = selectedCP;
01481         EnablePointEdit(true);
01482 
01483         const HuginBase::ControlPoint & p = currentPoints[m_selectedPoint].second;
01484         m_x1Text->SetValue(wxString::Format(wxT("%.2f"),p.x1));
01485         m_y1Text->SetValue(wxString::Format(wxT("%.2f"),p.y1));
01486         m_x2Text->SetValue(wxString::Format(wxT("%.2f"),p.x2));
01487         m_y2Text->SetValue(wxString::Format(wxT("%.2f"),p.y2));
01488         m_cpModeChoice->SetSelection(p.mode);
01489         m_leftImg->selectPoint(m_selectedPoint);
01490         m_rightImg->selectPoint(m_selectedPoint);
01491 
01492     } else {
01493         m_selectedPoint = UINT_MAX;
01494         EnablePointEdit(false);
01495     }
01496 
01497     for ( int j=0; j < m_cpList->GetColumnCount() ; j++ )
01498     {
01499         //get saved width
01500         // -1 is auto
01501         int width = wxConfigBase::Get()->Read(wxString::Format( wxT("/CPEditorPanel/ColumnWidth%d"), j ), -1);
01502         if(width != -1)
01503             m_cpList->SetColumnWidth(j, width);
01504     }
01505 
01506     m_cpList->Thaw();
01507 }
01508 
01509 void CPEditorPanel::EnablePointEdit(bool state)
01510 {
01511     m_delButton->Enable(state);
01512     XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Enable(state);
01513     m_x1Text->Enable(state);
01514     m_y1Text->Enable(state);
01515     m_x2Text->Enable(state);
01516     m_y2Text->Enable(state);
01517     m_cpModeChoice->Enable(state);
01518 }
01519 
01520 void CPEditorPanel::OnTextPointChange(wxCommandEvent &e)
01521 {
01522     DEBUG_TRACE("");
01523     // find selected point
01524     long item = -1;
01525     item = m_cpList->GetNextItem(item,
01526                                  wxLIST_NEXT_ALL,
01527                                  wxLIST_STATE_SELECTED);
01528     // no selected item.
01529     if (item == -1) {
01530         return;
01531     }
01532     unsigned int nr = (unsigned int) item;
01533     assert(nr < currentPoints.size());
01534     HuginBase::ControlPoint cp = currentPoints[nr].second;
01535 
01536     // update point state
01537     double oldValue=cp.x1;
01538     bool valid_input=hugin_utils::str2double(m_x1Text->GetValue(), cp.x1);
01539     if(valid_input)
01540         valid_input=(cp.x1>=0) && (cp.x1<=m_pano->getSrcImage(cp.image1Nr).getWidth());
01541     if (!valid_input) {
01542         m_x1Text->Clear();
01543         *m_x1Text << oldValue;
01544         return;
01545     }
01546     oldValue=cp.y1;
01547     valid_input = hugin_utils::str2double(m_y1Text->GetValue(), cp.y1);
01548     if(valid_input)
01549         valid_input=(cp.y1>=0) && (cp.y1<=m_pano->getSrcImage(cp.image1Nr).getHeight());
01550     if (!valid_input) {
01551         m_y1Text->Clear();
01552         *m_y1Text << oldValue;
01553         return;
01554     }
01555     oldValue=cp.x2;
01556     valid_input = hugin_utils::str2double(m_x2Text->GetValue(), cp.x2);
01557     if(valid_input)
01558         valid_input=(cp.x2>=0) && (cp.x2<=m_pano->getSrcImage(cp.image2Nr).getWidth());
01559     if (!valid_input) {
01560         m_x2Text->Clear();
01561         *m_x2Text << oldValue;
01562         return;
01563     }
01564     oldValue=cp.y2;
01565     valid_input = hugin_utils::str2double(m_y2Text->GetValue(), cp.y2);
01566     if(valid_input)
01567         valid_input=(cp.y2>=0) && (cp.y2<=m_pano->getSrcImage(cp.image1Nr).getHeight());
01568     if (!valid_input) {
01569         m_y2Text->Clear();
01570         *m_y2Text << oldValue;
01571         return;
01572     }
01573 
01574     cp.mode = m_cpModeChoice->GetSelection();
01575     // if point was mirrored, reverse before setting it.
01576     if (set_contains(mirroredPoints, nr)) {
01577         cp.mirror();
01578     }
01579     PanoCommand::GlobalCmdHist::getInstance().addCommand(
01580         new PanoCommand::ChangeCtrlPointCmd(*m_pano, currentPoints[nr].first, cp)
01581         );
01582 
01583 }
01584 
01585 void CPEditorPanel::OnLeftChoiceChange(wxCommandEvent & e)
01586 {
01587     DEBUG_TRACE("OnLeftChoiceChange() to " << e.GetSelection());
01588     if (m_listenToPageChange && e.GetSelection() >= 0) {
01589         setLeftImage((unsigned int) e.GetSelection());
01590     }
01591 }
01592 
01593 void CPEditorPanel::OnRightChoiceChange(wxCommandEvent & e)
01594 {
01595     DEBUG_TRACE("OnRightChoiceChange() to " << e.GetSelection());
01596     if (m_listenToPageChange && e.GetSelection() >= 0) {
01597         setRightImage((unsigned int) e.GetSelection());
01598     }
01599 }
01600 void CPEditorPanel::OnCPListSelect(wxListEvent & ev)
01601 {
01602     int t = ev.GetIndex();
01603     DEBUG_TRACE("selected: " << t);
01604     if (t >=0) {
01605         SelectLocalPoint((unsigned int) t);
01606         changeState(NO_POINT);
01607     }
01608     EnablePointEdit(true);
01609 }
01610 
01611 void CPEditorPanel::OnCPListDeselect(wxListEvent & ev)
01612 {
01613     // disable controls
01614     // when doing changes to this procedure do also check
01615     // interaction with control point table
01616     // e.g. m_selectedPoint=UINT_MAX will result in a endless loop and crash
01617     changeState(NO_POINT);
01618     EnablePointEdit(false);
01619     m_leftImg->deselect();
01620     m_rightImg->deselect();
01621 }
01622 
01623 void CPEditorPanel::OnZoom(wxCommandEvent & e)
01624 {
01625     int leftX = 0;
01626     int leftY = 0;
01627     int rightX = 0;
01628     int rightY = 0;
01629     const wxSize leftSize = m_leftImg->GetClientSize();
01630     const wxSize rightSize = m_rightImg->GetClientSize();
01631     if (m_leftImg->getScale() > 0)
01632     {
01633         // remember old scroll position
01634         leftX = (m_leftImg->GetScrollPos(wxHORIZONTAL) + leftSize.GetWidth() / 2 )/ m_leftImg->getScale();
01635         leftY = (m_leftImg->GetScrollPos(wxVERTICAL) + leftSize.GetHeight() / 2) / m_leftImg->getScale();
01636         rightX = (m_rightImg->GetScrollPos(wxHORIZONTAL) + rightSize.GetWidth() / 2) / m_rightImg->getScale();
01637         rightY = (m_rightImg->GetScrollPos(wxVERTICAL) + rightSize.GetHeight() / 2) / m_rightImg->getScale();
01638     };
01639     double factor;
01640     switch (e.GetSelection()) {
01641     case 0:
01642         factor = 1;
01643         m_detailZoomFactor = factor;
01644         break;
01645     case 1:
01646         // fit to window
01647         factor = 0;
01648         break;
01649     case 2:
01650         factor = 2;
01651         m_detailZoomFactor = factor;
01652         break;
01653     case 3:
01654         factor = 1.5;
01655         m_detailZoomFactor = factor;
01656         break;
01657     case 4:
01658         factor = 0.75;
01659         break;
01660     case 5:
01661         factor = 0.5;
01662         break;
01663     case 6:
01664         factor = 0.25;
01665         break;
01666     default:
01667         DEBUG_ERROR("unknown scale factor");
01668         factor = 1;
01669     }
01670     m_leftImg->setScale(factor);
01671     m_rightImg->setScale(factor);
01672     // if a point is selected, keep it in view
01673     if (m_selectedPoint < UINT_MAX) {
01674         SelectLocalPoint(m_selectedPoint);
01675     }
01676     else
01677     {
01678         if (factor > 0)
01679         {
01680             // scroll to keep old position in view
01681             m_leftImg->Scroll(leftX*factor - leftSize.GetWidth() / 2, leftY*factor - leftSize.GetHeight() / 2);
01682             m_rightImg->Scroll(rightX*factor - rightSize.GetWidth() / 2, rightY*factor - rightSize.GetHeight() / 2);
01683         };
01684     }
01685 }
01686 
01687 void CPEditorPanel::OnKey(wxKeyEvent & e)
01688 {
01689     DEBUG_DEBUG("key " << e.GetKeyCode()
01690                 << " origin: id:" << e.GetId() << " obj: "
01691                 << e.GetEventObject());
01692 
01693     if (e.m_keyCode == WXK_DELETE){
01694         DEBUG_DEBUG("Delete pressed");
01695         // remove working points..
01696         if (cpCreationState != NO_POINT) {
01697             changeState(NO_POINT);
01698         } else {
01699             // remove selected point
01700             // find selected point
01701             long item = -1;
01702             item = m_cpList->GetNextItem(item,
01703                                          wxLIST_NEXT_ALL,
01704                                          wxLIST_STATE_SELECTED);
01705             // no selected item.
01706             if (item == -1) {
01707                 wxBell();
01708                 return;
01709             }
01710             unsigned int pNr = localPNr2GlobalPNr((unsigned int) item);
01711             DEBUG_DEBUG("about to delete point " << pNr);
01712             PanoCommand::GlobalCmdHist::getInstance().addCommand(
01713                 new PanoCommand::RemoveCtrlPointCmd(*m_pano,pNr)
01714                 );
01715         }
01716     } else if (e.m_keyCode == '0') {
01717         wxCommandEvent dummy;
01718         dummy.SetInt(1);
01719         OnZoom(dummy);
01720         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(1);
01721     } else if (e.m_keyCode == '1') {
01722         wxCommandEvent dummy;
01723         dummy.SetInt(0);
01724         OnZoom(dummy);
01725         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(0);
01726     } else if (e.m_keyCode == '2') {
01727         wxCommandEvent dummy;
01728         dummy.SetInt(2);
01729         OnZoom(dummy);
01730         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(2);
01731     } else if (e.CmdDown() && e.GetKeyCode() == WXK_LEFT) {
01732         // move to previous
01733         wxCommandEvent dummy;
01734         OnPrevImg(dummy);
01735     } else if (e.CmdDown() && e.GetKeyCode() == WXK_RIGHT) {
01736         // move to next
01737         wxCommandEvent dummy;
01738         OnNextImg(dummy);
01739     } else if (e.GetKeyCode() == 'f') {
01740         bool left =  e.GetEventObject() == m_leftImg;
01741         if (cpCreationState == NO_POINT) {
01742             FineTuneSelectedPoint(left);
01743         } else if (cpCreationState == BOTH_POINTS_SELECTED) { 
01744             FineTuneNewPoint(left);
01745         }
01746     } else if (e.GetKeyCode() == 'g') {
01747         // generate keypoints
01748         long th = wxGetNumberFromUser(_("Create control points.\nTo create less points,\nenter a higher number."), _("Corner Detection threshold"), _("Create control points"), 400, 0, 32000);
01749         if (th == -1) {
01750             return;
01751         }
01752         long scale = wxGetNumberFromUser(_("Create control points"), _("Corner Detection scale"), _("Create control points"), 2);
01753         if (scale == -1) {
01754             return;
01755         }
01756 
01757         try {
01758             wxBusyCursor busy;
01759             DEBUG_DEBUG("corner threshold: " << th << "  scale: " << scale);
01760             PanoCommand::GlobalCmdHist::getInstance().addCommand(
01761                 new PanoCommand::wxAddCtrlPointGridCmd(*m_pano, m_leftImageNr, m_rightImageNr, scale, th)
01762                             );
01763         } catch (std::exception & e) {
01764             wxLogError(_("Error during control point creation:\n") + wxString(e.what(), wxConvLocal));
01765         }
01766     } else {
01767         e.Skip();
01768     }
01769 }
01770 
01771 void CPEditorPanel::OnAddButton(wxCommandEvent & e)
01772 {
01773     // check if the point can be created..
01774     if (cpCreationState == BOTH_POINTS_SELECTED) {
01775         CreateNewPoint();
01776     }
01777 }
01778 
01779 void CPEditorPanel::OnDeleteButton(wxCommandEvent & e)
01780 {
01781     DEBUG_TRACE("");
01782     // check if a point has been selected, but not added.
01783     if (cpCreationState != NO_POINT) {
01784         changeState(NO_POINT);
01785     } else {
01786         // find selected point
01787         long item = -1;
01788         item = m_cpList->GetNextItem(item,
01789                                      wxLIST_NEXT_ALL,
01790                                      wxLIST_STATE_SELECTED);
01791         // no selected item.
01792         if (item == -1) {
01793             wxBell();
01794             return;
01795         }
01796         // get the global point number
01797         unsigned int pNr = localPNr2GlobalPNr((unsigned int) item);
01798 
01799         PanoCommand::GlobalCmdHist::getInstance().addCommand(
01800             new PanoCommand::RemoveCtrlPointCmd(*m_pano,pNr )
01801             );
01802         m_leftChoice->CalcCPDistance(m_pano);
01803         m_rightChoice->CalcCPDistance(m_pano);
01804     }
01805 }
01806 
01807 // show a global control point
01808 void CPEditorPanel::ShowControlPoint(unsigned int cpNr)
01809 {
01810     const HuginBase::ControlPoint & p = m_pano->getCtrlPoint(cpNr);
01811     setLeftImage(p.image1Nr);
01812     setRightImage(p.image2Nr);
01813     // FIXME reset display state
01814     changeState(NO_POINT);
01815 
01816     SelectGlobalPoint(cpNr);
01817 }
01818 
01819 void CPEditorPanel::changeState(CPCreationState newState)
01820 {
01821     DEBUG_TRACE(cpCreationState << " --> " << newState);
01822     // handle global state changes.
01823     bool fineTune = m_fineTuneCB->IsChecked() && (m_leftImageNr != m_rightImageNr);
01824     switch(newState) {
01825     case NO_POINT:
01826         // disable all drawing search boxes.
01827         m_leftImg->showSearchArea(false);
01828         m_rightImg->showSearchArea(false);
01829         // but draw template size, if fine-tune enabled
01830         m_leftImg->showTemplateArea(fineTune);
01831         m_rightImg->showTemplateArea(fineTune);
01832         m_addButton->Enable(false);
01833         if (m_selectedPoint < UINT_MAX) {
01834             m_delButton->Enable(true);
01835         } else {
01836             m_delButton->Enable(false);
01837         }
01838         if (cpCreationState != NO_POINT) {
01839             // reset zoom to previous setting
01840             wxCommandEvent tmpEvt;
01841             tmpEvt.SetInt(XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->GetSelection());
01842             OnZoom(tmpEvt);
01843             m_leftImg->clearNewPoint();
01844             m_rightImg->clearNewPoint();
01845         }
01846         break;
01847     case LEFT_POINT:
01848         // disable search area on left window
01849         m_leftImg->showSearchArea(false);
01850         // show search area on right window
01851         m_rightImg->showSearchArea(fineTune);
01852 
01853         // show template area
01854         m_leftImg->showTemplateArea(fineTune);
01855         m_rightImg->showTemplateArea(false);
01856 
01857         // unselect point
01858         ClearSelection();
01859         m_addButton->Enable(false);
01860         m_delButton->Enable(false);
01861         MainFrame::Get()->SetStatusText(_("Select point in right image"),0);
01862         break;
01863     case RIGHT_POINT:
01864         m_leftImg->showSearchArea(fineTune);
01865         m_rightImg->showSearchArea(false);
01866 
01867         m_leftImg->showTemplateArea(false);
01868         m_rightImg->showTemplateArea(fineTune);
01869 
01870         ClearSelection();
01871         m_addButton->Enable(false);
01872         m_delButton->Enable(false);
01873         MainFrame::Get()->SetStatusText(_("Select point in left image"),0);
01874         break;
01875     case LEFT_POINT_RETRY:
01876     case RIGHT_POINT_RETRY:
01877         m_leftImg->showSearchArea(false);
01878         m_rightImg->showSearchArea(false);
01879         // but draw template size, if fine-tune enabled
01880         m_leftImg->showTemplateArea(false);
01881         m_rightImg->showTemplateArea(false);
01882         m_addButton->Enable(false);
01883         m_delButton->Enable(false);
01884         break;
01885     case BOTH_POINTS_SELECTED:
01886         m_leftImg->showTemplateArea(false);
01887         m_rightImg->showTemplateArea(false);
01888         m_leftImg->showSearchArea(false);
01889         m_rightImg->showSearchArea(false);
01890         m_addButton->Enable(true);
01891         m_delButton->Enable(false);
01892     }
01893     // apply the change
01894     cpCreationState = newState;
01895 }
01896 
01897 void CPEditorPanel::OnPrevImg(wxCommandEvent & e)
01898 {
01899     if (m_pano->getNrOfImages() < 2) return;
01900     int nImgs = m_pano->getNrOfImages();
01901     int left = m_leftImageNr -1;
01902     int right = m_rightImageNr -1;
01903     if (left < 0) {
01904         left += nImgs;
01905     } else if (left >= nImgs) {
01906         left -= nImgs;
01907     }
01908 
01909     if (right < 0) {
01910         right += nImgs;
01911     } else if (right >= nImgs) {
01912         right -= nImgs;
01913     }
01914     setLeftImage((unsigned int) left);
01915     setRightImage((unsigned int) right);
01916 }
01917 
01918 void CPEditorPanel::OnNextImg(wxCommandEvent & e)
01919 {
01920     if (m_pano->getNrOfImages() < 2) return;
01921     int nImgs = m_pano->getNrOfImages();
01922     int left = m_leftImageNr + 1;
01923     int right = m_rightImageNr + 1;
01924     if (left < 0) {
01925         left += nImgs;
01926     } else if (left >= nImgs) {
01927         left -= nImgs;
01928     }
01929 
01930     if (right < 0) {
01931         right += nImgs;
01932     } else if (right >= nImgs) {
01933         right -= nImgs;
01934     }
01935     setLeftImage((unsigned int) left);
01936     setRightImage((unsigned int) right);
01937 }
01938 
01939 void CPEditorPanel::OnFineTuneButton(wxCommandEvent & e)
01940 {
01941     if (cpCreationState == NO_POINT) {
01942         FineTuneSelectedPoint(false);
01943     } else if (cpCreationState == BOTH_POINTS_SELECTED) {
01944         FineTuneNewPoint(false);
01945     }
01946 }
01947 
01948 void CPEditorPanel::OnActionContextMenu(wxContextMenuEvent& e)
01949 {
01950     m_cpActionContextMenu->SetLabel(XRCID("cp_menu_create_cp"), wxString::Format(_("Create cp (Current setting: %s)"), MainFrame::Get()->GetSelectedCPGenerator().c_str()));
01951     PopupMenu(m_cpActionContextMenu);
01952 };
01953 
01954 void CPEditorPanel::OnActionButton(wxCommandEvent& e)
01955 {
01956     switch (m_cpActionButtonMode)
01957     {
01958         case CPTAB_ACTION_CREATE_CP:
01959             OnCreateCPButton(e);
01960             break;
01961         case CPTAB_ACTION_CLEAN_CP:
01962             OnCleanCPButton(e);
01963             break;
01964         case CPTAB_ACTION_CELESTE:
01965         default:
01966             OnCelesteButton(e);
01967             break;
01968     };
01969 };
01970 
01971 void CPEditorPanel::OnCreateCPButton(wxCommandEvent& e)
01972 {
01973     if (m_leftImageNr == m_rightImageNr)
01974     {
01975         // when the same image is selected left and right we are running linefind 
01976         // with default parameters
01977         CPDetectorSetting linefindSetting;
01978 #ifdef __WXMSW__
01979         linefindSetting.SetProg(wxT("linefind.exe"));
01980 #else
01981         linefindSetting.SetProg(wxT("linefind"));
01982 #endif
01983         linefindSetting.SetArgs(wxT("-o %o %s"));
01984         HuginBase::UIntSet imgs;
01985         imgs.insert(m_leftImageNr);
01986         MainFrame::Get()->RunCPGenerator(linefindSetting, imgs);
01987     }
01988     else
01989     {
01990         HuginBase::UIntSet imgs;
01991         imgs.insert(m_leftImageNr);
01992         imgs.insert(m_rightImageNr);
01993         MainFrame::Get()->RunCPGenerator(imgs);
01994     };
01995 };
01996 
01997 void CPEditorPanel::OnCelesteButton(wxCommandEvent & e)
01998 {
01999     if (currentPoints.empty())
02000     {
02001         wxMessageBox(_("Cannot run celeste without at least one control point connecting the two images"),_("Error"));
02002         std::cout << "Cannot run celeste without at least one control point connecting the two images" << std::endl;
02003     }
02004     else
02005     {
02006         ProgressReporterDialog progress(4, _("Running Celeste"), _("Running Celeste"), this);
02007         progress.updateDisplayValue(_("Loading model file"));
02008 
02009         struct celeste::svm_model* model=MainFrame::Get()->GetSVMModel();
02010         if(model==NULL)
02011         {
02012             return;
02013         };
02014 
02015         // Get Celeste parameters
02016         wxConfigBase *cfg = wxConfigBase::Get();
02017         // SVM threshold
02018         double threshold = HUGIN_CELESTE_THRESHOLD;
02019         cfg->Read(wxT("/Celeste/Threshold"), &threshold, HUGIN_CELESTE_THRESHOLD);
02020 
02021         // Mask resolution - 1 sets it to fine
02022         bool t = (cfg->Read(wxT("/Celeste/Filter"), HUGIN_CELESTE_FILTER) == 0);
02023         int radius=(t)?10:20;
02024         DEBUG_TRACE("Running Celeste");
02025 
02026         if (!progress.updateDisplayValue(_("Running Celeste")))
02027         {
02028             return;
02029         }
02030         // Image to analyse
02031         ImageCache::EntryPtr img=ImageCache::getInstance().getImage(m_pano->getImage(m_leftImageNr).getFilename());
02032         vigra::UInt16RGBImage in;
02033         if(img->image16->width()>0)
02034         {
02035             in.resize(img->image16->size());
02036             vigra::omp::copyImage(srcImageRange(*(img->image16)),destImage(in));
02037         }
02038         else
02039         {
02040             ImageCache::ImageCacheRGB8Ptr im8=img->get8BitImage();
02041             in.resize(im8->size());
02042             vigra::omp::transformImage(srcImageRange(*im8),destImage(in),vigra::functor::Arg1()*vigra::functor::Param(65535/255));
02043         };
02044         // convert to sRGB if icc profile found in file
02045         if (!img->iccProfile->empty())
02046         { 
02047             HuginBase::Color::ApplyICCProfile(in, *(img->iccProfile), TYPE_RGB_16);
02048         };
02049         if (!progress.updateDisplayValue())
02050         {
02051             return;
02052         };
02053         HuginBase::UIntSet cloudCP = celeste::getCelesteControlPoints(model, in, currentPoints, radius, threshold, 800);
02054         in.resize(0,0);
02055         if (!progress.updateDisplay())
02056         {
02057             return;
02058         }
02059 
02060         if(cloudCP.size()>0)
02061         {
02062             PanoCommand::GlobalCmdHist::getInstance().addCommand(
02063                 new PanoCommand::RemoveCtrlPointsCmd(*m_pano,cloudCP)
02064                 );
02065         };
02066 
02067         progress.updateDisplayValue();
02068         wxMessageBox(wxString::Format(_("Removed %lu control points"), static_cast<unsigned long int>(cloudCP.size())), _("Celeste result"), wxOK | wxICON_INFORMATION, this);
02069         DEBUG_TRACE("Finished running Celeste");
02070     }
02071 }
02072 
02073 void CPEditorPanel::OnCleanCPButton(wxCommandEvent& e)
02074 {
02075     if (currentPoints.size() < 2)
02076     {
02077         wxBell();
02078         return;
02079     };
02080     // calculate mean and variance only for currently active cp
02081     double mean = 0;
02082     double var = 0;
02083     size_t n = 0;
02084     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02085     {
02086         n++;
02087         double x = it->second.error;
02088         double delta = x - mean;
02089         mean += delta / n;
02090         var += delta*(x - mean);
02091     }
02092     var = var / (n - 1);
02093     const double limit = (sqrt(var) > mean) ? mean : (mean + sqrt(var));
02094     HuginBase::UIntSet removedCPs;
02095     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02096     {
02097         if (it->second.error > limit)
02098         {
02099             removedCPs.insert(it->first);
02100         };
02101     };
02102     if (!removedCPs.empty())
02103     {
02104         wxMessageBox(wxString::Format(_("Removed %lu control points"), (unsigned long int)removedCPs.size()), _("Cleaning"), wxOK | wxICON_INFORMATION, this);
02105         PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::RemoveCtrlPointsCmd(*m_pano, removedCPs));
02106     }
02107     else
02108     {
02109         wxBell();
02110     }
02111 };
02112 
02113 void CPEditorPanel::OnActionSelectCreate(wxCommandEvent& e)
02114 {
02115     m_cpActionButtonMode = CPTAB_ACTION_CREATE_CP;
02116     wxString s(_("Create cp"));
02117     s.Append(wxT("\u25bc"));
02118     m_actionButton->SetLabel(s);
02119     m_actionButton->SetToolTip(_("Create control points for image pair with currently selected control point detector on photos tab."));
02120     Layout();
02121     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02122 };
02123 
02124 void CPEditorPanel::OnActionSelectCeleste(wxCommandEvent& e)
02125 {
02126     m_cpActionButtonMode = CPTAB_ACTION_CELESTE;
02127     wxString s(_("Celeste"));
02128     s.Append(wxT("\u25bc"));
02129     m_actionButton->SetLabel(s);
02130     m_actionButton->SetToolTip(_("Tries to remove control points from clouds"));
02131     Layout();
02132     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02133 };
02134 
02135 void CPEditorPanel::OnActionSelectCleanCP(wxCommandEvent& e)
02136 {
02137     m_cpActionButtonMode = CPTAB_ACTION_CLEAN_CP;
02138     wxString s(_("Clean cp"));
02139     s.Append(wxT("\u25bc"));
02140     m_actionButton->SetLabel(s);
02141     m_actionButton->SetToolTip(_("Remove outlying control points by statistical method"));
02142     Layout();
02143     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02144 };
02145 
02146 hugin_utils::FDiff2D CPEditorPanel::LocalFineTunePoint(unsigned int srcNr,
02147                                           const vigra::Diff2D & srcPnt,
02148                                           hugin_utils::FDiff2D & movedSrcPnt,
02149                                           unsigned int moveNr,
02150                                           const hugin_utils::FDiff2D & movePnt)
02151 {
02152     long templWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE);
02153     long sWidth = templWidth + wxConfigBase::Get()->Read(wxT("/Finetune/LocalSearchWidth"),HUGIN_FT_LOCAL_SEARCH_WIDTH);
02154     vigra_ext::CorrelationResult result;
02155     PointFineTune(srcNr,
02156                   srcPnt,
02157                   templWidth,
02158                   moveNr,
02159                   movePnt,
02160                   sWidth,
02161                   result);
02162     movedSrcPnt = result.corrPos;
02163     if (result.corrPos.x < 0 || result.corrPos.y < 0 || result.maxpos.x < 0 || result.maxpos.y < 0)
02164     {
02165         return hugin_utils::FDiff2D(-1, -1);
02166     }
02167     return result.maxpos;
02168 }
02169 
02170 void CPEditorPanel::FineTuneSelectedPoint(bool left)
02171 {
02172     DEBUG_DEBUG(" selected Point: " << m_selectedPoint);
02173     if (m_selectedPoint == UINT_MAX) return;
02174     DEBUG_ASSERT(m_selectedPoint < currentPoints.size());
02175 
02176     HuginBase::ControlPoint cp = currentPoints[m_selectedPoint].second;
02177 
02178     unsigned int srcNr = cp.image1Nr;
02179     unsigned int moveNr = cp.image2Nr;
02180     vigra::Diff2D srcPnt(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
02181     vigra::Diff2D movePnt(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
02182     if (left) {
02183         srcNr = cp.image2Nr;
02184         moveNr = cp.image1Nr;
02185         srcPnt = vigra::Diff2D(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
02186         movePnt = vigra::Diff2D(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
02187     }
02188 
02189     hugin_utils::FDiff2D movedSrcPnt;
02190     hugin_utils::FDiff2D result = LocalFineTunePoint(srcNr, srcPnt, movedSrcPnt, moveNr, movePnt);
02191 
02192     if (result.x < 0 || result.y < 0)
02193     {
02194         wxBell();
02195         return;
02196     };
02197     
02198     if (left) {
02199        cp.x1 = result.x;
02200        cp.y1 = result.y;
02201        cp.x2 = movedSrcPnt.x;
02202        cp.y2 = movedSrcPnt.y;
02203     } else {
02204        cp.x2 = result.x;
02205        cp.y2 = result.y;
02206        cp.x1 = movedSrcPnt.x;
02207        cp.y1 = movedSrcPnt.y;
02208     }
02209 
02210     // if point was mirrored, reverse before setting it.
02211     if (set_contains(mirroredPoints, m_selectedPoint)) {
02212         cp.mirror();
02213     }
02214     PanoCommand::GlobalCmdHist::getInstance().addCommand(
02215         new PanoCommand::ChangeCtrlPointCmd(*m_pano, currentPoints[m_selectedPoint].first, cp)
02216         );
02217 }
02218 
02219 
02220 void CPEditorPanel::FineTuneNewPoint(bool left)
02221 {
02222     if (!(cpCreationState == RIGHT_POINT_RETRY ||
02223           cpCreationState == LEFT_POINT_RETRY ||
02224           cpCreationState == BOTH_POINTS_SELECTED))
02225     {
02226         return;
02227     }
02228 
02229     hugin_utils::FDiff2D leftP = m_leftImg->getNewPoint();
02230     hugin_utils::FDiff2D rightP = m_rightImg->getNewPoint();
02231 
02232     unsigned int srcNr = m_leftImageNr;
02233     vigra::Diff2D srcPnt(leftP.toDiff2D());
02234     unsigned int moveNr = m_rightImageNr;
02235     vigra::Diff2D movePnt(rightP.toDiff2D());
02236     if (left) {
02237         srcNr = m_rightImageNr;
02238         srcPnt = rightP.toDiff2D();
02239         moveNr = m_leftImageNr;
02240         movePnt = leftP.toDiff2D();
02241     }
02242 
02243     hugin_utils::FDiff2D movedSrcPnt;
02244     hugin_utils::FDiff2D result = LocalFineTunePoint(srcNr, srcPnt, movedSrcPnt, moveNr, movePnt);
02245 
02246     if (result.x < 0 || result.y < 0)
02247     {
02248         wxBell();
02249         return;
02250     };
02251     if (left) {
02252         m_leftImg->setNewPoint(result);
02253         m_leftImg->update();
02254         m_rightImg->setNewPoint(movedSrcPnt);
02255         m_rightImg->update();
02256 
02257     } else {
02258         m_rightImg->setNewPoint(result);
02259         m_rightImg->update();
02260         m_leftImg->setNewPoint(movedSrcPnt);
02261         m_leftImg->update();
02262     }
02263 }
02264 
02265 hugin_utils::FDiff2D CPEditorPanel::EstimatePoint(const hugin_utils::FDiff2D & p, bool left)
02266 {
02267     size_t nrNormalCp = 0;
02268     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02269     {
02270         if (it->second.mode == HuginBase::ControlPoint::X_Y)
02271         {
02272             ++nrNormalCp;
02273         };
02274     };
02275     if (nrNormalCp==0)
02276     {
02277         DEBUG_WARN("Cannot estimate position without at least one point");
02278         return hugin_utils::FDiff2D(0, 0);
02279     }
02280 
02281     // get copy of SrcPanoImage and reset position
02282     HuginBase::SrcPanoImage leftImg = m_pano->getSrcImage(left ? m_leftImageNr : m_rightImageNr);
02283     leftImg.setYaw(0);
02284     leftImg.setPitch(0);
02285     leftImg.setRoll(0);
02286     leftImg.setX(0);
02287     leftImg.setY(0);
02288     leftImg.setZ(0);
02289     HuginBase::SrcPanoImage rightImg = m_pano->getSrcImage(left ? m_rightImageNr : m_leftImageNr);
02290     rightImg.setYaw(0);
02291     rightImg.setPitch(0);
02292     rightImg.setRoll(0);
02293     rightImg.setX(0);
02294     rightImg.setY(0);
02295     rightImg.setZ(0);
02296     // generate a temporary pano
02297     HuginBase::Panorama optPano;
02298     optPano.addImage(leftImg);
02299     optPano.addImage(rightImg);
02300     // construct OptimizeVector
02301     HuginBase::OptimizeVector optVec;
02302     std::set<std::string> opt;
02303     optVec.push_back(opt);
02304     opt.insert("y");
02305     opt.insert("p");
02306     if (nrNormalCp > 1)
02307     {
02308         opt.insert("r");
02309     };
02310     optVec.push_back(opt);
02311     optPano.setOptimizeVector(optVec);
02312     // now add control points, need to check image numbers
02313     HuginBase::CPVector cps;
02314     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02315     {
02316         HuginBase::ControlPoint cp(it->second);
02317         if (cp.mode == HuginBase::ControlPoint::X_Y)
02318         {
02319             cp.image1Nr = left ? 0 : 1;
02320             cp.image2Nr = left ? 1 : 0;
02321             cps.push_back(cp);
02322         };
02323     };
02324     optPano.setCtrlPoints(cps);
02325     deregisterPTWXDlgFcn();
02326     HuginBase::PTools::optimize(optPano);
02327     registerPTWXDlgFcn();
02328 
02329     // now transform the wanted point p to other image
02330     HuginBase::PTools::Transform transformBackward;
02331     transformBackward.createInvTransform(optPano.getImage(0), optPano.getOptions());
02332     HuginBase::PTools::Transform transformForward;
02333     transformForward.createTransform(optPano.getImage(1), optPano.getOptions());
02334     hugin_utils::FDiff2D t;
02335     if (transformBackward.transformImgCoord(t, p))
02336     {
02337         if (transformForward.transformImgCoord(t, t))
02338         {
02339             // clip to fit to
02340             if (t.x < 0) t.x = 0;
02341             if (t.y < 0) t.y = 0;
02342             if (t.x > optPano.getImage(1).getWidth()) t.x = optPano.getImage(1).getWidth();
02343             if (t.y > optPano.getImage(1).getHeight()) t.y = optPano.getImage(1).getHeight();
02344             DEBUG_DEBUG("estimated point " << t.x << "," << t.y);
02345             return t;
02346         };
02347     };
02348     wxBell();
02349     return hugin_utils::FDiff2D(0, 0);
02350 }
02351 
02352 void CPEditorPanel::OnColumnWidthChange( wxListEvent & e )
02353 {
02354     int colNum = e.GetColumn();
02355     wxConfigBase::Get()->Write( wxString::Format(wxT("/CPEditorPanel/ColumnWidth%d"),colNum), m_cpList->GetColumnWidth(colNum) );
02356 }
02357 
02358 CPImageCtrl::ImageRotation CPEditorPanel::GetRot(double yaw, double pitch, double roll)
02359 {
02360     CPImageCtrl::ImageRotation rot = CPImageCtrl::ROT0;
02361     // normalize roll angle
02362     while (roll > 360) roll-= 360;
02363     while (roll < 0) roll += 360;
02364 
02365     while (pitch > 180) pitch -= 360;
02366     while (pitch < -180) pitch += 360;
02367     bool headOver = (pitch > 90 || pitch < -90);
02368 
02369     if (wxConfig::Get()->Read(wxT("/CPEditorPanel/AutoRot"),1L)) {
02370         if (roll >= 315 || roll < 45) {
02371             rot = headOver ? CPImageCtrl::ROT180 : CPImageCtrl::ROT0;
02372         } else if (roll >= 45 && roll < 135) {
02373             rot = headOver ? CPImageCtrl::ROT270 : CPImageCtrl::ROT90;
02374         } else if (roll >= 135 && roll < 225) {
02375             rot = headOver ? CPImageCtrl::ROT0 : CPImageCtrl::ROT180;
02376         } else {
02377             rot = headOver ? CPImageCtrl::ROT90 : CPImageCtrl::ROT270;
02378         }
02379     }
02380     return rot;
02381 }
02382 
02383 IMPLEMENT_DYNAMIC_CLASS(CPEditorPanel, wxPanel)
02384 
02385 CPEditorPanelXmlHandler::CPEditorPanelXmlHandler()
02386                 : wxXmlResourceHandler()
02387 {
02388     AddWindowStyles();
02389 }
02390 
02391 wxObject *CPEditorPanelXmlHandler::DoCreateResource()
02392 {
02393     XRC_MAKE_INSTANCE(cp, CPEditorPanel)
02394 
02395     cp->Create(m_parentAsWindow,
02396                    GetID(),
02397                    GetPosition(), GetSize(),
02398                    GetStyle(wxT("style")),
02399                    GetName());
02400 
02401     SetupWindow( cp);
02402 
02403     return cp;
02404 }
02405 
02406 bool CPEditorPanelXmlHandler::CanHandle(wxXmlNode *node)
02407 {
02408     return IsOfClass(node, wxT("CPEditorPanel"));
02409 }
02410 
02411 IMPLEMENT_DYNAMIC_CLASS(CPEditorPanelXmlHandler, wxXmlResourceHandler)
02412 

Generated on 5 May 2016 for Hugintrunk by  doxygen 1.4.7