Skip to content

Commit 17648a3

Browse files
g3forcerhololkeolke
andcommitted
Add camera intrinsic calibration plugin
Co-authored-by: Devin Schwab <[email protected]>
1 parent 0e3931a commit 17648a3

22 files changed

+1712
-132
lines changed

CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ find_package(Qt5Core REQUIRED)
3131
find_package(Qt5Widgets REQUIRED)
3232
find_package(Qt5OpenGL REQUIRED)
3333
# Note: Bluefox SDK is not compatible with some components of OpenCV. Take care when enabling more.
34-
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs videoio)
34+
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs videoio calib3d)
3535

3636
include(src/shared/CMakeLists.txt.inc)
3737

@@ -142,6 +142,7 @@ set (SRCS ${SRCS}
142142

143143
src/app/gui/maskwidget.cpp
144144
src/app/gui/automatedcolorcalibwidget.cpp
145+
src/app/gui/camera_intrinsic_calib_widget.cpp
145146
src/app/gui/cameracalibwidget.cpp
146147
src/app/gui/colorpicker.cpp
147148
src/app/gui/glLUTwidget.cpp
@@ -155,6 +156,7 @@ set (SRCS ${SRCS}
155156

156157
src/app/plugins/plugin_mask.cpp
157158
src/app/plugins/plugin_cameracalib.cpp
159+
src/app/plugins/plugin_camera_intrinsic_calib.cpp
158160
src/app/plugins/plugin_colorcalib.cpp
159161
src/app/plugins/plugin_colorthreshold.cpp
160162
src/app/plugins/plugin_detect_balls.cpp
@@ -183,6 +185,7 @@ qt5_wrap_cpp (MOC_SRCS
183185

184186
src/app/gui/maskwidget.h
185187
src/app/gui/automatedcolorcalibwidget.h
188+
src/app/gui/camera_intrinsic_calib_widget.h
186189
src/app/gui/cameracalibwidget.h
187190
src/app/gui/glLUTwidget.h
188191
src/app/gui/glwidget.h
@@ -201,9 +204,12 @@ qt5_wrap_cpp (MOC_SRCS
201204
src/app/plugins/plugin_colorcalib.h
202205
src/app/plugins/plugin_colorthreshold.h
203206
src/app/plugins/plugin_auto_color_calibration.h
207+
src/app/plugins/plugin_camera_intrinsic_calib.h
204208

205209
src/app/stacks/multistack_robocup_ssl.h
206210

211+
src/shared/util/camera_parameters.h
212+
207213
${OPTIONAL_HEADERS}
208214
)
209215

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
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(); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#ifndef CAMERA_INTRINSIC_CALIB_WIDGET_H
2+
#define CAMERA_INTRINSIC_CALIB_WIDGET_H
3+
4+
#include <camera_calibration.h>
5+
6+
#include <QCheckBox>
7+
#include <QComboBox>
8+
#include <QLabel>
9+
#include <QPushButton>
10+
#include <QSpinBox>
11+
#include <QWidget>
12+
13+
class CameraIntrinsicCalibrationWidget : public QWidget {
14+
Q_OBJECT
15+
public:
16+
enum class Pattern : int { CHECKERBOARD = 0, CIRCLES, ASYMMETRIC_CIRCLES };
17+
18+
public:
19+
explicit CameraIntrinsicCalibrationWidget(CameraParameters &camera_params);
20+
~CameraIntrinsicCalibrationWidget() override = default;
21+
22+
CameraParameters &camera_params;
23+
24+
protected:
25+
QComboBox *pattern_selector;
26+
QSpinBox *grid_width;
27+
QSpinBox *grid_height;
28+
QCheckBox *detect_pattern_checkbox;
29+
QCheckBox *corner_subpixel_correction_checkbox;
30+
QLabel *num_data_points_label;
31+
QLabel *rms_label;
32+
QLabel *images_loaded_label;
33+
QPushButton *clear_data_button;
34+
QPushButton *capture_button;
35+
QPushButton *calibrate_button;
36+
QPushButton *load_images_button;
37+
QPushButton *reset_model_button;
38+
39+
public:
40+
bool patternDetectionEnabled() const { return detect_pattern_checkbox->isChecked(); }
41+
bool cornerSubPixCorrectionEnabled() const { return corner_subpixel_correction_checkbox->isChecked(); }
42+
bool isCapturing() const { return capture_button->isChecked(); }
43+
bool isLoadingFiles() const { return should_load_images; }
44+
bool isConfigurationEnabled() const { return !isCapturing() && !isLoadingFiles() && !calibrating; }
45+
void setNumDataPoints(int n);
46+
Pattern getPattern() const { return static_cast<Pattern>(pattern_selector->currentIndex()); }
47+
void setImagesLoaded(int n, int total);
48+
void imagesLoaded();
49+
50+
public slots:
51+
void clearDataClicked();
52+
void calibrateClicked();
53+
void updateConfigurationEnabled();
54+
void loadImagesClicked();
55+
void grid_height_changed(int) const;
56+
void grid_height_vartype_changed(VarType* varType);
57+
void grid_width_changed(int) const;
58+
void grid_width_vartype_changed(VarType* varType);
59+
void resetModelClicked() const;
60+
61+
public:
62+
void setRms(double rms);
63+
64+
public:
65+
bool should_clear_data = false;
66+
bool should_load_images = false;
67+
bool should_calibrate = false;
68+
bool calibrating = false;
69+
};
70+
71+
#endif /* CAMERA_INTRINSIC_CALIB_WIDGET_H */

0 commit comments

Comments
 (0)