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

Generated on 4 Dec 2016 for Hugintrunk by  doxygen 1.4.7