CPEditorPanel.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include "hugin_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.empty())
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.maxi > -1 ? corrRes.maxpos : p);
00846                         thisImg->update();
00847                         otherImg->setNewPoint(corrRes.maxi > -1 ? corrRes.corrPos : newPoint);
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     if (tmplImgNr == subjImgNr)
01110     {
01111         // for line control points (assumed when both points are in the same image)
01112         // check the distance before running fine-tune
01113         // if both points are in the search area decrease search area
01114         const double distance = (tmplPoint - o_subjPoint.toDiff2D()).magnitude();
01115         if (distance < sWidth)
01116         {
01117             sWidth = 0.9 * distance;
01118         };
01119         if (sWidth < 1.1 * templSize)
01120         {
01121             MainFrame::Get()->SetStatusText(_("Distance between line control points too short, skipping fine-tune."));
01122             wxBell();
01123             return false;
01124         };
01125     };
01126     MainFrame::Get()->SetStatusText(_("searching similar points..."),0);
01127 
01128     double corrThresh=HUGIN_FT_CORR_THRESHOLD;
01129     wxConfigBase::Get()->Read(wxT("/Finetune/CorrThreshold"),&corrThresh,
01130                               HUGIN_FT_CORR_THRESHOLD);
01131 
01132     double curvThresh = HUGIN_FT_CURV_THRESHOLD;
01133     wxConfigBase::Get()->Read(wxT("/Finetune/CurvThreshold"),&curvThresh,
01134                               HUGIN_FT_CURV_THRESHOLD);
01135 
01136     // fixme: just cutout suitable gray 
01137     ImageCache::ImageCacheRGB8Ptr subjImg = ImageCache::getInstance().getImage(m_pano->getImage(subjImgNr).getFilename())->get8BitImage();
01138     ImageCache::ImageCacheRGB8Ptr tmplImg = ImageCache::getInstance().getImage(m_pano->getImage(tmplImgNr).getFilename())->get8BitImage();
01139 
01140     res = PointFineTuneProjectionAware(m_pano->getImage(tmplImgNr), *(tmplImg), tmplPoint, templSize,
01141         m_pano->getImage(subjImgNr), *(subjImg), o_subjPoint.toDiff2D(), sWidth);
01142 
01143     // invert curvature. we always assume its a maxima, the curvature there is negative
01144     // however, we allow the user to specify a positive threshold, so we need to
01145     // invert it
01146     res.curv.x = - res.curv.x;
01147     res.curv.y = - res.curv.y;
01148 
01149     MainFrame::Get()->SetStatusText(wxString::Format(_("Point fine-tuned, angle: %.0f deg, correlation coefficient: %0.3f, curvature: %0.3f %0.3f "),
01150                                     res.maxAngle, res.maxi, res.curv.x, res.curv.y ),0);
01151     if (res.corrPos.x < 0 || res.corrPos.y < 0 || res.maxpos.x < 0 || res.maxpos.y < 0)
01152     {
01153         // invalid transformation in fine tune
01154         wxMessageDialog dlg(this,
01155             _("No similar point found."),
01156 #ifdef _WIN32
01157             _("Hugin"),
01158 #else
01159             wxT(""),
01160 #endif
01161             wxICON_ERROR | wxOK);
01162         dlg.SetExtendedMessage(_("An internal transformation went wrong.\nCheck that the point is inside the image."));
01163         dlg.ShowModal();
01164         return false;
01165     }
01166     if (res.maxi < corrThresh || res.curv.x < curvThresh || res.curv.y < curvThresh )
01167     {
01168         // Bad correlation result.
01169         wxMessageDialog dlg(this,
01170             _("No similar point found."),
01171 #ifdef _WIN32
01172             _("Hugin"),
01173 #else
01174             wxT(""),
01175 #endif
01176             wxICON_ERROR | wxOK);
01177         dlg.SetExtendedMessage(wxString::Format(_("Check the similarity visually.\nCorrelation coefficient (%.3f) is lower than the threshold set in the preferences."),
01178                              res.maxi));
01179         dlg.ShowModal();
01180         return false;
01181     }
01182 
01183     return true;
01184 }
01185 
01186 void CPEditorPanel::panoramaChanged(HuginBase::Panorama &pano)
01187 {
01188     int nGui = m_cpModeChoice->GetCount();
01189     int nPano = pano.getNextCPTypeLineNumber()+1;
01190     DEBUG_DEBUG("mode choice: " << nGui << " entries, required: " << nPano);
01191 
01192     if (nGui > nPano)
01193     {
01194         m_cpModeChoice->Freeze();
01195         // remove some items.
01196         for (int i = nGui-1; i > nPano-1; --i) {
01197             m_cpModeChoice->Delete(i);
01198         }
01199         if (nPano > 3) {
01200             m_cpModeChoice->SetString(nPano-1, _("Add new Line"));
01201         }
01202         m_cpModeChoice->Thaw();
01203     } else if (nGui < nPano) {
01204         m_cpModeChoice->Freeze();
01205         if (nGui > 3) {
01206             m_cpModeChoice->SetString(nGui-1, wxString::Format(_("Line %d"), nGui-1));
01207         }
01208         for (int i = nGui; i < nPano-1; i++) {
01209             m_cpModeChoice->Append(wxString::Format(_("Line %d"), i));
01210         }
01211         m_cpModeChoice->Append(_("Add new Line"));
01212         m_cpModeChoice->Thaw();
01213     }
01214     UpdateTransforms();
01215     // check if number of control points has changed, if so we need to update our variables
01216     if (pano.getNrOfCtrlPoints() != m_countCP)
01217     {
01218         m_countCP = pano.getNrOfCtrlPoints();
01219         UpdateDisplay(false);
01220     };
01221     DEBUG_TRACE("");
01222 }
01223 
01224 void CPEditorPanel::panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet &changed)
01225 {
01226     unsigned int nrImages = pano.getNrOfImages();
01227     unsigned int nrTabs = m_leftChoice->GetCount();
01228     DEBUG_TRACE("nrImages:" << nrImages << " nrTabs:" << nrTabs);
01229 
01230 #ifdef __WXMSW__
01231     int oldLeftSelection = m_leftChoice->GetSelection();
01232     int oldRightSelection = m_rightChoice->GetSelection();
01233 #endif
01234 
01235     if (nrImages == 0)
01236     {
01237         // disable controls
01238         m_cpModeChoice->Disable();
01239         m_addButton->Disable();
01240         m_delButton->Disable();
01241         m_autoAddCB->Disable();
01242         m_fineTuneCB->Disable();
01243         m_estimateCB->Disable();
01244         XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Disable();
01245         m_actionButton->Disable();
01246         XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice)->Disable();
01247         XRCCTRL(*this, "cp_editor_previous_img", wxButton)->Disable();
01248         XRCCTRL(*this, "cp_editor_next_img", wxButton)->Disable();
01249         m_leftChoice->Disable();
01250         m_rightChoice->Disable();
01251     }
01252     else
01253     {
01254         // enable controls
01255         m_cpModeChoice->Enable();
01256         m_autoAddCB->Enable();
01257         m_fineTuneCB->Enable();
01258         m_estimateCB->Enable();
01259         XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Enable();
01260         m_actionButton->Enable();
01261         XRCCTRL(*this, "cp_editor_choice_zoom", wxChoice)->Enable();
01262         XRCCTRL(*this, "cp_editor_previous_img", wxButton)->Enable();
01263         XRCCTRL(*this, "cp_editor_next_img", wxButton)->Enable();
01264         m_leftChoice->Enable();
01265         m_rightChoice->Enable();
01266 
01267         ImageCache::getInstance().softFlush();
01268 
01269         for (unsigned int i=0; i < ((nrTabs < nrImages)? nrTabs: nrImages); i++) {
01270             wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
01271             m_leftChoice->SetString(i, wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01272             m_rightChoice->SetString(i, wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01273         }
01274         // wxChoice on windows looses the selection when setting new labels. Restore selection
01275 #ifdef __WXMSW__
01276         m_leftChoice->SetSelection(oldLeftSelection);
01277         m_rightChoice->SetSelection(oldRightSelection);
01278 #endif
01279         // add tab bar entries, if needed
01280         if (nrTabs < nrImages)
01281         {
01282             for (unsigned int i=nrTabs; i < nrImages; i++)
01283             {
01284                 wxFileName fileName(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
01285                 m_leftChoice->Append(wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01286                 m_rightChoice->Append(wxString::Format(wxT("%d"), i) + wxT(". - ") + fileName.GetFullName());
01287             }
01288         }
01289     }
01290     if (nrTabs > nrImages)
01291     {
01292         // remove tab bar entries if needed
01293         // we have to disable listening to notebook selection events,
01294         // else we might update to a noexisting image
01295         m_listenToPageChange = false;
01296         for (int i=nrTabs-1; i >= (int)nrImages; i--) {
01297             m_leftChoice->Delete(i);
01298             m_rightChoice->Delete(i);
01299         }
01300         m_listenToPageChange = true;
01301         if (nrImages > 0) {
01302             // select some other image if we deleted the current image
01303             if (m_leftImageNr >= nrImages) {
01304                 setLeftImage(nrImages -1);
01305             }
01306             if (m_rightImageNr >= nrImages) {
01307                 setRightImage(nrImages -1);
01308             }
01309         } else {
01310             DEBUG_DEBUG("setting no images");
01311             m_leftImageNr = UINT_MAX;
01312             m_leftFile = "";
01313             m_rightImageNr = UINT_MAX;
01314             m_rightFile = "";
01315             // no image anymore..
01316             m_leftImg->setImage(m_leftFile, CPImageCtrl::ROT0);
01317             m_rightImg->setImage(m_rightFile, CPImageCtrl::ROT0);
01318         }
01319     }
01320 
01321     // update changed images
01322     bool update(false);
01323     for(HuginBase::UIntSet::const_iterator it = changed.begin(); it != changed.end(); ++it) {
01324         unsigned int imgNr = *it;
01325         // we only need to update the view if the currently
01326         // selected images were changed.
01327         // changing the images via the tabbar will always
01328         // take the current state directly from the pano
01329         // object
01330         DEBUG_DEBUG("image changed "<< imgNr);
01331         double yaw = const_map_get(m_pano->getImageVariables(imgNr), "y").getValue();
01332         double pitch = const_map_get(m_pano->getImageVariables(imgNr), "p").getValue();
01333         double roll = const_map_get(m_pano->getImageVariables(imgNr), "r").getValue();
01334         CPImageCtrl::ImageRotation rot = GetRot(yaw, pitch, roll);
01335         if (m_leftImageNr == imgNr) {
01336             DEBUG_DEBUG("left image dirty "<< imgNr);
01337             if (m_leftFile != pano.getImage(imgNr).getFilename()
01338                 || m_leftRot != rot ) 
01339             {
01340                 m_leftRot = rot;
01341                 m_leftFile = pano.getImage(imgNr).getFilename();
01342                 m_leftImg->setImage(m_leftFile, m_leftRot);
01343             }
01344             update=true;
01345         }
01346 
01347         if (m_rightImageNr == imgNr) {
01348             DEBUG_DEBUG("right image dirty "<< imgNr);
01349             if (m_rightFile != pano.getImage(imgNr).getFilename()
01350                  || m_rightRot != rot ) 
01351             {
01352                 m_rightRot = rot;
01353                 m_rightFile = pano.getImage(imgNr).getFilename();
01354                 m_rightImg->setImage(m_rightFile, m_rightRot);
01355             }
01356             update=true;
01357         }
01358     }
01359     // check if number of control points has changed, if so we need to update our variables
01360     if (pano.getNrOfCtrlPoints() != m_countCP)
01361     {
01362         m_countCP = pano.getNrOfCtrlPoints();
01363         update = true;
01364     };
01365 
01366     // if there is no selection, select the first one.
01367     if (m_rightImageNr == UINT_MAX && nrImages > 0) {
01368         setRightImage(0);
01369     }
01370     if (m_leftImageNr == UINT_MAX && nrImages > 0) {
01371         setLeftImage(0);
01372     }
01373 
01374     if (update || nrImages == 0) {
01375         UpdateDisplay(false);
01376     }
01377     m_leftChoice->CalcCPDistance(m_pano);
01378     m_rightChoice->CalcCPDistance(m_pano);
01379 }
01380 
01381 void CPEditorPanel::UpdateDisplay(bool newPair)
01382 {
01383     DEBUG_DEBUG("")
01384     int fI = m_leftChoice->GetSelection();
01385     int sI = m_rightChoice->GetSelection();
01386 
01387     // valid selection and already set left image
01388     if (fI >= 0 && m_leftImageNr != UINT_MAX)
01389     {
01390         // set image number to selection
01391         m_leftImageNr = (unsigned int) fI;
01392     }
01393     // valid selection and already set right image
01394     if (sI >= 0 && m_rightImageNr != UINT_MAX)
01395     {
01396         // set image number to selection
01397         m_rightImageNr = (unsigned int) sI;
01398     }
01399     // reset selection
01400     m_x1Text->Clear();
01401     m_y1Text->Clear();
01402     m_x2Text->Clear();
01403     m_y2Text->Clear();
01404     if (m_cpModeChoice->GetSelection() < 3) {
01405         m_cpModeChoice->SetSelection(0);
01406     }
01407 
01408     m_leftImg->setSameImage(m_leftImageNr==m_rightImageNr);
01409     m_rightImg->setSameImage(m_leftImageNr==m_rightImageNr);
01410 
01411     // update control points
01412     const HuginBase::CPVector & controlPoints = m_pano->getCtrlPoints();
01413     currentPoints.clear();
01414     mirroredPoints.clear();
01415 
01416     // create a list of all control points
01417     HuginBase::CPVector::size_type i = 0;
01418     m_leftImg->clearCtrlPointList();
01419     m_rightImg->clearCtrlPointList();
01420     for (HuginBase::CPVector::size_type index = 0; index < controlPoints.size(); ++index)
01421     {
01422         HuginBase::ControlPoint point(controlPoints[index]);
01423         if ((point.image1Nr == m_leftImageNr) && (point.image2Nr == m_rightImageNr)){
01424             m_leftImg->setCtrlPoint(point, false);
01425             m_rightImg->setCtrlPoint(point, true);
01426             currentPoints.push_back(std::make_pair(index, point));
01427             i++;
01428         } else if ((point.image2Nr == m_leftImageNr) && (point.image1Nr == m_rightImageNr)){
01429             m_leftImg->setCtrlPoint(point, true);
01430             m_rightImg->setCtrlPoint(point, false);
01431             point.mirror();
01432             mirroredPoints.insert(i);
01433             currentPoints.push_back(std::make_pair(index, point));
01434             i++;
01435         }
01436     }
01437     m_leftImg->update();
01438     m_rightImg->update();
01439 
01440     // put these control points into our listview.
01441     unsigned int selectedCP = UINT_MAX;
01442     for ( int i=0; i < m_cpList->GetItemCount() ; i++ ) {
01443       if ( m_cpList->GetItemState( i, wxLIST_STATE_SELECTED ) ) {
01444         selectedCP = i;            // remembers the old selection
01445       }
01446     }
01447     m_cpList->Freeze();
01448     m_cpList->DeleteAllItems();
01449 
01450     for (unsigned int i=0; i < currentPoints.size(); ++i) {
01451         const HuginBase::ControlPoint & p(currentPoints[i].second);
01452         DEBUG_DEBUG("inserting LVItem " << i);
01453         m_cpList->InsertItem(i,wxString::Format(wxT("%d"),i));
01454         m_cpList->SetItem(i,1,wxString::Format(wxT("%.2f"),p.x1));
01455         m_cpList->SetItem(i,2,wxString::Format(wxT("%.2f"),p.y1));
01456         m_cpList->SetItem(i,3,wxString::Format(wxT("%.2f"),p.x2));
01457         m_cpList->SetItem(i,4,wxString::Format(wxT("%.2f"),p.y2));
01458         wxString mode;
01459         switch (p.mode) {
01460         case HuginBase::ControlPoint::X_Y:
01461             mode = _("normal");
01462             break;
01463         case HuginBase::ControlPoint::X:
01464             mode = _("vert. Line");
01465             break;
01466         case HuginBase::ControlPoint::Y:
01467             mode = _("horiz. Line");
01468             break;
01469         default:
01470             mode = wxString::Format(_("Line %d"), p.mode);
01471             break;
01472         }
01473         m_cpList->SetItem(i,5,mode);
01474         m_cpList->SetItem(i,6,wxString::Format(wxT("%.2f"),p.error));
01475     }
01476 
01477     if ( selectedCP < (unsigned int) m_cpList->GetItemCount() && ! newPair) {
01478         // sets an old selection again, only if the images have not changed
01479         m_cpList->SetItemState( selectedCP,
01480                                 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
01481         m_cpList->EnsureVisible(selectedCP);
01482         m_selectedPoint = selectedCP;
01483         EnablePointEdit(true);
01484 
01485         const HuginBase::ControlPoint & p = currentPoints[m_selectedPoint].second;
01486         m_x1Text->SetValue(wxString::Format(wxT("%.2f"),p.x1));
01487         m_y1Text->SetValue(wxString::Format(wxT("%.2f"),p.y1));
01488         m_x2Text->SetValue(wxString::Format(wxT("%.2f"),p.x2));
01489         m_y2Text->SetValue(wxString::Format(wxT("%.2f"),p.y2));
01490         m_cpModeChoice->SetSelection(p.mode);
01491         m_leftImg->selectPoint(m_selectedPoint);
01492         m_rightImg->selectPoint(m_selectedPoint);
01493 
01494     } else {
01495         m_selectedPoint = UINT_MAX;
01496         EnablePointEdit(false);
01497     }
01498 
01499     for ( int j=0; j < m_cpList->GetColumnCount() ; j++ )
01500     {
01501         //get saved width
01502         // -1 is auto
01503         int width = wxConfigBase::Get()->Read(wxString::Format( wxT("/CPEditorPanel/ColumnWidth%d"), j ), -1);
01504         if(width != -1)
01505             m_cpList->SetColumnWidth(j, width);
01506     }
01507 
01508     m_cpList->Thaw();
01509 }
01510 
01511 void CPEditorPanel::EnablePointEdit(bool state)
01512 {
01513     m_delButton->Enable(state);
01514     XRCCTRL(*this, "cp_editor_finetune_button", wxButton)->Enable(state);
01515     m_x1Text->Enable(state);
01516     m_y1Text->Enable(state);
01517     m_x2Text->Enable(state);
01518     m_y2Text->Enable(state);
01519     m_cpModeChoice->Enable(state);
01520 }
01521 
01522 void CPEditorPanel::OnTextPointChange(wxCommandEvent &e)
01523 {
01524     DEBUG_TRACE("");
01525     // find selected point
01526     long item = -1;
01527     item = m_cpList->GetNextItem(item,
01528                                  wxLIST_NEXT_ALL,
01529                                  wxLIST_STATE_SELECTED);
01530     // no selected item.
01531     if (item == -1) {
01532         return;
01533     }
01534     unsigned int nr = (unsigned int) item;
01535     assert(nr < currentPoints.size());
01536     HuginBase::ControlPoint cp = currentPoints[nr].second;
01537 
01538     // update point state
01539     double oldValue=cp.x1;
01540     bool valid_input=hugin_utils::str2double(m_x1Text->GetValue(), cp.x1);
01541     if(valid_input)
01542         valid_input=(cp.x1>=0) && (cp.x1<=m_pano->getSrcImage(cp.image1Nr).getWidth());
01543     if (!valid_input) {
01544         m_x1Text->Clear();
01545         *m_x1Text << oldValue;
01546         return;
01547     }
01548     oldValue=cp.y1;
01549     valid_input = hugin_utils::str2double(m_y1Text->GetValue(), cp.y1);
01550     if(valid_input)
01551         valid_input=(cp.y1>=0) && (cp.y1<=m_pano->getSrcImage(cp.image1Nr).getHeight());
01552     if (!valid_input) {
01553         m_y1Text->Clear();
01554         *m_y1Text << oldValue;
01555         return;
01556     }
01557     oldValue=cp.x2;
01558     valid_input = hugin_utils::str2double(m_x2Text->GetValue(), cp.x2);
01559     if(valid_input)
01560         valid_input=(cp.x2>=0) && (cp.x2<=m_pano->getSrcImage(cp.image2Nr).getWidth());
01561     if (!valid_input) {
01562         m_x2Text->Clear();
01563         *m_x2Text << oldValue;
01564         return;
01565     }
01566     oldValue=cp.y2;
01567     valid_input = hugin_utils::str2double(m_y2Text->GetValue(), cp.y2);
01568     if(valid_input)
01569         valid_input=(cp.y2>=0) && (cp.y2<=m_pano->getSrcImage(cp.image1Nr).getHeight());
01570     if (!valid_input) {
01571         m_y2Text->Clear();
01572         *m_y2Text << oldValue;
01573         return;
01574     }
01575 
01576     cp.mode = m_cpModeChoice->GetSelection();
01577     // if point was mirrored, reverse before setting it.
01578     if (set_contains(mirroredPoints, nr)) {
01579         cp.mirror();
01580     }
01581     PanoCommand::GlobalCmdHist::getInstance().addCommand(
01582         new PanoCommand::ChangeCtrlPointCmd(*m_pano, currentPoints[nr].first, cp)
01583         );
01584 
01585 }
01586 
01587 void CPEditorPanel::OnLeftChoiceChange(wxCommandEvent & e)
01588 {
01589     DEBUG_TRACE("OnLeftChoiceChange() to " << e.GetSelection());
01590     if (m_listenToPageChange && e.GetSelection() >= 0) {
01591         setLeftImage((unsigned int) e.GetSelection());
01592     }
01593 }
01594 
01595 void CPEditorPanel::OnRightChoiceChange(wxCommandEvent & e)
01596 {
01597     DEBUG_TRACE("OnRightChoiceChange() to " << e.GetSelection());
01598     if (m_listenToPageChange && e.GetSelection() >= 0) {
01599         setRightImage((unsigned int) e.GetSelection());
01600     }
01601 }
01602 void CPEditorPanel::OnCPListSelect(wxListEvent & ev)
01603 {
01604     int t = ev.GetIndex();
01605     DEBUG_TRACE("selected: " << t);
01606     if (t >=0) {
01607         SelectLocalPoint((unsigned int) t);
01608         changeState(NO_POINT);
01609     }
01610     EnablePointEdit(true);
01611 }
01612 
01613 void CPEditorPanel::OnCPListDeselect(wxListEvent & ev)
01614 {
01615     // disable controls
01616     // when doing changes to this procedure do also check
01617     // interaction with control point table
01618     // e.g. m_selectedPoint=UINT_MAX will result in a endless loop and crash
01619     changeState(NO_POINT);
01620     EnablePointEdit(false);
01621     m_leftImg->deselect();
01622     m_rightImg->deselect();
01623 }
01624 
01625 void CPEditorPanel::OnZoom(wxCommandEvent & e)
01626 {
01627     int leftX = 0;
01628     int leftY = 0;
01629     int rightX = 0;
01630     int rightY = 0;
01631     const wxSize leftSize = m_leftImg->GetClientSize();
01632     const wxSize rightSize = m_rightImg->GetClientSize();
01633     if (m_leftImg->getScale() > 0)
01634     {
01635         // remember old scroll position
01636         leftX = (m_leftImg->GetScrollPos(wxHORIZONTAL) + leftSize.GetWidth() / 2 )/ m_leftImg->getScale();
01637         leftY = (m_leftImg->GetScrollPos(wxVERTICAL) + leftSize.GetHeight() / 2) / m_leftImg->getScale();
01638         rightX = (m_rightImg->GetScrollPos(wxHORIZONTAL) + rightSize.GetWidth() / 2) / m_rightImg->getScale();
01639         rightY = (m_rightImg->GetScrollPos(wxVERTICAL) + rightSize.GetHeight() / 2) / m_rightImg->getScale();
01640     };
01641     double factor;
01642     switch (e.GetSelection()) {
01643     case 0:
01644         factor = 1;
01645         m_detailZoomFactor = factor;
01646         break;
01647     case 1:
01648         // fit to window
01649         factor = 0;
01650         break;
01651     case 2:
01652         factor = 2;
01653         m_detailZoomFactor = factor;
01654         break;
01655     case 3:
01656         factor = 1.5;
01657         m_detailZoomFactor = factor;
01658         break;
01659     case 4:
01660         factor = 0.75;
01661         break;
01662     case 5:
01663         factor = 0.5;
01664         break;
01665     case 6:
01666         factor = 0.25;
01667         break;
01668     default:
01669         DEBUG_ERROR("unknown scale factor");
01670         factor = 1;
01671     }
01672     m_leftImg->setScale(factor);
01673     m_rightImg->setScale(factor);
01674     // if a point is selected, keep it in view
01675     if (m_selectedPoint < UINT_MAX) {
01676         SelectLocalPoint(m_selectedPoint);
01677     }
01678     else
01679     {
01680         if (factor > 0)
01681         {
01682             // scroll to keep old position in view
01683             m_leftImg->Scroll(leftX*factor - leftSize.GetWidth() / 2, leftY*factor - leftSize.GetHeight() / 2);
01684             m_rightImg->Scroll(rightX*factor - rightSize.GetWidth() / 2, rightY*factor - rightSize.GetHeight() / 2);
01685         };
01686     }
01687 }
01688 
01689 void CPEditorPanel::OnKey(wxKeyEvent & e)
01690 {
01691     DEBUG_DEBUG("key " << e.GetKeyCode()
01692                 << " origin: id:" << e.GetId() << " obj: "
01693                 << e.GetEventObject());
01694 
01695     if (e.m_keyCode == WXK_DELETE){
01696         DEBUG_DEBUG("Delete pressed");
01697         // remove working points..
01698         if (cpCreationState != NO_POINT) {
01699             changeState(NO_POINT);
01700         } else {
01701             // remove selected point
01702             // find selected point
01703             long item = -1;
01704             item = m_cpList->GetNextItem(item,
01705                                          wxLIST_NEXT_ALL,
01706                                          wxLIST_STATE_SELECTED);
01707             // no selected item.
01708             if (item == -1) {
01709                 wxBell();
01710                 return;
01711             }
01712             unsigned int pNr = localPNr2GlobalPNr((unsigned int) item);
01713             DEBUG_DEBUG("about to delete point " << pNr);
01714             PanoCommand::GlobalCmdHist::getInstance().addCommand(
01715                 new PanoCommand::RemoveCtrlPointCmd(*m_pano,pNr)
01716                 );
01717         }
01718     } else if (e.m_keyCode == '0') {
01719         wxCommandEvent dummy;
01720         dummy.SetInt(1);
01721         OnZoom(dummy);
01722         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(1);
01723     } else if (e.m_keyCode == '1') {
01724         wxCommandEvent dummy;
01725         dummy.SetInt(0);
01726         OnZoom(dummy);
01727         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(0);
01728     } else if (e.m_keyCode == '2') {
01729         wxCommandEvent dummy;
01730         dummy.SetInt(2);
01731         OnZoom(dummy);
01732         XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->SetSelection(2);
01733     } else if (e.CmdDown() && e.GetKeyCode() == WXK_LEFT) {
01734         // move to previous
01735         wxCommandEvent dummy;
01736         OnPrevImg(dummy);
01737     } else if (e.CmdDown() && e.GetKeyCode() == WXK_RIGHT) {
01738         // move to next
01739         wxCommandEvent dummy;
01740         OnNextImg(dummy);
01741     } else if (e.GetKeyCode() == 'f') {
01742         bool left =  e.GetEventObject() == m_leftImg;
01743         if (cpCreationState == NO_POINT) {
01744             FineTuneSelectedPoint(left);
01745         } else if (cpCreationState == BOTH_POINTS_SELECTED) { 
01746             FineTuneNewPoint(left);
01747         }
01748     } else if (e.GetKeyCode() == 'g') {
01749         // generate keypoints
01750         long th = wxGetNumberFromUser(_("Create control points.\nTo create less points,\nenter a higher number."), _("Corner Detection threshold"), _("Create control points"), 400, 0, 32000);
01751         if (th == -1) {
01752             return;
01753         }
01754         long scale = wxGetNumberFromUser(_("Create control points"), _("Corner Detection scale"), _("Create control points"), 2);
01755         if (scale == -1) {
01756             return;
01757         }
01758 
01759         try {
01760             wxBusyCursor busy;
01761             DEBUG_DEBUG("corner threshold: " << th << "  scale: " << scale);
01762             PanoCommand::GlobalCmdHist::getInstance().addCommand(
01763                 new PanoCommand::wxAddCtrlPointGridCmd(*m_pano, m_leftImageNr, m_rightImageNr, scale, th)
01764                             );
01765         } catch (std::exception & e) {
01766             wxLogError(_("Error during control point creation:\n") + wxString(e.what(), wxConvLocal));
01767         }
01768     } else {
01769         e.Skip();
01770     }
01771 }
01772 
01773 void CPEditorPanel::OnAddButton(wxCommandEvent & e)
01774 {
01775     // check if the point can be created..
01776     if (cpCreationState == BOTH_POINTS_SELECTED) {
01777         CreateNewPoint();
01778     }
01779 }
01780 
01781 void CPEditorPanel::OnDeleteButton(wxCommandEvent & e)
01782 {
01783     DEBUG_TRACE("");
01784     // check if a point has been selected, but not added.
01785     if (cpCreationState != NO_POINT) {
01786         changeState(NO_POINT);
01787     } else {
01788         // find selected point
01789         long item = -1;
01790         item = m_cpList->GetNextItem(item,
01791                                      wxLIST_NEXT_ALL,
01792                                      wxLIST_STATE_SELECTED);
01793         // no selected item.
01794         if (item == -1) {
01795             wxBell();
01796             return;
01797         }
01798         // get the global point number
01799         unsigned int pNr = localPNr2GlobalPNr((unsigned int) item);
01800 
01801         PanoCommand::GlobalCmdHist::getInstance().addCommand(
01802             new PanoCommand::RemoveCtrlPointCmd(*m_pano,pNr )
01803             );
01804         m_leftChoice->CalcCPDistance(m_pano);
01805         m_rightChoice->CalcCPDistance(m_pano);
01806     }
01807 }
01808 
01809 // show a global control point
01810 void CPEditorPanel::ShowControlPoint(unsigned int cpNr)
01811 {
01812     const HuginBase::ControlPoint & p = m_pano->getCtrlPoint(cpNr);
01813     setLeftImage(p.image1Nr);
01814     setRightImage(p.image2Nr);
01815     // FIXME reset display state
01816     changeState(NO_POINT);
01817 
01818     SelectGlobalPoint(cpNr);
01819 }
01820 
01821 void CPEditorPanel::changeState(CPCreationState newState)
01822 {
01823     DEBUG_TRACE(cpCreationState << " --> " << newState);
01824     // handle global state changes.
01825     bool fineTune = m_fineTuneCB->IsChecked() && (m_leftImageNr != m_rightImageNr);
01826     switch(newState) {
01827     case NO_POINT:
01828         // disable all drawing search boxes.
01829         m_leftImg->showSearchArea(false);
01830         m_rightImg->showSearchArea(false);
01831         // but draw template size, if fine-tune enabled
01832         m_leftImg->showTemplateArea(fineTune);
01833         m_rightImg->showTemplateArea(fineTune);
01834         m_addButton->Enable(false);
01835         if (m_selectedPoint < UINT_MAX) {
01836             m_delButton->Enable(true);
01837         } else {
01838             m_delButton->Enable(false);
01839         }
01840         if (cpCreationState != NO_POINT) {
01841             // reset zoom to previous setting
01842             wxCommandEvent tmpEvt;
01843             tmpEvt.SetInt(XRCCTRL(*this,"cp_editor_choice_zoom",wxChoice)->GetSelection());
01844             OnZoom(tmpEvt);
01845             m_leftImg->clearNewPoint();
01846             m_rightImg->clearNewPoint();
01847         }
01848         break;
01849     case LEFT_POINT:
01850         // disable search area on left window
01851         m_leftImg->showSearchArea(false);
01852         // show search area on right window
01853         m_rightImg->showSearchArea(fineTune);
01854 
01855         // show template area
01856         m_leftImg->showTemplateArea(fineTune);
01857         m_rightImg->showTemplateArea(false);
01858 
01859         // unselect point
01860         ClearSelection();
01861         m_addButton->Enable(false);
01862         m_delButton->Enable(false);
01863         MainFrame::Get()->SetStatusText(_("Select point in right image"),0);
01864         break;
01865     case RIGHT_POINT:
01866         m_leftImg->showSearchArea(fineTune);
01867         m_rightImg->showSearchArea(false);
01868 
01869         m_leftImg->showTemplateArea(false);
01870         m_rightImg->showTemplateArea(fineTune);
01871 
01872         ClearSelection();
01873         m_addButton->Enable(false);
01874         m_delButton->Enable(false);
01875         MainFrame::Get()->SetStatusText(_("Select point in left image"),0);
01876         break;
01877     case LEFT_POINT_RETRY:
01878     case RIGHT_POINT_RETRY:
01879         m_leftImg->showSearchArea(false);
01880         m_rightImg->showSearchArea(false);
01881         // but draw template size, if fine-tune enabled
01882         m_leftImg->showTemplateArea(false);
01883         m_rightImg->showTemplateArea(false);
01884         m_addButton->Enable(false);
01885         m_delButton->Enable(false);
01886         break;
01887     case BOTH_POINTS_SELECTED:
01888         m_leftImg->showTemplateArea(false);
01889         m_rightImg->showTemplateArea(false);
01890         m_leftImg->showSearchArea(false);
01891         m_rightImg->showSearchArea(false);
01892         m_addButton->Enable(true);
01893         m_delButton->Enable(false);
01894     }
01895     // apply the change
01896     cpCreationState = newState;
01897 }
01898 
01899 void CPEditorPanel::OnPrevImg(wxCommandEvent & e)
01900 {
01901     if (m_pano->getNrOfImages() < 2) return;
01902     int nImgs = m_pano->getNrOfImages();
01903     int left = m_leftImageNr -1;
01904     int right = m_rightImageNr -1;
01905     if (left < 0) {
01906         left += nImgs;
01907     } else if (left >= nImgs) {
01908         left -= nImgs;
01909     }
01910 
01911     if (right < 0) {
01912         right += nImgs;
01913     } else if (right >= nImgs) {
01914         right -= nImgs;
01915     }
01916     setLeftImage((unsigned int) left);
01917     setRightImage((unsigned int) right);
01918 }
01919 
01920 void CPEditorPanel::OnNextImg(wxCommandEvent & e)
01921 {
01922     if (m_pano->getNrOfImages() < 2) return;
01923     int nImgs = m_pano->getNrOfImages();
01924     int left = m_leftImageNr + 1;
01925     int right = m_rightImageNr + 1;
01926     if (left < 0) {
01927         left += nImgs;
01928     } else if (left >= nImgs) {
01929         left -= nImgs;
01930     }
01931 
01932     if (right < 0) {
01933         right += nImgs;
01934     } else if (right >= nImgs) {
01935         right -= nImgs;
01936     }
01937     setLeftImage((unsigned int) left);
01938     setRightImage((unsigned int) right);
01939 }
01940 
01941 void CPEditorPanel::OnFineTuneButton(wxCommandEvent & e)
01942 {
01943     if (cpCreationState == NO_POINT) {
01944         FineTuneSelectedPoint(false);
01945     } else if (cpCreationState == BOTH_POINTS_SELECTED) {
01946         FineTuneNewPoint(false);
01947     }
01948 }
01949 
01950 void CPEditorPanel::OnActionContextMenu(wxContextMenuEvent& e)
01951 {
01952     m_cpActionContextMenu->SetLabel(XRCID("cp_menu_create_cp"), wxString::Format(_("Create cp (Current setting: %s)"), MainFrame::Get()->GetSelectedCPGenerator().c_str()));
01953     PopupMenu(m_cpActionContextMenu);
01954 };
01955 
01956 void CPEditorPanel::OnActionButton(wxCommandEvent& e)
01957 {
01958     switch (m_cpActionButtonMode)
01959     {
01960         case CPTAB_ACTION_CREATE_CP:
01961             OnCreateCPButton(e);
01962             break;
01963         case CPTAB_ACTION_CLEAN_CP:
01964             OnCleanCPButton(e);
01965             break;
01966         case CPTAB_ACTION_CELESTE:
01967         default:
01968             OnCelesteButton(e);
01969             break;
01970     };
01971 };
01972 
01973 void CPEditorPanel::OnCreateCPButton(wxCommandEvent& e)
01974 {
01975     if (m_leftImageNr == m_rightImageNr)
01976     {
01977         // when the same image is selected left and right we are running linefind 
01978         // with default parameters
01979         CPDetectorSetting linefindSetting;
01980 #ifdef __WXMSW__
01981         linefindSetting.SetProg(wxT("linefind.exe"));
01982 #else
01983         linefindSetting.SetProg(wxT("linefind"));
01984 #endif
01985         linefindSetting.SetArgs(wxT("-o %o %s"));
01986         HuginBase::UIntSet imgs;
01987         imgs.insert(m_leftImageNr);
01988         MainFrame::Get()->RunCPGenerator(linefindSetting, imgs);
01989     }
01990     else
01991     {
01992         HuginBase::UIntSet imgs;
01993         imgs.insert(m_leftImageNr);
01994         imgs.insert(m_rightImageNr);
01995         MainFrame::Get()->RunCPGenerator(imgs);
01996     };
01997 };
01998 
01999 void CPEditorPanel::OnCelesteButton(wxCommandEvent & e)
02000 {
02001     if (currentPoints.empty())
02002     {
02003         wxMessageBox(_("Cannot run celeste without at least one control point connecting the two images"),_("Error"));
02004         std::cout << "Cannot run celeste without at least one control point connecting the two images" << std::endl;
02005     }
02006     else
02007     {
02008         ProgressReporterDialog progress(4, _("Running Celeste"), _("Running Celeste"), this);
02009         progress.updateDisplayValue(_("Loading model file"));
02010 
02011         struct celeste::svm_model* model=MainFrame::Get()->GetSVMModel();
02012         if(model==NULL)
02013         {
02014             return;
02015         };
02016 
02017         // Get Celeste parameters
02018         wxConfigBase *cfg = wxConfigBase::Get();
02019         // SVM threshold
02020         double threshold = HUGIN_CELESTE_THRESHOLD;
02021         cfg->Read(wxT("/Celeste/Threshold"), &threshold, HUGIN_CELESTE_THRESHOLD);
02022 
02023         // Mask resolution - 1 sets it to fine
02024         bool t = (cfg->Read(wxT("/Celeste/Filter"), HUGIN_CELESTE_FILTER) == 0);
02025         int radius=(t)?10:20;
02026         DEBUG_TRACE("Running Celeste");
02027 
02028         if (!progress.updateDisplayValue(_("Running Celeste")))
02029         {
02030             return;
02031         }
02032         // Image to analyse
02033         ImageCache::EntryPtr img=ImageCache::getInstance().getImage(m_pano->getImage(m_leftImageNr).getFilename());
02034         vigra::UInt16RGBImage in;
02035         if(img->image16->width()>0)
02036         {
02037             in.resize(img->image16->size());
02038             vigra::omp::copyImage(srcImageRange(*(img->image16)),destImage(in));
02039         }
02040         else
02041         {
02042             ImageCache::ImageCacheRGB8Ptr im8=img->get8BitImage();
02043             in.resize(im8->size());
02044             vigra::omp::transformImage(srcImageRange(*im8),destImage(in),vigra::functor::Arg1()*vigra::functor::Param(65535/255));
02045         };
02046         // convert to sRGB if icc profile found in file
02047         if (!img->iccProfile->empty())
02048         { 
02049             HuginBase::Color::ApplyICCProfile(in, *(img->iccProfile), TYPE_RGB_16);
02050         };
02051         if (!progress.updateDisplayValue())
02052         {
02053             return;
02054         };
02055         HuginBase::UIntSet cloudCP = celeste::getCelesteControlPoints(model, in, currentPoints, radius, threshold, 800);
02056         in.resize(0,0);
02057         if (!progress.updateDisplay())
02058         {
02059             return;
02060         }
02061 
02062         if(!cloudCP.empty())
02063         {
02064             PanoCommand::GlobalCmdHist::getInstance().addCommand(
02065                 new PanoCommand::RemoveCtrlPointsCmd(*m_pano,cloudCP)
02066                 );
02067         };
02068 
02069         progress.updateDisplayValue();
02070         wxMessageBox(wxString::Format(_("Removed %lu control points"), static_cast<unsigned long int>(cloudCP.size())), _("Celeste result"), wxOK | wxICON_INFORMATION, this);
02071         DEBUG_TRACE("Finished running Celeste");
02072     }
02073 }
02074 
02075 void CPEditorPanel::OnCleanCPButton(wxCommandEvent& e)
02076 {
02077     if (currentPoints.size() < 2)
02078     {
02079         wxBell();
02080         return;
02081     };
02082     // calculate mean and variance only for currently active cp
02083     double mean = 0;
02084     double var = 0;
02085     size_t n = 0;
02086     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02087     {
02088         n++;
02089         double x = it->second.error;
02090         double delta = x - mean;
02091         mean += delta / n;
02092         var += delta*(x - mean);
02093     }
02094     var = var / (n - 1);
02095     const double limit = (sqrt(var) > mean) ? mean : (mean + sqrt(var));
02096     HuginBase::UIntSet removedCPs;
02097     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02098     {
02099         if (it->second.error > limit)
02100         {
02101             removedCPs.insert(it->first);
02102         };
02103     };
02104     if (!removedCPs.empty())
02105     {
02106         wxMessageBox(wxString::Format(_("Removed %lu control points"), (unsigned long int)removedCPs.size()), _("Cleaning"), wxOK | wxICON_INFORMATION, this);
02107         PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::RemoveCtrlPointsCmd(*m_pano, removedCPs));
02108     }
02109     else
02110     {
02111         wxBell();
02112     }
02113 };
02114 
02115 void CPEditorPanel::OnActionSelectCreate(wxCommandEvent& e)
02116 {
02117     m_cpActionButtonMode = CPTAB_ACTION_CREATE_CP;
02118     wxString s(_("Create cp"));
02119     s.Append(wxT("\u25bc"));
02120     m_actionButton->SetLabel(s);
02121     m_actionButton->SetToolTip(_("Create control points for image pair with currently selected control point detector on photos tab."));
02122     Layout();
02123     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02124 };
02125 
02126 void CPEditorPanel::OnActionSelectCeleste(wxCommandEvent& e)
02127 {
02128     m_cpActionButtonMode = CPTAB_ACTION_CELESTE;
02129     wxString s(_("Celeste"));
02130     s.Append(wxT("\u25bc"));
02131     m_actionButton->SetLabel(s);
02132     m_actionButton->SetToolTip(_("Tries to remove control points from clouds"));
02133     Layout();
02134     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02135 };
02136 
02137 void CPEditorPanel::OnActionSelectCleanCP(wxCommandEvent& e)
02138 {
02139     m_cpActionButtonMode = CPTAB_ACTION_CLEAN_CP;
02140     wxString s(_("Clean cp"));
02141     s.Append(wxT("\u25bc"));
02142     m_actionButton->SetLabel(s);
02143     m_actionButton->SetToolTip(_("Remove outlying control points by statistical method"));
02144     Layout();
02145     wxConfig::Get()->Write(wxT("/CPEditorPanel/ActionMode"), static_cast<long>(m_cpActionButtonMode));
02146 };
02147 
02148 hugin_utils::FDiff2D CPEditorPanel::LocalFineTunePoint(unsigned int srcNr,
02149                                           const vigra::Diff2D & srcPnt,
02150                                           hugin_utils::FDiff2D & movedSrcPnt,
02151                                           unsigned int moveNr,
02152                                           const hugin_utils::FDiff2D & movePnt)
02153 {
02154     long templWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE);
02155     long sWidth = templWidth + wxConfigBase::Get()->Read(wxT("/Finetune/LocalSearchWidth"),HUGIN_FT_LOCAL_SEARCH_WIDTH);
02156     vigra_ext::CorrelationResult result;
02157     if (!PointFineTune(srcNr, srcPnt, templWidth, moveNr, movePnt, sWidth, result))
02158     {
02159         return hugin_utils::FDiff2D(-1, -1);
02160     };
02161     movedSrcPnt = result.corrPos;
02162     if (result.corrPos.x < 0 || result.corrPos.y < 0 || result.maxpos.x < 0 || result.maxpos.y < 0)
02163     {
02164         return hugin_utils::FDiff2D(-1, -1);
02165     }
02166     return result.maxpos;
02167 }
02168 
02169 void CPEditorPanel::FineTuneSelectedPoint(bool left)
02170 {
02171     DEBUG_DEBUG(" selected Point: " << m_selectedPoint);
02172     if (m_selectedPoint == UINT_MAX) return;
02173     DEBUG_ASSERT(m_selectedPoint < currentPoints.size());
02174 
02175     HuginBase::ControlPoint cp = currentPoints[m_selectedPoint].second;
02176 
02177     unsigned int srcNr = cp.image1Nr;
02178     unsigned int moveNr = cp.image2Nr;
02179     vigra::Diff2D srcPnt(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
02180     vigra::Diff2D movePnt(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
02181     if (left) {
02182         srcNr = cp.image2Nr;
02183         moveNr = cp.image1Nr;
02184         srcPnt = vigra::Diff2D(hugin_utils::roundi(cp.x2), hugin_utils::roundi(cp.y2));
02185         movePnt = vigra::Diff2D(hugin_utils::roundi(cp.x1), hugin_utils::roundi(cp.y1));
02186     }
02187 
02188     hugin_utils::FDiff2D movedSrcPnt;
02189     hugin_utils::FDiff2D result = LocalFineTunePoint(srcNr, srcPnt, movedSrcPnt, moveNr, movePnt);
02190 
02191     if (result.x < 0 || result.y < 0)
02192     {
02193         wxBell();
02194         return;
02195     };
02196     
02197     if (left) {
02198        cp.x1 = result.x;
02199        cp.y1 = result.y;
02200        cp.x2 = movedSrcPnt.x;
02201        cp.y2 = movedSrcPnt.y;
02202     } else {
02203        cp.x2 = result.x;
02204        cp.y2 = result.y;
02205        cp.x1 = movedSrcPnt.x;
02206        cp.y1 = movedSrcPnt.y;
02207     }
02208 
02209     // if point was mirrored, reverse before setting it.
02210     if (set_contains(mirroredPoints, m_selectedPoint)) {
02211         cp.mirror();
02212     }
02213     PanoCommand::GlobalCmdHist::getInstance().addCommand(
02214         new PanoCommand::ChangeCtrlPointCmd(*m_pano, currentPoints[m_selectedPoint].first, cp)
02215         );
02216 }
02217 
02218 
02219 void CPEditorPanel::FineTuneNewPoint(bool left)
02220 {
02221     if (!(cpCreationState == RIGHT_POINT_RETRY ||
02222           cpCreationState == LEFT_POINT_RETRY ||
02223           cpCreationState == BOTH_POINTS_SELECTED))
02224     {
02225         return;
02226     }
02227 
02228     hugin_utils::FDiff2D leftP = m_leftImg->getNewPoint();
02229     hugin_utils::FDiff2D rightP = m_rightImg->getNewPoint();
02230 
02231     unsigned int srcNr = m_leftImageNr;
02232     vigra::Diff2D srcPnt(leftP.toDiff2D());
02233     unsigned int moveNr = m_rightImageNr;
02234     vigra::Diff2D movePnt(rightP.toDiff2D());
02235     if (left) {
02236         srcNr = m_rightImageNr;
02237         srcPnt = rightP.toDiff2D();
02238         moveNr = m_leftImageNr;
02239         movePnt = leftP.toDiff2D();
02240     }
02241 
02242     hugin_utils::FDiff2D movedSrcPnt;
02243     hugin_utils::FDiff2D result = LocalFineTunePoint(srcNr, srcPnt, movedSrcPnt, moveNr, movePnt);
02244 
02245     if (result.x < 0 || result.y < 0)
02246     {
02247         wxBell();
02248         return;
02249     };
02250     if (left) {
02251         m_leftImg->setNewPoint(result);
02252         m_leftImg->update();
02253         m_rightImg->setNewPoint(movedSrcPnt);
02254         m_rightImg->update();
02255 
02256     } else {
02257         m_rightImg->setNewPoint(result);
02258         m_rightImg->update();
02259         m_leftImg->setNewPoint(movedSrcPnt);
02260         m_leftImg->update();
02261     }
02262 }
02263 
02264 hugin_utils::FDiff2D CPEditorPanel::EstimatePoint(const hugin_utils::FDiff2D & p, bool left)
02265 {
02266     size_t nrNormalCp = 0;
02267     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02268     {
02269         if (it->second.mode == HuginBase::ControlPoint::X_Y)
02270         {
02271             ++nrNormalCp;
02272         };
02273     };
02274     if (nrNormalCp==0)
02275     {
02276         DEBUG_WARN("Cannot estimate position without at least one point");
02277         return hugin_utils::FDiff2D(0, 0);
02278     }
02279 
02280     // get copy of SrcPanoImage and reset position
02281     HuginBase::SrcPanoImage leftImg = m_pano->getSrcImage(left ? m_leftImageNr : m_rightImageNr);
02282     leftImg.setYaw(0);
02283     leftImg.setPitch(0);
02284     leftImg.setRoll(0);
02285     leftImg.setX(0);
02286     leftImg.setY(0);
02287     leftImg.setZ(0);
02288     HuginBase::SrcPanoImage rightImg = m_pano->getSrcImage(left ? m_rightImageNr : m_leftImageNr);
02289     rightImg.setYaw(0);
02290     rightImg.setPitch(0);
02291     rightImg.setRoll(0);
02292     rightImg.setX(0);
02293     rightImg.setY(0);
02294     rightImg.setZ(0);
02295     // generate a temporary pano
02296     HuginBase::Panorama optPano;
02297     optPano.addImage(leftImg);
02298     optPano.addImage(rightImg);
02299     // construct OptimizeVector
02300     HuginBase::OptimizeVector optVec;
02301     std::set<std::string> opt;
02302     optVec.push_back(opt);
02303     opt.insert("y");
02304     opt.insert("p");
02305     if (nrNormalCp > 1)
02306     {
02307         opt.insert("r");
02308     };
02309     optVec.push_back(opt);
02310     optPano.setOptimizeVector(optVec);
02311     // now add control points, need to check image numbers
02312     HuginBase::CPVector cps;
02313     for (HuginBase::CPointVector::const_iterator it = currentPoints.begin(); it != currentPoints.end(); ++it)
02314     {
02315         HuginBase::ControlPoint cp(it->second);
02316         if (cp.mode == HuginBase::ControlPoint::X_Y)
02317         {
02318             cp.image1Nr = left ? 0 : 1;
02319             cp.image2Nr = left ? 1 : 0;
02320             cps.push_back(cp);
02321         };
02322     };
02323     optPano.setCtrlPoints(cps);
02324     deregisterPTWXDlgFcn();
02325     HuginBase::PTools::optimize(optPano);
02326     registerPTWXDlgFcn();
02327 
02328     // now transform the wanted point p to other image
02329     HuginBase::PTools::Transform transformBackward;
02330     transformBackward.createInvTransform(optPano.getImage(0), optPano.getOptions());
02331     HuginBase::PTools::Transform transformForward;
02332     transformForward.createTransform(optPano.getImage(1), optPano.getOptions());
02333     hugin_utils::FDiff2D t;
02334     if (transformBackward.transformImgCoord(t, p))
02335     {
02336         if (transformForward.transformImgCoord(t, t))
02337         {
02338             // clip to fit to
02339             if (t.x < 0) t.x = 0;
02340             if (t.y < 0) t.y = 0;
02341             if (t.x > optPano.getImage(1).getWidth()) t.x = optPano.getImage(1).getWidth();
02342             if (t.y > optPano.getImage(1).getHeight()) t.y = optPano.getImage(1).getHeight();
02343             DEBUG_DEBUG("estimated point " << t.x << "," << t.y);
02344             return t;
02345         };
02346     };
02347     wxBell();
02348     return hugin_utils::FDiff2D(0, 0);
02349 }
02350 
02351 void CPEditorPanel::OnColumnWidthChange( wxListEvent & e )
02352 {
02353     int colNum = e.GetColumn();
02354     wxConfigBase::Get()->Write( wxString::Format(wxT("/CPEditorPanel/ColumnWidth%d"),colNum), m_cpList->GetColumnWidth(colNum) );
02355 }
02356 
02357 CPImageCtrl::ImageRotation CPEditorPanel::GetRot(double yaw, double pitch, double roll)
02358 {
02359     CPImageCtrl::ImageRotation rot = CPImageCtrl::ROT0;
02360     // normalize roll angle
02361     while (roll > 360) roll-= 360;
02362     while (roll < 0) roll += 360;
02363 
02364     while (pitch > 180) pitch -= 360;
02365     while (pitch < -180) pitch += 360;
02366     bool headOver = (pitch > 90 || pitch < -90);
02367 
02368     if (wxConfig::Get()->Read(wxT("/CPEditorPanel/AutoRot"),1L)) {
02369         if (roll >= 315 || roll < 45) {
02370             rot = headOver ? CPImageCtrl::ROT180 : CPImageCtrl::ROT0;
02371         } else if (roll >= 45 && roll < 135) {
02372             rot = headOver ? CPImageCtrl::ROT270 : CPImageCtrl::ROT90;
02373         } else if (roll >= 135 && roll < 225) {
02374             rot = headOver ? CPImageCtrl::ROT0 : CPImageCtrl::ROT180;
02375         } else {
02376             rot = headOver ? CPImageCtrl::ROT90 : CPImageCtrl::ROT270;
02377         }
02378     }
02379     return rot;
02380 }
02381 
02382 IMPLEMENT_DYNAMIC_CLASS(CPEditorPanel, wxPanel)
02383 
02384 CPEditorPanelXmlHandler::CPEditorPanelXmlHandler()
02385                 : wxXmlResourceHandler()
02386 {
02387     AddWindowStyles();
02388 }
02389 
02390 wxObject *CPEditorPanelXmlHandler::DoCreateResource()
02391 {
02392     XRC_MAKE_INSTANCE(cp, CPEditorPanel)
02393 
02394     cp->Create(m_parentAsWindow,
02395                    GetID(),
02396                    GetPosition(), GetSize(),
02397                    GetStyle(wxT("style")),
02398                    GetName());
02399 
02400     SetupWindow( cp);
02401 
02402     return cp;
02403 }
02404 
02405 bool CPEditorPanelXmlHandler::CanHandle(wxXmlNode *node)
02406 {
02407     return IsOfClass(node, wxT("CPEditorPanel"));
02408 }
02409 
02410 IMPLEMENT_DYNAMIC_CLASS(CPEditorPanelXmlHandler, wxXmlResourceHandler)
02411 

Generated on 25 Apr 2018 for Hugintrunk by  doxygen 1.4.7