29 #include "ui_calibrationDialog.h"
31 #include <opencv2/core/core.hpp>
32 #include <opencv2/imgproc/imgproc.hpp>
33 #include <opencv2/imgproc/imgproc_c.h>
34 #include <opencv2/calib3d/calib3d.hpp>
35 #if CV_MAJOR_VERSION >= 3
36 #include <opencv2/calib3d/calib3d_c.h>
38 #include <opencv2/highgui/highgui.hpp>
39 #if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
43 #include <QFileDialog>
44 #include <QMessageBox>
45 #include <QCloseEvent>
47 #include <QTextStream>
50 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
62 #define kArucoDictNameSize 21
63 static const char * kArucoDictNames[kArucoDictNameSize] = {
97 rightSuffix_(
"right"),
98 savingDirectory_(savingDirectory),
99 processingData_(
false),
100 savedCalibration_(
false),
118 qRegisterMetaType<cv::Mat>(
"cv::Mat");
120 ui_ =
new Ui_calibrationDialog();
123 connect(
ui_->toolButton_generateBoard, SIGNAL(clicked()),
this, SLOT(
generateBoard()));
124 connect(
ui_->pushButton_calibrate, SIGNAL(clicked()),
this, SLOT(
calibrate()));
125 connect(
ui_->pushButton_restart, SIGNAL(clicked()),
this, SLOT(
restart()));
126 connect(
ui_->pushButton_save, SIGNAL(clicked()),
this, SLOT(
save()));
127 connect(
ui_->checkBox_switchImages, SIGNAL(stateChanged(
int)),
this, SLOT(
restart()));
128 connect(
ui_->checkBox_unlock, SIGNAL(stateChanged(
int)), SLOT(
unlock()));
130 connect(
ui_->comboBox_board_type, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
setBoardType(
int)));
131 connect(
ui_->comboBox_marker_dictionary, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
setMarkerDictionary(
int)));
132 connect(
ui_->spinBox_boardWidth, SIGNAL(valueChanged(
int)),
this, SLOT(
setBoardWidth(
int)));
133 connect(
ui_->spinBox_boardHeight, SIGNAL(valueChanged(
int)),
this, SLOT(
setBoardHeight(
int)));
134 connect(
ui_->doubleSpinBox_squareSize, SIGNAL(valueChanged(
double)),
this, SLOT(
setSquareSize(
double)));
135 connect(
ui_->doubleSpinBox_markerLength, SIGNAL(valueChanged(
double)),
this, SLOT(
setMarkerLength(
double)));
136 connect(
ui_->doubleSpinBox_subpixel_error, SIGNAL(valueChanged(
double)),
this, SLOT(
setSubpixelMaxError(
double)));
140 connect(
ui_->spinBox_maxScale, SIGNAL(valueChanged(
int)),
this, SLOT(
setMaxScale(
int)));
142 connect(
ui_->buttonBox, SIGNAL(rejected()),
this, SLOT(close()));
144 ui_->image_view->setFocus();
147 ui_->progressBar_count->setFormat(
"%v");
149 ui_->progressBar_count_2->setFormat(
"%v");
151 ui_->radioButton_raw->setChecked(
true);
153 ui_->checkBox_switchImages->setChecked(switchImages);
155 ui_->comboBox_calib_model->setCurrentIndex(1);
159 timestamp_ = QDateTime::currentDateTime().toString(
"yyyyMMddhhmmss");
162 ui_->comboBox_board_type->setItemData(1, 0, Qt::UserRole - 1);
163 ui_->comboBox_board_type->setItemData(2, 0, Qt::UserRole - 1);
164 #elif CV_MAJOR_VERSION < 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION < 6)
165 ui_->comboBox_board_type->setItemData(2, 0, Qt::UserRole - 1);
166 #elif CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION < 8
167 ui_->comboBox_board_type->setItemData(1, 0, Qt::UserRole - 1);
181 settings.beginGroup(group);
183 settings.setValue(
"board_type",
ui_->comboBox_board_type->currentIndex());
184 settings.setValue(
"board_width",
ui_->spinBox_boardWidth->value());
185 settings.setValue(
"board_height",
ui_->spinBox_boardHeight->value());
186 settings.setValue(
"board_square_size",
ui_->doubleSpinBox_squareSize->value());
187 settings.setValue(
"marker_type",
ui_->comboBox_marker_dictionary->currentIndex());
188 settings.setValue(
"marker_length",
ui_->doubleSpinBox_markerLength->value());
189 settings.setValue(
"subpixel_refinement",
ui_->checkBox_subpixel_refinement->isChecked());
190 settings.setValue(
"subpixel_max_error",
ui_->doubleSpinBox_subpixel_error->value());
191 settings.setValue(
"calibration_data_saved",
ui_->checkBox_saveCalibrationData->isChecked());
192 settings.setValue(
"max_scale",
ui_->spinBox_maxScale->value());
193 settings.setValue(
"geometry", this->saveGeometry());
194 settings.setValue(
"calibration_model",
ui_->comboBox_calib_model->currentIndex());
206 settings.beginGroup(group);
208 this->
setBoardType(settings.value(
"board_type",
ui_->comboBox_board_type->currentIndex()).toInt());
209 this->
setBoardWidth(settings.value(
"board_width",
ui_->spinBox_boardWidth->value()).toInt());
210 this->
setBoardHeight(settings.value(
"board_height",
ui_->spinBox_boardHeight->value()).toInt());
211 this->
setSquareSize(settings.value(
"board_square_size",
ui_->doubleSpinBox_squareSize->value()).toDouble());
212 this->
setMarkerDictionary(settings.value(
"marker_type",
ui_->comboBox_marker_dictionary->currentIndex()).toInt());
213 this->
setMarkerLength(settings.value(
"marker_length",
ui_->doubleSpinBox_markerLength->value()).toDouble());
214 this->
setSubpixelRefinement(settings.value(
"subpixel_refinement",
ui_->checkBox_subpixel_refinement->isChecked()).toBool());
215 this->
setSubpixelMaxError(settings.value(
"subpixel_max_error",
ui_->doubleSpinBox_subpixel_error->value()).toDouble());
216 this->
setCalibrationDataSaved(settings.value(
"calibration_data_saved",
ui_->checkBox_saveCalibrationData->isChecked()).toBool());
217 this->
setMaxScale(settings.value(
"max_scale",
ui_->spinBox_maxScale->value()).toDouble());
218 int model = settings.value(
"calibration_model",
ui_->comboBox_calib_model->currentIndex()).toInt();
231 QByteArray
bytes = settings.value(
"geometry", QByteArray()).toByteArray();
234 this->restoreGeometry(
bytes);
251 cv::Mat
drawChessboard(
int squareSize,
int boardWidth,
int boardHeight,
int borderSize)
253 int imageWidth = squareSize*boardWidth + borderSize;
254 int imageHeight = squareSize*boardHeight + borderSize;
255 cv::Mat chessboard(imageWidth, imageHeight, CV_8UC1, 255);
256 unsigned char color = 0;
257 for(
int i=borderSize;
i<imageHeight-borderSize;
i=
i+squareSize) {
259 for(
int j=borderSize;
j<imageWidth-borderSize;
j=
j+squareSize) {
260 cv::Mat roi=chessboard(cv::Rect(
i,
j,squareSize,squareSize));
270 int squareSizeInPixels = 200;
275 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
278 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
279 charucoBoard_->generateImage(
280 cv::Size(squareSizeInPixels*
ui_->spinBox_boardWidth->value(),
281 squareSizeInPixels*
ui_->spinBox_boardHeight->value()),
283 squareSizeInPixels/4, 1);
286 cv::Size(squareSizeInPixels*
ui_->spinBox_boardWidth->value(),
287 squareSizeInPixels*
ui_->spinBox_boardHeight->value()),
289 squareSizeInPixels/4, 1);
292 int arucoDict =
ui_->comboBox_marker_dictionary->currentIndex();
293 stream <<
"charuco_" << (arucoDict<kArucoDictNameSize?kArucoDictNames[arucoDict]:
"NA") <<
"_"
294 <<
ui_->spinBox_boardWidth->value() <<
"x" <<
ui_->spinBox_boardHeight->value()
295 <<
"_ratio" <<
float(
ui_->doubleSpinBox_markerLength->value())/
float(
ui_->doubleSpinBox_squareSize->value());
297 catch(
const cv::Exception &
e)
300 QMessageBox::critical(
this, tr(
"Generating Board"),
301 tr(
"Cannot generate the board. Make sure the dictionary "
302 "selected is big enough for the board size. Error:\"%1\"").
arg(
e.what()));
311 ui_->spinBox_boardWidth->value(),
312 ui_->spinBox_boardHeight->value(),
313 squareSizeInPixels/4);
315 stream <<
"/chessboard_" <<
ui_->spinBox_boardWidth->value() <<
"x" <<
ui_->spinBox_boardHeight->value();
319 if(!filePath.isEmpty())
321 cv::imwrite(filePath.toStdString(), image);
332 ui_->groupBox_progress->setVisible(visible);
337 ui_->checkBox_switchImages->setChecked(switched);
342 ui_->comboBox_calib_model->setCurrentIndex(0);
347 ui_->comboBox_calib_model->setCurrentIndex(1);
352 ui_->comboBox_calib_model->setCurrentIndex(2);
361 ui_->groupBox_progress->setVisible(
true);
367 ui_->progressBar_count_2->setVisible(
stereo_);
378 ui_->label_baseline_name->setVisible(
stereo_);
384 ui_->radioButton_stereoRectified->setVisible(
stereo_);
385 ui_->checkBox_switchImages->setVisible(
stereo_);
386 ui_->doubleSpinBox_stereoBaseline->setVisible(
stereo_);
387 ui_->label_stereoBaseline->setVisible(
stereo_);
392 return ui_->spinBox_boardWidth->value();
396 return ui_->spinBox_boardHeight->value();
400 return ui_->doubleSpinBox_squareSize->value();
404 return ui_->doubleSpinBox_markerLength->value();
409 if(
type !=
ui_->comboBox_board_type->currentIndex())
411 ui_->comboBox_board_type->setCurrentIndex(
type);
418 if(dictionary !=
ui_->comboBox_marker_dictionary->currentIndex())
420 ui_->comboBox_marker_dictionary->setCurrentIndex(dictionary);
427 if(width !=
ui_->spinBox_boardWidth->value())
429 ui_->spinBox_boardWidth->setValue(width);
436 if(height !=
ui_->spinBox_boardHeight->value())
438 ui_->spinBox_boardHeight->setValue(height);
445 if(
size !=
ui_->doubleSpinBox_squareSize->value())
447 ui_->doubleSpinBox_squareSize->setValue(
size);
449 if(
ui_->doubleSpinBox_markerLength->value() >=
ui_->doubleSpinBox_squareSize->value())
451 if(
ui_->comboBox_board_type->currentIndex()==0)
453 ui_->doubleSpinBox_markerLength->setValue(
ui_->doubleSpinBox_squareSize->value()-0.000001);
457 UWARN(
"Marker length (%f) cannot be larger than square size (%f), setting square size to %f. Decrease marker length first.",
458 ui_->doubleSpinBox_markerLength->value(),
459 ui_->doubleSpinBox_squareSize->value(),
460 ui_->doubleSpinBox_squareSize->value()+0.000001);
461 ui_->doubleSpinBox_squareSize->setValue(
ui_->doubleSpinBox_markerLength->value()+0.000001);
469 if(length !=
ui_->doubleSpinBox_markerLength->value())
471 ui_->doubleSpinBox_markerLength->setValue(
length);
473 if(
ui_->doubleSpinBox_markerLength->value() >=
ui_->doubleSpinBox_squareSize->value())
475 UWARN(
"Marker length (%f) cannot be larger than square size (%f), setting marker length to %f. Increase square size first.",
476 ui_->doubleSpinBox_markerLength->value(),
477 ui_->doubleSpinBox_squareSize->value(),
478 ui_->doubleSpinBox_markerLength->value()-0.000001);
479 ui_->doubleSpinBox_markerLength->setValue(
ui_->doubleSpinBox_squareSize->value()-0.000001);
486 if(enabled !=
ui_->checkBox_subpixel_refinement->isChecked())
488 ui_->checkBox_subpixel_refinement->setChecked(enabled);
495 if(
value !=
ui_->doubleSpinBox_subpixel_error->value())
497 ui_->doubleSpinBox_subpixel_error->setValue(
value);
504 if(enabled !=
ui_->checkBox_saveCalibrationData->isChecked())
506 ui_->checkBox_saveCalibrationData->setChecked(enabled);
513 if(length !=
ui_->doubleSpinBox_stereoBaseline->value())
515 ui_->doubleSpinBox_stereoBaseline->setValue(
length);
521 if(
scale !=
ui_->spinBox_maxScale->value())
523 ui_->spinBox_maxScale->setValue(
scale);
534 QMessageBox::StandardButton
b = QMessageBox::question(
this, tr(
"Save calibration?"),
535 tr(
"The camera is calibrated but you didn't "
536 "save the calibration, do you want to save it?"),
537 QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Yes);
539 if(
b == QMessageBox::Yes)
546 else if(b == QMessageBox::Ignore)
555 if(event->isAccepted())
572 QMetaObject::invokeMethod(
this,
"processImages",
573 Q_ARG(cv::Mat,
e->data().imageRaw()),
574 Q_ARG(cv::Mat,
e->data().depthOrRightRaw()),
575 Q_ARG(QString, QString(
e->cameraName().c_str())));
583 void matchCharucoImagePoints(
584 const cv::aruco::CharucoBoard &board,
585 const std::vector< cv::Point2f > & detectedCorners,
586 const std::vector< int > & detectedIds,
587 std::vector< cv::Point3f > & objectPoints)
589 UASSERT(detectedIds.size() == detectedCorners.size());
590 objectPoints.clear();
592 #if CV_MAJOR_VERSION < 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION < 7)
593 objectPoints.reserve(detectedIds.size());
596 for(
size_t i = 0;
i < detectedIds.size();
i++) {
597 int pointId = detectedIds[
i];
598 UASSERT(pointId >= 0 && pointId < (
int)board.chessboardCorners.size());
599 objectPoints.push_back(board.chessboardCorners[pointId]);
603 board.matchImagePoints(detectedCorners, detectedIds, objectPoints, imgPts);
610 cv::InputArray charucoIds = cv::noArray(),
611 cv::Scalar cornerColor = cv::Scalar(255, 0, 0)) {
613 CV_Assert(image.getMat().total() != 0 &&
614 (image.getMat().channels() == 1 || image.getMat().channels() == 3));
615 CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total()) ||
616 charucoIds.getMat().total() == 0);
618 unsigned int nCorners = (
unsigned int)charucoCorners.getMat().total();
619 for(
unsigned int i = 0;
i < nCorners;
i++) {
620 cv::Point2f corner = charucoCorners.getMat().at< cv::Point2f >(
i);
623 cv::rectangle(image, corner - cv::Point2f(3, 3), corner + cv::Point2f(3, 3), cornerColor, 1, cv::LINE_AA);
626 if(charucoIds.total() != 0) {
627 int id = charucoIds.getMat().at<
int >(
i);
630 cv::putText(image,
s.str(), corner + cv::Point2f(5, -5), cv::FONT_HERSHEY_SIMPLEX, 0.5,
638 UDEBUG(
"Processing images");
644 else if(cameraName.isEmpty())
654 std::vector<cv::Mat> inputRawImages(2);
655 if(
ui_->checkBox_switchImages->isChecked())
657 inputRawImages[0] = imageRight;
658 inputRawImages[1] = imageLeft;
662 inputRawImages[0] = imageLeft;
663 inputRawImages[1] = imageRight;
666 std::vector<cv::Mat> images(2);
667 images[0] = inputRawImages[0];
668 images[1] = inputRawImages[1];
672 bool boardFound[2] = {
false};
673 bool boardAccepted[2] = {
false};
674 bool readyToCalibrate[2] = {
false};
676 std::vector<std::vector<cv::Point2f> > pointBuf(2);
677 std::vector<std::vector<cv::Point3f> > objectBuf(2);
678 std::vector<std::vector< int > > pointIds(2);
680 bool depthDetected =
false;
684 if(!images[
id].
empty())
686 if(images[
id].
type() == CV_16UC1)
689 cv::minMaxLoc(images[
id], &
min, &
max);
700 depthDetected =
true;
703 viewGray = cv::Mat(images[
id].
rows, images[
id].
cols, CV_8UC1);
704 for(
int i=0;
i<images[
id].rows; ++
i)
706 for(
int j=0;
j<images[
id].cols; ++
j)
708 viewGray.at<
unsigned char>(
i,
j) = (
unsigned char)
std::min(
float(
std::max(images[
id].at<unsigned short>(
i,
j) -
minIrs_[
id], 0)) * factor, 255.0
f);
711 cvtColor(viewGray, images[
id], cv::COLOR_GRAY2BGR);
713 else if(images[
id].channels() == 3)
715 cvtColor(images[
id], viewGray, cv::COLOR_BGR2GRAY);
719 viewGray = images[
id];
720 cvtColor(viewGray, images[
id], cv::COLOR_GRAY2BGR);
725 UERROR(
"Image %d is empty!! Should not!",
id);
732 if(!
ui_->pushButton_save->isEnabled())
734 std::vector< int > markerIds;
735 std::vector< std::vector< cv::Point2f > > markerCorners;
736 cv::Size boardSize(
ui_->spinBox_boardWidth->value(),
ui_->spinBox_boardHeight->value());
737 if(!viewGray.empty())
739 int flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE;
741 if(!viewGray.empty())
743 int maxScale =
ui_->spinBox_maxScale->value();
750 cv::resize(viewGray, timg, cv::Size(),
scale,
scale, CV_INTER_CUBIC);
753 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
755 std::vector< std::vector< cv::Point2f > > rejected;
759 UDEBUG(
"Detecting aruco markers...");
760 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
762 arucoDetector_->detectMarkers(timg, markerCorners, markerIds, rejected);
764 cv::aruco::detectMarkers(timg, markerDictionary_, markerCorners, markerIds, arucoDetectorParams_, rejected);
767 UDEBUG(
"Refining aruco markers...");
768 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
769 arucoDetector_->refineDetectedMarkers(timg, *charucoBoard_, markerCorners, markerIds, rejected);
771 cv::aruco::refineDetectedMarkers(timg, charucoBoard_, markerCorners, markerIds, rejected);
774 UDEBUG(
"Finding charuco corners (markers=%ld)...", markerCorners.size());
775 if(markerIds.size() > 0)
777 UASSERT(markerIds.size() == markerCorners.size());
778 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
779 UASSERT(charucoDetector_.get());
780 charucoDetector_->detectBoard(timg, pointBuf[
id], pointIds[
id], markerCorners, markerIds);
782 cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, timg, charucoBoard_, pointBuf[
id], pointIds[
id], cv::noArray(), cv::noArray(), 1);
784 UDEBUG(
"Found %ld charuco corners (requires 12)", pointBuf[
id].
size());
785 if(pointBuf[
id].
size() >= 12) {
787 matchCharucoImagePoints(*charucoBoard_, pointBuf[
id], pointIds[
id], objectBuf[
id]);
788 boardFound[
id] = !objectBuf[
id].empty() && objectBuf[
id].size() == pointBuf[
id].size();
795 boardFound[
id] = cv::findChessboardCorners(timg, boardSize, pointBuf[
id], flags);
804 cv::Mat cornersMat(pointBuf[
id]);
805 cornersMat *= 1./
scale;
817 std::vector<cv::Point2f> originalPoints = pointBuf[
id];
818 std::vector<cv::Point2f> rejectedPoints;
819 std::vector<int> rejectedPointIds;
820 if(
ui_->checkBox_subpixel_refinement->isChecked())
823 float minSquareDistance = -1.0f;
824 for(
unsigned int i=0;
i<pointBuf[
id].size()-1; ++
i)
826 float d = cv::norm(pointBuf[
id][
i] - pointBuf[
id][
i+1]);
827 if(minSquareDistance == -1.0
f || minSquareDistance >
d)
829 minSquareDistance =
d;
832 float ratio =
ui_->comboBox_board_type->currentIndex() >= 1 ?6.0f:2.0f;
833 float radius = minSquareDistance==-1.0f?5.0f:(minSquareDistance/
ratio);
834 cv::cornerSubPix( viewGray, pointBuf[
id], cv::Size(radius, radius), cv::Size(-1,-1),
835 cv::TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1 ));
838 float threshold =
ui_->doubleSpinBox_subpixel_error->value();
841 std::vector<cv::Point3f> filteredObjectPts;
842 std::vector<cv::Point2f> filteredPoints;
843 std::vector<int> filteredPointIds;
844 for(
size_t i=0;
i<pointBuf[
id].size(); ++
i)
846 float d = cv::norm(pointBuf[
id][
i] - originalPoints[
i]);
849 filteredObjectPts.push_back(objectBuf[
id][
i]);
850 filteredPoints.push_back(pointBuf[
id][
i]);
851 filteredPointIds.push_back(pointIds[
id][
i]);
855 UWARN(
"Filtered point: subpix error for image=%d cam=%d pt=%d radius=%f: %f > %f",
currentId_,
id, pointIds[
id][
i], radius,
d, threshold);
856 rejectedPoints.push_back(pointBuf[
id][
i]);
857 rejectedPointIds.push_back(pointIds[
id][
i]);
860 objectBuf[
id] = filteredObjectPts;
861 pointBuf[
id] = filteredPoints;
862 pointIds[
id] = filteredPointIds;
867 images[
id] = images[
id].clone();
869 if(
ui_->comboBox_board_type->currentIndex() >= 1 ) {
870 if(markerIds.size() > 0)
871 cv::aruco::drawDetectedMarkers(images[
id], markerCorners, cv::noArray(), cv::Scalar(255,0,0));
874 if(pointBuf[
id].
size() > 0)
876 if(rejectedPoints.size() > 0)
879 if(pointBuf[
id].
size() < rejectedPoints.size())
882 UWARN(
"Ignoring whole board of image %d cam=%d because too many points were filtered.",
currentId_,
id);
883 boardFound[
id] =
false;
887 std::vector<float>
params(4,0);
889 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
892 float area =
getArea(markerCorners[markerCorners.size()/2], cv::Size(4,4)) * (boardSize.width*boardSize.height);
898 bool addSample =
true;
899 if(!
ui_->checkBox_keep_all->isChecked())
903 if(
fabs(
params[0] -
imageParams_[
id][
i].at(0)) < (
ui_->comboBox_board_type->currentIndex() >= 1 ?0.2:0.1)*
ui_->doubleSpinBox_sample_factor->value() &&
904 fabs(
params[1] -
imageParams_[
id][
i].at(1)) < (
ui_->comboBox_board_type->currentIndex() >= 1 ?0.2:0.1)*
ui_->doubleSpinBox_sample_factor->value() &&
915 boardAccepted[
id] =
true;
920 UINFO(
"[%d] Added board %d, total=%d. (x=%f, y=%f, size=%f, skew=%f)",
id,
currentId_, (
int)
imagePoints_[
id].
size(),
params[0],
params[1],
params[2],
params[3]);
923 std::vector<float> xRange(2,
imageParams_[
id][0].at(0));
924 std::vector<float> yRange(2,
imageParams_[
id][0].at(1));
925 std::vector<float> sizeRange(2,
imageParams_[
id][0].at(2));
926 std::vector<float> skewRange(2,
imageParams_[
id][0].at(3));
936 if(skewRange[0] == 0 || skewRange[0] == 1)
955 float xGood = xRange[1] - xRange[0];
956 float yGood = yRange[1] - yRange[0];
957 float sizeGood = sizeRange[1] - sizeRange[0];
958 float skewGood = skewRange[1] - skewRange[0];
962 ui_->progressBar_x->setValue(xGood*100);
963 ui_->progressBar_y->setValue(yGood*100);
964 ui_->progressBar_size->setValue(sizeGood*100);
965 ui_->progressBar_skew->setValue(skewGood*100);
974 ui_->progressBar_x_2->setValue(xGood*100);
975 ui_->progressBar_y_2->setValue(yGood*100);
976 ui_->progressBar_size_2->setValue(sizeGood*100);
977 ui_->progressBar_skew_2->setValue(skewGood*100);
989 (sizeGood > 0.4 || (
ui_->comboBox_calib_model->currentIndex()==0 && sizeGood > 0.25)) &&
992 readyToCalibrate[
id] =
true;
996 if(inputRawImages[
id].
type() == CV_16UC1)
1001 for(
size_t i = 0;
i < pointBuf[
id].size(); ++
i)
1003 const cv::Point2f &
p = pointBuf[
id][
i];
1006 roi.width =
std::min(roi.width, inputRawImages[
id].cols - roi.x);
1007 roi.height =
std::min(roi.height, inputRawImages[
id].rows - roi.y);
1011 cv::minMaxLoc(inputRawImages[
id](roi), &
min, &
max);
1031 ui_->label_baseline->setVisible(!depthDetected);
1032 ui_->label_baseline_name->setVisible(!depthDetected);
1033 ui_->label_stereoError->setVisible(!depthDetected);
1035 if(
ui_->checkBox_saveCalibrationData->isChecked() && (boardAccepted[0] || boardAccepted[1]))
1040 QString imagesWithBoardDir = rawImagesDir+
"_board_detection";
1041 if(!QDir(rawImagesDir).exists())
1043 UINFO(
"Creating dir %s", rawImagesDir.toStdString().c_str());
1044 QDir().mkpath(rawImagesDir);
1046 if(!QDir(imagesWithBoardDir).exists())
1048 UINFO(
"Creating dir %s", imagesWithBoardDir.toStdString().c_str());
1049 QDir().mkpath(imagesWithBoardDir);
1051 cv::imwrite((rawImagesDir+
"/"+QString::number(
currentId_)+
".png").toStdString(), inputRawImages[
id]);
1052 cv::imwrite((imagesWithBoardDir+
"/"+QString::number(
currentId_)+
".jpg").toStdString(), images[
id]);
1056 if(
stereo_ && boardFound[0] && boardFound[1] && (boardAccepted[0] || boardAccepted[1]))
1059 std::vector< int > combinedIds;
1060 std::vector<cv::Point2f> leftCorners;
1061 std::vector<cv::Point2f> rightCorners;
1062 std::vector<cv::Point3f> objectPoints;
1063 for(
size_t i=0;
i<pointIds[0].size(); ++
i)
1065 for(
size_t j=0;
j<pointIds[1].size(); ++
j)
1067 if(pointIds[0][
i] == pointIds[1][
j])
1069 leftCorners.push_back(pointBuf[0][
i]);
1070 rightCorners.push_back(pointBuf[1][
j]);
1071 objectPoints.push_back(objectBuf[0][
i]);
1072 combinedIds.push_back(pointIds[0][
i]);
1077 if(objectPoints.size() >=6)
1088 if(!
stereo_ && readyToCalibrate[0])
1097 if(
ui_->radioButton_rectified->isChecked())
1099 if(
models_[0].isValidForRectification())
1101 images[0] =
models_[0].rectifyImage(images[0]);
1103 if(
models_[1].isValidForRectification())
1105 images[1] =
models_[1].rectifyImage(images[1]);
1108 else if(
ui_->radioButton_stereoRectified->isChecked() &&
1117 if(
ui_->checkBox_showHorizontalLines->isChecked())
1124 cv::line(images[
id], cv::Point(0,
i), cv::Point(
imageSize_[
id].width,
i), CV_RGB(0,255,0));
1132 ui_->image_view->setImage(
uCvMat2QImage(images[0]).mirrored(
ui_->checkBox_mirror->isChecked(),
false));
1136 ui_->image_view_2->setImage(
uCvMat2QImage(images[1]).mirrored(
ui_->checkBox_mirror->isChecked(),
false));
1152 timestamp_ = QDateTime::currentDateTime().toString(
"yyyyMMddhhmmss");
1176 ui_->comboBox_board_type->setEnabled(
true);
1177 ui_->comboBox_marker_dictionary->setEnabled(
true);
1178 ui_->spinBox_boardWidth->setEnabled(
true);
1179 ui_->spinBox_boardHeight->setEnabled(
true);
1180 ui_->doubleSpinBox_squareSize->setEnabled(
true);
1181 ui_->doubleSpinBox_markerLength->setEnabled(
true);
1182 ui_->checkBox_subpixel_refinement->setEnabled(
true);
1183 ui_->doubleSpinBox_subpixel_error->setEnabled(
true);
1184 ui_->checkBox_saveCalibrationData->setEnabled(
true);
1186 ui_->pushButton_calibrate->setEnabled(
ui_->checkBox_unlock->isChecked());
1187 ui_->pushButton_save->setEnabled(
false);
1188 ui_->radioButton_raw->setChecked(
true);
1189 ui_->radioButton_rectified->setEnabled(
false);
1190 ui_->radioButton_stereoRectified->setEnabled(
false);
1192 ui_->progressBar_count->reset();
1194 ui_->progressBar_x->reset();
1195 ui_->progressBar_y->reset();
1196 ui_->progressBar_size->reset();
1197 ui_->progressBar_skew->reset();
1199 ui_->progressBar_count_2->reset();
1201 ui_->progressBar_x_2->reset();
1202 ui_->progressBar_y_2->reset();
1203 ui_->progressBar_size_2->reset();
1204 ui_->progressBar_skew_2->reset();
1206 ui_->label_serial->clear();
1207 ui_->label_fx->setNum(0);
1208 ui_->label_fy->setNum(0);
1209 ui_->label_cx->setNum(0);
1210 ui_->label_cy->setNum(0);
1211 ui_->label_fovx->setNum(0);
1212 ui_->label_fovy->setNum(0);
1213 ui_->label_baseline->setNum(0);
1214 ui_->label_stereoError->setNum(0);
1215 ui_->label_error->setNum(0);
1216 ui_->label_error_2->setNum(0);
1217 ui_->lineEdit_K->clear();
1218 ui_->lineEdit_D->clear();
1219 ui_->lineEdit_R->clear();
1220 ui_->lineEdit_P->clear();
1221 ui_->label_fx_2->setNum(0);
1222 ui_->label_fy_2->setNum(0);
1223 ui_->label_cx_2->setNum(0);
1224 ui_->label_cy_2->setNum(0);
1225 ui_->label_fovx_2->setNum(0);
1226 ui_->label_fovy_2->setNum(0);
1227 ui_->lineEdit_K_2->clear();
1228 ui_->lineEdit_D_2->clear();
1229 ui_->lineEdit_R_2->clear();
1230 ui_->lineEdit_P_2->clear();
1235 markerDictionary_.release();
1236 arucoDetectorParams_.release();
1237 charucoBoard_.release();
1238 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
1240 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1241 arucoDetectorParams_.reset(
new cv::aruco::DetectorParameters());
1243 arucoDetectorParams_ = cv::aruco::DetectorParameters::create();
1246 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=3)
1247 arucoDetectorParams_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_CONTOUR;
1249 arucoDetectorParams_->doCornerRefinement =
true;
1252 int arucoDictionary =
ui_->comboBox_marker_dictionary->currentIndex();
1253 if(arucoDictionary >= 17)
1255 #if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION <4 || (CV_MINOR_VERSION ==4 && CV_SUBMINOR_VERSION<2)))
1256 UERROR(
"Cannot set AprilTag dictionary. OpenCV version should be at least 3.4.2, "
1257 "current version is %s.", CV_VERSION);
1267 arucoDictionary = 0;
1269 arucoDetectorParams_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG;
1273 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1274 markerDictionary_.reset(
new cv::aruco::Dictionary());
1275 *markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(arucoDictionary));
1276 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=2)
1277 markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(arucoDictionary));
1279 markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(arucoDictionary));
1281 UDEBUG(
"Creating charuco board: %dx%d square=%f marker=%f aruco dict=%d",
1282 ui_->spinBox_boardWidth->value(),
1283 ui_->spinBox_boardHeight->value(),
1284 ui_->doubleSpinBox_squareSize->value(),
1285 ui_->doubleSpinBox_markerLength->value(),
1288 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1289 charucoBoard_.reset(
new cv::aruco::CharucoBoard(
1290 cv::Size(
ui_->spinBox_boardWidth->value(),
ui_->spinBox_boardHeight->value()),
1291 ui_->doubleSpinBox_squareSize->value(),
1292 ui_->doubleSpinBox_markerLength->value(),
1293 *markerDictionary_));
1294 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 8)
1295 charucoBoard_->setLegacyPattern(
ui_->comboBox_board_type->currentIndex()==1);
1297 arucoDetector_.reset(
new cv::aruco::ArucoDetector(*markerDictionary_, *arucoDetectorParams_));
1298 charucoDetector_.reset(
new cv::aruco::CharucoDetector(*charucoBoard_, cv::aruco::CharucoParameters(), *arucoDetectorParams_));
1300 charucoBoard_ = cv::aruco::CharucoBoard::create(
1301 ui_->spinBox_boardWidth->value(),
1302 ui_->spinBox_boardHeight->value(),
1303 ui_->doubleSpinBox_squareSize->value(),
1304 ui_->doubleSpinBox_markerLength->value(),
1311 for(
int i = 0;
i <
ui_->spinBox_boardHeight->value(); ++
i ) {
1312 for(
int j = 0;
j <
ui_->spinBox_boardWidth->value(); ++
j ) {
1313 chessboardPoints_.push_back(cv::Point3f(
float(
j*
ui_->doubleSpinBox_squareSize->value() ),
float(
i*
ui_->doubleSpinBox_squareSize->value() ), 0));
1319 ui_->comboBox_marker_dictionary->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1320 ui_->doubleSpinBox_markerLength->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1321 ui_->label_markerDictionary->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1322 ui_->label_markerLength->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1327 ui_->pushButton_calibrate->setEnabled(
true);
1335 ui_->comboBox_board_type->setEnabled(
false);
1336 ui_->comboBox_marker_dictionary->setEnabled(
false);
1337 ui_->spinBox_boardWidth->setEnabled(
false);
1338 ui_->spinBox_boardHeight->setEnabled(
false);
1339 ui_->doubleSpinBox_squareSize->setEnabled(
false);
1340 ui_->doubleSpinBox_markerLength->setEnabled(
false);
1341 ui_->checkBox_subpixel_refinement->setEnabled(
false);
1342 ui_->doubleSpinBox_subpixel_error->setEnabled(
false);
1343 ui_->checkBox_saveCalibrationData->setEnabled(
false);
1345 QMessageBox mb(QMessageBox::Information,
1346 tr(
"Calibrating..."),
1347 tr(
"Operation in progress..."));
1349 QApplication::processEvents();
1351 QApplication::processEvents();
1355 QString dummyOutput;
1356 QTextStream logStream(&dummyOutput);
1357 if(
ui_->checkBox_saveCalibrationData->isChecked())
1360 if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1361 logStream.setDevice(&logFile);
1365 std::cout <<
"Board type = " <<
ui_->comboBox_board_type->currentIndex() << std::endl;
1366 std::cout <<
"Board width = " <<
ui_->spinBox_boardWidth->value() << std::endl;
1367 std::cout <<
"Board height = " <<
ui_->spinBox_boardHeight->value() << std::endl;
1368 std::cout <<
"Square size = " <<
ui_->doubleSpinBox_squareSize->value() << std::endl;
1369 std::cout <<
"Subpixel refinement = " <<
ui_->checkBox_subpixel_refinement->isChecked() << std::endl;
1370 std::cout <<
"Subpixel max error = " <<
ui_->doubleSpinBox_subpixel_error->value() << std::endl;
1371 logStream <<
"Board type = " <<
ui_->comboBox_board_type->currentIndex() <<
ENDL;
1372 logStream <<
"Board width = " <<
ui_->spinBox_boardWidth->value() <<
ENDL;
1373 logStream <<
"Board height = " <<
ui_->spinBox_boardHeight->value() <<
ENDL;
1374 logStream <<
"Square size = " <<
ui_->doubleSpinBox_squareSize->value() <<
ENDL;
1375 logStream <<
"Subpixel refinement = " <<
ui_->checkBox_subpixel_refinement->isChecked() <<
ENDL;
1376 logStream <<
"Subpixel max error = " <<
ui_->doubleSpinBox_subpixel_error->value() <<
ENDL;
1377 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
1379 std::cout <<
"Marker dictionary = " <<
ui_->comboBox_marker_dictionary->currentIndex() << std::endl;
1380 std::cout <<
"Marker length = " <<
ui_->doubleSpinBox_markerLength->value() << std::endl;
1381 logStream <<
"Marker dictionary = " <<
ui_->comboBox_marker_dictionary->currentIndex() <<
ENDL;
1382 logStream <<
"Marker length = " <<
ui_->doubleSpinBox_markerLength->value() <<
ENDL;
1388 logStream <<
"Calibrating camera " <<
id <<
" (samples=" <<
imagePoints_[
id].size() <<
")" <<
ENDL;
1391 std::vector<cv::Mat> rvecs, tvecs;
1392 std::vector<float> reprojErrs;
1394 K = cv::Mat::eye(3,3,CV_64FC1);
1395 UINFO(
"calibrate!");
1398 #if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
1399 bool fishEye =
ui_->comboBox_calib_model->currentIndex()==0;
1413 cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC |
1414 cv::fisheye::CALIB_CHECK_COND |
1415 cv::fisheye::CALIB_FIX_SKEW);
1417 catch(
const cv::Exception &
e)
1419 UERROR(
"Error: %s (try restarting the calibration)",
e.what());
1420 QMessageBox::warning(
this, tr(
"Calibration failed!"), tr(
"Error: %1 (try restarting the calibration)").
arg(
e.what()));
1428 cv::Mat stdDevsMatInt, stdDevsMatExt;
1429 cv::Mat perViewErrorsMat;
1430 rms = cv::calibrateCamera(
1441 ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0);
1444 UINFO(
"Per view errors:");
1445 logStream <<
"Per view errors:" <<
ENDL;
1446 for(
int i=0;
i<perViewErrorsMat.rows; ++
i)
1449 logStream <<
"Image " <<
imageIds_[
id][
i] <<
": " << perViewErrorsMat.at<
double>(
i,0) <<
ENDL;
1454 UINFO(
"Re-projection error reported by calibrateCamera: %f", rms);
1455 logStream <<
"Re-projection error reported by calibrateCamera: " << rms <<
ENDL;
1458 std::vector<cv::Point2f> imagePoints2;
1459 int i, totalPoints = 0;
1460 double totalErr = 0, err;
1465 #if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
1468 cv::fisheye::projectPoints( cv::Mat(
objectPoints_[
id][
i]), imagePoints2, rvecs[
i], tvecs[
i],
K,
D);
1473 cv::projectPoints( cv::Mat(
objectPoints_[
id][
i]), rvecs[
i], tvecs[
i],
K,
D, imagePoints2);
1475 err = cv::norm(cv::Mat(
imagePoints_[
id][
i]), cv::Mat(imagePoints2), CV_L2);
1479 totalErr += err*err;
1483 double totalAvgErr =
std::sqrt(totalErr/totalPoints);
1485 UINFO(
"avg re projection error = %f", totalAvgErr);
1486 logStream <<
"avg re projection error = " << totalAvgErr <<
ENDL;
1488 cv::Mat
P(3,4,CV_64FC1);
1489 P.at<
double>(2,3) = 1;
1490 K.copyTo(
P.colRange(0,3).rowRange(0,3));
1492 #if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
1496 cv::Mat newD = cv::Mat::zeros(1,6,CV_64FC1);
1497 newD.at<
double>(0,0) =
D.at<
double>(0,0);
1498 newD.at<
double>(0,1) =
D.at<
double>(0,1);
1499 newD.at<
double>(0,4) =
D.at<
double>(0,2);
1500 newD.at<
double>(0,5) =
D.at<
double>(0,3);
1507 std::cout <<
"K = " <<
K << std::endl;
1508 std::cout <<
"D = " <<
D << std::endl;
1509 std::cout <<
"width = " <<
imageSize_[
id].width << std::endl;
1510 std::cout <<
"height = " <<
imageSize_[
id].height << std::endl;
1511 UINFO(
"FOV horizontal=%f vertical=%f",
models_[
id].horizontalFOV(),
models_[
id].verticalFOV());
1513 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1514 std::string strStream;
1515 logStream <<
"K = " << (strStream <<
K).
c_str() <<
ENDL;
1517 logStream <<
"D = " << (strStream <<
D).
c_str() <<
ENDL;
1521 logStream <<
"FOV horizontal=" <<
models_[
id].horizontalFOV() <<
" vertical=" <<
models_[
id].verticalFOV() <<
ENDL;
1529 ui_->label_fovx->setNum(
models_[
id].horizontalFOV());
1530 ui_->label_fovy->setNum(
models_[
id].verticalFOV());
1531 ui_->label_error->setNum(totalAvgErr);
1533 std::stringstream strK, strD, strR, strP;
1538 ui_->lineEdit_K->setText(strK.str().c_str());
1539 ui_->lineEdit_D->setText(strD.str().c_str());
1540 ui_->lineEdit_R->setText(strR.str().c_str());
1541 ui_->lineEdit_P->setText(strP.str().c_str());
1549 ui_->label_fovx_2->setNum(
models_[
id].horizontalFOV());
1550 ui_->label_fovy_2->setNum(
models_[
id].verticalFOV());
1551 ui_->label_error_2->setNum(totalAvgErr);
1553 std::stringstream strK, strD, strR, strP;
1558 ui_->lineEdit_K_2->setText(strK.str().c_str());
1559 ui_->lineEdit_D_2->setText(strD.str().c_str());
1560 ui_->lineEdit_R_2->setText(strR.str().c_str());
1561 ui_->lineEdit_P_2->setText(strP.str().c_str());
1570 ui_->doubleSpinBox_stereoBaseline->value() > 0 &&
1573 UWARN(
"Expected stereo baseline is set to %f m, but computed baseline is %f m. Rescaling baseline...",
1576 P.at<
double>(0,3) = -
P.at<
double>(0,0)*
ui_->doubleSpinBox_stereoBaseline->value();
1578 UWARN(
"Scale %f (setting square size from %f to %f)",
scale,
ui_->doubleSpinBox_squareSize->value(),
ui_->doubleSpinBox_squareSize->value()*
scale);
1580 ui_->doubleSpinBox_squareSize->setValue(
ui_->doubleSpinBox_squareSize->value()*
scale);
1588 std::stringstream strR1, strP1, strR2, strP2;
1593 ui_->lineEdit_R->setText(strR1.str().c_str());
1594 ui_->lineEdit_P->setText(strP1.str().c_str());
1595 ui_->lineEdit_R_2->setText(strR2.str().c_str());
1596 ui_->lineEdit_P_2->setText(strP2.str().c_str());
1612 if(
models_[0].isValidForRectification())
1614 models_[0].initRectificationMap();
1616 if(
models_[1].isValidForRectification())
1618 models_[1].initRectificationMap();
1620 if(
models_[0].isValidForRectification() ||
models_[1].isValidForRectification())
1622 ui_->radioButton_rectified->setEnabled(
true);
1627 ui_->radioButton_stereoRectified->setEnabled(
true);
1628 ui_->radioButton_stereoRectified->setChecked(
true);
1629 ui_->pushButton_save->setEnabled(
true);
1631 if(
ui_->checkBox_saveCalibrationData->isChecked())
1638 ui_->radioButton_rectified->setChecked(
ui_->radioButton_rectified->isEnabled());
1641 else if(
models_[0].isValidForRectification())
1643 models_[0].initRectificationMap();
1644 ui_->radioButton_rectified->setEnabled(
true);
1645 ui_->radioButton_rectified->setChecked(
true);
1646 ui_->pushButton_save->setEnabled(
true);
1648 if(
ui_->checkBox_saveCalibrationData->isChecked())
1654 UINFO(
"End calibration");
1664 UERROR(
"No stereo correspondences!");
1668 if(logStream) (*logStream) <<
"stereo calibration (samples=" <<
stereoImagePoints_[0].size() <<
")..." <<
ENDL;
1670 if (left.
K_raw().empty() || left.
D_raw().empty())
1672 UERROR(
"Empty intrinsic parameters (K, D) for the %s camera! Aborting stereo calibration...",
leftSuffix_.toStdString().c_str());
1675 if (right.
K_raw().empty() || right.
D_raw().empty())
1677 UERROR(
"Empty intrinsic parameters (K, D) for the %s camera! Aborting stereo calibration...",
rightSuffix_.toStdString().c_str());
1683 UERROR(
"left model (%dx%d) has not the same size as the processed images (%dx%d)",
1690 UERROR(
"right model (%dx%d) has not the same size as the processed images (%dx%d)",
1700 #if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
1701 bool fishEye = left.
D_raw().cols == 6;
1706 cv::Vec4d D_left(left.
D_raw().at<
double>(0,0), left.
D_raw().at<
double>(0,1), left.
D_raw().at<
double>(0,4), left.
D_raw().at<
double>(0,5));
1707 cv::Vec4d D_right(right.
D_raw().at<
double>(0,0), right.
D_raw().at<
double>(0,1), right.
D_raw().at<
double>(0,4), right.
D_raw().at<
double>(0,5));
1728 rms = cv::fisheye::stereoCalibrate(
1732 left.
K_raw(), D_left, right.
K_raw(), D_right,
1734 cv::fisheye::CALIB_FIX_INTRINSIC,
1735 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1736 UINFO(
"stereo calibration... done with RMS error=%f", rms);
1738 catch(
const cv::Exception &
e)
1740 UERROR(
"Error: %s (try restarting the calibration)",
e.what());
1744 std::cout <<
"R = " <<
R << std::endl;
1745 std::cout <<
"T = " << Tvec << std::endl;
1749 UINFO(
"Compute stereo rectification");
1751 cv::Mat
R1,
R2, P1, P2,
Q;
1753 left.
K_raw(), D_left,
1754 right.
K_raw(), D_right,
1755 imageSize,
R, Tvec,
R1,
R2, P1, P2,
Q,
1756 cv::CALIB_ZERO_DISPARITY, 0, imageSize);
1766 std::cout <<
"R1 = " <<
R1 << std::endl;
1767 std::cout <<
"R2 = " <<
R2 << std::endl;
1768 std::cout <<
"P1 = " << P1 << std::endl;
1769 std::cout <<
"P2 = " << P2 << std::endl;
1772 if(P1.at<
double>(0,0) < 0)
1774 P1.at<
double>(0,0) *= -1;
1775 P1.at<
double>(1,1) *= -1;
1777 if(P2.at<
double>(0,0) < 0)
1779 P2.at<
double>(0,0) *= -1;
1780 P2.at<
double>(1,1) *= -1;
1782 if(P2.at<
double>(0,3) > 0)
1784 P2.at<
double>(0,3) *= -1;
1786 P2.at<
double>(0,3) = P2.at<
double>(0,3) * left.
K_raw().at<
double>(0,0) / P2.at<
double>(0,0);
1787 P1.at<
double>(0,0) = P1.at<
double>(1,1) = left.
K_raw().at<
double>(0,0);
1788 P2.at<
double>(0,0) = P2.at<
double>(1,1) = left.
K_raw().at<
double>(0,0);
1790 std::cout <<
"P1n = " << P1 << std::endl;
1791 std::cout <<
"P2n = " << P2 << std::endl;
1794 cv::Mat
T(3,1,CV_64FC1);
1795 T.at <
double>(0,0) = Tvec[0];
1796 T.at <
double>(1,0) = Tvec[1];
1797 T.at <
double>(2,0) = Tvec[2];
1818 #if CV_MAJOR_VERSION < 3
1819 rms = cv::stereoCalibrate(
1825 imageSize,
R,
T,
E,
F,
1826 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5),
1827 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0));
1828 #elif CV_MAJOR_VERSION == 3 and (CV_MINOR_VERSION < 4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION < 1))
1830 rms = cv::stereoCalibrate(
1836 imageSize,
R,
T,
E,
F,
1837 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0),
1838 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1840 cv::Mat perViewErrorsMat;
1841 rms = cv::stereoCalibrate(
1847 imageSize,
R,
T,
E,
F,
1849 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0),
1850 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1853 UINFO(
"Per stereo view errors: %dx%d", perViewErrorsMat.rows, perViewErrorsMat.cols);
1854 if(logStream) (*logStream) <<
"Per stereo view errors:" <<
ENDL;
1855 for(
int i=0;
i<perViewErrorsMat.rows; ++
i)
1857 UINFO(
"Image %d: %f <-> %f",
stereoImageIds_[
i], perViewErrorsMat.at<
double>(
i,0), perViewErrorsMat.at<
double>(
i,1));
1858 if(logStream) (*logStream) <<
"Image " <<
stereoImageIds_[
i] <<
": " << perViewErrorsMat.at<
double>(
i,0) <<
" <-> " << perViewErrorsMat.at<
double>(
i,0) <<
ENDL;
1862 UINFO(
"stereo calibration... done with RMS error=%f", rms);
1863 if(logStream) (*logStream) <<
"stereo calibration... done with RMS error=" << rms <<
ENDL;
1864 ui_->label_stereoError->setNum(rms);
1866 std::cout <<
"R = " <<
R << std::endl;
1867 std::cout <<
"T = " <<
T << std::endl;
1868 std::cout <<
"E = " <<
E << std::endl;
1869 std::cout <<
"F = " <<
F << std::endl;
1871 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1872 std::string strStream;
1873 if(logStream) (*logStream) <<
"R = " << (strStream<<
R).
c_str() <<
ENDL;
1875 if(logStream) (*logStream) <<
"T = " << (strStream<<
T).
c_str() <<
ENDL;
1877 if(logStream) (*logStream) <<
"E = " << (strStream<<
E).
c_str() <<
ENDL;
1879 if(logStream) (*logStream) <<
"F = " << (strStream<<
F).
c_str() <<
ENDL;
1885 UINFO(
"Compute stereo rectification");
1887 cv::Mat
R1,
R2, P1, P2,
Q;
1888 cv::stereoRectify(left.
K_raw(), left.
D_raw(),
1890 imageSize,
R,
T,
R1,
R2, P1, P2,
Q,
1891 cv::CALIB_ZERO_DISPARITY, 0, imageSize);
1893 std::cout <<
"R1 = " <<
R1 << std::endl;
1894 std::cout <<
"P1 = " << P1 << std::endl;
1895 std::cout <<
"R2 = " <<
R2 << std::endl;
1896 std::cout <<
"P2 = " << P2 << std::endl;
1898 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1899 if(logStream) (*logStream) <<
"R1 = " << (strStream<<
R1).
c_str() <<
ENDL;
1901 if(logStream) (*logStream) <<
"P1 = " << (strStream<<P1).
c_str() <<
ENDL;
1903 if(logStream) (*logStream) <<
"R2 = " << (strStream<<
R2).
c_str() <<
ENDL;
1905 if(logStream) (*logStream) <<
"P2 = " << (strStream<<P2).
c_str() <<
ENDL;
1910 std::vector<cv::Vec3f>
lines[2];
1911 UINFO(
"Computing re-projection errors...");
1912 if(logStream) (*logStream) <<
"Computing re-projection epipolar errors..." <<
ENDL;
1919 cv::undistortPoints(imgpt0, imgpt0, left.
K_raw(), left.
D_raw(),
R1, P1);
1920 cv::undistortPoints(imgpt1, imgpt1, right.
K_raw(), right.
D_raw(),
R2, P2);
1921 computeCorrespondEpilines(imgpt0, 1,
F,
lines[0]);
1922 computeCorrespondEpilines(imgpt1, 2,
F,
lines[1]);
1924 double sampleErr = 0.0;
1925 for(
int j = 0;
j < npt;
j++ )
1934 if(logStream) (*logStream) <<
"Stereo image " <<
stereoImageIds_[
i] <<
": " << sampleErr/npt <<
ENDL;
1938 double totalAvgErr = err/(double)npoints;
1939 UINFO(
"stereo avg re projection error = %f", totalAvgErr);
1940 if(logStream) (*logStream) <<
"stereo avg re projection error = " << totalAvgErr <<
ENDL;
1969 QString cameraName =
models_[0].name().c_str();
1970 QString filePath = QFileDialog::getSaveFileName(
this, tr(
"Export"),
savingDirectory_+
"/"+cameraName+
".yaml",
"*.yaml");
1972 if(!filePath.isEmpty())
1974 QString
name = QFileInfo(filePath).baseName();
1975 QString dir = QFileInfo(filePath).absoluteDir().absolutePath();
1979 QMessageBox::information(
this, tr(
"Export"), tr(
"Calibration file saved to \"%1\".").
arg(filePath));
1980 UINFO(
"Saved \"%s\"!", filePath.toStdString().c_str());
1986 UERROR(
"Error saving \"%s\"", filePath.toStdString().c_str());
1995 QString filePath = QFileDialog::getSaveFileName(
this, tr(
"Export"),
savingDirectory_ +
"/" + cameraName,
"*.yaml");
1996 QString
name = QFileInfo(filePath).baseName();
1997 QString dir = QFileInfo(filePath).absoluteDir().absolutePath();
2000 bool switched =
ui_->checkBox_switchImages->isChecked();
2002 std::string
base = (dir+QDir::separator()+
name).toStdString();
2005 std::string posePath =
base+
"_pose.yaml";
2008 QMessageBox::information(
this, tr(
"Export"), tr(
"Calibration files saved:\n \"%1\"\n \"%2\"\n \"%3\".").
2009 arg(leftPath.c_str()).
arg(rightPath.c_str()).arg(posePath.c_str()));
2010 UINFO(
"Saved \"%s\" and \"%s\"!", leftPath.c_str(), rightPath.c_str());
2016 UERROR(
"Error saving \"%s\" and \"%s\"", leftPath.c_str(), rightPath.c_str());
2030 cv::Point2f up_left;
2031 cv::Point2f up_right;
2032 cv::Point2f down_right;
2033 cv::Point2f down_left;
2034 if((
int)
corners.size() == (boardSize.width * boardSize.height))
2037 up_right =
corners[boardSize.width-1];
2043 cv::Rect rect = cv::boundingRect(
corners);
2044 up_left = cv::Point2f(rect.x, rect.y);
2045 up_right = cv::Point2f(rect.x+rect.width, rect.y);
2046 down_right = cv::Point2f(rect.x+rect.width, rect.y+rect.height);
2047 down_left = cv::Point2f(rect.x, rect.y+rect.height);
2049 cv::Point2f
a = up_right - up_left;
2050 cv::Point2f
b = down_right - up_right;
2051 cv::Point2f
c = down_left - down_right;
2052 cv::Point2f
p =
b +
c;
2053 cv::Point2f
q =
a +
b;
2054 return std::fabs(
p.x*
q.y -
p.y*
q.x) / 2.0f;
2059 UASSERT(fourCorners.size() == 4);
2060 std::vector<cv::Point2f>
corners = fourCorners;
2071 cv::Point2f up_left =
corners[0];
2072 cv::Point2f up_right =
corners[boardSize.width-1];
2076 cv::Point2f ab = up_left - up_right;
2077 cv::Point2f cb = down_right - up_right;
2078 float angle =
std::acos(ab.dot(cb) / (cv::norm(ab) * cv::norm(cb)));
2080 float r = 2.0f * std::fabs((CV_PI / 2.0
f) -
angle);
2081 return r > 1.0f?1.0f:r;
2089 float & x,
float & y,
float & size,
float & skew)
2092 size =
std::sqrt(area / (imageSize.width * imageSize.height));
2103 x = meanX / imageSize.width;
2104 y = meanY / imageSize.height;