CPEditorPanel.cpp

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

Generated on 26 Aug 2016 for Hugintrunk by  doxygen 1.4.7