void getCameraParam(mrpt::utils::TCamera & param)const{ param.ncols = getFrameWidth(); param.nrows = getFrameHeight(); param.fx(getFx()); param.fy(getFy()); param.cx(getCx()); param.cy(getCy()); }
// Enter calib. params manually: void camera_calib_guiDialog::OnbtnManualRectClick(wxCommandEvent& event) { wxMessageBox(_("Please, enter calibration parameters manually next to overpass automatically obtained parameters."),_("Manual parameters")); wxString s; if (camera_params.intrinsicParams(0,0)==0) { wxMessageBox(_("Run the calibration first"),_("Error")); return; } s = wxGetTextFromUser(_("Focus length in X pixel size (fx):"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.intrinsicParams(0,0)), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.intrinsicParams(0,0))) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Focus length in Y pixel size (fy):"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.intrinsicParams(1,1)), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.intrinsicParams(1,1))) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Image center X (cx):"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.intrinsicParams(0,2)), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.intrinsicParams(0,2))) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Image center Y (cy):"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.intrinsicParams(1,2)), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.intrinsicParams(1,2))) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Distortion param p1:"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.dist[0]), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.dist[0])) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Distortion param p2:"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.dist[1]), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.dist[1])) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Distortion param k1:"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.dist[2]), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.dist[2])) { wxMessageBox(_("Invalid number")); return; } s = wxGetTextFromUser(_("Distortion param k2:"),_("Manual parameters"),wxString::Format(wxT("%.07f"),camera_params.dist[3]), this ); if (s.IsEmpty()) return; if (!s.ToDouble(&camera_params.dist[3])) { wxMessageBox(_("Invalid number")); return; } refreshDisplayedImage(); }
// save matrices: void camera_calib_guiDialog::OnbtnSaveClick(wxCommandEvent& event) { if (camera_params.intrinsicParams(0,0)==0) { wxMessageBox(_("Run the calibration first"),_("Error")); return; } { wxFileDialog dlg( this, _("Save intrinsic parameters matrix"), _("."), _("intrinsic_matrix.txt"), _("Text files (*.txt)|*.txt|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (wxID_OK!=dlg.ShowModal()) return; camera_params.intrinsicParams.saveToTextFile( string(dlg.GetPath().mb_str()) ); } { wxFileDialog dlg( this, _("Save distortion parameters"), _("."), _("distortion_matrix.txt"), _("Text files (*.txt)|*.txt|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (wxID_OK!=dlg.ShowModal()) return; CMatrixDouble M(1,4); for (unsigned i=0;i<4;i++) M(0,i) = camera_params.dist[i]; M.saveToTextFile( string(dlg.GetPath().mb_str()) ); } }
// Shows the image selected in the listbox: void camera_calib_guiDialog::refreshDisplayedImage() { try { if (!lbFiles->GetCount()) { // No images: return; } // Assure there's one selected: if (lbFiles->GetSelection()==wxNOT_FOUND) lbFiles->SetSelection(0); const string selFile = string(lbFiles->GetStringSelection().mb_str()); TCalibrationImageList::iterator it = lst_images.find(selFile); if (it==lst_images.end()) return; // Zoom: const std::string strZoom = string(cbZoom->GetStringSelection().mb_str()); const double zoomVal = 0.01*atof(strZoom.c_str()); ASSERT_(zoomVal>0 && zoomVal<30) TImageSize imgSizes(0,0); // Generate the images on-the-fly: CImage imgOrgColor; it->second.img_original.colorImage(imgOrgColor); imgSizes = imgOrgColor.getSize(); // Rectify: CImage imgRect; if (camera_params.intrinsicParams(0,0)==0) { // Not calibrated yet: imgRect = imgOrgColor; imgRect.scaleImage(imgSizes.x*zoomVal,imgSizes.y*zoomVal, IMG_INTERP_NN); } else { imgOrgColor.rectifyImage(imgRect,camera_params); imgRect.scaleImage(imgSizes.x*zoomVal,imgSizes.y*zoomVal, IMG_INTERP_NN); // Draw reprojected: for (unsigned int k=0;k<it->second.projectedPoints_undistorted.size();k++) imgRect.drawCircle( zoomVal*it->second.projectedPoints_undistorted[k].x, zoomVal*it->second.projectedPoints_undistorted[k].y, 4, TColor(0,255,64) ); imgRect.drawCircle( 10,10, 4, TColor(0,255,64) ); imgRect.textOut(18,4,"Reprojected corners",TColor::white); } // Zoom images: imgOrgColor.scaleImage(imgSizes.x*zoomVal,imgSizes.y*zoomVal, IMG_INTERP_NN); imgSizes = imgOrgColor.getSize(); CImage imgCheck = imgOrgColor; // Draw the board: for (unsigned int k=0;k<it->second.detected_corners.size();k++) { imgCheck.cross(it->second.detected_corners[k].x *zoomVal, it->second.detected_corners[k].y *zoomVal, TColor::blue, '+', 3 ); imgCheck.drawCircle( it->second.projectedPoints_distorted[k].x*zoomVal, it->second.projectedPoints_distorted[k].y*zoomVal, 4, TColor(0,255,64) ); } imgCheck.drawCircle( 10,10, 4, TColor(0,255,64) ); imgCheck.textOut(18,4,"Reprojected corners",TColor::white); imgCheck.cross( 10,30, TColor::blue, '+', 3 ); imgCheck.textOut(18,24,"Detected corners",TColor::white); this->bmpOriginal->AssignImage( imgCheck ); this->bmpRectified->AssignImage( imgRect ); it->second.img_original.unload(); // Plot: this->bmpOriginal->SetMinSize(wxSize(imgSizes.x,imgSizes.y)); this->bmpOriginal->SetMaxSize(wxSize(imgSizes.x,imgSizes.y)); this->bmpOriginal->SetSize(imgSizes.x,imgSizes.y); this->bmpRectified->SetMinSize(wxSize(imgSizes.x,imgSizes.y)); this->bmpRectified->SetMaxSize(wxSize(imgSizes.x,imgSizes.y)); this->bmpRectified->SetSize(imgSizes.x,imgSizes.y); this->FlexGridSizer11->RecalcSizes(); this->FlexGridSizer14->RecalcSizes(); //this->ScrolledWindow2->SetVirtualSize(wxSize(imgSizes.x,imgSizes.y)); //this->ScrolledWindow3->SetVirtualSize(wxSize(imgSizes.x,imgSizes.y)); this->ScrolledWindow2->SetScrollbars(1,1,imgSizes.x,imgSizes.y); this->ScrolledWindow3->SetScrollbars(1,1,imgSizes.x,imgSizes.y); this->bmpOriginal->Refresh(false); this->bmpRectified->Refresh(false); } catch(std::exception &e) { wxMessageBox(_U(e.what()),_("Error"),wxICON_INFORMATION,this); } }
camera_calib_guiDialog::camera_calib_guiDialog(wxWindow* parent,wxWindowID id) { // Load my custom icons: #if wxCHECK_VERSION(2, 8, 0) wxArtProvider::Push(new MyArtProvider); #else wxArtProvider::PushProvider(new MyArtProvider); #endif //(*Initialize(camera_calib_guiDialog) wxStaticBoxSizer* StaticBoxSizer2; wxFlexGridSizer* FlexGridSizer4; wxFlexGridSizer* FlexGridSizer16; wxStaticBoxSizer* StaticBoxSizer4; wxFlexGridSizer* FlexGridSizer10; wxFlexGridSizer* FlexGridSizer3; wxFlexGridSizer* FlexGridSizer5; wxFlexGridSizer* FlexGridSizer9; wxFlexGridSizer* FlexGridSizer2; wxFlexGridSizer* FlexGridSizer7; wxStaticBoxSizer* StaticBoxSizer3; wxFlexGridSizer* FlexGridSizer15; wxFlexGridSizer* FlexGridSizer18; wxFlexGridSizer* FlexGridSizer8; wxFlexGridSizer* FlexGridSizer13; wxFlexGridSizer* FlexGridSizer12; wxFlexGridSizer* FlexGridSizer6; wxStaticBoxSizer* StaticBoxSizer1; wxFlexGridSizer* FlexGridSizer1; wxFlexGridSizer* FlexGridSizer17; wxStaticBoxSizer* StaticBoxSizer5; Create(parent, id, _("Camera calibration GUI - Part of the MRPT project"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxMAXIMIZE_BOX, _T("id")); FlexGridSizer1 = new wxFlexGridSizer(1, 2, 0, 0); FlexGridSizer1->AddGrowableCol(1); FlexGridSizer1->AddGrowableRow(0); FlexGridSizer2 = new wxFlexGridSizer(3, 1, 0, 0); FlexGridSizer2->AddGrowableCol(0); FlexGridSizer2->AddGrowableRow(0); FlexGridSizer2->AddGrowableRow(2); StaticBoxSizer1 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("List of images")); FlexGridSizer4 = new wxFlexGridSizer(2, 1, 0, 0); FlexGridSizer4->AddGrowableCol(0); FlexGridSizer4->AddGrowableRow(1); FlexGridSizer5 = new wxFlexGridSizer(0, 4, 0, 0); btnCaptureNow = new wxButton(this, ID_BUTTON8, _("Grab now..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON8")); wxFont btnCaptureNowFont(wxDEFAULT,wxDEFAULT,wxFONTSTYLE_NORMAL,wxBOLD,false,wxEmptyString,wxFONTENCODING_DEFAULT); btnCaptureNow->SetFont(btnCaptureNowFont); FlexGridSizer5->Add(btnCaptureNow, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); Button11 = new wxButton(this, ID_BUTTON1, _("Add image(s)..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1")); FlexGridSizer5->Add(Button11, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); Button22 = new wxButton(this, ID_BUTTON2, _("Clear all"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2")); FlexGridSizer5->Add(Button22, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); btnSaveImages = new wxButton(this, ID_BUTTON9, _("Save all..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON9")); btnSaveImages->Disable(); FlexGridSizer5->Add(btnSaveImages, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer4->Add(FlexGridSizer5, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer15 = new wxFlexGridSizer(1, 2, 0, 0); FlexGridSizer15->AddGrowableCol(0); FlexGridSizer15->AddGrowableRow(0); lbFiles = new wxListBox(this, ID_LISTBOX1, wxDefaultPosition, wxSize(294,84), 0, 0, wxLB_ALWAYS_SB|wxVSCROLL|wxHSCROLL|wxALWAYS_SHOW_SB, wxDefaultValidator, _T("ID_LISTBOX1")); FlexGridSizer15->Add(lbFiles, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer16 = new wxFlexGridSizer(0, 1, 0, 0); StaticText5 = new wxStaticText(this, ID_STATICTEXT5, _("Zoom:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT5")); FlexGridSizer16->Add(StaticText5, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); cbZoom = new wxChoice(this, ID_CHOICE1, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE1")); cbZoom->Append(_("25%")); cbZoom->Append(_("50%")); cbZoom->Append(_("75%")); cbZoom->SetSelection( cbZoom->Append(_("100%")) ); cbZoom->Append(_("150%")); cbZoom->Append(_("200%")); cbZoom->Append(_("300%")); cbZoom->Append(_("400%")); cbZoom->Append(_("500%")); FlexGridSizer16->Add(cbZoom, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer15->Add(FlexGridSizer16, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer4->Add(FlexGridSizer15, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); StaticBoxSizer1->Add(FlexGridSizer4, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer2->Add(StaticBoxSizer1, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2); StaticBoxSizer3 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Checkerboard detection parameters")); FlexGridSizer6 = new wxFlexGridSizer(2, 2, 0, 0); FlexGridSizer6->AddGrowableCol(0); FlexGridSizer6->AddGrowableCol(1); StaticBoxSizer4 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Number of inner corners: ")); FlexGridSizer17 = new wxFlexGridSizer(1, 4, 0, 0); StaticText1 = new wxStaticText(this, ID_STATICTEXT1, _("In X:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1")); FlexGridSizer17->Add(StaticText1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); edSizeX = new wxSpinCtrl(this, ID_SPINCTRL1, _T("5"), wxDefaultPosition, wxSize(50,-1), 0, 1, 200, 5, _T("ID_SPINCTRL1")); edSizeX->SetValue(_T("5")); FlexGridSizer17->Add(edSizeX, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); StaticText2 = new wxStaticText(this, ID_STATICTEXT2, _("In Y:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT2")); FlexGridSizer17->Add(StaticText2, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); edSizeY = new wxSpinCtrl(this, ID_SPINCTRL2, _T("8"), wxDefaultPosition, wxSize(50,-1), 0, 1, 200, 8, _T("ID_SPINCTRL2")); edSizeY->SetValue(_T("8")); FlexGridSizer17->Add(edSizeY, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); StaticBoxSizer4->Add(FlexGridSizer17, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer6->Add(StaticBoxSizer4, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 2); wxString __wxRadioBoxChoices_1[2] = { _("OpenCV\'s default"), _("Scaramuzza et al.\'s") }; rbMethod = new wxRadioBox(this, ID_RADIOBOX1, _(" Detector method: "), wxDefaultPosition, wxDefaultSize, 2, __wxRadioBoxChoices_1, 1, 0, wxDefaultValidator, _T("ID_RADIOBOX1")); FlexGridSizer6->Add(rbMethod, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2); StaticBoxSizer5 = new wxStaticBoxSizer(wxHORIZONTAL, this, _(" Size of quads (in mm): ")); FlexGridSizer18 = new wxFlexGridSizer(1, 4, 0, 0); StaticText3 = new wxStaticText(this, ID_STATICTEXT3, _("In X:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT3")); FlexGridSizer18->Add(StaticText3, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); edLengthX = new wxTextCtrl(this, ID_TEXTCTRL1, _("40.0"), wxDefaultPosition, wxSize(40,-1), 0, wxDefaultValidator, _T("ID_TEXTCTRL1")); FlexGridSizer18->Add(edLengthX, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); StaticText6 = new wxStaticText(this, ID_STATICTEXT6, _("In Y:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT6")); FlexGridSizer18->Add(StaticText6, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); edLengthY = new wxTextCtrl(this, ID_TEXTCTRL3, _("40.0"), wxDefaultPosition, wxSize(40,-1), 0, wxDefaultValidator, _T("ID_TEXTCTRL3")); FlexGridSizer18->Add(edLengthY, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); StaticBoxSizer5->Add(FlexGridSizer18, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer6->Add(StaticBoxSizer5, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 2); cbNormalize = new wxCheckBox(this, ID_CHECKBOX1, _("Normalize image"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX1")); cbNormalize->SetValue(true); FlexGridSizer6->Add(cbNormalize, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); StaticBoxSizer3->Add(FlexGridSizer6, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer2->Add(StaticBoxSizer3, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2); StaticBoxSizer2 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Calibration")); FlexGridSizer7 = new wxFlexGridSizer(2, 1, 0, 0); FlexGridSizer7->AddGrowableCol(0); FlexGridSizer7->AddGrowableRow(0); FlexGridSizer9 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer9->AddGrowableCol(0); FlexGridSizer9->AddGrowableRow(0); txtLog = new wxTextCtrl(this, ID_TEXTCTRL2, _("(Calibration results)"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL|wxVSCROLL, wxDefaultValidator, _T("ID_TEXTCTRL2")); wxFont txtLogFont = wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT); if ( !txtLogFont.Ok() ) txtLogFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); txtLogFont.SetPointSize(9); txtLog->SetFont(txtLogFont); FlexGridSizer9->Add(txtLog, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer7->Add(FlexGridSizer9, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer8 = new wxFlexGridSizer(2, 3, 0, 0); FlexGridSizer8->AddGrowableCol(0); FlexGridSizer8->AddGrowableCol(1); btnRunCalib = new wxButton(this, ID_BUTTON3, _("Calibrate"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON3")); btnRunCalib->SetDefault(); wxFont btnRunCalibFont(wxDEFAULT,wxDEFAULT,wxFONTSTYLE_NORMAL,wxBOLD,false,wxEmptyString,wxFONTENCODING_DEFAULT); btnRunCalib->SetFont(btnRunCalibFont); FlexGridSizer8->Add(btnRunCalib, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); btnSave = new wxButton(this, ID_BUTTON6, _("Save matrices..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON6")); FlexGridSizer8->Add(btnSave, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer8->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); btnManualRect = new wxButton(this, ID_BUTTON7, _("Manual params..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON7")); FlexGridSizer8->Add(btnManualRect, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); btnAbout = new wxButton(this, ID_BUTTON5, _("About"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON5")); FlexGridSizer8->Add(btnAbout, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); btnClose = new wxButton(this, ID_BUTTON4, _("Quit"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON4")); FlexGridSizer8->Add(btnClose, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); FlexGridSizer7->Add(FlexGridSizer8, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); StaticBoxSizer2->Add(FlexGridSizer7, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer2->Add(StaticBoxSizer2, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2); FlexGridSizer1->Add(FlexGridSizer2, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); FlexGridSizer3 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer3->AddGrowableCol(0); FlexGridSizer3->AddGrowableRow(0); Notebook1 = new wxNotebook(this, ID_NOTEBOOK1, wxDefaultPosition, wxSize(719,570), 0, _T("ID_NOTEBOOK1")); Panel2 = new wxPanel(Notebook1, ID_PANEL2, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL2")); FlexGridSizer13 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer13->AddGrowableCol(0); FlexGridSizer13->AddGrowableRow(0); ScrolledWindow2 = new wxScrolledWindow(Panel2, ID_SCROLLEDWINDOW2, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL, _T("ID_SCROLLEDWINDOW2")); FlexGridSizer11 = new wxFlexGridSizer(0, 1, 0, 0); FlexGridSizer11->AddGrowableCol(0); FlexGridSizer11->AddGrowableRow(0); bmpOriginal = new mrpt::gui::wxMRPTImageControl(ScrolledWindow2,ID_CUSTOM2,wxDefaultPosition.x,wxDefaultPosition.y,wxSize(640,480).GetWidth(), wxSize(640,480).GetHeight() ); FlexGridSizer11->Add(bmpOriginal, 1, wxALL|wxALIGN_LEFT|wxALIGN_TOP, 0); ScrolledWindow2->SetSizer(FlexGridSizer11); FlexGridSizer11->Fit(ScrolledWindow2); FlexGridSizer11->SetSizeHints(ScrolledWindow2); FlexGridSizer13->Add(ScrolledWindow2, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); Panel2->SetSizer(FlexGridSizer13); FlexGridSizer13->Fit(Panel2); FlexGridSizer13->SetSizeHints(Panel2); Panel3 = new wxPanel(Notebook1, ID_PANEL3, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL3")); FlexGridSizer10 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer10->AddGrowableCol(0); FlexGridSizer10->AddGrowableRow(0); ScrolledWindow3 = new wxScrolledWindow(Panel3, ID_SCROLLEDWINDOW3, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL, _T("ID_SCROLLEDWINDOW3")); FlexGridSizer14 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer14->AddGrowableCol(0); FlexGridSizer14->AddGrowableRow(0); bmpRectified = new mrpt::gui::wxMRPTImageControl(ScrolledWindow3,ID_CUSTOM1,wxDefaultPosition.x,wxDefaultPosition.y,wxSize(640,480).GetWidth(), wxSize(640,480).GetHeight() ); FlexGridSizer14->Add(bmpRectified, 1, wxALL|wxALIGN_LEFT|wxALIGN_TOP, 0); ScrolledWindow3->SetSizer(FlexGridSizer14); FlexGridSizer14->Fit(ScrolledWindow3); FlexGridSizer14->SetSizeHints(ScrolledWindow3); FlexGridSizer10->Add(ScrolledWindow3, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); Panel3->SetSizer(FlexGridSizer10); FlexGridSizer10->Fit(Panel3); FlexGridSizer10->SetSizeHints(Panel3); Panel1 = new wxPanel(Notebook1, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1")); FlexGridSizer12 = new wxFlexGridSizer(1, 1, 0, 0); FlexGridSizer12->AddGrowableCol(0); FlexGridSizer12->AddGrowableRow(0); m_3Dview = new CMyGLCanvas(Panel1,ID_XY_GLCANVAS,wxDefaultPosition,wxSize(-1,300),wxTAB_TRAVERSAL,_T("ID_XY_GLCANVAS")); FlexGridSizer12->Add(m_3Dview, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); Panel1->SetSizer(FlexGridSizer12); FlexGridSizer12->Fit(Panel1); FlexGridSizer12->SetSizeHints(Panel1); Notebook1->AddPage(Panel2, _("Original Image"), false); Notebook1->AddPage(Panel3, _("Rectified image and reprojected points"), true); Notebook1->AddPage(Panel1, _("3D view"), false); FlexGridSizer3->Add(Notebook1, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2); FlexGridSizer1->Add(FlexGridSizer3, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0); SetSizer(FlexGridSizer1); FlexGridSizer1->Fit(this); FlexGridSizer1->SetSizeHints(this); Connect(ID_BUTTON8,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnCaptureNowClick); Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnAddImage); Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnListClear); Connect(ID_BUTTON9,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnSaveImagesClick); Connect(ID_LISTBOX1,wxEVT_COMMAND_LISTBOX_SELECTED,(wxObjectEventFunction)&camera_calib_guiDialog::OnlbFilesSelect); Connect(ID_CHOICE1,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&camera_calib_guiDialog::OncbZoomSelect); Connect(ID_BUTTON3,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnRunCalibClick); Connect(ID_BUTTON6,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnSaveClick); Connect(ID_BUTTON7,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnManualRectClick); Connect(ID_BUTTON5,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnAboutClick); Connect(ID_BUTTON4,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&camera_calib_guiDialog::OnbtnCloseClick); //*) camera_params.intrinsicParams(0,0) = 0; // Indicate calib didn't run yet. wxIcon icon; icon.CopyFromBitmap( wxBitmap(wxImage( icono_main_xpm )) ); this->SetIcon( icon ); this->show3Dview(); // Empty 3D scene Center(); this->SetTitle( _U( format("Camera calibration %s - Part of the MRPT project",CAMERA_CALIB_GUI_VERSION).c_str() ) ); Maximize(); }
/* ------------------------------------------------------- checkerBoardCameraCalibration ------------------------------------------------------- */ bool mrpt::vision::checkerBoardCameraCalibration( TCalibrationImageList &images, unsigned int check_size_x, unsigned int check_size_y, double check_squares_length_X_meters, double check_squares_length_Y_meters, mrpt::utils::TCamera &out_camera_params, bool normalize_image, double *out_MSE, bool skipDrawDetectedImgs, bool useScaramuzzaAlternativeDetector ) { #if MRPT_HAS_OPENCV try { ASSERT_(check_size_x>2) ASSERT_(check_size_y>2) ASSERT_(check_squares_length_X_meters>0) ASSERT_(check_squares_length_Y_meters>0) if (images.size()<1) { std::cout << "ERROR: No input images." << std::endl; return false; } const unsigned CORNERS_COUNT = check_size_x * check_size_y; const CvSize check_size = cvSize(check_size_x, check_size_y); // First: Assure all images are loaded: // ------------------------------------------- TCalibrationImageList::iterator it; for (it=images.begin();it!=images.end();it++) { TImageCalibData &dat = it->second; dat.projectedPoints_distorted.clear(); // Clear reprojected points. dat.projectedPoints_undistorted.clear(); // Skip if images are marked as "externalStorage": if (!dat.img_original.isExternallyStored() && !mrpt::system::extractFileExtension(it->first).empty() ) { if (!dat.img_original.loadFromFile(it->first)) THROW_EXCEPTION_CUSTOM_MSG1("Error reading image: %s",it->first.c_str()); dat.img_checkboard = dat.img_original; dat.img_rectified = dat.img_original; } } // For each image, find checkerboard corners: // ----------------------------------------------- //const unsigned int N = images.size(); unsigned int i; vector<CvPoint2D64f> corners_list; // = new CvPoint2D32f[ N * CORNERS_COUNT]; unsigned int valid_detected_imgs = 0; CvSize imgSize = cvSize(0,0); vector<string> pointsIdx2imageFile; int find_chess_flags = CV_CALIB_CB_ADAPTIVE_THRESH; if (normalize_image) find_chess_flags |= CV_CALIB_CB_NORMALIZE_IMAGE; for (i=0,it=images.begin();it!=images.end();it++,i++) { TImageCalibData &dat = it->second; // Make grayscale version: const CImage img_gray( dat.img_original, FAST_REF_OR_CONVERT_TO_GRAY ); if (!i) { imgSize = cvSize(img_gray.getWidth(),img_gray.getHeight() ); out_camera_params.ncols = imgSize.width; out_camera_params.nrows = imgSize.height; } else { if (imgSize.height != (int)img_gray.getHeight() || imgSize.width != (int)img_gray.getWidth()) { std::cout << "ERROR: All the images must have the same size" << std::endl; return false; } } // Try with expanded versions of the image if it fails to detect the checkerboard: unsigned corners_count; bool corners_found=false; corners_count = CORNERS_COUNT; corners_list.resize( (1+valid_detected_imgs)*CORNERS_COUNT ); dat.detected_corners.clear(); // Do detection (this includes the "refine corners" with cvFindCornerSubPix): vector<TPixelCoordf> detectedCoords; corners_found = mrpt::vision::findChessboardCorners( img_gray, detectedCoords, check_size_x,check_size_y, normalize_image, // normalize_image useScaramuzzaAlternativeDetector ); corners_count = detectedCoords.size(); // Copy the data into the overall array of coords: ASSERT_(detectedCoords.size()<=CORNERS_COUNT); for (size_t p=0;p<detectedCoords.size();p++) { corners_list[valid_detected_imgs*CORNERS_COUNT+p].x = detectedCoords[p].x; corners_list[valid_detected_imgs*CORNERS_COUNT+p].y = detectedCoords[p].y; } if (corners_found && corners_count!=CORNERS_COUNT) corners_found = false; cout << format("Img %s: %s\n", mrpt::system::extractFileName(it->first).c_str() , corners_found ? "DETECTED" : "NOT DETECTED" ); if( corners_found ) { // save the corners in the data structure: int x, y; unsigned int k; for( y = 0, k = 0; y < check_size.height; y++ ) for( x = 0; x < check_size.width; x++, k++ ) dat.detected_corners.push_back( mrpt::utils::TPixelCoordf( corners_list[valid_detected_imgs*CORNERS_COUNT + k].x, corners_list[valid_detected_imgs*CORNERS_COUNT + k].y ) ); // Draw the checkerboard in the corresponding image: // ---------------------------------------------------- if ( !dat.img_original.isExternallyStored() ) { const int r = 4; CvPoint prev_pt= cvPoint(0, 0); const int line_max = 8; CvScalar line_colors[8]; line_colors[0] = CV_RGB(255,0,0); line_colors[1] = CV_RGB(255,128,0); line_colors[2] = CV_RGB(255,128,0); line_colors[3] = CV_RGB(200,200,0); line_colors[4] = CV_RGB(0,255,0); line_colors[5] = CV_RGB(0,200,200); line_colors[6] = CV_RGB(0,0,255); line_colors[7] = CV_RGB(255,0,255); // Checkboad as color image: dat.img_original.colorImage( dat.img_checkboard ); void *rgb_img = dat.img_checkboard.getAs<IplImage>(); for( y = 0, k = 0; y < check_size.height; y++ ) { CvScalar color = line_colors[y % line_max]; for( x = 0; x < check_size.width; x++, k++ ) { CvPoint pt; pt.x = cvRound(corners_list[valid_detected_imgs*CORNERS_COUNT + k].x); pt.y = cvRound(corners_list[valid_detected_imgs*CORNERS_COUNT + k].y); if( k != 0 ) cvLine( rgb_img, prev_pt, pt, color ); cvLine( rgb_img, cvPoint( pt.x - r, pt.y - r ), cvPoint( pt.x + r, pt.y + r ), color ); cvLine( rgb_img, cvPoint( pt.x - r, pt.y + r), cvPoint( pt.x + r, pt.y - r), color ); cvCircle( rgb_img, pt, r+1, color ); prev_pt = pt; } } } } if( corners_found ) { pointsIdx2imageFile.push_back( it->first ); valid_detected_imgs++; } } // end find corners std::cout << valid_detected_imgs << " valid images." << std::endl; if (!valid_detected_imgs) { std::cout << "ERROR: No valid images. Perhaps the checkerboard size is incorrect?" << std::endl; return false; } // --------------------------------------------- // Calculate the camera parameters // --------------------------------------------- // Was: FillEtalonObjPoints vector<CvPoint3D64f> obj_points( valid_detected_imgs * CORNERS_COUNT ); { unsigned int y,k; for( y = 0, k = 0; y < check_size_y; y++ ) { for( unsigned int x = 0; x < check_size_x; x++, k++ ) { obj_points[k].x =-check_squares_length_X_meters * x; // The "-" is for convenience, so the camera poses appear with Z>0 obj_points[k].y = check_squares_length_Y_meters * y; obj_points[k].z = 0; } } } // Repeat the pattern N times: for( i= 1; i< valid_detected_imgs; i++ ) memcpy( &obj_points[CORNERS_COUNT*i], &obj_points[0], CORNERS_COUNT*sizeof(obj_points[0])); // Number of detected points in each image (constant): vector<int> numsPoints(valid_detected_imgs, (int)CORNERS_COUNT ); double proj_matrix[9]; double distortion[4]; vector<CvPoint3D64f> transVects( valid_detected_imgs ); vector<double> rotMatrs( valid_detected_imgs * 9 ); // Calibrate camera cvCalibrateCamera_64d( valid_detected_imgs, &numsPoints[0], imgSize, &corners_list[0], &obj_points[0], distortion, proj_matrix, (double*)&transVects[0], &rotMatrs[0], 0 ); // Load matrix: out_camera_params.intrinsicParams = CMatrixDouble33( proj_matrix ); out_camera_params.dist.assign(0); for (int i=0;i<4;i++) out_camera_params.dist[i] = distortion[i]; // Load camera poses: for (i=0;i<valid_detected_imgs;i++) { const double *R = &rotMatrs[9*i]; CMatrixDouble HM(4,4); HM.zeros(); HM(3,3)=1; HM(0,0)=R[0]; HM(1,0)=R[3]; HM(2,0)=R[6]; HM(0,1)=R[1]; HM(1,1)=R[4]; HM(2,1)=R[7]; HM(0,2)=R[2]; HM(1,2)=R[5]; HM(2,2)=R[8]; HM(0,3)=transVects[i].x; HM(1,3)=transVects[i].y; HM(2,3)=transVects[i].z; CPose3D p = CPose3D(0,0,0) - CPose3D(HM); images[ pointsIdx2imageFile[i] ].reconstructed_camera_pose = p; std::cout << "Img: " << mrpt::system::extractFileName(pointsIdx2imageFile[i]) << ": " << p << std::endl; } { CConfigFileMemory cfg; out_camera_params.saveToConfigFile("CAMERA_PARAMS",cfg); std::cout << cfg.getContent() << std::endl; } // ---------------------------------------- // Undistort images: // ---------------------------------------- for (it=images.begin();it!=images.end();it++) { TImageCalibData &dat = it->second; if (!dat.img_original.isExternallyStored()) dat.img_original.rectifyImage( dat.img_rectified, out_camera_params); } // end undistort // ----------------------------------------------- // Reproject points to measure the fit sqr error // ----------------------------------------------- double sqrErr = 0; for (i=0;i<valid_detected_imgs;i++) { TImageCalibData & dat = images[ pointsIdx2imageFile[i] ]; if (dat.detected_corners.size()!=CORNERS_COUNT) continue; // Reproject all the points into pixel coordinates: // ----------------------------------------------------- vector<TPoint3D> lstPatternPoints(CORNERS_COUNT); // Points as seen from the camera: for (unsigned int p=0;p<CORNERS_COUNT;p++) lstPatternPoints[p] = TPoint3D(obj_points[p].x,obj_points[p].y,obj_points[p].z); vector<TPixelCoordf> &projectedPoints = dat.projectedPoints_undistorted; vector<TPixelCoordf> &projectedPoints_distorted = dat.projectedPoints_distorted; vision::pinhole::projectPoints_no_distortion( lstPatternPoints, // Input points dat.reconstructed_camera_pose, out_camera_params.intrinsicParams, // calib matrix projectedPoints // Output points in pixels ); vision::pinhole::projectPoints_with_distortion( lstPatternPoints, // Input points dat.reconstructed_camera_pose, out_camera_params.intrinsicParams, // calib matrix out_camera_params.getDistortionParamsAsVector(), projectedPoints_distorted// Output points in pixels ); ASSERT_(projectedPoints.size()==CORNERS_COUNT); ASSERT_(projectedPoints_distorted.size()==CORNERS_COUNT); for (unsigned int p=0;p<CORNERS_COUNT;p++) { const double px = projectedPoints[p].x; const double py = projectedPoints[p].y; const double px_d = projectedPoints_distorted[p].x; const double py_d = projectedPoints_distorted[p].y; // Only draw if the img is NOT external: if (!dat.img_original.isExternallyStored()) { if( px >= 0 && px < imgSize.width && py >= 0 && py < imgSize.height ) cvCircle( dat.img_rectified.getAs<IplImage>(), cvPoint(px,py), 4, CV_RGB(0,0,255) ); } // Accumulate error: sqrErr+=square(px_d-dat.detected_corners[p].x)+square(py_d-dat.detected_corners[p].y); // Error relative to the original (distorted) image. } } if (valid_detected_imgs) { sqrErr /= CORNERS_COUNT*valid_detected_imgs; std::cout << "Average err. of reprojection: " << sqrt(sqrErr) << " pixels" << std::endl; } if(out_MSE) *out_MSE = sqrt(sqrErr); return true; } catch(std::exception &e) { std::cout << e.what() << std::endl; return false; } #else THROW_EXCEPTION("Function not available: MRPT was compiled without OpenCV") #endif }
/* ------------------------------------------------------- checkerBoardCameraCalibration ------------------------------------------------------- */ bool mrpt::vision::checkerBoardCameraCalibration( TCalibrationImageList &images, unsigned int check_size_x, unsigned int check_size_y, double check_squares_length_X_meters, double check_squares_length_Y_meters, mrpt::utils::TCamera &out_camera_params, bool normalize_image, double *out_MSE, bool skipDrawDetectedImgs, bool useScaramuzzaAlternativeDetector ) { MRPT_UNUSED_PARAM(skipDrawDetectedImgs); #if MRPT_HAS_OPENCV try { ASSERT_(check_size_x>2) ASSERT_(check_size_y>2) ASSERT_(check_squares_length_X_meters>0) ASSERT_(check_squares_length_Y_meters>0) if (images.size()<1) { std::cout << "ERROR: No input images." << std::endl; return false; } const unsigned CORNERS_COUNT = check_size_x * check_size_y; const CvSize check_size = cvSize(check_size_x, check_size_y); // Fill the pattern of expected pattern points only once out of the loop: vector<cv::Point3f> pattern_obj_points(CORNERS_COUNT); { unsigned int y,k; for( y = 0, k = 0; y < check_size_y; y++ ) { for( unsigned int x = 0; x < check_size_x; x++, k++ ) { pattern_obj_points[k].x =-check_squares_length_X_meters * x; // The "-" is for convenience, so the camera poses appear with Z>0 pattern_obj_points[k].y = check_squares_length_Y_meters * y; pattern_obj_points[k].z = 0; } } } // First: Assure all images are loaded: // ------------------------------------------- TCalibrationImageList::iterator it; for (it=images.begin();it!=images.end();++it) { TImageCalibData &dat = it->second; dat.projectedPoints_distorted.clear(); // Clear reprojected points. dat.projectedPoints_undistorted.clear(); // Skip if images are marked as "externalStorage": if (!dat.img_original.isExternallyStored() && !mrpt::system::extractFileExtension(it->first).empty() ) { if (!dat.img_original.loadFromFile(it->first)) THROW_EXCEPTION_CUSTOM_MSG1("Error reading image: %s",it->first.c_str()); dat.img_checkboard = dat.img_original; dat.img_rectified = dat.img_original; } } // For each image, find checkerboard corners: // ----------------------------------------------- vector<vector<cv::Point3f> > objectPoints; // final container for detected stuff vector<vector<cv::Point2f> > imagePoints; // final container for detected stuff unsigned int valid_detected_imgs = 0; vector<string> pointsIdx2imageFile; cv::Size imgSize(0,0); unsigned int i; for (i=0,it=images.begin();it!=images.end();it++,i++) { TImageCalibData &dat = it->second; // Make grayscale version: const CImage img_gray( dat.img_original, FAST_REF_OR_CONVERT_TO_GRAY ); if (!i) { imgSize = cv::Size(img_gray.getWidth(),img_gray.getHeight() ); out_camera_params.ncols = imgSize.width; out_camera_params.nrows = imgSize.height; } else { if (imgSize.height != (int)img_gray.getHeight() || imgSize.width != (int)img_gray.getWidth()) { std::cout << "ERROR: All the images must have the same size" << std::endl; return false; } } // Try with expanded versions of the image if it fails to detect the checkerboard: unsigned corners_count; bool corners_found=false; corners_count = CORNERS_COUNT; vector<cv::Point2f> this_img_pts(CORNERS_COUNT); // Temporary buffer for points, to be added if the points pass the checks. dat.detected_corners.clear(); // Do detection (this includes the "refine corners" with cvFindCornerSubPix): vector<TPixelCoordf> detectedCoords; corners_found = mrpt::vision::findChessboardCorners( img_gray, detectedCoords, check_size_x,check_size_y, normalize_image, // normalize_image useScaramuzzaAlternativeDetector ); corners_count = detectedCoords.size(); // Copy the data into the overall array of coords: ASSERT_(detectedCoords.size()<=CORNERS_COUNT); for (size_t p=0;p<detectedCoords.size();p++) { this_img_pts[p].x = detectedCoords[p].x; this_img_pts[p].y = detectedCoords[p].y; } if (corners_found && corners_count!=CORNERS_COUNT) corners_found = false; cout << format("Img %s: %s\n", mrpt::system::extractFileName(it->first).c_str() , corners_found ? "DETECTED" : "NOT DETECTED" ); if( corners_found ) { // save the corners in the data structure: int x, y; unsigned int k; for( y = 0, k = 0; y < check_size.height; y++ ) for( x = 0; x < check_size.width; x++, k++ ) dat.detected_corners.push_back( mrpt::utils::TPixelCoordf( this_img_pts[k].x, this_img_pts[k].y ) ); // Draw the checkerboard in the corresponding image: // ---------------------------------------------------- if ( !dat.img_original.isExternallyStored() ) { const int r = 4; CvPoint prev_pt= cvPoint(0, 0); const int line_max = 8; CvScalar line_colors[8]; line_colors[0] = CV_RGB(255,0,0); line_colors[1] = CV_RGB(255,128,0); line_colors[2] = CV_RGB(255,128,0); line_colors[3] = CV_RGB(200,200,0); line_colors[4] = CV_RGB(0,255,0); line_colors[5] = CV_RGB(0,200,200); line_colors[6] = CV_RGB(0,0,255); line_colors[7] = CV_RGB(255,0,255); // Checkboad as color image: dat.img_original.colorImage( dat.img_checkboard ); void *rgb_img = dat.img_checkboard.getAs<IplImage>(); for( y = 0, k = 0; y < check_size.height; y++ ) { CvScalar color = line_colors[y % line_max]; for( x = 0; x < check_size.width; x++, k++ ) { CvPoint pt; pt.x = cvRound(this_img_pts[k].x); pt.y = cvRound(this_img_pts[k].y); if( k != 0 ) cvLine( rgb_img, prev_pt, pt, color ); cvLine( rgb_img, cvPoint( pt.x - r, pt.y - r ), cvPoint( pt.x + r, pt.y + r ), color ); cvLine( rgb_img, cvPoint( pt.x - r, pt.y + r), cvPoint( pt.x + r, pt.y - r), color ); cvCircle( rgb_img, pt, r+1, color ); prev_pt = pt; } } } // Accept this image as good: pointsIdx2imageFile.push_back( it->first ); imagePoints.push_back( this_img_pts ); objectPoints.push_back( pattern_obj_points ); valid_detected_imgs++; } } // end find corners std::cout << valid_detected_imgs << " valid images." << std::endl; if (!valid_detected_imgs) { std::cout << "ERROR: No valid images. Perhaps the checkerboard size is incorrect?" << std::endl; return false; } // --------------------------------------------- // Calculate the camera parameters // --------------------------------------------- // Calibrate camera cv::Mat cameraMatrix, distCoeffs(1,5,CV_64F,cv::Scalar::all(0)); vector<cv::Mat> rvecs, tvecs; const double cv_calib_err = cv::calibrateCamera( objectPoints,imagePoints,imgSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0 /*flags*/ ); // Load matrix: out_camera_params.intrinsicParams = CMatrixDouble33( cameraMatrix.ptr<double>() ); out_camera_params.dist.assign(0); for (int i=0;i<5;i++) out_camera_params.dist[i] = distCoeffs.ptr<double>()[i]; // Load camera poses: for (i=0;i<valid_detected_imgs;i++) { CMatrixDouble44 HM; HM.zeros(); HM(3,3)=1; { // Convert rotation vectors -> rot matrices: cv::Mat cv_rot; cv::Rodrigues(rvecs[i],cv_rot); Eigen::Matrix3d rot; cv::my_cv2eigen(cv_rot, rot ); HM.block<3,3>(0,0) = rot; } { Eigen::Matrix<double,3,1> trans; cv::my_cv2eigen(tvecs[i], trans ); HM.block<3,1>(0,3) = trans; } CPose3D p = CPose3D(0,0,0) - CPose3D(HM); images[ pointsIdx2imageFile[i] ].reconstructed_camera_pose = p; std::cout << "Img: " << mrpt::system::extractFileName(pointsIdx2imageFile[i]) << ": " << p << std::endl; } { CConfigFileMemory cfg; out_camera_params.saveToConfigFile("CAMERA_PARAMS",cfg); std::cout << cfg.getContent() << std::endl; } // ---------------------------------------- // Undistort images: // ---------------------------------------- for (it=images.begin();it!=images.end();++it) { TImageCalibData &dat = it->second; if (!dat.img_original.isExternallyStored()) dat.img_original.rectifyImage( dat.img_rectified, out_camera_params); } // end undistort // ----------------------------------------------- // Reproject points to measure the fit sqr error // ----------------------------------------------- double sqrErr = 0; for (i=0;i<valid_detected_imgs;i++) { TImageCalibData & dat = images[ pointsIdx2imageFile[i] ]; if (dat.detected_corners.size()!=CORNERS_COUNT) continue; // Reproject all the points into pixel coordinates: // ----------------------------------------------------- vector<TPoint3D> lstPatternPoints(CORNERS_COUNT); // Points as seen from the camera: for (unsigned int p=0;p<CORNERS_COUNT;p++) lstPatternPoints[p] = TPoint3D(pattern_obj_points[p].x,pattern_obj_points[p].y,pattern_obj_points[p].z); vector<TPixelCoordf> &projectedPoints = dat.projectedPoints_undistorted; vector<TPixelCoordf> &projectedPoints_distorted = dat.projectedPoints_distorted; vision::pinhole::projectPoints_no_distortion( lstPatternPoints, // Input points dat.reconstructed_camera_pose, out_camera_params.intrinsicParams, // calib matrix projectedPoints // Output points in pixels ); vision::pinhole::projectPoints_with_distortion( lstPatternPoints, // Input points dat.reconstructed_camera_pose, out_camera_params.intrinsicParams, // calib matrix out_camera_params.getDistortionParamsAsVector(), projectedPoints_distorted// Output points in pixels ); ASSERT_(projectedPoints.size()==CORNERS_COUNT); ASSERT_(projectedPoints_distorted.size()==CORNERS_COUNT); for (unsigned int p=0;p<CORNERS_COUNT;p++) { const double px = projectedPoints[p].x; const double py = projectedPoints[p].y; const double px_d = projectedPoints_distorted[p].x; const double py_d = projectedPoints_distorted[p].y; // Only draw if the img is NOT external: if (!dat.img_original.isExternallyStored()) { if( px >= 0 && px < imgSize.width && py >= 0 && py < imgSize.height ) cvCircle( dat.img_rectified.getAs<IplImage>(), cvPoint(px,py), 4, CV_RGB(0,0,255) ); } // Accumulate error: sqrErr+=square(px_d-dat.detected_corners[p].x)+square(py_d-dat.detected_corners[p].y); // Error relative to the original (distorted) image. } } if (valid_detected_imgs) { sqrErr /= CORNERS_COUNT*valid_detected_imgs; std::cout << "Average err. of reprojection: " << sqrt(sqrErr) << " pixels (OpenCV error=" << cv_calib_err << ")\n"; } if(out_MSE) *out_MSE = sqrt(sqrErr); return true; } catch(std::exception &e) { std::cout << e.what() << std::endl; return false; } #else THROW_EXCEPTION("Function not available: MRPT was compiled without OpenCV") #endif }