/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

/*
  App      : kinect-stereo-calib
  Web pages:
  https://www.mrpt.org/list-of-mrpt-apps/application-kinect-stereo-calib
       https://www.mrpt.org/Kinect_and_MRPT
  Usage    : Run and follow on-screen instructions
*/

#include "kinect_calibrate_guiMain.h"

#include <mrpt/gui/about_box.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>

//(*InternalHeaders(kinect_calibrate_guiDialog)
#include <wx/artprov.h>
#include <wx/bitmap.h>
#include <wx/font.h>
#include <wx/image.h>
#include <wx/intl.h>
#include <wx/settings.h>
#include <wx/string.h>
//*)

#include <mrpt/config/CConfigFile.h>
#include <mrpt/containers/yaml.h>
#include <mrpt/gui/WxUtils.h>
#include <mrpt/hwdrivers/CKinect.h>
#include <mrpt/io/CMemoryStream.h>
#include <mrpt/io/vector_loadsave.h>
#include <mrpt/poses/CPose3DQuat.h>
#include <mrpt/system/CTicTac.h>
#include <mrpt/system/filesystem.h>
#include <mrpt/vision/chessboard_find_corners.h>
#include <mrpt/vision/chessboard_stereo_camera_calib.h>

#include <fstream>

using namespace mrpt;
using namespace mrpt::obs;
using namespace mrpt::io;
using namespace mrpt::img;
using namespace mrpt::system;
using namespace mrpt::hwdrivers;
using namespace std;

#include "../wx-common/mrpt_logo.xpm"
#include "imgs/kinect-covered-projector.h"
#include "imgs/kinect.xpm"

// A custom Art provider for customizing the icons:
class MyArtProvider : public wxArtProvider
{
 protected:
  wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client, const wxSize& size) override;
};
wxBitmap MyArtProvider::CreateBitmap(
    const wxArtID& id, const wxArtClient& client, const wxSize& size)
{
  if (id == wxART_MAKE_ART_ID(MAIN_ICON)) return wxBitmap(kinect_xpm);
  if (id == wxART_MAKE_ART_ID(IMG_MRPT_LOGO)) return wxBitmap(mrpt_logo_xpm);

  // Any wxWidgets icons not implemented here
  // will be provided by the default art provider.
  return wxNullBitmap;
}

const wxString imageWildcards =
    _("Image files "
      "(*.png;*.jpg;...)|*.png;*.PNG;*.JPG;*.jpg;*.BMP;*.bmp;*.TIF;*.tif|All "
      "files (*.*)|*.*");

//(*IdInit(kinect_calibrate_guiDialog)
const long kinect_calibrate_guiDialog::ID_STATICTEXT27 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON15 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON16 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON17 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL8 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT28 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT29 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT30 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL10 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT31 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL9 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT11 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT9 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON8 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_RADIOBOX1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT22 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL7 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON6 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT12 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON7 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL7 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT23 = wxNewId();
const long kinect_calibrate_guiDialog::ID_LISTBOX1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON9 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON10 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON11 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON12 = wxNewId();
const long kinect_calibrate_guiDialog::ID_RADIOBOX2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT19 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT20 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT25 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT13 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT14 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT15 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL6 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT16 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL7 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL11 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX6 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX7 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX8 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL12 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT26 = wxNewId();
const long kinect_calibrate_guiDialog::ID_SPINCTRL8 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CHECKBOX2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL13 = wxNewId();
const long kinect_calibrate_guiDialog::ID_NOTEBOOK3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON14 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT24 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON13 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL8 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_CUSTOM6 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL10 = wxNewId();
const long kinect_calibrate_guiDialog::ID_NOTEBOOK2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL6 = wxNewId();
const long kinect_calibrate_guiDialog::ID_XY_GLCANVAS = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT3 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON18 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON20 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON19 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON21 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BITMAPBUTTON1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_GRID1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL5 = wxNewId();
const long kinect_calibrate_guiDialog::ID_NOTEBOOK1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_STATICTEXT10 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TEXTCTRL4 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_BUTTON2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_PANEL2 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TIMER1 = wxNewId();
const long kinect_calibrate_guiDialog::ID_TIMER2 = wxNewId();
//*)

BEGIN_EVENT_TABLE(kinect_calibrate_guiDialog, wxDialog)
//(*EventTable(kinect_calibrate_guiDialog)
//*)
END_EVENT_TABLE()

