|
| 1 | +#include "camera_intrinsic_calib_widget.h" |
| 2 | + |
| 3 | +#include <QGroupBox> |
| 4 | +#include <QPushButton> |
| 5 | +#include <QSpinBox> |
| 6 | +#include <QVBoxLayout> |
| 7 | + |
| 8 | +CameraIntrinsicCalibrationWidget::CameraIntrinsicCalibrationWidget(CameraParameters& camera_params) |
| 9 | + : camera_params{camera_params} { |
| 10 | + auto calibration_steps_layout = new QVBoxLayout; |
| 11 | + |
| 12 | + // pattern configuration |
| 13 | + { |
| 14 | + auto pattern_config_layout = new QVBoxLayout; |
| 15 | + |
| 16 | + // pattern select dropdown |
| 17 | + { |
| 18 | + auto pattern_selector_label = new QLabel(tr("Pattern type:")); |
| 19 | + |
| 20 | + pattern_selector = new QComboBox(); |
| 21 | + pattern_selector->addItems({"Checkerboard", "Circles", "Asymmetric Circles"}); |
| 22 | + |
| 23 | + auto hbox = new QHBoxLayout; |
| 24 | + hbox->addWidget(pattern_selector_label); |
| 25 | + hbox->addWidget(pattern_selector); |
| 26 | + |
| 27 | + pattern_config_layout->addLayout(hbox); |
| 28 | + } |
| 29 | + |
| 30 | + // pattern size config |
| 31 | + { |
| 32 | + auto grid_dimensions_label = new QLabel(tr("Grid Dimensions (width x height):")); |
| 33 | + |
| 34 | + grid_width = new QSpinBox(); |
| 35 | + grid_width->setMinimum(2); |
| 36 | + grid_width->setValue(camera_params.additional_calibration_information->grid_width->getInt()); |
| 37 | + connect(grid_width, SIGNAL(valueChanged(int)), this, SLOT(grid_width_changed(int))); |
| 38 | + connect(camera_params.additional_calibration_information->grid_width, SIGNAL(hasChanged(VarType*)), this, |
| 39 | + SLOT(grid_width_vartype_changed(VarType*))); |
| 40 | + |
| 41 | + auto grid_dim_separator_label = new QLabel(tr("x")); |
| 42 | + |
| 43 | + grid_height = new QSpinBox(); |
| 44 | + grid_height->setMinimum(2); |
| 45 | + grid_height->setValue(camera_params.additional_calibration_information->grid_height->getInt()); |
| 46 | + connect(grid_height, SIGNAL(valueChanged(int)), this, SLOT(grid_height_changed(int))); |
| 47 | + connect(camera_params.additional_calibration_information->grid_height, SIGNAL(hasChanged(VarType*)), this, |
| 48 | + SLOT(grid_height_vartype_changed(VarType*))); |
| 49 | + |
| 50 | + auto hbox = new QHBoxLayout; |
| 51 | + hbox->addWidget(grid_dimensions_label); |
| 52 | + hbox->addStretch(); |
| 53 | + hbox->addWidget(grid_width); |
| 54 | + hbox->addWidget(grid_dim_separator_label); |
| 55 | + hbox->addWidget(grid_height); |
| 56 | + |
| 57 | + pattern_config_layout->addLayout(hbox); |
| 58 | + } |
| 59 | + |
| 60 | + auto pattern_config_groupbox = new QGroupBox(tr("Pattern Configuration")); |
| 61 | + pattern_config_groupbox->setLayout(pattern_config_layout); |
| 62 | + |
| 63 | + calibration_steps_layout->addWidget(pattern_config_groupbox); |
| 64 | + } |
| 65 | + |
| 66 | + // calibration instructions |
| 67 | + { |
| 68 | + auto calibration_instructions_layout = new QVBoxLayout; |
| 69 | + |
| 70 | + calibration_instructions_layout->addWidget( |
| 71 | + new QLabel(tr("Enable pattern detection here and enable display in the " |
| 72 | + "VisualizationPlugin.\n" |
| 73 | + "Verify that your pattern is detected properly.\nIf not " |
| 74 | + "detected double check the grid size and pattern type " |
| 75 | + "and verify that greyscale image has good contrast."))); |
| 76 | + |
| 77 | + // detect pattern checkbox |
| 78 | + { |
| 79 | + auto label = new QLabel(tr("Detect Pattern")); |
| 80 | + |
| 81 | + detect_pattern_checkbox = new QCheckBox(); |
| 82 | + |
| 83 | + auto hbox = new QHBoxLayout; |
| 84 | + hbox->addWidget(label); |
| 85 | + hbox->addWidget(detect_pattern_checkbox); |
| 86 | + |
| 87 | + calibration_instructions_layout->addLayout(hbox); |
| 88 | + } |
| 89 | + |
| 90 | + // do corner subpixel correction checkbox |
| 91 | + { |
| 92 | + auto label = new QLabel(tr("Do Corner Subpixel Correction:")); |
| 93 | + |
| 94 | + corner_subpixel_correction_checkbox = new QCheckBox(); |
| 95 | + corner_subpixel_correction_checkbox->setChecked(true); |
| 96 | + |
| 97 | + auto hbox = new QHBoxLayout; |
| 98 | + hbox->addWidget(label); |
| 99 | + hbox->addWidget(corner_subpixel_correction_checkbox); |
| 100 | + |
| 101 | + calibration_instructions_layout->addLayout(hbox); |
| 102 | + |
| 103 | + calibration_instructions_layout->addWidget( |
| 104 | + new QLabel(tr("When you can see a chessboard in the image, you can " |
| 105 | + "start capturing data.\n" |
| 106 | + "After each new sample, " |
| 107 | + "a calibration will be done and the " |
| 108 | + "calibration error will be shown as RMS.\n" |
| 109 | + "Make sure to capture enough images from different " |
| 110 | + "poses (like ~30)."))); |
| 111 | + |
| 112 | + capture_button = new QPushButton(tr("Capture")); |
| 113 | + capture_button->setCheckable(true); |
| 114 | + connect(capture_button, SIGNAL(clicked()), this, SLOT(updateConfigurationEnabled())); |
| 115 | + calibration_instructions_layout->addWidget(capture_button); |
| 116 | + |
| 117 | + calibrate_button = new QPushButton(tr("Calibrate")); |
| 118 | + connect(calibrate_button, SIGNAL(clicked()), this, SLOT(calibrateClicked())); |
| 119 | + calibration_instructions_layout->addWidget(calibrate_button); |
| 120 | + |
| 121 | + calibration_instructions_layout->addWidget( |
| 122 | + new QLabel(tr("Images where a chessboard was detected " |
| 123 | + "are saved in 'test-data/intrinsic_calibration'.\n" |
| 124 | + "You can load all images again to redo or tune " |
| 125 | + "the calibration."))); |
| 126 | + |
| 127 | + load_images_button = new QPushButton(tr("Load saved images")); |
| 128 | + connect(load_images_button, SIGNAL(clicked()), this, SLOT(loadImagesClicked())); |
| 129 | + calibration_instructions_layout->addWidget(load_images_button); |
| 130 | + |
| 131 | + reset_model_button = new QPushButton(tr("Reset model")); |
| 132 | + connect(reset_model_button, SIGNAL(clicked()), this, SLOT(resetModelClicked())); |
| 133 | + calibration_instructions_layout->addWidget(reset_model_button); |
| 134 | + } |
| 135 | + |
| 136 | + // images loaded |
| 137 | + { |
| 138 | + auto hbox = new QHBoxLayout; |
| 139 | + hbox->addWidget(new QLabel(tr("Images loaded: "))); |
| 140 | + |
| 141 | + images_loaded_label = new QLabel(tr("0 / 0")); |
| 142 | + hbox->addWidget(images_loaded_label); |
| 143 | + |
| 144 | + calibration_instructions_layout->addLayout(hbox); |
| 145 | + } |
| 146 | + |
| 147 | + auto calibration_instructions_groupbox = new QGroupBox(tr("Calibration Instructions")); |
| 148 | + calibration_instructions_groupbox->setLayout(calibration_instructions_layout); |
| 149 | + |
| 150 | + calibration_steps_layout->addWidget(calibration_instructions_groupbox); |
| 151 | + } |
| 152 | + |
| 153 | + // capture control buttons |
| 154 | + { |
| 155 | + auto capture_control_layout = new QVBoxLayout; |
| 156 | + auto capture_control_groupbox = new QGroupBox(tr("Calibration Data")); |
| 157 | + capture_control_groupbox->setLayout(capture_control_layout); |
| 158 | + |
| 159 | + // captured data info |
| 160 | + { |
| 161 | + auto hbox = new QHBoxLayout; |
| 162 | + hbox->addWidget(new QLabel(tr("Number of data points: "))); |
| 163 | + |
| 164 | + num_data_points_label = new QLabel(tr("0")); |
| 165 | + hbox->addWidget(num_data_points_label); |
| 166 | + |
| 167 | + capture_control_layout->addLayout(hbox); |
| 168 | + } |
| 169 | + |
| 170 | + // calibration RMS error |
| 171 | + { |
| 172 | + auto hbox = new QHBoxLayout; |
| 173 | + hbox->addWidget(new QLabel(tr("Calibration RMS: "))); |
| 174 | + |
| 175 | + rms_label = new QLabel(tr("-")); |
| 176 | + hbox->addWidget(rms_label); |
| 177 | + |
| 178 | + capture_control_layout->addLayout(hbox); |
| 179 | + } |
| 180 | + |
| 181 | + capture_control_layout->addWidget( |
| 182 | + new QLabel(tr("The calibration result can be found under \n" |
| 183 | + "Camera Calibrator -> Camera Parameters -> Intrinsic Parameters"))); |
| 184 | + |
| 185 | + capture_control_layout->addSpacing(50); |
| 186 | + |
| 187 | + // control buttons |
| 188 | + { |
| 189 | + clear_data_button = new QPushButton(tr("Clear Data")); |
| 190 | + connect(clear_data_button, SIGNAL(clicked()), this, SLOT(clearDataClicked())); |
| 191 | + |
| 192 | + auto hbox = new QHBoxLayout; |
| 193 | + hbox->addWidget(clear_data_button); |
| 194 | + |
| 195 | + capture_control_layout->addLayout(hbox); |
| 196 | + } |
| 197 | + |
| 198 | + calibration_steps_layout->addWidget(capture_control_groupbox); |
| 199 | + } |
| 200 | + |
| 201 | + // push widgets to top |
| 202 | + calibration_steps_layout->addStretch(); |
| 203 | + |
| 204 | + this->setLayout(calibration_steps_layout); |
| 205 | +} |
| 206 | + |
| 207 | +void CameraIntrinsicCalibrationWidget::setNumDataPoints(int n) { num_data_points_label->setText(QString("%1").arg(n)); } |
| 208 | + |
| 209 | +void CameraIntrinsicCalibrationWidget::clearDataClicked() { |
| 210 | + setImagesLoaded(0, 0); |
| 211 | + should_clear_data = true; |
| 212 | +} |
| 213 | + |
| 214 | +void CameraIntrinsicCalibrationWidget::calibrateClicked() { |
| 215 | + should_calibrate = true; |
| 216 | + updateConfigurationEnabled(); |
| 217 | +} |
| 218 | + |
| 219 | +void CameraIntrinsicCalibrationWidget::updateConfigurationEnabled() { |
| 220 | + pattern_selector->setEnabled(isConfigurationEnabled()); |
| 221 | + grid_width->setEnabled(isConfigurationEnabled()); |
| 222 | + grid_height->setEnabled(isConfigurationEnabled()); |
| 223 | + clear_data_button->setEnabled(isConfigurationEnabled()); |
| 224 | + detect_pattern_checkbox->setEnabled(isConfigurationEnabled()); |
| 225 | + corner_subpixel_correction_checkbox->setEnabled(isConfigurationEnabled()); |
| 226 | + load_images_button->setEnabled(isConfigurationEnabled()); |
| 227 | + calibrate_button->setEnabled(isConfigurationEnabled()); |
| 228 | + reset_model_button->setEnabled(isConfigurationEnabled()); |
| 229 | +} |
| 230 | + |
| 231 | +void CameraIntrinsicCalibrationWidget::loadImagesClicked() { |
| 232 | + setImagesLoaded(0, 0); |
| 233 | + should_load_images = true; |
| 234 | + updateConfigurationEnabled(); |
| 235 | +} |
| 236 | + |
| 237 | +void CameraIntrinsicCalibrationWidget::resetModelClicked() const { camera_params.intrinsic_parameters->reset(); } |
| 238 | + |
| 239 | +void CameraIntrinsicCalibrationWidget::setRms(double rms) { rms_label->setText(QString("%1").arg(rms)); } |
| 240 | + |
| 241 | +void CameraIntrinsicCalibrationWidget::grid_height_changed(int height) const { |
| 242 | + camera_params.additional_calibration_information->grid_height->setInt(height); |
| 243 | +} |
| 244 | + |
| 245 | +void CameraIntrinsicCalibrationWidget::grid_height_vartype_changed(VarType* varType) { |
| 246 | + grid_height->setValue(((VarInt*)varType)->getInt()); |
| 247 | +} |
| 248 | + |
| 249 | +void CameraIntrinsicCalibrationWidget::grid_width_changed(int width) const { |
| 250 | + camera_params.additional_calibration_information->grid_width->setInt(width); |
| 251 | +} |
| 252 | + |
| 253 | +void CameraIntrinsicCalibrationWidget::grid_width_vartype_changed(VarType* varType) { |
| 254 | + grid_width->setValue(((VarInt*)varType)->getInt()); |
| 255 | +} |
| 256 | + |
| 257 | +void CameraIntrinsicCalibrationWidget::setImagesLoaded(int n, int total) { |
| 258 | + images_loaded_label->setText(QString("%1 / %2").arg(n).arg(total)); |
| 259 | +} |
| 260 | + |
| 261 | +void CameraIntrinsicCalibrationWidget::imagesLoaded() { updateConfigurationEnabled(); } |
0 commit comments