kinect_calibrate_guiDialog::kinect_calibrate_guiDialog(wxWindow* parent, wxWindowID id) :
    m_config(_("kinect-stereo-calib"))
{
  m_grabstate = gsIdle;

  // Load my custom icons:
  wxArtProvider::Push(new MyArtProvider);

  //(*Initialize(kinect_calibrate_guiDialog)
  wxFlexGridSizer* FlexGridSizer30;
  wxStaticBoxSizer* StaticBoxSizer2;
  wxFlexGridSizer* FlexGridSizer21;
  wxFlexGridSizer* FlexGridSizer28;
  wxFlexGridSizer* FlexGridSizer8;
  wxFlexGridSizer* FlexGridSizer1;
  wxFlexGridSizer* FlexGridSizer2;
  wxFlexGridSizer* FlexGridSizer25;
  wxFlexGridSizer* FlexGridSizer15;
  wxStaticBoxSizer* StaticBoxSizer7;
  wxFlexGridSizer* FlexGridSizer17;
  wxFlexGridSizer* FlexGridSizer29;
  wxFlexGridSizer* FlexGridSizer11;
  wxFlexGridSizer* FlexGridSizer19;
  wxFlexGridSizer* FlexGridSizer41;
  wxFlexGridSizer* FlexGridSizer40;
  wxFlexGridSizer* FlexGridSizer7;
  wxFlexGridSizer* FlexGridSizer4;
  wxFlexGridSizer* FlexGridSizer9;
  wxFlexGridSizer* FlexGridSizer26;
  wxFlexGridSizer* FlexGridSizer14;
  wxStaticBoxSizer* StaticBoxSizer3;
  wxFlexGridSizer* FlexGridSizer33;
  wxFlexGridSizer* FlexGridSizer6;
  wxFlexGridSizer* FlexGridSizer38;
  wxFlexGridSizer* FlexGridSizer27;
  wxFlexGridSizer* FlexGridSizer42;
  wxFlexGridSizer* FlexGridSizer37;
  wxFlexGridSizer* FlexGridSizer3;
  wxFlexGridSizer* FlexGridSizer22;
  wxStaticBoxSizer* StaticBoxSizer8;
  wxFlexGridSizer* FlexGridSizer31;
  wxFlexGridSizer* FlexGridSizer43;
  wxFlexGridSizer* FlexGridSizer39;
  wxStaticBoxSizer* StaticBoxSizer4;
  wxFlexGridSizer* FlexGridSizer16;
  wxFlexGridSizer* FlexGridSizer34;
  wxFlexGridSizer* FlexGridSizer23;
  wxFlexGridSizer* FlexGridSizer10;
  wxBoxSizer* BoxSizer1;
  wxFlexGridSizer* FlexGridSizer13;
  wxFlexGridSizer* FlexGridSizer18;
  wxFlexGridSizer* FlexGridSizer36;
  wxFlexGridSizer* FlexGridSizer35;
  wxFlexGridSizer* FlexGridSizer5;
  wxFlexGridSizer* FlexGridSizer24;
  wxFlexGridSizer* FlexGridSizer32;
  wxStaticBoxSizer* StaticBoxSizer1;
  wxFlexGridSizer* FlexGridSizer20;

  Create(
      parent, wxID_ANY, _("Kinect & stereo camera calibration wizard - Part of MRPT"),
      wxDefaultPosition, wxDefaultSize,
      wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX, _T("wxID_ANY"));
  Move(wxPoint(-1, -1));
  FlexGridSizer1 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer1->AddGrowableCol(0);
  FlexGridSizer1->AddGrowableRow(0);
  Notebook1 = new wxNotebook(
      this, ID_NOTEBOOK1, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM, _T("ID_NOTEBOOK1"));
  Panel8 = new wxPanel(
      Notebook1, ID_PANEL8, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL8"));
  FlexGridSizer37 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer37->AddGrowableCol(0);
  FlexGridSizer37->AddGrowableRow(0);
  FlexGridSizer38 = new wxFlexGridSizer(3, 1, 0, 0);
  FlexGridSizer38->AddGrowableCol(0);
  StaticText26 = new wxStaticText(
      Panel8, ID_STATICTEXT27, _("What do you want to do\?"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT27"));
  wxFont StaticText26Font(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  StaticText26->SetFont(StaticText26Font);
  FlexGridSizer38->Add(
      StaticText26, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer38->Add(-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer39 = new wxFlexGridSizer(3, 1, 0, 0);
  FlexGridSizer39->AddGrowableCol(0);
  btnOpCalibKinect = new wxButton(
      Panel8, ID_BUTTON15, _("Calibrate a Kinect sensor (live)"), wxDefaultPosition, wxSize(-1, 50),
      0, wxDefaultValidator, _T("ID_BUTTON15"));
  FlexGridSizer39->Add(btnOpCalibKinect, 1, wxEXPAND, 5);
  btnOpCalibStereoGeneric = new wxButton(
      Panel8, ID_BUTTON16, _("Calibrate a stereo/Kinect camera (from images in disk)"),
      wxDefaultPosition, wxSize(-1, 50), 0, wxDefaultValidator, _T("ID_BUTTON16"));
  FlexGridSizer39->Add(btnOpCalibStereoGeneric, 1, wxEXPAND, 5);
  btnOpTestKinect = new wxButton(
      Panel8, ID_BUTTON17, _("Visually inspect the correctness of a Kinect calib. (live)"),
      wxDefaultPosition, wxSize(-1, 50), 0, wxDefaultValidator, _T("ID_BUTTON17"));
  FlexGridSizer39->Add(btnOpTestKinect, 1, wxEXPAND, 5);
  FlexGridSizer38->Add(FlexGridSizer39, 1, wxEXPAND, 0);
  FlexGridSizer37->Add(
      FlexGridSizer38, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 0);
  Panel8->SetSizer(FlexGridSizer37);
  FlexGridSizer37->Fit(Panel8);
  FlexGridSizer37->SetSizeHints(Panel8);
  Panel9 = new wxPanel(
      Notebook1, ID_PANEL9, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL9"));
  FlexGridSizer40 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer40->AddGrowableCol(0);
  FlexGridSizer40->AddGrowableRow(0);
  FlexGridSizer41 = new wxFlexGridSizer(7, 1, 0, 0);
  StaticText27 = new wxStaticText(
      Panel9, ID_STATICTEXT28, _("Prepare to calibrate your Kinect sensor"), wxDefaultPosition,
      wxDefaultSize, 0, _T("ID_STATICTEXT28"));
  wxFont StaticText27Font(15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  StaticText27->SetFont(StaticText27Font);
  FlexGridSizer41->Add(
      StaticText27, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticText28 = new wxStaticText(
      Panel9, ID_STATICTEXT29,
      _("1) Make sure the IR projector is covered, for example, by tapying a "
        "piece of some opaque material on it."),
      wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT29"));
  FlexGridSizer41->Add(StaticText28, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  m_imgStaticKinect = new mrpt::gui::wxMRPTImageControl(
      Panel9, ID_CUSTOM2, wxDefaultPosition.x, wxDefaultPosition.y, wxSize(300, 116).GetWidth(),
      wxSize(300, 116).GetHeight());
  FlexGridSizer41->Add(
      m_imgStaticKinect, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticText29 = new wxStaticText(
      Panel9, ID_STATICTEXT30,
      _("2) Print a checkerboard and stick it firmly to some rigid board or "
        "paperboard.\n   If you don\'t have any pattern at hand, get one "
        "from:"),
      wxPoint(48, 256), wxSize(661, 37), 0, _T("ID_STATICTEXT30"));
  FlexGridSizer41->Add(StaticText29, 1, wxEXPAND, 5);
  TextCtrl3 = new wxTextCtrl(
      Panel9, ID_TEXTCTRL10,
      _("http://www.mrpt.org/downloads/"
        "camera-calibration-checker-board_9x7.pdf"),
      wxDefaultPosition, wxDefaultSize, wxTE_READONLY, wxDefaultValidator, _T("ID_TEXTCTRL10"));
  FlexGridSizer41->Add(TextCtrl3, 1, wxEXPAND, 5);
  StaticText30 = new wxStaticText(
      Panel9, ID_STATICTEXT31,
      _("3) Connect the Kinect to the computer and wait a few seconds until "
        "the driver establishes connection."),
      wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT31"));
  FlexGridSizer41->Add(StaticText30, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  btnNext1 = new wxButton(
      Panel9, ID_BUTTON3, _("START"), wxPoint(376, 368), wxSize(117, 45), 0, wxDefaultValidator,
      _T("ID_BUTTON3"));
  btnNext1->SetDefault();
  FlexGridSizer41->Add(btnNext1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer40->Add(
      FlexGridSizer41, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  Panel9->SetSizer(FlexGridSizer40);
  FlexGridSizer40->Fit(Panel9);
  FlexGridSizer40->SetSizeHints(Panel9);
  Panel3 = new wxPanel(
      Notebook1, ID_PANEL3, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL3"));
  FlexGridSizer5 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer5->AddGrowableCol(1);
  FlexGridSizer5->AddGrowableRow(0);
  m_realtimeview_test = new mrpt::gui::wxMRPTImageControl(
      Panel3, ID_CUSTOM3, wxDefaultPosition.x, wxDefaultPosition.y, wxSize(640, 480).GetWidth(),
      wxSize(640, 480).GetHeight());
  FlexGridSizer5->Add(
      m_realtimeview_test, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer7 = new wxFlexGridSizer(7, 1, 0, 0);
  FlexGridSizer7->AddGrowableCol(0);
  FlexGridSizer7->AddGrowableRow(6);
  StaticText11 = new wxStaticText(
      Panel3, ID_STATICTEXT11, _("Press \"Connect\" to start grabbing:"), wxDefaultPosition,
      wxDefaultSize, 0, _T("ID_STATICTEXT11"));
  FlexGridSizer7->Add(StaticText11, 1, wxEXPAND, 5);
  btnConnect = new wxButton(
      Panel3, ID_BUTTON5, _("Connect..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON5"));
  FlexGridSizer7->Add(btnConnect, 1, wxEXPAND, 5);
  StaticText9 = new wxStaticText(
      Panel3, ID_STATICTEXT9,
      _("You should be seeing the \nRGB channel on the left panel.\n\nIf "
        "everything works OK, \npress \"Continue\"."),
      wxPoint(40, 32), wxDefaultSize, 0, _T("ID_STATICTEXT9"));
  FlexGridSizer7->Add(StaticText9, 1, wxEXPAND, 5);
  btnNext2 = new wxButton(
      Panel3, ID_BUTTON4, _("CONTINUE..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON4"));
  btnNext2->SetDefault();
  btnNext2->Disable();
  FlexGridSizer7->Add(btnNext2, 1, wxEXPAND, 5);
  StaticText5 = new wxStaticText(
      Panel3, ID_STATICTEXT5,
      _("(If no error was found but still \nhave no images, try "
        "disconnecting \nand connecting again:)"),
      wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT5"));
  FlexGridSizer7->Add(
      StaticText5, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  btnDisconnect = new wxButton(
      Panel3, ID_BUTTON8, _("Disconnect"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON8"));
  btnDisconnect->Disable();
  FlexGridSizer7->Add(
      btnDisconnect, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer5->Add(FlexGridSizer7, 1, wxEXPAND, 0);
  Panel3->SetSizer(FlexGridSizer5);
  FlexGridSizer5->Fit(Panel3);
  FlexGridSizer5->SetSizeHints(Panel3);
  Panel4 = new wxPanel(
      Notebook1, ID_PANEL4, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL4"));
  FlexGridSizer3 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer3->AddGrowableCol(1);
  FlexGridSizer3->AddGrowableRow(0);
  m_realtimeview_cap = new mrpt::gui::wxMRPTImageControl(
      Panel4, ID_CUSTOM1, wxDefaultPosition.x, wxDefaultPosition.y, wxSize(640, 480).GetWidth(),
      wxSize(640, 480).GetHeight());
  FlexGridSizer3->Add(
      m_realtimeview_cap, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  Panel7 = new wxPanel(
      Panel4, ID_PANEL7, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL7"));
  FlexGridSizer4 = new wxFlexGridSizer(3, 1, 0, 0);
  FlexGridSizer4->AddGrowableCol(0);
  FlexGridSizer4->AddGrowableRow(0);
  StaticBoxSizer1 = new wxStaticBoxSizer(wxHORIZONTAL, Panel7, _(" INSTRUCTIONS "));
  BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
  TextCtrl2 = new wxTextCtrl(
      Panel7, ID_TEXTCTRL5,
      _("- Place the Kinect sensor such that the checkerboard is completely "
        "visible in its two channels (visible & IR). Click in the channel "
        "switch button to make sure of this.\n- Make sure the Kinect and the "
        "board are STATIC: don\'t hold them by hand, etc.\n- Only then, "
        "CLICK on \"CAPTURE\" to save one pair of images. DON\'T MOVE the "
        "Kinect while the pair of images are being captured. \n- Repeat to "
        "take as many image pairs as you want, trying to make sure that the "
        "checkerboard has been seen in as many different parts of the image "
        "field as possible."),
      wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_WORDWRAP,
      wxDefaultValidator, _T("ID_TEXTCTRL5"));
  TextCtrl2->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
  BoxSizer1->Add(TextCtrl2, 1, wxEXPAND, 5);
  StaticBoxSizer1->Add(BoxSizer1, 1, wxEXPAND, 0);
  FlexGridSizer4->Add(StaticBoxSizer1, 1, wxEXPAND, 5);
  StaticBoxSizer3 = new wxStaticBoxSizer(wxHORIZONTAL, Panel7, _("Checkerboard parameters "));
  FlexGridSizer6 = new wxFlexGridSizer(1, 1, 0, 0);
  StaticBoxSizer4 = new wxStaticBoxSizer(wxHORIZONTAL, Panel7, _("Number of inner corners: "));
  FlexGridSizer17 = new wxFlexGridSizer(1, 4, 0, 0);
  StaticText1 = new wxStaticText(
      Panel7, 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(
      Panel7, ID_SPINCTRL1, _T("7"), wxDefaultPosition, wxSize(50, -1), 0, 1, 200, 7,
      _T("ID_SPINCTRL1"));
  edSizeX->SetValue(_T("7"));
  FlexGridSizer17->Add(edSizeX, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticText2 = new wxStaticText(
      Panel7, 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(
      Panel7, ID_SPINCTRL2, _T("9"), wxDefaultPosition, wxSize(50, -1), 0, 1, 200, 9,
      _T("ID_SPINCTRL2"));
  edSizeY->SetValue(_T("9"));
  FlexGridSizer17->Add(edSizeY, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticBoxSizer4->Add(FlexGridSizer17, 1, wxEXPAND, 0);
  FlexGridSizer6->Add(StaticBoxSizer4, 1, wxEXPAND, 5);
  StaticBoxSizer3->Add(FlexGridSizer6, 1, wxEXPAND, 3);
  FlexGridSizer4->Add(
      StaticBoxSizer3, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticBoxSizer2 = new wxStaticBoxSizer(wxHORIZONTAL, Panel7, _(" Controls "));
  FlexGridSizer8 = new wxFlexGridSizer(6, 1, 0, 0);
  FlexGridSizer8->AddGrowableCol(0);
  wxString __wxRadioBoxChoices_1[2] = {_("RGB (Visible)"), _("IR")};
  rbChannelSwitch = new wxRadioBox(
      Panel7, ID_RADIOBOX1, _(" Channel switch"), wxDefaultPosition, wxDefaultSize, 2,
      __wxRadioBoxChoices_1, 1, 0, wxDefaultValidator, _T("ID_RADIOBOX1"));
  rbChannelSwitch->SetSelection(0);
  FlexGridSizer8->Add(rbChannelSwitch, 1, wxEXPAND, 5);
  cbNormalize = new wxCheckBox(
      Panel7, ID_CHECKBOX1, _("Normalize IR image"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX1"));
  cbNormalize->SetValue(true);
  FlexGridSizer8->Add(
      cbNormalize, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer25 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer25->AddGrowableCol(0);
  FlexGridSizer25->AddGrowableCol(1);
  StaticText21 = new wxStaticText(
      Panel7, ID_STATICTEXT22, _("Tilt (degrees):"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT22"));
  FlexGridSizer25->Add(
      StaticText21, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edTilt = new wxSpinCtrl(
      Panel7, ID_SPINCTRL7, _T("0"), wxDefaultPosition, wxDefaultSize, 0, -30, 30, 0,
      _T("ID_SPINCTRL7"));
  edTilt->SetValue(_T("0"));
  FlexGridSizer25->Add(edTilt, 1, wxEXPAND, 5);
  FlexGridSizer8->Add(FlexGridSizer25, 1, wxEXPAND, 0);
  btnCapture = new wxButton(
      Panel7, ID_BUTTON6, _("CAPTURE NOW"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON6"));
  wxFont btnCaptureFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  btnCapture->SetFont(btnCaptureFont);
  FlexGridSizer8->Add(btnCapture, 1, wxEXPAND, 5);
  lbNumCaptured = new wxStaticText(
      Panel7, ID_STATICTEXT12, _("Captured image pairs: 0"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT12"));
  FlexGridSizer8->Add(lbNumCaptured, 1, wxEXPAND, 5);
  btnNextCalib = new wxButton(
      Panel7, ID_BUTTON7, _("OK, go to calibrate >>"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON7"));
  btnNextCalib->Disable();
  wxFont btnNextCalibFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  btnNextCalib->SetFont(btnNextCalibFont);
  FlexGridSizer8->Add(btnNextCalib, 1, wxEXPAND, 5);
  StaticBoxSizer2->Add(FlexGridSizer8, 1, wxEXPAND, 3);
  FlexGridSizer4->Add(StaticBoxSizer2, 1, wxEXPAND, 5);
  Panel7->SetSizer(FlexGridSizer4);
  FlexGridSizer4->Fit(Panel7);
  FlexGridSizer4->SetSizeHints(Panel7);
  FlexGridSizer3->Add(Panel7, 1, wxEXPAND, 5);
  Panel4->SetSizer(FlexGridSizer3);
  FlexGridSizer3->Fit(Panel4);
  FlexGridSizer3->SetSizeHints(Panel4);
  Panel6 = new wxPanel(
      Notebook1, ID_PANEL6, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL6"));
  FlexGridSizer9 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer9->AddGrowableCol(0);
  FlexGridSizer9->AddGrowableRow(0);
  FlexGridSizer9->AddGrowableRow(1);
  FlexGridSizer10 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer10->AddGrowableCol(0);
  FlexGridSizer10->AddGrowableCol(1);
  FlexGridSizer10->AddGrowableRow(0);
  FlexGridSizer22 = new wxFlexGridSizer(4, 1, 0, 0);
  FlexGridSizer22->AddGrowableCol(0);
  FlexGridSizer22->AddGrowableRow(1);
  StaticText22 = new wxStaticText(
      Panel6, ID_STATICTEXT23, _("List of stereo images:"), wxDefaultPosition, wxDefaultSize,
      wxALIGN_CENTRE, _T("ID_STATICTEXT23"));
  wxFont StaticText22Font(-1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  StaticText22->SetFont(StaticText22Font);
  FlexGridSizer22->Add(
      StaticText22, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 2);
  lbImagePairs = new wxListBox(
      Panel6, ID_LISTBOX1, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SINGLE,
      wxDefaultValidator, _T("ID_LISTBOX1"));
  FlexGridSizer22->Add(lbImagePairs, 1, wxEXPAND, 5);
  FlexGridSizer26 = new wxFlexGridSizer(3, 2, 0, 0);
  FlexGridSizer26->AddGrowableCol(0);
  FlexGridSizer26->AddGrowableCol(1);
  btnListRemoveSelected = new wxButton(
      Panel6, ID_BUTTON9, _("Remove selected"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON9"));
  wxFont btnListRemoveSelectedFont(
      8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnListRemoveSelected->SetFont(btnListRemoveSelectedFont);
  FlexGridSizer26->Add(btnListRemoveSelected, 1, wxEXPAND, 5);
  btnListLoad = new wxButton(
      Panel6, ID_BUTTON10, _("Load image pair..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON10"));
  wxFont btnListLoadFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnListLoad->SetFont(btnListLoadFont);
  FlexGridSizer26->Add(btnListLoad, 1, wxEXPAND, 5);
  btnLoadImageList = new wxButton(
      Panel6, ID_BUTTON11, _("Load list of images..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON11"));
  wxFont btnLoadImageListFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnLoadImageList->SetFont(btnLoadImageListFont);
  FlexGridSizer26->Add(btnLoadImageList, 1, wxEXPAND, 5);
  btnListSave = new wxButton(
      Panel6, ID_BUTTON12, _("Save all images..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON12"));
  wxFont btnListSaveFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnListSave->SetFont(btnListSaveFont);
  FlexGridSizer26->Add(btnListSave, 1, wxEXPAND, 5);
  FlexGridSizer22->Add(FlexGridSizer26, 1, wxEXPAND, 0);
  FlexGridSizer10->Add(FlexGridSizer22, 1, wxEXPAND, 0);
  FlexGridSizer32 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer32->AddGrowableCol(0);
  FlexGridSizer32->AddGrowableRow(1);
  wxString __wxRadioBoxChoices_2[4] = {
      _("Original"), _("Detected chessboard"), _("Reprojected corners"), _("Undistorted")};
  rbShowImages = new wxRadioBox(
      Panel6, ID_RADIOBOX2, _(" Show: "), wxDefaultPosition, wxDefaultSize, 4,
      __wxRadioBoxChoices_2, 1, 0, wxDefaultValidator, _T("ID_RADIOBOX2"));
  rbShowImages->SetSelection(0);
  wxFont rbShowImagesFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  rbShowImages->SetFont(rbShowImagesFont);
  FlexGridSizer32->Add(
      rbShowImages, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer42 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer42->AddGrowableCol(0);
  FlexGridSizer42->AddGrowableCol(1);
  FlexGridSizer42->AddGrowableRow(0);
  FlexGridSizer23 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer23->AddGrowableCol(0);
  FlexGridSizer23->AddGrowableRow(1);
  StaticText18 = new wxStaticText(
      Panel6, ID_STATICTEXT19, _("(Left camera)"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT19"));
  FlexGridSizer23->Add(
      StaticText18, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
  m_view_left = new mrpt::gui::wxMRPTImageControl(
      Panel6, ID_CUSTOM4, wxDefaultPosition.x, wxDefaultPosition.y, wxSize(320, 240).GetWidth(),
      wxSize(320, 240).GetHeight());
  FlexGridSizer23->Add(m_view_left, 1, wxEXPAND, 0);
  FlexGridSizer42->Add(FlexGridSizer23, 1, wxEXPAND, 1);
  FlexGridSizer24 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer24->AddGrowableCol(0);
  FlexGridSizer24->AddGrowableRow(1);
  StaticText19 = new wxStaticText(
      Panel6, ID_STATICTEXT20, _("(Right camera)"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT20"));
  FlexGridSizer24->Add(
      StaticText19, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
  m_view_right = new mrpt::gui::wxMRPTImageControl(
      Panel6, ID_CUSTOM5, wxDefaultPosition.x, wxDefaultPosition.y, wxSize(320, 240).GetWidth(),
      wxSize(320, 240).GetHeight());
  FlexGridSizer24->Add(m_view_right, 1, wxEXPAND, 0);
  FlexGridSizer42->Add(FlexGridSizer24, 1, wxEXPAND, 1);
  FlexGridSizer32->Add(FlexGridSizer42, 1, wxEXPAND, 0);
  FlexGridSizer10->Add(FlexGridSizer32, 1, wxEXPAND, 0);
  FlexGridSizer9->Add(FlexGridSizer10, 1, wxEXPAND, 0);
  FlexGridSizer11 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer11->AddGrowableCol(1);
  FlexGridSizer11->AddGrowableRow(0);
  FlexGridSizer29 = new wxFlexGridSizer(3, 1, 0, 0);
  FlexGridSizer29->AddGrowableCol(0);
  FlexGridSizer29->AddGrowableRow(1);
  FlexGridSizer30 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer30->AddGrowableCol(0);
  StaticText24 = new wxStaticText(
      Panel6, ID_STATICTEXT25, _("Calibration options"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT25"));
  wxFont StaticText24Font(-1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  StaticText24->SetFont(StaticText24Font);
  FlexGridSizer30->Add(
      StaticText24, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer29->Add(FlexGridSizer30, 1, wxEXPAND, 0);
  Notebook2 =
      new wxNotebook(Panel6, ID_NOTEBOOK3, wxDefaultPosition, wxDefaultSize, 0, _T("ID_NOTEBOOK3"));
  Panel11 = new wxPanel(
      Notebook2, ID_PANEL11, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL11"));
  FlexGridSizer20 = new wxFlexGridSizer(2, 1, 0, 0);
  StaticBoxSizer7 = new wxStaticBoxSizer(wxHORIZONTAL, Panel11, _("Number of inner corners: "));
  FlexGridSizer21 = new wxFlexGridSizer(1, 4, 0, 0);
  StaticText12 = new wxStaticText(
      Panel11, ID_STATICTEXT13, _("In X:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT13"));
  FlexGridSizer21->Add(
      StaticText12, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edCalibCheckX = new wxSpinCtrl(
      Panel11, ID_SPINCTRL3, _T("7"), wxDefaultPosition, wxSize(50, -1), 0, 1, 200, 7,
      _T("ID_SPINCTRL3"));
  edCalibCheckX->SetValue(_T("7"));
  FlexGridSizer21->Add(
      edCalibCheckX, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticText13 = new wxStaticText(
      Panel11, ID_STATICTEXT14, _("In Y:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT14"));
  FlexGridSizer21->Add(
      StaticText13, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edCalibCheckY = new wxSpinCtrl(
      Panel11, ID_SPINCTRL4, _T("9"), wxDefaultPosition, wxSize(50, -1), 0, 1, 200, 9,
      _T("ID_SPINCTRL4"));
  edCalibCheckY->SetValue(_T("9"));
  FlexGridSizer21->Add(
      edCalibCheckY, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticBoxSizer7->Add(FlexGridSizer21, 1, wxEXPAND, 0);
  FlexGridSizer20->Add(StaticBoxSizer7, 1, wxEXPAND, 0);
  StaticBoxSizer8 = new wxStaticBoxSizer(wxHORIZONTAL, Panel11, _(" Size of quads (in mm): "));
  FlexGridSizer33 = new wxFlexGridSizer(1, 4, 0, 0);
  StaticText14 = new wxStaticText(
      Panel11, ID_STATICTEXT15, _("In X:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT15"));
  FlexGridSizer33->Add(
      StaticText14, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edCalibSizeX = new wxTextCtrl(
      Panel11, ID_TEXTCTRL6, _("40.0"), wxDefaultPosition, wxSize(40, -1), 0, wxDefaultValidator,
      _T("ID_TEXTCTRL6"));
  FlexGridSizer33->Add(
      edCalibSizeX, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticText15 = new wxStaticText(
      Panel11, ID_STATICTEXT16, _("In Y:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT16"));
  FlexGridSizer33->Add(
      StaticText15, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edCalibSizeY = new wxTextCtrl(
      Panel11, ID_TEXTCTRL7, _("40.0"), wxDefaultPosition, wxSize(40, -1), 0, wxDefaultValidator,
      _T("ID_TEXTCTRL7"));
  FlexGridSizer33->Add(
      edCalibSizeY, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  StaticBoxSizer8->Add(FlexGridSizer33, 1, wxEXPAND, 0);
  FlexGridSizer20->Add(StaticBoxSizer8, 1, wxEXPAND, 0);
  Panel11->SetSizer(FlexGridSizer20);
  FlexGridSizer20->Fit(Panel11);
  FlexGridSizer20->SetSizeHints(Panel11);
  Panel12 = new wxPanel(
      Notebook2, ID_PANEL12, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL12"));
  FlexGridSizer35 = new wxFlexGridSizer(3, 2, 0, 0);
  FlexGridSizer35->AddGrowableCol(0);
  cbOptK1 = new wxCheckBox(
      Panel12, ID_CHECKBOX4, _("k1 (r^2 dist.)"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX4"));
  cbOptK1->SetValue(true);
  FlexGridSizer35->Add(cbOptK1, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
  cbOptK2 = new wxCheckBox(
      Panel12, ID_CHECKBOX5, _("k2 (r^4 dist.)"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX5"));
  cbOptK2->SetValue(true);
  FlexGridSizer35->Add(cbOptK2, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
  cbOptK3 = new wxCheckBox(
      Panel12, ID_CHECKBOX6, _("k3 (r^6 dist.)"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX6"));
  cbOptK3->SetValue(true);
  FlexGridSizer35->Add(cbOptK3, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
  FlexGridSizer35->Add(-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  cbOptT1 = new wxCheckBox(
      Panel12, ID_CHECKBOX7, _("t1 (tang. dist.)"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX7"));
  cbOptT1->SetValue(true);
  FlexGridSizer35->Add(cbOptT1, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
  cbOptT2 = new wxCheckBox(
      Panel12, ID_CHECKBOX8, _("t2 (tang. dist.)"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX8"));
  cbOptT2->SetValue(true);
  FlexGridSizer35->Add(cbOptT2, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
  Panel12->SetSizer(FlexGridSizer35);
  FlexGridSizer35->Fit(Panel12);
  FlexGridSizer35->SetSizeHints(Panel12);
  Panel13 = new wxPanel(
      Notebook2, ID_PANEL13, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL13"));
  FlexGridSizer34 = new wxFlexGridSizer(3, 1, 0, 0);
  FlexGridSizer34->AddGrowableCol(0);
  FlexGridSizer36 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer36->AddGrowableCol(1);
  StaticText25 = new wxStaticText(
      Panel13, ID_STATICTEXT26, _("Max. iterations:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT26"));
  FlexGridSizer36->Add(
      StaticText25, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edMaxIters = new wxSpinCtrl(
      Panel13, ID_SPINCTRL8, _T("1000"), wxDefaultPosition, wxDefaultSize, 0, 0, 10000, 1000,
      _T("ID_SPINCTRL8"));
  edMaxIters->SetValue(_T("1000"));
  FlexGridSizer36->Add(
      edMaxIters, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer34->Add(FlexGridSizer36, 1, wxEXPAND, 0);
  cbCalibUseRobust = new wxCheckBox(
      Panel13, ID_CHECKBOX3, _("Pseudo-Huber robust kernel"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX3"));
  cbCalibUseRobust->SetValue(true);
  FlexGridSizer34->Add(
      cbCalibUseRobust, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  cbCalibNormalize = new wxCheckBox(
      Panel13, ID_CHECKBOX2, _("Normalize images"), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_CHECKBOX2"));
  cbCalibNormalize->SetValue(true);
  FlexGridSizer34->Add(
      cbCalibNormalize, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  Panel13->SetSizer(FlexGridSizer34);
  FlexGridSizer34->Fit(Panel13);
  FlexGridSizer34->SetSizeHints(Panel13);
  Notebook2->AddPage(Panel11, _("Board"), true);
  Notebook2->AddPage(Panel12, _("Params. to find"), false);
  Notebook2->AddPage(Panel13, _("Settings"), false);
  FlexGridSizer29->Add(Notebook2, 1, wxEXPAND, 0);
  FlexGridSizer31 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer31->AddGrowableCol(0);
  btnRunCalib = new wxButton(
      Panel6, ID_BUTTON14, _("Run optimizer..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON14"));
  wxFont btnRunCalibFont(-1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  btnRunCalib->SetFont(btnRunCalibFont);
  FlexGridSizer31->Add(btnRunCalib, 1, wxEXPAND, 5);
  FlexGridSizer29->Add(FlexGridSizer31, 1, wxEXPAND, 0);
  FlexGridSizer11->Add(FlexGridSizer29, 1, wxEXPAND, 0);
  FlexGridSizer27 = new wxFlexGridSizer(2, 1, 0, 0);
  FlexGridSizer27->AddGrowableCol(0);
  FlexGridSizer27->AddGrowableRow(1);
  FlexGridSizer28 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer28->AddGrowableCol(0);
  StaticText23 = new wxStaticText(
      Panel6, ID_STATICTEXT24, _("Calibration results:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT24"));
  wxFont StaticText23Font(-1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  StaticText23->SetFont(StaticText23Font);
  FlexGridSizer28->Add(
      StaticText23, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  btnSaveCalib = new wxButton(
      Panel6, ID_BUTTON13, _("Save calibration..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON13"));
  FlexGridSizer28->Add(
      btnSaveCalib, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer27->Add(FlexGridSizer28, 1, wxEXPAND, 0);
  NotebookCalibResults =
      new wxNotebook(Panel6, ID_NOTEBOOK2, wxDefaultPosition, wxDefaultSize, 0, _T("ID_NOTEBOOK2"));
  Panel1 = new wxPanel(
      NotebookCalibResults, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL,
      _T("ID_PANEL1"));
  FlexGridSizer19 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer19->AddGrowableCol(0);
  FlexGridSizer19->AddGrowableRow(0);
  edLogCalibResult = new wxTextCtrl(
      Panel1, ID_TEXTCTRL8, wxEmptyString, wxDefaultPosition, wxDefaultSize,
      wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP, wxDefaultValidator, _T("ID_TEXTCTRL8"));
  wxFont edLogCalibResultFont(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  edLogCalibResult->SetFont(edLogCalibResultFont);
  FlexGridSizer19->Add(edLogCalibResult, 1, wxEXPAND, 0);
  Panel1->SetSizer(FlexGridSizer19);
  FlexGridSizer19->Fit(Panel1);
  FlexGridSizer19->SetSizeHints(Panel1);
  Panel10 = new wxPanel(
      NotebookCalibResults, ID_PANEL10, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL,
      _T("ID_PANEL10"));
  FlexGridSizer43 = new wxFlexGridSizer(1, 1, 0, 0);
  FlexGridSizer43->AddGrowableCol(0);
  FlexGridSizer43->AddGrowableRow(0);
  m_plot3D_cameras = new CMyGLCanvas(
      Panel10, ID_CUSTOM6, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_CUSTOM6"));
  FlexGridSizer43->Add(m_plot3D_cameras, 1, wxEXPAND, 0);
  Panel10->SetSizer(FlexGridSizer43);
  FlexGridSizer43->Fit(Panel10);
  FlexGridSizer43->SetSizeHints(Panel10);
  NotebookCalibResults->AddPage(Panel1, _("Report"), false);
  NotebookCalibResults->AddPage(Panel10, _("6D camera poses"), true);
  FlexGridSizer27->Add(NotebookCalibResults, 1, wxEXPAND, 0);
  FlexGridSizer11->Add(FlexGridSizer27, 1, wxEXPAND, 0);
  FlexGridSizer9->Add(FlexGridSizer11, 1, wxEXPAND, 0);
  Panel6->SetSizer(FlexGridSizer9);
  FlexGridSizer9->Fit(Panel6);
  FlexGridSizer9->SetSizeHints(Panel6);
  Panel5 = new wxPanel(
      Notebook1, ID_PANEL5, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL5"));
  FlexGridSizer15 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer15->AddGrowableCol(0);
  FlexGridSizer15->AddGrowableRow(0);
  m_plot3D = new CMyGLCanvas(
      Panel5, ID_XY_GLCANVAS, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL,
      _T("ID_XY_GLCANVAS"));
  FlexGridSizer15->Add(m_plot3D, 1, wxEXPAND, 1);
  FlexGridSizer16 = new wxFlexGridSizer(6, 1, 0, 0);
  FlexGridSizer16->AddGrowableCol(0);
  FlexGridSizer16->AddGrowableRow(5);
  StaticText3 = new wxStaticText(
      Panel5, ID_STATICTEXT3, _("Press \"Connect\" to start grabbing:"), wxDefaultPosition,
      wxDefaultSize, 0, _T("ID_STATICTEXT3"));
  FlexGridSizer16->Add(StaticText3, 1, wxEXPAND, 5);
  btnConnectLive3D = new wxButton(
      Panel5, ID_BUTTON18, _("Connect..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON18"));
  FlexGridSizer16->Add(btnConnectLive3D, 1, wxEXPAND, 5);
  btnDisconnectLive = new wxButton(
      Panel5, ID_BUTTON20, _("Disconnect"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON20"));
  btnDisconnectLive->Disable();
  FlexGridSizer16->Add(btnDisconnectLive, 1, wxEXPAND, 5);
  StaticText4 = new wxStaticText(
      Panel5, ID_STATICTEXT4,
      _("You should be seeing the\nlive colored 3D point cloud. \n\nBy "
        "loading a correct calibration\nfile, the colors should match "
        "\naccurately to the real objects\neven close to sharp edges.\n\nIf "
        "no error was found but still\nhave no images, try disconnecting "
        "\nand connecting again."),
      wxPoint(40, 32), wxDefaultSize, 0, _T("ID_STATICTEXT4"));
  FlexGridSizer16->Add(StaticText4, 1, wxEXPAND, 5);
  FlexGridSizer18 = new wxFlexGridSizer(1, 3, 0, 0);
  btnLoadCalib = new wxButton(
      Panel5, ID_BUTTON19, _("Load calib..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON19"));
  wxFont btnLoadCalibFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnLoadCalib->SetFont(btnLoadCalibFont);
  FlexGridSizer18->Add(
      btnLoadCalib, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  btnSaveCalibLive = new wxButton(
      Panel5, ID_BUTTON21, _("Save calib..."), wxDefaultPosition, wxDefaultSize, 0,
      wxDefaultValidator, _T("ID_BUTTON21"));
  wxFont btnSaveCalibLiveFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  btnSaveCalibLive->SetFont(btnSaveCalibLiveFont);
  FlexGridSizer18->Add(
      btnSaveCalibLive, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  btnHelpLiveCalib = new wxBitmapButton(
      Panel5, ID_BITMAPBUTTON1,
      wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_HELP")), wxART_BUTTON),
      wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T("ID_BITMAPBUTTON1"));
  btnHelpLiveCalib->SetDefault();
  btnHelpLiveCalib->SetToolTip(_("Help..."));
  FlexGridSizer18->Add(
      btnHelpLiveCalib, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer16->Add(FlexGridSizer18, 1, wxEXPAND, 0);
  m_grid_live_calib = new wxGrid(
      Panel5, ID_GRID1, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL, _T("ID_GRID1"));
  m_grid_live_calib->CreateGrid(3, 1);
  wxFont m_grid_live_calibFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  m_grid_live_calib->SetFont(m_grid_live_calibFont);
  m_grid_live_calib->EnableEditing(true);
  m_grid_live_calib->EnableGridLines(true);
  m_grid_live_calib->SetRowLabelSize(90);
  m_grid_live_calib->SetDefaultColSize(100, true);
  wxFont GridLabelFont_1(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  m_grid_live_calib->SetLabelFont(GridLabelFont_1);
  m_grid_live_calib->SetDefaultCellFont(m_grid_live_calib->GetFont());
  m_grid_live_calib->SetDefaultCellTextColour(m_grid_live_calib->GetForegroundColour());
  FlexGridSizer16->Add(m_grid_live_calib, 1, wxEXPAND, 5);
  FlexGridSizer15->Add(FlexGridSizer16, 1, wxEXPAND, 0);
  Panel5->SetSizer(FlexGridSizer15);
  FlexGridSizer15->Fit(Panel5);
  FlexGridSizer15->SetSizeHints(Panel5);
  Notebook1->AddPage(Panel8, _("Welcome"), true);
  Notebook1->AddPage(Panel9, _("0) Instructions"), false);
  Notebook1->AddPage(Panel3, _("1) Test connection"), false);
  Notebook1->AddPage(Panel4, _("2) Capture"), false);
  Notebook1->AddPage(Panel6, _("3) Stereo/RGBD calibration"), false);
  Notebook1->AddPage(Panel5, _("4) Live Kinect test"), false);
  FlexGridSizer1->Add(Notebook1, 1, wxEXPAND, 5);
  Panel2 = new wxPanel(
      this, ID_PANEL2, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL2"));
  FlexGridSizer2 = new wxFlexGridSizer(0, 2, 0, 0);
  FlexGridSizer2->AddGrowableCol(0);
  FlexGridSizer13 = new wxFlexGridSizer(1, 2, 0, 0);
  FlexGridSizer13->AddGrowableCol(1);
  FlexGridSizer13->AddGrowableRow(0);
  StaticText10 = new wxStaticText(
      Panel2, ID_STATICTEXT10, _("Log:"), wxDefaultPosition, wxDefaultSize, 0,
      _T("ID_STATICTEXT10"));
  FlexGridSizer13->Add(
      StaticText10, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  edLogTest = new wxTextCtrl(
      Panel2, ID_TEXTCTRL4, wxEmptyString, wxDefaultPosition, wxSize(-1, 50),
      wxTE_MULTILINE | wxTE_READONLY | wxHSCROLL | wxVSCROLL, wxDefaultValidator,
      _T("ID_TEXTCTRL4"));
  wxFont edLogTestFont(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  edLogTest->SetFont(edLogTestFont);
  FlexGridSizer13->Add(edLogTest, 1, wxEXPAND, 5);
  FlexGridSizer2->Add(FlexGridSizer13, 1, wxEXPAND, 0);
  FlexGridSizer14 = new wxFlexGridSizer(1, 2, 0, 0);
  bntAbout = new wxButton(
      Panel2, ID_BUTTON1, _("About"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON1"));
  FlexGridSizer14->Add(bntAbout, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  btnQuit = new wxButton(
      Panel2, ID_BUTTON2, _("Quit"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
      _T("ID_BUTTON2"));
  FlexGridSizer14->Add(btnQuit, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  FlexGridSizer2->Add(
      FlexGridSizer14, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 0);
  Panel2->SetSizer(FlexGridSizer2);
  FlexGridSizer2->Fit(Panel2);
  FlexGridSizer2->SetSizeHints(Panel2);
  FlexGridSizer1->Add(Panel2, 1, wxEXPAND, 0);
  SetSizer(FlexGridSizer1);
  timConsoleDump.SetOwner(this, ID_TIMER1);
  timConsoleDump.Start(100, false);
  timMisc.SetOwner(this, ID_TIMER2);
  timMisc.Start(2, false);
  FlexGridSizer1->Fit(this);
  FlexGridSizer1->SetSizeHints(this);
  Center();

  using kcgd = kinect_calibrate_guiDialog;

  Bind(wxEVT_BUTTON, &kcgd::OnbtnOpCalibKinectClick, this, ID_BUTTON15);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnOpCalibStereoGenericClick, this, ID_BUTTON16);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnOpTestKinectClick, this, ID_BUTTON17);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnNext1Click, this, ID_BUTTON3);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnConnectClick, this, ID_BUTTON5);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnNext1Click, this, ID_BUTTON4);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnDisconnectClick, this, ID_BUTTON8);
  Bind(wxEVT_RADIOBOX, &kcgd::OnrbChannelSwitchSelect, this, ID_RADIOBOX1);
  Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &kcgd::OnedTiltChange, this, ID_SPINCTRL7);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnCaptureClick, this, ID_BUTTON6);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnNextCalibClick, this, ID_BUTTON7);
  Bind(wxEVT_LISTBOX, &kcgd::OnlbImagePairsSelect, this, ID_LISTBOX1);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnListRemoveSelectedClick, this, ID_BUTTON9);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnListLoadClick, this, ID_BUTTON10);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnLoadImageListClick, this, ID_BUTTON11);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnListSaveClick, this, ID_BUTTON12);
  Bind(wxEVT_RADIOBOX, &kcgd::OnrbShowImagesSelect, this, ID_RADIOBOX2);
  Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &kcgd::OnNeedsToUpdate6DCamPlot, this, ID_SPINCTRL3);
  Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &kcgd::OnNeedsToUpdate6DCamPlot, this, ID_SPINCTRL4);
  Bind(wxEVT_COMMAND_TEXT_UPDATED, &kcgd::OnedCalibSizeXText, this, ID_TEXTCTRL6);
  Bind(wxEVT_COMMAND_TEXT_UPDATED, &kcgd::OnedCalibSizeXText, this, ID_TEXTCTRL7);
  Bind(wxEVT_CHECKBOX, &kcgd::OncbCalibNormalizeClick, this, ID_CHECKBOX3);
  Bind(wxEVT_CHECKBOX, &kcgd::OncbCalibNormalizeClick, this, ID_CHECKBOX2);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnRunCalibClick, this, ID_BUTTON14);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnSaveCalibClick, this, ID_BUTTON13);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnConnectLive3DClick, this, ID_BUTTON18);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnDisconnectLiveClick, this, ID_BUTTON20);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnLoadCalibClick, this, ID_BUTTON19);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnSaveCalibLiveClick, this, ID_BUTTON21);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnHelpLiveCalibClick, this, ID_BITMAPBUTTON1);
  Panel5->Bind(wxEVT_SET_FOCUS, &kcgd::OnPanel5SetFocus, this);
  Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, &kcgd::OnNotebook1PageChanging, this, ID_NOTEBOOK1);
  Bind(wxEVT_BUTTON, &kcgd::OnAbout, this, ID_BUTTON1);
  Bind(wxEVT_BUTTON, &kcgd::OnbtnQuitClick, this, ID_BUTTON2);
  Bind(wxEVT_TIMER, &kcgd::OntimConsoleDumpTrigger, this, ID_TIMER1);
  Bind(wxEVT_TIMER, &kcgd::OntimMiscTrigger, this, ID_TIMER2);
  Bind(wxEVT_CLOSE_WINDOW, &kcgd::OnClose, this, wxID_ANY);
  Bind(wxEVT_SIZE, &kcgd::OnResize, this);
  //*)

  Bind(
#if wxCHECK_VERSION(3, 1, 0)
      wxEVT_GRID_CELL_CHANGED,
#else
      wxEVT_GRID_CELL_CHANGE,
#endif
      &kcgd::Onm_grid_live_calibCellChange, this, ID_GRID1);

  // Set std::cout/cerr out:
  m_my_redirector = std::make_unique<CMyRedirector>(
      edLogTest,
      false,  // yieldApplication
      0,      // bufferSize
      true,   // also_cerr
      true,   // threadSafe -> we must call dumpNow()
      true    // also_to_cout_cerr
  );

  // App icon:
  this->SetIcon(wxIcon(kinect_xpm));

  // Load image from embedded JPEG image in .h:
  try
  {
    mrpt::io::CMemoryStream img1_jpeg_stream(
        kinect_covered_projector_img_jpeg, sizeof(kinect_covered_projector_img_jpeg));
    mrpt::img::CImage img1;
    img1.loadFromStreamAsJPEG(img1_jpeg_stream);
    m_imgStaticKinect->AssignImage(img1);
    m_imgStaticKinect->Refresh(false);
  }
  catch (...)
  {
  }

  // Prepare 3D scene: (of live view)
  // ------------------------------------------
  auto openGLSceneRef = m_plot3D->getOpenGLSceneRef();
  openGLSceneRef = mrpt::opengl::Scene::Create();

  // Ground plane:
  {
    mrpt::opengl::CGridPlaneXY::Ptr obj =
        mrpt::opengl::CGridPlaneXY::Create(-10, 10, -10, 10, 0, 1);
    obj->setColor_u8(TColor(200, 200, 200));
    openGLSceneRef->insert(obj);
  }
  //// XYZ corner:
  // m_plot3D->m_openGLScene->insert(
  // mrpt::opengl::stock_objects::CornerXYZSimple(0.5,2) );

  // 3D points:
  m_gl_3d_points = mrpt::opengl::CPointCloudColoured::Create();
  m_gl_3d_points->setPointSize(2);
  openGLSceneRef->insert(m_gl_3d_points);

  m_gl_corner_left = mrpt::opengl::stock_objects::CornerXYZSimple(0.03f, 2);
  m_gl_corner_right = mrpt::opengl::stock_objects::CornerXYZSimple(0.03f, 2);
  openGLSceneRef->insert(m_gl_corner_left);
  openGLSceneRef->insert(m_gl_corner_right);

  // Prepare 3D scene: (of calibrate view)
  // ------------------------------------------
  CalibUpdate3DViewCameras();

  // Init. grid cells:
  this->LiveCalibGridInitialize();
}

kinect_calibrate_guiDialog::~kinect_calibrate_guiDialog()
{
  //(*Destroy(kinect_calibrate_guiDialog)
  //*)
}

void kinect_calibrate_guiDialog::OnQuit(wxCommandEvent& event) { Close(); }
void kinect_calibrate_guiDialog::OnAbout(wxCommandEvent& event)
{
  mrpt::gui::show_mrpt_about_box_wxWidgets(this, "kinect-stereo-calib");
}

// Don't allow the user to change tab by hand:
void kinect_calibrate_guiDialog::OnNotebook1PageChanging(wxNotebookEvent& event)
{
  if (event.GetSelection() == 0)
  {
    if (event.GetOldSelection() == 1 || event.GetOldSelection() == 2)
    {
      this->StopLiveGrabThreads();
      event.Skip();  // Permit change
    }
    else
    {
      if (wxYES == ::wxMessageBox(
                       _("Your unsaved data will be lost, are you sure "
                         "you want to leave this page?"),
                       _("Confirm"), wxYES_NO, this))
        event.Skip();  // Permit change
      else
        event.Veto();  // Veto change
    }
  }
  else if (event.GetSelection() == 5 && event.GetOldSelection() == 4)
  {
    event.Skip();  // Permit change
  }
  else
  {
    event.Veto();  // Veto change
  }
}

void kinect_calibrate_guiDialog::OnbtnNext1Click(wxCommandEvent& event)
{
  if (Notebook1->GetSelection() == 1)
  {
    btnConnect->Enable(true);
    btnDisconnect->Enable(false);
    btnNext2->Enable(false);
  }

  Notebook1->ChangeSelection(Notebook1->GetSelection() + 1);
}

void kinect_calibrate_guiDialog::OnbtnQuitClick(wxCommandEvent& event) { Close(); }

void kinect_calibrate_guiDialog::OnbtnConnectClick(wxCommandEvent& event)
{
  btnNext2->Enable(false);

  if (m_cap_thread.joinable())
  {
    // Shoulnd't reach here...just in case:
    btnConnect->Enable(false);
    return;
  }

  m_grabstate = gsIdle;
  m_calib_images.clear();

  m_cap_thread_data.quit = false;
  m_cap_thread_data.flag_grab_depth = false;  // we don't need depth data for image registration.
  m_findcorners_thread_data.quit = false;

  // Launch thread:
  m_cap_thread = std::thread(&kinect_calibrate_guiDialog::thread_grabbing, this);
  m_findcorners_thread = std::thread(&kinect_calibrate_guiDialog::thread_find_corners, this);
  btnConnect->Enable(false);
  btnDisconnect->Enable(true);

  // ...
}

void kinect_calibrate_guiDialog::OntimConsoleDumpTrigger(wxTimerEvent& event)
{
  if (m_my_redirector) m_my_redirector->dumpNow();
}

// The thread in charge of opening the Kinect sensor & grabbing data.
void kinect_calibrate_guiDialog::thread_grabbing()
{
  TThreadParam& p = this->m_cap_thread_data;
  p.terminated = false;

  try
  {
    CKinect kinect;

    // We only have to grab the intensity channel:
    kinect.enableGrabRGB(true);  // RGB / IR channels:
    kinect.enableGrabDepth(p.flag_grab_depth);
    kinect.enableGrab3DPoints(false);
    kinect.enableGrabAccelerometers(false);

    // Open:
    kinect.setVideoChannel(
        p.select_IR_channel ? CKinect::VIDEO_CHANNEL_IR : CKinect::VIDEO_CHANNEL_RGB);
    bool last_select_IR_channel = p.select_IR_channel;

    cout << "Calling CKinect::initialize()...";
    kinect.initialize();
    cout << "OK\n";

    CTicTac tictac;
    int nImgs = 0;
    bool there_is_obs = true, hard_error = false;

    kinect.setTiltAngleDegrees(p.tilt_ang_deg);
    double old_tilt_ang_deg = p.tilt_ang_deg;

    while (!hard_error && !p.quit)
    {
      // Grab new observation from the camera:
      CObservation3DRangeScan::Ptr obs =
          std::make_shared<CObservation3DRangeScan>();  // Smart pointers
                                                        // to observations

      kinect.getNextObservation(*obs, there_is_obs, hard_error);

      if (!hard_error && there_is_obs)
      {
        std::atomic_store(&p.new_obs, obs);
      }

      if (old_tilt_ang_deg != p.tilt_ang_deg)
      {
        old_tilt_ang_deg = p.tilt_ang_deg;
        kinect.setTiltAngleDegrees(p.tilt_ang_deg);
      }

      // Shall we switch between IR/RGB channels?
      if (last_select_IR_channel != p.select_IR_channel)
      {
        kinect.setVideoChannel(
            p.select_IR_channel ? CKinect::VIDEO_CHANNEL_IR : CKinect::VIDEO_CHANNEL_RGB);
        last_select_IR_channel = p.select_IR_channel;
      }

      nImgs++;
      if (nImgs > 10)
      {
        p.Hz = nImgs / tictac.Tac();
        nImgs = 0;
        tictac.Tic();
      }
    }
  }
  catch (const std::exception& e)
  {
    cout << "[Kinect thread] Exception: " << mrpt::exception_to_str(e) << endl;
    p.quit = true;  // Exit for some error
  }
  p.terminated = true;
}

void kinect_calibrate_guiDialog::OntimMiscTrigger(wxTimerEvent& event)
{
  // if the thread was launched and has closed (e.g. for some error), clear
  // the handle:
  if (m_cap_thread.joinable() && m_cap_thread_data.terminated)
  {
    btnConnect->Enable(true);
    btnDisconnect->Enable(false);
  }

  // If we have a new image, process it depending on the current tab:
  // -----------------------------------------------------------------
  if (m_cap_thread.joinable())
  {  // we're grabbing:

    CObservation3DRangeScan::Ptr possiblyNewObs = std::atomic_load(&m_cap_thread_data.new_obs);
    if (possiblyNewObs && possiblyNewObs->timestamp != INVALID_TIMESTAMP &&
        (!m_last_obs || possiblyNewObs->timestamp != m_last_obs->timestamp))
    {  // It IS a new observation:
      m_last_obs = possiblyNewObs;

      if (m_last_obs->hasIntensityImage)
      {
        try
        {
          ProcessNewGrabbedObs();
        }
        catch (const std::exception& e)
        {
          cerr << mrpt::exception_to_str(e);
        }
      }

    }  // end It IS a new observation
  }    // end if m_cap_thread -> we're grabbing
}

void kinect_calibrate_guiDialog::StopLiveGrabThreads()
{
  wxBusyInfo info(_("Waiting for grab threads to close..."), this);
  wxTheApp->Yield();

  if (m_cap_thread.joinable())
  {
    m_cap_thread_data.quit = true;
    cout << "Waiting for the grabbing thread to end...\n";
    if (m_cap_thread.joinable()) m_cap_thread.join();
    cout << "Grabbing thread closed.\n";
  }
  if (m_findcorners_thread.joinable())
  {
    m_findcorners_thread_data.quit = true;
    cout << "Waiting for the corner find thread to end...\n";
    if (m_findcorners_thread.joinable()) m_findcorners_thread.join();
    cout << "Corner finding thread closed.\n";
  }
}

void kinect_calibrate_guiDialog::OnClose(wxCloseEvent& event)
{
  StopLiveGrabThreads();
  event.Skip();
}

// ---------------------------------------
// PROCESS NEW IMAGE (In: m_last_obs)
// ---------------------------------------
void kinect_calibrate_guiDialog::ProcessNewGrabbedObs()
{
  switch (Notebook1->GetSelection())
  {
    default:
      break;

    // ------------------------------------------
    //   Tab 1: Testing
    // ------------------------------------------
    case 2:
    {
      CImage im = m_last_obs->intensityImage;

      const std::string s = mrpt::system::dateTimeLocalToString(m_last_obs->timestamp);
      im.textOut(6, 6, s, TColor::black());
      im.textOut(5, 5, s, TColor::white());

      m_realtimeview_test->AssignImage(im);
      m_realtimeview_test->Refresh(false);
      if (!btnNext2->IsEnabled()) btnNext2->Enable();
    }
    break;

    // ------------------------------------------
    //   Tab 2: Capturing
    // ------------------------------------------
    case 3:
    {
      // Capture stuff --------------------
      static int cnt_skip_frames = 0;
      vector<string> center_messages;

      if (m_grabstate != gsIdle)
        center_messages.emplace_back("* DON'T MOVE EITHER THE SENSOR OR THE CHESSBOARD *");

      switch (m_grabstate)
      {
        case gsIdle:
          cnt_skip_frames = 0;
          break;

        case gsSwitchingRGB:
          center_messages.emplace_back(" Switching to RGB channel...");
          m_cap_thread_data.select_IR_channel = false;
          if (m_last_obs->intensityImageChannel == CObservation3DRangeScan::CH_VISIBLE)
          {
            if (cnt_skip_frames++ > 40)
            {
              cnt_skip_frames = 0;
              m_grabstate = gsCapturingRGB;
            }
          }
          break;

        case gsCapturingRGB:
        {
          // Grab RGB (right) image:
          m_calib_images.resize(m_calib_images.size() + 1);

          mrpt::vision::TImageStereoCalibData& scd = *m_calib_images.rbegin();
          scd.right.img_original = m_last_obs->intensityImage;

          // Switch to IR:
          m_grabstate = gsSwitchingIR;
        }
        break;

        case gsSwitchingIR:
          center_messages.emplace_back(" Switching to IR channel...");
          m_cap_thread_data.select_IR_channel = true;
          if (m_last_obs->intensityImageChannel == CObservation3DRangeScan::CH_IR)
          {
            if (cnt_skip_frames++ > 40)
            {
              cnt_skip_frames = 0;
              m_grabstate = gsCapturingIR;
            }
          }
          break;

        case gsCapturingIR:
        {
          // Grab IR (left) image:
          mrpt::vision::TImageStereoCalibData& scd = *m_calib_images.rbegin();
          scd.left.img_original = m_last_obs->intensityImage;

          // Done capturing: Back to idle:
          m_grabstate = gsIdle;
          lbNumCaptured->SetLabel(wxString::Format(
              _("Captured image pairs: %u"), static_cast<unsigned int>(m_calib_images.size())));
          btnCapture->Enable();
          btnNextCalib->Enable();

          // Switch back to user-selected channel:
          m_cap_thread_data.select_IR_channel = (rbChannelSwitch->GetSelection() == 1);
        }
        break;
      };

      // Display stuff --------------------
      CImage img_to_show;
      // (Destroys org img)
      img_to_show = std::move(m_last_obs->intensityImage);

      if (!img_to_show.isColor() && cbNormalize->IsChecked()) img_to_show.equalizeHist(img_to_show);

      // Do live detect & draw chessboard in parallel:
      if (m_findcorners_thread_data.ready_for_new_images)
      {
        m_findcorners_thread_data.image = img_to_show;
        m_findcorners_thread_data.image_timestamp = m_last_obs->timestamp;
      }

      static std::vector<TPixelCoordf> last_valid_corners;
      static mrpt::system::TTimeStamp last_valid_corners_tim = mrpt::Clock::now();

      if (m_findcorners_thread_data.detected_corners_done)
      {
        // We have new corners to draw:
        if (!m_findcorners_thread_data.detected_corners.empty())
        {
          last_valid_corners_tim = mrpt::Clock::now();
          last_valid_corners = m_findcorners_thread_data.detected_corners;
        }
        m_findcorners_thread_data.detected_corners_done =
            false;  // Signal that we've read the data.
      }

      // Makes an RGB color even if it was grayscale so we can draw color
      // stuff:
      img_to_show = img_to_show.colorImage();

      // Draw detected corners "persistently" during some instants:
      static bool at_least_detected_once = false;
      if (mrpt::system::timeDifference(last_valid_corners_tim, mrpt::Clock::now()) < 0.5)
      {
        const unsigned int cx = edSizeX->GetValue(), cy = edSizeY->GetValue();
        img_to_show.drawChessboardCorners(last_valid_corners, cx, cy);

        if (!last_valid_corners.empty()) at_least_detected_once = true;
      }
      else
      {
        center_messages.emplace_back("*WARNING*: No chessboard detected!");
        if (!at_least_detected_once)
          center_messages.emplace_back("Make sure the Rows x Cols size is correct.");
      }

      // Messages:
      img_to_show.selectTextFont("10x20");
      for (size_t i = 0; i < center_messages.size(); i++)
      {
        const string& s = center_messages[i];
        const int x = img_to_show.getWidth() / 2 - 10 * s.size() / 2;
        const int y = 230 + 25 * i;
        img_to_show.textOut(x, y, s, TColor::black());
        img_to_show.textOut(x - 1, y - 1, s, TColor::white());
      }

      // Show:
      m_realtimeview_cap->AssignImage(img_to_show);
      m_realtimeview_cap->Refresh(false);
    }
    break;

    // ------------------------------------------
    //   Tab 4: Live test:
    // ------------------------------------------
    case 5:
    {
      m_plot3D->getOpenGLSceneRef()->getViewport()->addTextMessage(
          20, 20, mrpt::system::dateTimeLocalToString(m_last_obs->timestamp));

      m_last_obs->cameraParams = m_calib_result.cam_params.leftCamera;
      m_last_obs->cameraParamsIntensity = m_calib_result.cam_params.rightCamera;

      const mrpt::poses::CPose3D l2r = mrpt::poses::CPose3D(0, 0, 0, -90.0_deg, 0, -90.0_deg) +
                                       (-m_calib_result.right2left_camera_pose);

      m_last_obs->relativePoseIntensityWRTDepth = l2r;  // L->R (Depth -> Intensity/RGB)

      T3DPointsProjectionParams pp;
      pp.takeIntoAccountSensorPoseOnRobot = false;

      m_last_obs->unprojectInto(*m_gl_3d_points, pp);

      m_gl_corner_left->setPose(mrpt::poses::CPose3D());
      m_gl_corner_right->setPose(l2r);

      m_plot3D->Refresh(false);
    }
    break;
  }
}

// User clicks to switch channels:
void kinect_calibrate_guiDialog::OnrbChannelSwitchSelect(wxCommandEvent& event)
{
  // The actual change of channel is done in the grabber thread.
  m_cap_thread_data.select_IR_channel = (rbChannelSwitch->GetSelection() == 1);
}

// The thread in charge of finding corners in parallel to the GUI
void kinect_calibrate_guiDialog::thread_find_corners()
{
  TThreadDetectCornerParam& p = this->m_findcorners_thread_data;
  p.terminated = false;
  p.ready_for_new_images = true;

  mrpt::system::TTimeStamp last_img_proc = INVALID_TIMESTAMP;

  while (!p.quit)
  {
    try
    {
      // New image to process??
      p.ready_for_new_images = true;

      if (!p.detected_corners_done && last_img_proc != p.image_timestamp &&
          (p.image_timestamp != INVALID_TIMESTAMP))
      {
        // Yes:
        p.ready_for_new_images = false;

        const unsigned int cx = edSizeX->GetValue(), cy = edSizeY->GetValue();

        const CImage img_gray(p.image, FAST_REF_OR_CONVERT_TO_GRAY);
        bool detect_ok = mrpt::vision::findChessboardCorners(
            img_gray, p.detected_corners, cx, cy,
            cbNormalize->IsChecked()  // normalize_image
        );

        if (!detect_ok) p.detected_corners.clear();

        p.detected_corners_done = true;  // will be set to false when the buffer
        // "p.detected_corners" has been read.
        p.ready_for_new_images = true;
      }
      else
      {
        std::this_thread::sleep_for(2ms);
      }
    }
    catch (const std::exception& e)
    {
      cout << "[corner finding thread] Exception: " << mrpt::exception_to_str(e) << endl;
    }
  }
  p.terminated = true;
}

// Button: Capture one image pair.
void kinect_calibrate_guiDialog::OnbtnCaptureClick(wxCommandEvent& event)
{
  btnCapture->Disable();
  btnNextCalib->Disable();
  m_grabstate = gsSwitchingRGB;
}

void kinect_calibrate_guiDialog::OnbtnNextCalibClick(wxCommandEvent& event)
{
  // Stop grabbing:
  StopLiveGrabThreads();
  Notebook1->ChangeSelection(Notebook1->GetSelection() + 1);

  UpdateListOfImages();
}

void kinect_calibrate_guiDialog::OnedTiltChange(wxSpinEvent& event)
{
  m_cap_thread_data.tilt_ang_deg = edTilt->GetValue();
}

void kinect_calibrate_guiDialog::UpdateListOfImages()
{
  wxArrayString lstStrs;
  for (size_t i = 0; i < m_calib_images.size(); i++)
  {
    lstStrs.Add(wxString::Format(_("ImgPair #%u"), static_cast<unsigned int>(i)));
  }
  lbImagePairs->Set(lstStrs);

  lbImagePairs->SetSelection(m_calib_images.size() - 1);
  ProcessNewSelectedImageListBox();
}

void kinect_calibrate_guiDialog::OnlbImagePairsSelect(wxCommandEvent& event)
{
  this->ProcessNewSelectedImageListBox();
}
void kinect_calibrate_guiDialog::ProcessNewSelectedImageListBox()
{
  try
  {
    static std::optional<mrpt::img::CImage> noImg;
    if (!noImg)
    {
      noImg.emplace(320, 240, CH_RGB);
      noImg->filledRectangle(0, 0, 319, 239, TColor(200, 200, 200));
      noImg->textOut(100, 110, "(No image selected)", TColor::white());
    }

    const int sel = lbImagePairs->GetSelection();

    if (sel == wxNOT_FOUND || sel >= (int)m_calib_images.size())
    {
      this->m_view_left->AssignImage(*noImg);
      this->m_view_right->AssignImage(*noImg);
    }
    else
    {
      const int image_mode = rbShowImages->GetSelection();

      CImage il, ir;

      const auto& selPair = m_calib_images[sel];

      // Common part to all (but one) modes:
      if (image_mode != 1)
      {
        il = selPair.left.img_original;
        ir = selPair.right.img_original;

        if (cbCalibNormalize->IsChecked())
        {
          if (!il.isColor()) il.equalizeHist(il);
          if (!ir.isColor()) ir.equalizeHist(ir);
        }
      }

      switch (image_mode)
      {
        // ======= Original images =======
        case 0:
        {  // Nothing else to do.
        }
        break;
        // ======= Detected chessboard =======
        case 1:
        {
          if (!selPair.left.img_checkboard.isEmpty())
            il = selPair.left.img_checkboard;
          else
            il = *noImg;

          if (!selPair.right.img_checkboard.isEmpty())
            ir = selPair.right.img_checkboard;
          else
            ir = *noImg;
        }
        break;

        // ======= Reprojected corners =======
        case 2:
        {
          il = il.colorImage();
          ir = ir.colorImage();

          il.drawChessboardCorners(
              selPair.left.projectedPoints_distorted, m_calib_params.check_size_x,
              m_calib_params.check_size_y);
          ir.drawChessboardCorners(
              selPair.right.projectedPoints_distorted, m_calib_params.check_size_x,
              m_calib_params.check_size_y);
        }
        break;
        // ======= Undistorted image =======
        case 3:
        {
          mrpt::img::CImage ilD;
          il.undistort(ilD, m_calib_result.cam_params.leftCamera);
          il = ilD;

          mrpt::img::CImage irD;
          ir.undistort(irD, m_calib_result.cam_params.rightCamera);
          ir = irD;
        }
        break;
        // ======= Rectified images =======
        case 4:
        {
        }
        break;

        default:
          // Shouldn't arrive here!
          break;
      }

      // Resize images, keeping aspect ratio:
      const wxSize szView = m_view_left->GetSize();  // In theory, right&left will be always equal.
      const double szRatio = static_cast<double>(szView.x) / szView.y;

      const mrpt::img::TImageSize szL = il.getSize();
      const mrpt::img::TImageSize szR = ir.getSize();

      const double lRatio =
          static_cast<double>(szL.x) / szL.y;  // Don't assume both images have equal size
      const double rRatio = static_cast<double>(szR.x) / szR.y;

      mrpt::img::TImageSize trg_sz_l, trg_sz_r;

      if (szRatio < lRatio)
      {  // Fill y
        trg_sz_l.x = szView.x;
        trg_sz_l.y = szL.y * szView.x / static_cast<double>(szL.x);
      }
      else
      {  // Fill x
        trg_sz_l.x = szL.x * szView.y / static_cast<double>(szL.y);
        trg_sz_l.y = szView.y;
      }

      if (szRatio < rRatio)
      {  // Fill y
        trg_sz_r.x = szView.x;
        trg_sz_r.y = szR.y * szView.x / static_cast<double>(szR.x);
      }
      else
      {  // Fill x
        trg_sz_r.x = szR.x * szView.y / static_cast<double>(szR.y);
        trg_sz_r.y = szView.y;
      }

      il.scaleImage(il, trg_sz_l.x, trg_sz_l.y, IMG_INTERP_AREA);
      ir.scaleImage(ir, trg_sz_r.x, trg_sz_r.y, IMG_INTERP_AREA);

      this->m_view_left->AssignImage(il);
      this->m_view_right->AssignImage(ir);
    }

    this->m_view_left->Refresh();
    this->m_view_right->Refresh();
  }
  catch (const std::exception& e)
  {
    wxMessageBox(
        wxString::Format(_("[Draw selected image] Exception:\n %s"), mrpt::exception_to_str(e)));
  }
}

struct TCalibCallbackData
{
  kinect_calibrate_guiDialog* win;
  wxProgressDialog* pd;
};

void myCalibCallback(const mrpt::vision::TImageStereoCallbackData& d, void* user_data)
{
  if ((d.current_iter & 0x0F) != 0) return;

  auto* dat = reinterpret_cast<TCalibCallbackData*>(user_data);

  string s;
  switch (d.calibRound)
  {
    case -1:
      s = mrpt::format(
          "Detecting corners: %u images done out of %u", d.nImgsProcessed, d.nImgsToProcess);
      break;
    case 0:
      s = "Round #1: Calibration without distortion";
      break;
    case 1:
      s = "Round #2: Full calibration";
      break;
  };

  if (d.calibRound == 0 || d.calibRound == 1)
  {
    s += mrpt::format(" (RMSE=%.05f px)", d.current_rmse);
  }

  dat->pd->Update(d.current_iter, s.c_str());
  dat->pd->SetSize(500, 100);

  wxTheApp->Yield();
}

// button: Run optimizer
void kinect_calibrate_guiDialog::OnbtnRunCalibClick(wxCommandEvent& event)
{
  try
  {
    // ============ Set parameters ============
    m_calib_params.check_size_x = edCalibCheckX->GetValue();
    m_calib_params.check_size_y = edCalibCheckY->GetValue();

    double sx, sy;
    edCalibSizeX->GetValue().ToCDouble(&sx);
    edCalibSizeY->GetValue().ToCDouble(&sy);

    m_calib_params.check_squares_length_X_meters = 1e-3 * sx;
    m_calib_params.check_squares_length_Y_meters = 1e-3 * sy;
    m_calib_params.maxIters = edMaxIters->GetValue();
    m_calib_params.verbose = true;

    m_calib_params.optimize_k1 = cbOptK1->IsChecked();
    m_calib_params.optimize_k2 = cbOptK2->IsChecked();
    m_calib_params.optimize_k3 = cbOptK3->IsChecked();

    m_calib_params.optimize_t1 = cbOptT1->IsChecked();
    m_calib_params.optimize_t2 = cbOptT2->IsChecked();

    m_calib_params.normalize_image = cbCalibNormalize->IsChecked();
    m_calib_params.use_robust_kernel = cbCalibUseRobust->IsChecked();

    //			wxBusyInfo info(_("Running optimizer..."),this);
    //			wxTheApp->Yield();

    wxProgressDialog pd(
        _("Calibration in progress..."), _("(starting)"), m_calib_params.maxIters, this,
        wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_ELAPSED_TIME);
    pd.SetSize(500, 100);
    wxTheApp->Yield();

    // Prep. callback function:
    TCalibCallbackData callback_data;
    callback_data.win = this;
    callback_data.pd = &pd;

    m_calib_params.callback = &myCalibCallback;
    m_calib_params.callback_user_param = reinterpret_cast<void*>(&callback_data);

    edLogCalibResult->Clear();

    // Run calibration:
    const bool res =
        mrpt::vision::checkerBoardStereoCalibration(m_calib_images, m_calib_params, m_calib_result);

    pd.Close();

    // Repaint 6D camera poses:
    CalibUpdate3DViewCameras();
    m_plot3D_cameras->Refresh();

    // And reprojected corners:
    rbShowImages->SetSelection(2);
    ProcessNewSelectedImageListBox();

    if (!res)
    {
      edLogCalibResult->AppendText(_("\nCalibration returned an error status.\n"));
    }
    else
    {  // std::cout redir:
      CMyRedirector redir(edLogCalibResult, true, 400);

      time_t systime;
      time(&systime);
      struct tm* timeinfo = localtime(&systime);

      cout << mrpt::format(
          "# Stereo camera calibration report\n"
          "# Generated by kinect-stereo-calib - MRPT at %s"
          "# (This file is loadable from rawlog-edit and other MRPT "
          "tools)\n"
          "# "
          "--------------------------------------------------------------"
          "-------\n\n",
          asctime(timeinfo));

      const TCamera& lc = m_calib_result.cam_params.leftCamera;
      const TCamera& rc = m_calib_result.cam_params.rightCamera;

      // Inverse variance of the estimates, in this order:
      //  [fx fy cx cy k1 k2 k3 t1 t2].
      const auto& lc_inf = m_calib_result.left_params_inv_variance;
      const auto& rc_inf = m_calib_result.right_params_inv_variance;
      const double std_detector = 0.2;  // pixels

      cout << mrpt::format(
          "# Left camera (IR/Depth in Kinect) calibration parameters "
          "(and 95%% confidence intervals):\n"
          "[CAMERA_PARAMS_LEFT]\n"
          "resolution = [%u %u]\n"
          "cx         = %f  // +/- %.03f\n"
          "cy         = %f  // +/- %.03f\n"
          "fx         = %f  // +/- %.03f\n"
          "fy         = %f  // +/- %.03f\n"
          "dist       = [%e %e %e %e %e]    // The order is: [K1 K2 T1 "
          "T2 K3]\n\n",
          lc.ncols, lc.nrows, lc.cx(), 2.0 * std_detector / std::sqrt(lc_inf[2]), lc.cy(),
          2.0 * std_detector / std::sqrt(lc_inf[3]), lc.fx(),
          2.0 * std_detector / std::sqrt(lc_inf[0]), lc.fy(),
          2.0 * std_detector / std::sqrt(lc_inf[1]), lc.k1(), lc.k2(), lc.p1(), lc.p2(), lc.k3());

      cout << mrpt::format(
          "# Right camera (RGB in Kinect) calibration parameters  (and "
          "95%% confidence intervals):\n"
          "[CAMERA_PARAMS_RIGHT]\n"
          "resolution = [%u %u]\n"
          "cx         = %f  // +/- %.03f\n"
          "cy         = %f  // +/- %.03f\n"
          "fx         = %f  // +/- %.03f\n"
          "fy         = %f  // +/- %.03f\n"
          "dist       = [%e %e %e %e %e]    // The order is: [K1 K2 T1 "
          "T2 K3]\n\n",
          rc.ncols, rc.nrows, rc.cx(), 2.0 * std_detector / std::sqrt(rc_inf[2]), rc.cy(),
          2.0 * std_detector / std::sqrt(rc_inf[3]), rc.fx(),
          2.0 * std_detector / std::sqrt(rc_inf[0]), rc.fy(),
          2.0 * std_detector / std::sqrt(rc_inf[1]), rc.k1(), rc.k2(), rc.p1(), rc.p2(), rc.k3());

      // pose:
      const mrpt::poses::CPose3D RT_YPR(-m_calib_result.right2left_camera_pose);
      const mrpt::poses::CPose3DQuat RT_quat(RT_YPR);

      cout << mrpt::format(
          "# Relative pose of the right camera wrt to the left camera:\n"
          "# This assumes that both camera frames are such that +Z "
          "points\n"
          "# forwards, and +X and +Y to the right and downwards.\n"
          "[CAMERA_PARAMS_LEFT2RIGHT_POSE]\n"
          "translation_only     = [%e %e %e]\n"
          "rotation_matrix_only = %s\n"
          "pose_yaw_pitch_roll  = %s  // (YPR in degrees)\n"
          "pose_quaternion      = %s\n\n",
          RT_YPR.x(), RT_YPR.y(), RT_YPR.z(), RT_YPR.getRotationMatrix().inMatlabFormat(13).c_str(),
          RT_YPR.asString().c_str(), RT_quat.asString().c_str());

      cout << mrpt::format(
          "# Info about calibration parameters:\n"
          "[CALIB_METAINFO]\n"
          "number_good_cheesboards = %u\n"
          "average_reprojection_error = %f // RMSE pixels\n"
          "cheesboard_nx = %i\n"
          "cheesboard_ny = %i\n"
          "cheesboard_square_size_x = %f // (m)\n"
          "cheesboard_square_size_y = %f // (m)\n\n",
          static_cast<unsigned int>(m_calib_result.final_number_good_image_pairs),
          m_calib_result.final_rmse, m_calib_params.check_size_x, m_calib_params.check_size_y,
          m_calib_params.check_squares_length_X_meters,
          m_calib_params.check_squares_length_Y_meters);

    }  // end of std::cout redir

    wxMessageBox(
        wxString::Format(_("Optimization finished with RMSE=%.03fpx"), m_calib_result.final_rmse),
        _("Done"), 0, this);
  }
  catch (const std::exception& e)
  {
    wxMessageBox(wxString::Format(_("[Optimizer] Exception:\n %s"), mrpt::exception_to_str(e)));
  }
}

void kinect_calibrate_guiDialog::OnbtnSaveCalibClick(wxCommandEvent& event)
{
  WX_START_TRY

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  wxFileDialog dialog2(
      this, _("Save calibration file (YAML format)"), startPath, _("calib.yml"),
      _("Configuration files (*.yml)|*.yml|All files (*.*)|*.*"),
      wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  if (dialog2.ShowModal() == wxID_OK)
  {
    std::ofstream f(dialog2.GetPath().mbc_str());
    ASSERT_(f.is_open());

    mrpt::containers::yaml c = mrpt::containers::yaml::Map();

    c["right"] = m_calib_result.cam_params.rightCamera.asYAML();
    c["left"] = m_calib_result.cam_params.leftCamera.asYAML();

    c["right_camera_pose"] = m_calib_result.cam_params.rightCameraPose.asString();

    auto r = c["calibration_results"] = mrpt::containers::yaml::Map();
    r["iters"] = m_calib_result.final_iters;
    r["good_image_pairs"] = m_calib_result.final_number_good_image_pairs;
    r["final_rmse"] = m_calib_result.final_rmse;

    c.printAsYAML(f);
  }

  wxFileDialog dialog(
      this, _("Save calibration file (INI format)"), startPath, _("calib.ini"),
      _("Configuration files (*.ini,*.cfg)|*.ini;*.cfg|All files (*.*)|*.*"),
      wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  if (dialog.ShowModal() == wxID_OK)
  {
    if (!edLogCalibResult->SaveFile(dialog.GetPath()))
      throw std::runtime_error("Error saving file!");
  }

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnbtnOpCalibKinectClick(wxCommandEvent& event)
{
  Notebook1->ChangeSelection(1);
}

void kinect_calibrate_guiDialog::OnbtnOpCalibStereoGenericClick(wxCommandEvent& event)
{
  Notebook1->ChangeSelection(4);
}

void kinect_calibrate_guiDialog::OnbtnOpTestKinectClick(wxCommandEvent& event)
{
  LiveCalibUpdateToGrid();
  Notebook1->ChangeSelection(5);
}

void kinect_calibrate_guiDialog::OnbtnDisconnectClick(wxCommandEvent& event)
{
  this->StopLiveGrabThreads();

  btnConnect->Enable();
  btnNext2->Disable();
}

void kinect_calibrate_guiDialog::OnbtnListLoadClick(wxCommandEvent& event)
{
  WX_START_TRY

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  wxFileDialog dialog1(
      this, _("Choose LEFT image"), startPath, _(""), imageWildcards,
      wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (dialog1.ShowModal() == wxID_OK)
  {
    const string file_img_l = string(dialog1.GetPath().mb_str());

    const string fil_dir = mrpt::system::extractFileDirectory(file_img_l);

    startPath = fil_dir.c_str();
    m_config.Write(_("last_path"), startPath);

    wxFileDialog dialog2(
        this, _("Choose RIGHT image"), startPath, _(""), imageWildcards,
        wxFD_OPEN | wxFD_FILE_MUST_EXIST);
    if (dialog2.ShowModal() == wxID_OK)
    {
      const string file_img_r = string(dialog2.GetPath().mb_str());

      // Append image pair:
      m_calib_images.resize(m_calib_images.size() + 1);

      mrpt::vision::TImageStereoCalibData& scd = *m_calib_images.rbegin();

      if (!scd.left.img_original.loadFromFile(file_img_l))
        THROW_EXCEPTION_FMT("Error loading image: %s", file_img_l.c_str());
      if (!scd.right.img_original.loadFromFile(file_img_r))
        THROW_EXCEPTION_FMT("Error loading image: %s", file_img_r.c_str());

      UpdateListOfImages();
    }
  }

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnbtnListSaveClick(wxCommandEvent& event)
{
  WX_START_TRY

  if (m_calib_images.empty()) throw std::runtime_error("There are no images to save!");

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  startPath = startPath + _("/calib_images");

  wxDirDialog dlg(
      this,
      _("Choose destination DIRECTORY (will be created if it does not "
        "exist)"),
      startPath, wxDD_DEFAULT_STYLE);

  if (dlg.ShowModal() == wxID_OK)
  {
    const string trg_dir = string(dlg.GetPath().mb_str());
    mrpt::system::createDirectory(trg_dir);

    const size_t N = m_calib_images.size();

    const string sListFile = mrpt::format("%s/list_calibration_images.txt", trg_dir.c_str());
    if (mrpt::system::fileExists(sListFile))
      throw std::runtime_error(mrpt::format(
          "Error: target listing file already exists (won't "
          "overwrite as precaution): %s",
          sListFile.c_str()));

    {
      wxBusyCursor busy;
      wxTheApp->Yield();

      ofstream fo(sListFile.c_str());

      for (unsigned int i = 0; i < N; i++)
      {
        const mrpt::vision::TImageStereoCalibData& scd = m_calib_images[i];

        const string sLr = mrpt::format("left_%04u.png", i);
        const string sRr = mrpt::format("right_%04u.png", i);

        const string sL = mrpt::format("%s/%s", trg_dir.c_str(), sLr.c_str());
        const string sR = mrpt::format("%s/%s", trg_dir.c_str(), sRr.c_str());

        if (mrpt::system::fileExists(sL))
          throw std::runtime_error(mrpt::format(
              "Error: target image filename already exists "
              "(won't overwrite as precaution): %s",
              sL.c_str()));
        if (mrpt::system::fileExists(sR))
          throw std::runtime_error(mrpt::format(
              "Error: target image filename already exists "
              "(won't overwrite as precaution): %s",
              sR.c_str()));

        if (!scd.left.img_original.saveToFile(sL))
          throw std::runtime_error(mrpt::format("Error: can't write image file: %s", sL.c_str()));
        if (!scd.right.img_original.saveToFile(sR))
          throw std::runtime_error(mrpt::format("Error: can't write image file: %s", sR.c_str()));

        fo << sLr << endl << sRr << endl;
      }
    }

    wxMessageBox(
        mrpt::format("Images saved! An index file has been also generated:\n%s", sListFile.c_str()),
        _("Done!"));
  }

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnbtnListRemoveSelectedClick(wxCommandEvent& event)
{
  WX_START_TRY
  const int sel = lbImagePairs->GetSelection();

  if (sel == wxNOT_FOUND || sel >= (int)m_calib_images.size()) return;

  m_calib_images.erase(m_calib_images.begin() + sel);
  UpdateListOfImages();
  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnbtnLoadImageListClick(wxCommandEvent& event)
{
  WX_START_TRY

  wxMessageBox(
      _("Image list files contain one file name per line, in "
        "order:\nLEFT1\nRIGHT1\nLEFT2\nRIGHT2\n..."),
      _("Information"));

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  wxFileDialog dialog(
      this, _("Select text file with list of image files"), startPath, _(""),
      _("Text files (*.txt)|*.txt|All files (*.*)|*.*"), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (dialog.ShowModal() == wxID_OK)
  {
    const string file_list = string(dialog.GetPath().mb_str());

    const string fil_dir = mrpt::system::extractFileDirectory(file_list);
    startPath = fil_dir.c_str();
    m_config.Write(_("last_path"), startPath);

    std::vector<std::string> lst;
    mrpt::io::loadTextFile(lst, file_list);

    if (lst.size() == 0) throw std::runtime_error("Error: List file seems to be empty.");
    if ((lst.size() & 1) != 0)
      throw std::runtime_error("Error: Number of lines in file must be even.");

    const unsigned int N = lst.size() / 2;

    m_calib_images.clear();
    m_calib_images.resize(N);

    wxBusyCursor busy;
    wxTheApp->Yield();

    for (unsigned int i = 0; i < N; i++)
    {
      const string& sL = lst[2 * i + 0];
      const string& sR = lst[2 * i + 1];

      mrpt::vision::TImageStereoCalibData& scd = m_calib_images[i];

      if (!scd.left.img_original.loadFromFile(sL) &&
          !scd.left.img_original.loadFromFile(fil_dir + string("/") + sL))
        throw std::runtime_error(
            mrpt::format("Error: loading left image '%s' of %u'th pair", sL.c_str(), i).c_str());

      if (!scd.right.img_original.loadFromFile(sR) &&
          !scd.right.img_original.loadFromFile(fil_dir + string("/") + sR))
        throw std::runtime_error(
            mrpt::format("Error: loading right image '%s' of %u'th pair", sR.c_str(), i).c_str());
    }
    UpdateListOfImages();
  }

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnrbShowImagesSelect(wxCommandEvent& event)
{
  ProcessNewSelectedImageListBox();
}

void kinect_calibrate_guiDialog::OncbCalibNormalizeClick(wxCommandEvent& event)
{
  ProcessNewSelectedImageListBox();
}

void kinect_calibrate_guiDialog::OnResize(wxSizeEvent& event)
{
  if (Notebook1->GetSelection() == 4) ProcessNewSelectedImageListBox();

  event.Skip();
}

void kinect_calibrate_guiDialog::OnbtnConnectLive3DClick(wxCommandEvent& event)
{
  btnConnectLive3D->Enable(false);

  if (m_cap_thread.joinable())
  {
    // Shoulnd't reach here...just in case:
    return;
  }

  m_cap_thread_data.quit = false;
  m_cap_thread_data.flag_grab_depth = true;
  m_cap_thread_data.select_IR_channel = false;

  // Launch thread:
  m_cap_thread = std::thread(&kinect_calibrate_guiDialog::thread_grabbing, this);

  btnDisconnectLive->Enable(true);
}

void kinect_calibrate_guiDialog::OnbtnDisconnectLiveClick(wxCommandEvent& event)
{
  this->StopLiveGrabThreads();

  btnConnectLive3D->Enable(true);
  btnDisconnectLive->Enable(false);
}

void kinect_calibrate_guiDialog::OnbtnApplyCalibLiveClick(wxCommandEvent& event) {}

// Save current live-panel calib to disk:
void kinect_calibrate_guiDialog::OnbtnSaveCalibLiveClick(wxCommandEvent& event)
{
  WX_START_TRY

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  wxFileDialog dialog(
      this, _("Save calibration file"), startPath, _("calib.ini"),
      _("Configuration files (*.ini,*.cfg)|*.ini;*.cfg|All files (*.*)|*.*"),
      wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  if (dialog.ShowModal() == wxID_OK)
  {
    const string outFil = string(dialog.GetPath().mb_str());

    std::ofstream f(outFil.c_str());

    if (!f.is_open()) throw std::runtime_error("Error opening file for writing!");

    time_t systime;
    time(&systime);
    struct tm* timeinfo = localtime(&systime);

    f << mrpt::format(
        "# Stereo camera (manual) calibration report\n"
        "# Generated by kinect-stereo-calib - MRPT at %s"
        "# (This file is loadable from rawlog-edit and other MRPT tools)\n"
        "# "
        "------------------------------------------------------------------"
        "---\n\n",
        asctime(timeinfo));

    const TCamera& lc = m_calib_result.cam_params.leftCamera;
    const TCamera& rc = m_calib_result.cam_params.rightCamera;

    f << mrpt::format(
        "# Left camera (IR/Depth in Kinect) calibration parameters:\n"
        "[CAMERA_PARAMS_LEFT]\n"
        "resolution = [%u %u]\n"
        "cx         = %f\n"
        "cy         = %f\n"
        "fx         = %f\n"
        "fy         = %f\n"
        "dist       = [%e %e %e %e %e]    // The order is: [K1 K2 T1 T2 "
        "K3]\n\n",
        lc.ncols, lc.nrows, lc.cx(), lc.cy(), lc.fx(), lc.fy(), lc.k1(), lc.k2(), lc.p1(), lc.p2(),
        lc.k3());

    f << mrpt::format(
        "# Right camera (RGB in Kinect) calibration parameters:\n"
        "[CAMERA_PARAMS_RIGHT]\n"
        "resolution = [%u %u]\n"
        "cx         = %f\n"
        "cy         = %f\n"
        "fx         = %f\n"
        "fy         = %f\n"
        "dist       = [%e %e %e %e %e]    // The order is: [K1 K2 T1 T2 "
        "K3]\n\n",
        rc.ncols, rc.nrows, rc.cx(), rc.cy(), rc.fx(), rc.fy(), rc.k1(), rc.k2(), rc.p1(), rc.p2(),
        rc.k3());

    // pose:
    const mrpt::poses::CPose3D RT_YPR(-m_calib_result.right2left_camera_pose);
    const mrpt::poses::CPose3DQuat RT_quat(RT_YPR);

    f << mrpt::format(
        "# Relative pose of the right camera wrt to the left camera:\n"
        "# This assumes that both camera frames are such that +Z points\n"
        "# forwards, and +X and +Y to the right and downwards.\n"
        "[CAMERA_PARAMS_LEFT2RIGHT_POSE]\n"
        "translation_only     = [%e %e %e]\n"
        "rotation_matrix_only = %s\n"
        "pose_yaw_pitch_roll  = %s  // (YPR in degrees)\n"
        "pose_quaternion      = %s\n\n",
        RT_YPR.x(), RT_YPR.y(), RT_YPR.z(), RT_YPR.getRotationMatrix().inMatlabFormat(13).c_str(),
        RT_YPR.asString().c_str(), RT_quat.asString().c_str());
  }

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnbtnLoadCalibClick(wxCommandEvent& event)
{
  WX_START_TRY

  wxString startPath;
  m_config.Read(_("last_path"), &startPath);

  wxFileDialog dialog(
      this, _("Load calibration file"), startPath, _("calib.ini"),
      _("Configuration files (*.ini,*.cfg)|*.ini;*.cfg|All files (*.*)|*.*"),
      wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (dialog.ShowModal() == wxID_OK)
  {
    const string inFil = string(dialog.GetPath().mb_str());

    mrpt::config::CConfigFile cfg(inFil);

    m_calib_result.cam_params.leftCamera.loadFromConfigFile("CAMERA_PARAMS_LEFT", cfg);
    m_calib_result.cam_params.rightCamera.loadFromConfigFile("CAMERA_PARAMS_RIGHT", cfg);

    const string sYPR =
        cfg.read_string("CAMERA_PARAMS_LEFT2RIGHT_POSE", "pose_yaw_pitch_roll", "", true);

    // Load L->R pose
    mrpt::poses::CPose3D p;
    p.fromString(sYPR);

    // Convert to R->L pose:
    m_calib_result.right2left_camera_pose = -p;

    // Update grid:
    LiveCalibUpdateToGrid();
  }

  WX_END_TRY
}

// Aux. function
void kinect_calibrate_guiDialog::fillGridLine(
    int r, const char* label_prefix, const char* label, const std::string& val)
{
  m_grid_live_calib->SetRowLabelValue(
      r, (std::string(label_prefix) + std::string(".") + std::string(label)).c_str());
  m_grid_live_calib->SetCellValue(r, 0, val.c_str());
}

void kinect_calibrate_guiDialog::LiveCalibGridInitialize()
{
  // Fill all the fields of the calibration:
  m_grid_live_calib->BeginBatch();

  // Clear:
  m_grid_live_calib->DeleteCols(0, m_grid_live_calib->GetNumberCols(), false);
  m_grid_live_calib->DeleteRows(0, m_grid_live_calib->GetNumberRows(), false);

  // Build cells & labels:
  m_grid_live_calib->AppendCols(1, false);
  m_grid_live_calib->AppendRows(24, false);
  m_grid_live_calib->SetColLabelSize(0);  // Hide

  m_grid_live_calib->EndBatch();
}

// m_calib_result -> m_grid_live_calib
void kinect_calibrate_guiDialog::LiveCalibUpdateToGrid()
{
  // Fill all the fields of the calibration:
  m_grid_live_calib->BeginBatch();

  // Fill-in data:
  int r = 0;
  for (int lr = 0; lr < 2; lr++)
  {
    const char* sN = (lr == 0 ? "Left" : "Right");
    const mrpt::img::TCamera& c =
        (lr == 0 ? m_calib_result.cam_params.leftCamera : m_calib_result.cam_params.rightCamera);

    fillGridLine(r++, sN, "cx", format("%.4f", c.cx()));
    fillGridLine(r++, sN, "cy", format("%.4f", c.cy()));
    fillGridLine(r++, sN, "fx", format("%.4f", c.fx()));
    fillGridLine(r++, sN, "fy", format("%.4f", c.fy()));
    fillGridLine(r++, sN, "k1", format("%.4e", c.k1()));
    fillGridLine(r++, sN, "k2", format("%.4e", c.k2()));
    fillGridLine(r++, sN, "k3", format("%.4e", c.k3()));
    fillGridLine(r++, sN, "t1", format("%.4e", c.p1()));
    fillGridLine(r++, sN, "t2", format("%.4e", c.p2()));
  }

  // Show the L->R pose (more intuitive to edit by hand):
  const mrpt::poses::CPose3D l2r = -m_calib_result.right2left_camera_pose;
  fillGridLine(r++, "L2R_pose", "x", format("%.4f", l2r.x()));
  fillGridLine(r++, "L2R_pose", "y", format("%.4f", l2r.y()));
  fillGridLine(r++, "L2R_pose", "z", format("%.4f", l2r.z()));
  fillGridLine(r++, "L2R_pose", "yaw", format("%.4f", l2r.yaw()));
  fillGridLine(r++, "L2R_pose", "pitch", format("%.4f", l2r.pitch()));
  fillGridLine(r++, "L2R_pose", "roll", format("%.4f", l2r.roll()));

  m_grid_live_calib->EndBatch();
}

double kinect_calibrate_guiDialog::parseGridLine(int r)
{
  double R;
  const wxString s = m_grid_live_calib->GetCellValue(r, 0);

  if (!s.ToCDouble(&R))
    throw std::runtime_error(
        mrpt::format("Error parsing double string: '%s'", (const char*)s.mb_str(wxConvUTF8))
            .c_str());
  else
    return R;
}

// m_grid_live_calib -> m_calib_result
void kinect_calibrate_guiDialog::LiveCalibUpdateFromGrid()
{
  WX_START_TRY

  // Parse data:
  int r = 0;
  for (int lr = 0; lr < 2; lr++)
  {
    mrpt::img::TCamera& c =
        (lr == 0 ? m_calib_result.cam_params.leftCamera : m_calib_result.cam_params.rightCamera);

    c.cx(parseGridLine(r++));
    c.cy(parseGridLine(r++));
    c.fx(parseGridLine(r++));
    c.fy(parseGridLine(r++));

    c.k1(parseGridLine(r++));
    c.k2(parseGridLine(r++));
    c.k3(parseGridLine(r++));
    c.p1(parseGridLine(r++));
    c.p2(parseGridLine(r++));
  }

  // Show the L->R pose in Kinect coordinates (more intuitive to edit by
  // hand):
  const mrpt::poses::CPose3D l2r(
      parseGridLine(r), parseGridLine(r + 1),
      parseGridLine(r + 2),  // x,y,z
      parseGridLine(r + 3), parseGridLine(r + 4),
      parseGridLine(r + 5)  // yaw,pitch,roll
  );

  const mrpt::poses::CPose3D r2l = (-l2r);
  m_calib_result.right2left_camera_pose = r2l;

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnPanel5SetFocus(wxFocusEvent& event) {}
void kinect_calibrate_guiDialog::Onm_grid_live_calibCellChange(wxGridEvent& event)
{
  this->LiveCalibUpdateFromGrid();
}

void kinect_calibrate_guiDialog::OnbtnHelpLiveCalibClick(wxCommandEvent& event) {}

// Recreate 3D view of reconstructed 6D camera poses during calibration:
void kinect_calibrate_guiDialog::CalibUpdate3DViewCameras()
{
  WX_START_TRY

  mrpt::opengl::Scene::Ptr scene = mrpt::opengl::Scene::Create();

  // Ground plane:
  {
    mrpt::opengl::CGridPlaneXY::Ptr obj =
        mrpt::opengl::CGridPlaneXY::Create(-10, 10, -10, 10, 0, 1);
    obj->setColor_u8(TColor(200, 200, 200));
    scene->insert(obj);
  }
  // XYZ corner:
  scene->insert(mrpt::opengl::stock_objects::CornerXYZSimple(0.5, 2));

  const unsigned int check_size_x = edCalibCheckX->GetValue();
  const unsigned int check_size_y = edCalibCheckY->GetValue();

  double check_squares_length_X_meters, check_squares_length_Y_meters;
  edCalibSizeX->GetValue().ToCDouble(&check_squares_length_X_meters);
  edCalibSizeY->GetValue().ToCDouble(&check_squares_length_Y_meters);

  check_squares_length_X_meters *= 1e-3;
  check_squares_length_Y_meters *= 1e-3;

  if (!check_squares_length_X_meters || !check_squares_length_Y_meters) return;

  opengl::CSetOfObjects::Ptr gl_objs = std::make_shared<opengl::CSetOfObjects>();

  opengl::CGridPlaneXY::Ptr grid = std::make_shared<opengl::CGridPlaneXY>(
      0, check_size_x * check_squares_length_X_meters, 0,
      check_size_y * check_squares_length_Y_meters, 0, check_squares_length_X_meters);
  gl_objs->insert(grid);

  const size_t N = m_calib_result.left_cam_poses.size();
  ASSERT_EQUAL_(m_calib_result.image_pair_was_used.size(), N);

  for (size_t i = 0; i < N; i++)
  {
    if (m_calib_result.image_pair_was_used[i])
    {
      {
        mrpt::opengl::CSetOfObjects::Ptr cor =
            mrpt::opengl::stock_objects::CornerXYZSimple(0.05f, 2);
        cor->setName(mrpt::format("#%u", static_cast<unsigned int>(i)));
        cor->enableShowName(true);

        // Inversed poses are estimated.
        const auto p = -mrpt::poses::CPose3D(m_calib_result.left_cam_poses[i]);
        cor->setPose(p);

        gl_objs->insert(cor);
      }
      {
        mrpt::opengl::CSetOfObjects::Ptr cor =
            mrpt::opengl::stock_objects::CornerXYZSimple(0.05f, 2);
        // Inversed poses are estimated.
        mrpt::poses::CPose3D p =
            -(m_calib_result.right2left_camera_pose +
              mrpt::poses::CPose3D(m_calib_result.left_cam_poses[i]));
        cor->setPose(p);
        gl_objs->insert(cor);
      }
    }
  }

  gl_objs->setPose(mrpt::poses::CPose3D(0, 0, 0, 0.0_deg, 180.0_deg, 0.0_deg));
  scene->insert(gl_objs);

  m_plot3D_cameras->setOpenGLSceneRef(scene);

  WX_END_TRY
}

void kinect_calibrate_guiDialog::OnNeedsToUpdate6DCamPlot(wxSpinEvent& event)
{
  CalibUpdate3DViewCameras();
  m_plot3D_cameras->Refresh();
}

void kinect_calibrate_guiDialog::OnedCalibSizeXText(wxCommandEvent& event)
{
  CalibUpdate3DViewCameras();
  m_plot3D_cameras->Refresh();
}
