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 < 7)
165 ui_->comboBox_board_type->setItemData(2, 0, Qt::UserRole - 1);
179 settings.beginGroup(group);
181 settings.setValue(
"board_type",
ui_->comboBox_board_type->currentIndex());
182 settings.setValue(
"board_width",
ui_->spinBox_boardWidth->value());
183 settings.setValue(
"board_height",
ui_->spinBox_boardHeight->value());
184 settings.setValue(
"board_square_size",
ui_->doubleSpinBox_squareSize->value());
185 settings.setValue(
"marker_type",
ui_->comboBox_marker_dictionary->currentIndex());
186 settings.setValue(
"marker_length",
ui_->doubleSpinBox_markerLength->value());
187 settings.setValue(
"subpixel_refinement",
ui_->checkBox_subpixel_refinement->isChecked());
188 settings.setValue(
"subpixel_max_error",
ui_->doubleSpinBox_subpixel_error->value());
189 settings.setValue(
"calibration_data_saved",
ui_->checkBox_saveCalibrationData->isChecked());
190 settings.setValue(
"max_scale",
ui_->spinBox_maxScale->value());
191 settings.setValue(
"geometry", this->saveGeometry());
192 settings.setValue(
"calibration_model",
ui_->comboBox_calib_model->currentIndex());
204 settings.beginGroup(group);
206 this->
setBoardType(settings.value(
"board_type",
ui_->comboBox_board_type->currentIndex()).toInt());
207 this->
setBoardWidth(settings.value(
"board_width",
ui_->spinBox_boardWidth->value()).toInt());
208 this->
setBoardHeight(settings.value(
"board_height",
ui_->spinBox_boardHeight->value()).toInt());
209 this->
setSquareSize(settings.value(
"board_square_size",
ui_->doubleSpinBox_squareSize->value()).toDouble());
210 this->
setMarkerDictionary(settings.value(
"marker_type",
ui_->comboBox_marker_dictionary->currentIndex()).toInt());
211 this->
setMarkerLength(settings.value(
"marker_length",
ui_->doubleSpinBox_markerLength->value()).toDouble());
212 this->
setSubpixelRefinement(settings.value(
"subpixel_refinement",
ui_->checkBox_subpixel_refinement->isChecked()).toBool());
213 this->
setSubpixelMaxError(settings.value(
"subpixel_max_error",
ui_->doubleSpinBox_subpixel_error->value()).toDouble());
214 this->
setCalibrationDataSaved(settings.value(
"calibration_data_saved",
ui_->checkBox_saveCalibrationData->isChecked()).toBool());
215 this->
setMaxScale(settings.value(
"max_scale",
ui_->spinBox_maxScale->value()).toDouble());
216 int model = settings.value(
"calibration_model",
ui_->comboBox_calib_model->currentIndex()).toInt();
229 QByteArray
bytes = settings.value(
"geometry", QByteArray()).toByteArray();
232 this->restoreGeometry(
bytes);
249 cv::Mat
drawChessboard(
int squareSize,
int boardWidth,
int boardHeight,
int borderSize)
251 int imageWidth = squareSize*boardWidth + borderSize;
252 int imageHeight = squareSize*boardHeight + borderSize;
253 cv::Mat chessboard(imageWidth, imageHeight, CV_8UC1, 255);
254 unsigned char color = 0;
255 for(
int i=borderSize;
i<imageHeight-borderSize;
i=
i+squareSize) {
257 for(
int j=borderSize;
j<imageWidth-borderSize;
j=
j+squareSize) {
258 cv::Mat roi=chessboard(cv::Rect(
i,
j,squareSize,squareSize));
268 int squareSizeInPixels = 200;
273 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
276 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
277 charucoBoard_->generateImage(
278 cv::Size(squareSizeInPixels*
ui_->spinBox_boardWidth->value(),
279 squareSizeInPixels*
ui_->spinBox_boardHeight->value()),
281 squareSizeInPixels/4, 1);
284 cv::Size(squareSizeInPixels*
ui_->spinBox_boardWidth->value(),
285 squareSizeInPixels*
ui_->spinBox_boardHeight->value()),
287 squareSizeInPixels/4, 1);
290 int arucoDict =
ui_->comboBox_marker_dictionary->currentIndex();
291 stream <<
"charuco_" << (arucoDict<kArucoDictNameSize?kArucoDictNames[arucoDict]:
"NA") <<
"_"
292 <<
ui_->spinBox_boardWidth->value() <<
"x" <<
ui_->spinBox_boardHeight->value()
293 <<
"_ratio" <<
float(
ui_->doubleSpinBox_markerLength->value())/
float(
ui_->doubleSpinBox_squareSize->value());
295 catch(
const cv::Exception &
e)
298 QMessageBox::critical(
this, tr(
"Generating Board"),
299 tr(
"Cannot generate the board. Make sure the dictionary "
300 "selected is big enough for the board size. Error:\"%1\"").
arg(
e.what()));
309 ui_->spinBox_boardWidth->value(),
310 ui_->spinBox_boardHeight->value(),
311 squareSizeInPixels/4);
313 stream <<
"/chessboard_" <<
ui_->spinBox_boardWidth->value() <<
"x" <<
ui_->spinBox_boardHeight->value();
317 if(!filePath.isEmpty())
319 cv::imwrite(filePath.toStdString(), image);
330 ui_->groupBox_progress->setVisible(visible);
335 ui_->checkBox_switchImages->setChecked(switched);
340 ui_->comboBox_calib_model->setCurrentIndex(0);
345 ui_->comboBox_calib_model->setCurrentIndex(1);
350 ui_->comboBox_calib_model->setCurrentIndex(2);
359 ui_->groupBox_progress->setVisible(
true);
365 ui_->progressBar_count_2->setVisible(
stereo_);
376 ui_->label_baseline_name->setVisible(
stereo_);
382 ui_->radioButton_stereoRectified->setVisible(
stereo_);
383 ui_->checkBox_switchImages->setVisible(
stereo_);
384 ui_->doubleSpinBox_stereoBaseline->setVisible(
stereo_);
385 ui_->label_stereoBaseline->setVisible(
stereo_);
390 return ui_->spinBox_boardWidth->value();
394 return ui_->spinBox_boardHeight->value();
398 return ui_->doubleSpinBox_squareSize->value();
402 return ui_->doubleSpinBox_markerLength->value();
407 if(
type !=
ui_->comboBox_board_type->currentIndex())
409 ui_->comboBox_board_type->setCurrentIndex(
type);
416 if(dictionary !=
ui_->comboBox_marker_dictionary->currentIndex())
418 ui_->comboBox_marker_dictionary->setCurrentIndex(dictionary);
425 if(width !=
ui_->spinBox_boardWidth->value())
427 ui_->spinBox_boardWidth->setValue(width);
434 if(height !=
ui_->spinBox_boardHeight->value())
436 ui_->spinBox_boardHeight->setValue(height);
443 if(
size !=
ui_->doubleSpinBox_squareSize->value())
445 ui_->doubleSpinBox_squareSize->setValue(
size);
447 if(
ui_->doubleSpinBox_markerLength->value() >=
ui_->doubleSpinBox_squareSize->value())
449 if(
ui_->comboBox_board_type->currentIndex()==0)
451 ui_->doubleSpinBox_markerLength->setValue(
ui_->doubleSpinBox_squareSize->value()-0.000001);
455 UWARN(
"Marker length (%f) cannot be larger than square size (%f), setting square size to %f. Decrease marker length first.",
456 ui_->doubleSpinBox_markerLength->value(),
457 ui_->doubleSpinBox_squareSize->value(),
458 ui_->doubleSpinBox_squareSize->value()+0.000001);
459 ui_->doubleSpinBox_squareSize->setValue(
ui_->doubleSpinBox_markerLength->value()+0.000001);
467 if(length !=
ui_->doubleSpinBox_markerLength->value())
469 ui_->doubleSpinBox_markerLength->setValue(
length);
471 if(
ui_->doubleSpinBox_markerLength->value() >=
ui_->doubleSpinBox_squareSize->value())
473 UWARN(
"Marker length (%f) cannot be larger than square size (%f), setting marker length to %f. Increase square size first.",
474 ui_->doubleSpinBox_markerLength->value(),
475 ui_->doubleSpinBox_squareSize->value(),
476 ui_->doubleSpinBox_markerLength->value()-0.000001);
477 ui_->doubleSpinBox_markerLength->setValue(
ui_->doubleSpinBox_squareSize->value()-0.000001);
484 if(enabled !=
ui_->checkBox_subpixel_refinement->isChecked())
486 ui_->checkBox_subpixel_refinement->setChecked(enabled);
493 if(
value !=
ui_->doubleSpinBox_subpixel_error->value())
495 ui_->doubleSpinBox_subpixel_error->setValue(
value);
502 if(enabled !=
ui_->checkBox_saveCalibrationData->isChecked())
504 ui_->checkBox_saveCalibrationData->setChecked(enabled);
511 if(length !=
ui_->doubleSpinBox_stereoBaseline->value())
513 ui_->doubleSpinBox_stereoBaseline->setValue(
length);
519 if(
scale !=
ui_->spinBox_maxScale->value())
521 ui_->spinBox_maxScale->setValue(
scale);
532 QMessageBox::StandardButton
b = QMessageBox::question(
this, tr(
"Save calibration?"),
533 tr(
"The camera is calibrated but you didn't "
534 "save the calibration, do you want to save it?"),
535 QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Yes);
537 if(
b == QMessageBox::Yes)
544 else if(b == QMessageBox::Ignore)
553 if(event->isAccepted())
570 QMetaObject::invokeMethod(
this,
"processImages",
571 Q_ARG(cv::Mat,
e->data().imageRaw()),
572 Q_ARG(cv::Mat,
e->data().depthOrRightRaw()),
573 Q_ARG(QString, QString(
e->cameraName().c_str())));
581 void matchCharucoImagePoints(
582 const cv::aruco::CharucoBoard &board,
583 const std::vector< cv::Point2f > & detectedCorners,
584 const std::vector< int > & detectedIds,
585 std::vector< cv::Point3f > & objectPoints)
587 UASSERT(detectedIds.size() == detectedCorners.size());
588 objectPoints.clear();
590 #if CV_MAJOR_VERSION < 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION < 7)
591 objectPoints.reserve(detectedIds.size());
594 for(
size_t i = 0;
i < detectedIds.size();
i++) {
595 int pointId = detectedIds[
i];
596 UASSERT(pointId >= 0 && pointId < (
int)board.chessboardCorners.size());
597 objectPoints.push_back(board.chessboardCorners[pointId]);
600 board.matchImagePoints(detectedCorners, detectedIds, objectPoints, cv::noArray());
607 cv::InputArray charucoIds = cv::noArray(),
608 cv::Scalar cornerColor = cv::Scalar(255, 0, 0)) {
610 CV_Assert(image.getMat().total() != 0 &&
611 (image.getMat().channels() == 1 || image.getMat().channels() == 3));
612 CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total()) ||
613 charucoIds.getMat().total() == 0);
615 unsigned int nCorners = (
unsigned int)charucoCorners.getMat().total();
616 for(
unsigned int i = 0;
i < nCorners;
i++) {
617 cv::Point2f corner = charucoCorners.getMat().at< cv::Point2f >(
i);
620 cv::rectangle(image, corner - cv::Point2f(3, 3), corner + cv::Point2f(3, 3), cornerColor, 1, cv::LINE_AA);
623 if(charucoIds.total() != 0) {
624 int id = charucoIds.getMat().at<
int >(
i);
627 cv::putText(image,
s.str(), corner + cv::Point2f(5, -5), cv::FONT_HERSHEY_SIMPLEX, 0.5,
635 UDEBUG(
"Processing images");
641 else if(cameraName.isEmpty())
651 std::vector<cv::Mat> inputRawImages(2);
652 if(
ui_->checkBox_switchImages->isChecked())
654 inputRawImages[0] = imageRight;
655 inputRawImages[1] = imageLeft;
659 inputRawImages[0] = imageLeft;
660 inputRawImages[1] = imageRight;
663 std::vector<cv::Mat> images(2);
664 images[0] = inputRawImages[0];
665 images[1] = inputRawImages[1];
669 bool boardFound[2] = {
false};
670 bool boardAccepted[2] = {
false};
671 bool readyToCalibrate[2] = {
false};
673 std::vector<std::vector<cv::Point2f> > pointBuf(2);
674 std::vector<std::vector<cv::Point3f> > objectBuf(2);
675 std::vector<std::vector< int > > pointIds(2);
677 bool depthDetected =
false;
681 if(!images[
id].
empty())
683 if(images[
id].
type() == CV_16UC1)
686 cv::minMaxLoc(images[
id], &
min, &
max);
697 depthDetected =
true;
700 viewGray = cv::Mat(images[
id].
rows, images[
id].
cols, CV_8UC1);
701 for(
int i=0;
i<images[
id].rows; ++
i)
703 for(
int j=0;
j<images[
id].cols; ++
j)
705 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);
708 cvtColor(viewGray, images[
id], cv::COLOR_GRAY2BGR);
710 else if(images[
id].channels() == 3)
712 cvtColor(images[
id], viewGray, cv::COLOR_BGR2GRAY);
716 viewGray = images[
id];
717 cvtColor(viewGray, images[
id], cv::COLOR_GRAY2BGR);
722 UERROR(
"Image %d is empty!! Should not!",
id);
729 if(!
ui_->pushButton_save->isEnabled())
731 std::vector< int > markerIds;
732 std::vector< std::vector< cv::Point2f > > markerCorners;
733 cv::Size boardSize(
ui_->spinBox_boardWidth->value(),
ui_->spinBox_boardHeight->value());
734 if(!viewGray.empty())
736 int flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE;
738 if(!viewGray.empty())
740 int maxScale =
ui_->spinBox_maxScale->value();
747 cv::resize(viewGray, timg, cv::Size(),
scale,
scale, CV_INTER_CUBIC);
750 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
752 std::vector< std::vector< cv::Point2f > > rejected;
756 cv::aruco::detectMarkers(timg, markerDictionary_, markerCorners, markerIds, arucoDetectorParams_, rejected);
759 cv::aruco::refineDetectedMarkers(timg, charucoBoard_, markerCorners, markerIds, rejected);
762 if(markerIds.size() > 0)
764 UASSERT(markerIds.size() == markerCorners.size());
765 cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, timg, charucoBoard_, pointBuf[
id], pointIds[
id], cv::noArray(), cv::noArray(), 1);
766 if(pointBuf[
id].
size() >= 12) {
768 matchCharucoImagePoints(*charucoBoard_, pointBuf[
id], pointIds[
id], objectBuf[
id]);
769 boardFound[
id] = !objectBuf[
id].empty() && objectBuf[
id].size() == pointBuf[
id].size();
776 boardFound[
id] = cv::findChessboardCorners(timg, boardSize, pointBuf[
id], flags);
785 cv::Mat cornersMat(pointBuf[
id]);
786 cornersMat *= 1./
scale;
798 std::vector<cv::Point2f> originalPoints = pointBuf[
id];
799 std::vector<cv::Point2f> rejectedPoints;
800 std::vector<int> rejectedPointIds;
801 if(
ui_->checkBox_subpixel_refinement->isChecked())
804 float minSquareDistance = -1.0f;
805 for(
unsigned int i=0;
i<pointBuf[
id].size()-1; ++
i)
807 float d = cv::norm(pointBuf[
id][
i] - pointBuf[
id][
i+1]);
808 if(minSquareDistance == -1.0
f || minSquareDistance >
d)
810 minSquareDistance =
d;
813 float ratio =
ui_->comboBox_board_type->currentIndex() >= 1 ?6.0f:2.0f;
814 float radius = minSquareDistance==-1.0f?5.0f:(minSquareDistance/
ratio);
815 cv::cornerSubPix( viewGray, pointBuf[
id], cv::Size(radius, radius), cv::Size(-1,-1),
816 cv::TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1 ));
819 float threshold =
ui_->doubleSpinBox_subpixel_error->value();
822 std::vector<cv::Point3f> filteredObjectPts;
823 std::vector<cv::Point2f> filteredPoints;
824 std::vector<int> filteredPointIds;
825 for(
size_t i=0;
i<pointBuf[
id].size(); ++
i)
827 float d = cv::norm(pointBuf[
id][
i] - originalPoints[
i]);
830 filteredObjectPts.push_back(objectBuf[
id][
i]);
831 filteredPoints.push_back(pointBuf[
id][
i]);
832 filteredPointIds.push_back(pointIds[
id][
i]);
836 UWARN(
"Filtered point: subpix error for image=%d cam=%d pt=%d radius=%f: %f > %f",
currentId_,
id, pointIds[
id][
i], radius,
d, threshold);
837 rejectedPoints.push_back(pointBuf[
id][
i]);
838 rejectedPointIds.push_back(pointIds[
id][
i]);
841 objectBuf[
id] = filteredObjectPts;
842 pointBuf[
id] = filteredPoints;
843 pointIds[
id] = filteredPointIds;
848 images[
id] = images[
id].clone();
850 if(
ui_->comboBox_board_type->currentIndex() >= 1 ) {
851 if(markerIds.size() > 0)
852 cv::aruco::drawDetectedMarkers(images[
id], markerCorners, cv::noArray(), cv::Scalar(255,0,0));
855 if(pointBuf[
id].
size() > 0)
857 if(rejectedPoints.size() > 0)
860 if(pointBuf[
id].
size() < rejectedPoints.size())
863 UWARN(
"Ignoring whole board of image %d cam=%d because too many points were filtered.",
currentId_,
id);
864 boardFound[
id] =
false;
868 std::vector<float>
params(4,0);
870 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
873 float area =
getArea(markerCorners[markerCorners.size()/2], cv::Size(4,4)) * (boardSize.width*boardSize.height);
879 bool addSample =
true;
880 if(!
ui_->checkBox_keep_all->isChecked())
884 if(
fabs(
params[0] -
imageParams_[
id][
i].at(0)) < (
ui_->comboBox_board_type->currentIndex() >= 1 ?0.2:0.1)*
ui_->doubleSpinBox_sample_factor->value() &&
885 fabs(
params[1] -
imageParams_[
id][
i].at(1)) < (
ui_->comboBox_board_type->currentIndex() >= 1 ?0.2:0.1)*
ui_->doubleSpinBox_sample_factor->value() &&
896 boardAccepted[
id] =
true;
901 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]);
904 std::vector<float> xRange(2,
imageParams_[
id][0].at(0));
905 std::vector<float> yRange(2,
imageParams_[
id][0].at(1));
906 std::vector<float> sizeRange(2,
imageParams_[
id][0].at(2));
907 std::vector<float> skewRange(2,
imageParams_[
id][0].at(3));
917 if(skewRange[0] == 0 || skewRange[0] == 1)
936 float xGood = xRange[1] - xRange[0];
937 float yGood = yRange[1] - yRange[0];
938 float sizeGood = sizeRange[1] - sizeRange[0];
939 float skewGood = skewRange[1] - skewRange[0];
943 ui_->progressBar_x->setValue(xGood*100);
944 ui_->progressBar_y->setValue(yGood*100);
945 ui_->progressBar_size->setValue(sizeGood*100);
946 ui_->progressBar_skew->setValue(skewGood*100);
955 ui_->progressBar_x_2->setValue(xGood*100);
956 ui_->progressBar_y_2->setValue(yGood*100);
957 ui_->progressBar_size_2->setValue(sizeGood*100);
958 ui_->progressBar_skew_2->setValue(skewGood*100);
970 (sizeGood > 0.4 || (
ui_->comboBox_calib_model->currentIndex()==0 && sizeGood > 0.25)) &&
973 readyToCalibrate[
id] =
true;
977 if(inputRawImages[
id].
type() == CV_16UC1)
982 for(
size_t i = 0;
i < pointBuf[
id].size(); ++
i)
984 const cv::Point2f &
p = pointBuf[
id][
i];
987 roi.width =
std::min(roi.width, inputRawImages[
id].cols - roi.x);
988 roi.height =
std::min(roi.height, inputRawImages[
id].rows - roi.y);
992 cv::minMaxLoc(inputRawImages[
id](roi), &
min, &
max);
1012 ui_->label_baseline->setVisible(!depthDetected);
1013 ui_->label_baseline_name->setVisible(!depthDetected);
1014 ui_->label_stereoError->setVisible(!depthDetected);
1016 if(
ui_->checkBox_saveCalibrationData->isChecked() && (boardAccepted[0] || boardAccepted[1]))
1021 QString imagesWithBoardDir = rawImagesDir+
"_board_detection";
1022 if(!QDir(rawImagesDir).exists())
1024 UINFO(
"Creating dir %s", rawImagesDir.toStdString().c_str());
1025 QDir().mkpath(rawImagesDir);
1027 if(!QDir(imagesWithBoardDir).exists())
1029 UINFO(
"Creating dir %s", imagesWithBoardDir.toStdString().c_str());
1030 QDir().mkpath(imagesWithBoardDir);
1032 cv::imwrite((rawImagesDir+
"/"+QString::number(
currentId_)+
".png").toStdString(), inputRawImages[
id]);
1033 cv::imwrite((imagesWithBoardDir+
"/"+QString::number(
currentId_)+
".jpg").toStdString(), images[
id]);
1037 if(
stereo_ && boardFound[0] && boardFound[1] && (boardAccepted[0] || boardAccepted[1]))
1040 std::vector< int > combinedIds;
1041 std::vector<cv::Point2f> leftCorners;
1042 std::vector<cv::Point2f> rightCorners;
1043 std::vector<cv::Point3f> objectPoints;
1044 for(
size_t i=0;
i<pointIds[0].size(); ++
i)
1046 for(
size_t j=0;
j<pointIds[1].size(); ++
j)
1048 if(pointIds[0][
i] == pointIds[1][
j])
1050 leftCorners.push_back(pointBuf[0][
i]);
1051 rightCorners.push_back(pointBuf[1][
j]);
1052 objectPoints.push_back(objectBuf[0][
i]);
1053 combinedIds.push_back(pointIds[0][
i]);
1058 if(objectPoints.size() >=6)
1069 if(!
stereo_ && readyToCalibrate[0])
1078 if(
ui_->radioButton_rectified->isChecked())
1080 if(
models_[0].isValidForRectification())
1082 images[0] =
models_[0].rectifyImage(images[0]);
1084 if(
models_[1].isValidForRectification())
1086 images[1] =
models_[1].rectifyImage(images[1]);
1089 else if(
ui_->radioButton_stereoRectified->isChecked() &&
1098 if(
ui_->checkBox_showHorizontalLines->isChecked())
1105 cv::line(images[
id], cv::Point(0,
i), cv::Point(
imageSize_[
id].width,
i), CV_RGB(0,255,0));
1113 ui_->image_view->setImage(
uCvMat2QImage(images[0]).mirrored(
ui_->checkBox_mirror->isChecked(),
false));
1117 ui_->image_view_2->setImage(
uCvMat2QImage(images[1]).mirrored(
ui_->checkBox_mirror->isChecked(),
false));
1133 timestamp_ = QDateTime::currentDateTime().toString(
"yyyyMMddhhmmss");
1157 ui_->comboBox_board_type->setEnabled(
true);
1158 ui_->comboBox_marker_dictionary->setEnabled(
true);
1159 ui_->spinBox_boardWidth->setEnabled(
true);
1160 ui_->spinBox_boardHeight->setEnabled(
true);
1161 ui_->doubleSpinBox_squareSize->setEnabled(
true);
1162 ui_->doubleSpinBox_markerLength->setEnabled(
true);
1163 ui_->checkBox_subpixel_refinement->setEnabled(
true);
1164 ui_->doubleSpinBox_subpixel_error->setEnabled(
true);
1165 ui_->checkBox_saveCalibrationData->setEnabled(
true);
1167 ui_->pushButton_calibrate->setEnabled(
ui_->checkBox_unlock->isChecked());
1168 ui_->pushButton_save->setEnabled(
false);
1169 ui_->radioButton_raw->setChecked(
true);
1170 ui_->radioButton_rectified->setEnabled(
false);
1171 ui_->radioButton_stereoRectified->setEnabled(
false);
1173 ui_->progressBar_count->reset();
1175 ui_->progressBar_x->reset();
1176 ui_->progressBar_y->reset();
1177 ui_->progressBar_size->reset();
1178 ui_->progressBar_skew->reset();
1180 ui_->progressBar_count_2->reset();
1182 ui_->progressBar_x_2->reset();
1183 ui_->progressBar_y_2->reset();
1184 ui_->progressBar_size_2->reset();
1185 ui_->progressBar_skew_2->reset();
1187 ui_->label_serial->clear();
1188 ui_->label_fx->setNum(0);
1189 ui_->label_fy->setNum(0);
1190 ui_->label_cx->setNum(0);
1191 ui_->label_cy->setNum(0);
1192 ui_->label_fovx->setNum(0);
1193 ui_->label_fovy->setNum(0);
1194 ui_->label_baseline->setNum(0);
1195 ui_->label_stereoError->setNum(0);
1196 ui_->label_error->setNum(0);
1197 ui_->label_error_2->setNum(0);
1198 ui_->lineEdit_K->clear();
1199 ui_->lineEdit_D->clear();
1200 ui_->lineEdit_R->clear();
1201 ui_->lineEdit_P->clear();
1202 ui_->label_fx_2->setNum(0);
1203 ui_->label_fy_2->setNum(0);
1204 ui_->label_cx_2->setNum(0);
1205 ui_->label_cy_2->setNum(0);
1206 ui_->label_fovx_2->setNum(0);
1207 ui_->label_fovy_2->setNum(0);
1208 ui_->lineEdit_K_2->clear();
1209 ui_->lineEdit_D_2->clear();
1210 ui_->lineEdit_R_2->clear();
1211 ui_->lineEdit_P_2->clear();
1216 markerDictionary_.release();
1217 arucoDetectorParams_.release();
1218 charucoBoard_.release();
1219 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
1221 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1222 arucoDetectorParams_.reset(
new cv::aruco::DetectorParameters());
1224 arucoDetectorParams_ = cv::aruco::DetectorParameters::create();
1227 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=3)
1228 arucoDetectorParams_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_CONTOUR;
1230 arucoDetectorParams_->doCornerRefinement =
true;
1233 int arucoDictionary =
ui_->comboBox_marker_dictionary->currentIndex();
1234 if(arucoDictionary >= 17)
1236 #if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION <4 || (CV_MINOR_VERSION ==4 && CV_SUBMINOR_VERSION<2)))
1237 UERROR(
"Cannot set AprilTag dictionary. OpenCV version should be at least 3.4.2, "
1238 "current version is %s.", CV_VERSION);
1248 arucoDictionary = 0;
1250 arucoDetectorParams_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG;
1254 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1255 markerDictionary_.reset(
new cv::aruco::Dictionary());
1256 *markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(arucoDictionary));
1257 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=2)
1258 markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(arucoDictionary));
1260 markerDictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(arucoDictionary));
1262 UDEBUG(
"Creating charuco board: %dx%d square=%f marker=%f aruco dict=%d",
1263 ui_->spinBox_boardWidth->value(),
1264 ui_->spinBox_boardHeight->value(),
1265 ui_->doubleSpinBox_squareSize->value(),
1266 ui_->doubleSpinBox_markerLength->value(),
1269 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
1270 charucoBoard_.reset(
new cv::aruco::CharucoBoard(
1271 cv::Size(
ui_->spinBox_boardWidth->value(),
ui_->spinBox_boardHeight->value()),
1272 ui_->doubleSpinBox_squareSize->value(),
1273 ui_->doubleSpinBox_markerLength->value(),
1274 *markerDictionary_));
1275 charucoBoard_->setLegacyPattern(
ui_->comboBox_board_type->currentIndex()==1);
1277 charucoBoard_ = cv::aruco::CharucoBoard::create(
1278 ui_->spinBox_boardWidth->value(),
1279 ui_->spinBox_boardHeight->value(),
1280 ui_->doubleSpinBox_squareSize->value(),
1281 ui_->doubleSpinBox_markerLength->value(),
1288 for(
int i = 0;
i <
ui_->spinBox_boardHeight->value(); ++
i ) {
1289 for(
int j = 0;
j <
ui_->spinBox_boardWidth->value(); ++
j ) {
1290 chessboardPoints_.push_back(cv::Point3f(
float(
j*
ui_->doubleSpinBox_squareSize->value() ),
float(
i*
ui_->doubleSpinBox_squareSize->value() ), 0));
1296 ui_->comboBox_marker_dictionary->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1297 ui_->doubleSpinBox_markerLength->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1298 ui_->label_markerDictionary->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1299 ui_->label_markerLength->setVisible(
ui_->comboBox_board_type->currentIndex() >= 1 );
1304 ui_->pushButton_calibrate->setEnabled(
true);
1312 ui_->comboBox_board_type->setEnabled(
false);
1313 ui_->comboBox_marker_dictionary->setEnabled(
false);
1314 ui_->spinBox_boardWidth->setEnabled(
false);
1315 ui_->spinBox_boardHeight->setEnabled(
false);
1316 ui_->doubleSpinBox_squareSize->setEnabled(
false);
1317 ui_->doubleSpinBox_markerLength->setEnabled(
false);
1318 ui_->checkBox_subpixel_refinement->setEnabled(
false);
1319 ui_->doubleSpinBox_subpixel_error->setEnabled(
false);
1320 ui_->checkBox_saveCalibrationData->setEnabled(
false);
1322 QMessageBox mb(QMessageBox::Information,
1323 tr(
"Calibrating..."),
1324 tr(
"Operation in progress..."));
1326 QApplication::processEvents();
1328 QApplication::processEvents();
1332 QString dummyOutput;
1333 QTextStream logStream(&dummyOutput);
1334 if(
ui_->checkBox_saveCalibrationData->isChecked())
1337 if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1338 logStream.setDevice(&logFile);
1342 std::cout <<
"Board type = " <<
ui_->comboBox_board_type->currentIndex() << std::endl;
1343 std::cout <<
"Board width = " <<
ui_->spinBox_boardWidth->value() << std::endl;
1344 std::cout <<
"Board height = " <<
ui_->spinBox_boardHeight->value() << std::endl;
1345 std::cout <<
"Square size = " <<
ui_->doubleSpinBox_squareSize->value() << std::endl;
1346 std::cout <<
"Subpixel refinement = " <<
ui_->checkBox_subpixel_refinement->isChecked() << std::endl;
1347 std::cout <<
"Subpixel max error = " <<
ui_->doubleSpinBox_subpixel_error->value() << std::endl;
1348 logStream <<
"Board type = " <<
ui_->comboBox_board_type->currentIndex() <<
ENDL;
1349 logStream <<
"Board width = " <<
ui_->spinBox_boardWidth->value() <<
ENDL;
1350 logStream <<
"Board height = " <<
ui_->spinBox_boardHeight->value() <<
ENDL;
1351 logStream <<
"Square size = " <<
ui_->doubleSpinBox_squareSize->value() <<
ENDL;
1352 logStream <<
"Subpixel refinement = " <<
ui_->checkBox_subpixel_refinement->isChecked() <<
ENDL;
1353 logStream <<
"Subpixel max error = " <<
ui_->doubleSpinBox_subpixel_error->value() <<
ENDL;
1354 if(
ui_->comboBox_board_type->currentIndex() >= 1 )
1356 std::cout <<
"Marker dictionary = " <<
ui_->comboBox_marker_dictionary->currentIndex() << std::endl;
1357 std::cout <<
"Marker length = " <<
ui_->doubleSpinBox_markerLength->value() << std::endl;
1358 logStream <<
"Marker dictionary = " <<
ui_->comboBox_marker_dictionary->currentIndex() <<
ENDL;
1359 logStream <<
"Marker length = " <<
ui_->doubleSpinBox_markerLength->value() <<
ENDL;
1365 logStream <<
"Calibrating camera " <<
id <<
" (samples=" <<
imagePoints_[
id].size() <<
")" <<
ENDL;
1368 std::vector<cv::Mat> rvecs, tvecs;
1369 std::vector<float> reprojErrs;
1371 K = cv::Mat::eye(3,3,CV_64FC1);
1372 UINFO(
"calibrate!");
1375 #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)))
1376 bool fishEye =
ui_->comboBox_calib_model->currentIndex()==0;
1390 cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC |
1391 cv::fisheye::CALIB_CHECK_COND |
1392 cv::fisheye::CALIB_FIX_SKEW);
1394 catch(
const cv::Exception &
e)
1396 UERROR(
"Error: %s (try restarting the calibration)",
e.what());
1397 QMessageBox::warning(
this, tr(
"Calibration failed!"), tr(
"Error: %1 (try restarting the calibration)").
arg(
e.what()));
1405 cv::Mat stdDevsMatInt, stdDevsMatExt;
1406 cv::Mat perViewErrorsMat;
1407 rms = cv::calibrateCamera(
1418 ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0);
1421 UINFO(
"Per view errors:");
1422 logStream <<
"Per view errors:" <<
ENDL;
1423 for(
int i=0;
i<perViewErrorsMat.rows; ++
i)
1426 logStream <<
"Image " <<
imageIds_[
id][
i] <<
": " << perViewErrorsMat.at<
double>(
i,0) <<
ENDL;
1431 UINFO(
"Re-projection error reported by calibrateCamera: %f", rms);
1432 logStream <<
"Re-projection error reported by calibrateCamera: " << rms <<
ENDL;
1435 std::vector<cv::Point2f> imagePoints2;
1436 int i, totalPoints = 0;
1437 double totalErr = 0, err;
1442 #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)))
1445 cv::fisheye::projectPoints( cv::Mat(
objectPoints_[
id][
i]), imagePoints2, rvecs[
i], tvecs[
i],
K,
D);
1450 cv::projectPoints( cv::Mat(
objectPoints_[
id][
i]), rvecs[
i], tvecs[
i],
K,
D, imagePoints2);
1452 err = cv::norm(cv::Mat(
imagePoints_[
id][
i]), cv::Mat(imagePoints2), CV_L2);
1456 totalErr += err*err;
1460 double totalAvgErr =
std::sqrt(totalErr/totalPoints);
1462 UINFO(
"avg re projection error = %f", totalAvgErr);
1463 logStream <<
"avg re projection error = " << totalAvgErr <<
ENDL;
1465 cv::Mat
P(3,4,CV_64FC1);
1466 P.at<
double>(2,3) = 1;
1467 K.copyTo(
P.colRange(0,3).rowRange(0,3));
1469 #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)))
1473 cv::Mat newD = cv::Mat::zeros(1,6,CV_64FC1);
1474 newD.at<
double>(0,0) =
D.at<
double>(0,0);
1475 newD.at<
double>(0,1) =
D.at<
double>(0,1);
1476 newD.at<
double>(0,4) =
D.at<
double>(0,2);
1477 newD.at<
double>(0,5) =
D.at<
double>(0,3);
1484 std::cout <<
"K = " <<
K << std::endl;
1485 std::cout <<
"D = " <<
D << std::endl;
1486 std::cout <<
"width = " <<
imageSize_[
id].width << std::endl;
1487 std::cout <<
"height = " <<
imageSize_[
id].height << std::endl;
1488 UINFO(
"FOV horizontal=%f vertical=%f",
models_[
id].horizontalFOV(),
models_[
id].verticalFOV());
1490 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1491 std::string strStream;
1492 logStream <<
"K = " << (strStream <<
K).
c_str() <<
ENDL;
1494 logStream <<
"D = " << (strStream <<
D).
c_str() <<
ENDL;
1498 logStream <<
"FOV horizontal=" <<
models_[
id].horizontalFOV() <<
" vertical=" <<
models_[
id].verticalFOV() <<
ENDL;
1506 ui_->label_fovx->setNum(
models_[
id].horizontalFOV());
1507 ui_->label_fovy->setNum(
models_[
id].verticalFOV());
1508 ui_->label_error->setNum(totalAvgErr);
1510 std::stringstream strK, strD, strR, strP;
1515 ui_->lineEdit_K->setText(strK.str().c_str());
1516 ui_->lineEdit_D->setText(strD.str().c_str());
1517 ui_->lineEdit_R->setText(strR.str().c_str());
1518 ui_->lineEdit_P->setText(strP.str().c_str());
1526 ui_->label_fovx_2->setNum(
models_[
id].horizontalFOV());
1527 ui_->label_fovy_2->setNum(
models_[
id].verticalFOV());
1528 ui_->label_error_2->setNum(totalAvgErr);
1530 std::stringstream strK, strD, strR, strP;
1535 ui_->lineEdit_K_2->setText(strK.str().c_str());
1536 ui_->lineEdit_D_2->setText(strD.str().c_str());
1537 ui_->lineEdit_R_2->setText(strR.str().c_str());
1538 ui_->lineEdit_P_2->setText(strP.str().c_str());
1547 ui_->doubleSpinBox_stereoBaseline->value() > 0 &&
1550 UWARN(
"Expected stereo baseline is set to %f m, but computed baseline is %f m. Rescaling baseline...",
1553 P.at<
double>(0,3) = -
P.at<
double>(0,0)*
ui_->doubleSpinBox_stereoBaseline->value();
1555 UWARN(
"Scale %f (setting square size from %f to %f)",
scale,
ui_->doubleSpinBox_squareSize->value(),
ui_->doubleSpinBox_squareSize->value()*
scale);
1557 ui_->doubleSpinBox_squareSize->setValue(
ui_->doubleSpinBox_squareSize->value()*
scale);
1565 std::stringstream strR1, strP1, strR2, strP2;
1570 ui_->lineEdit_R->setText(strR1.str().c_str());
1571 ui_->lineEdit_P->setText(strP1.str().c_str());
1572 ui_->lineEdit_R_2->setText(strR2.str().c_str());
1573 ui_->lineEdit_P_2->setText(strP2.str().c_str());
1589 if(
models_[0].isValidForRectification())
1591 models_[0].initRectificationMap();
1593 if(
models_[1].isValidForRectification())
1595 models_[1].initRectificationMap();
1597 if(
models_[0].isValidForRectification() ||
models_[1].isValidForRectification())
1599 ui_->radioButton_rectified->setEnabled(
true);
1604 ui_->radioButton_stereoRectified->setEnabled(
true);
1605 ui_->radioButton_stereoRectified->setChecked(
true);
1606 ui_->pushButton_save->setEnabled(
true);
1608 if(
ui_->checkBox_saveCalibrationData->isChecked())
1615 ui_->radioButton_rectified->setChecked(
ui_->radioButton_rectified->isEnabled());
1618 else if(
models_[0].isValidForRectification())
1620 models_[0].initRectificationMap();
1621 ui_->radioButton_rectified->setEnabled(
true);
1622 ui_->radioButton_rectified->setChecked(
true);
1623 ui_->pushButton_save->setEnabled(
true);
1625 if(
ui_->checkBox_saveCalibrationData->isChecked())
1631 UINFO(
"End calibration");
1641 UERROR(
"No stereo correspondences!");
1645 if(logStream) (*logStream) <<
"stereo calibration (samples=" <<
stereoImagePoints_[0].size() <<
")..." <<
ENDL;
1647 if (left.
K_raw().empty() || left.
D_raw().empty())
1649 UERROR(
"Empty intrinsic parameters (K, D) for the %s camera! Aborting stereo calibration...",
leftSuffix_.toStdString().c_str());
1652 if (right.
K_raw().empty() || right.
D_raw().empty())
1654 UERROR(
"Empty intrinsic parameters (K, D) for the %s camera! Aborting stereo calibration...",
rightSuffix_.toStdString().c_str());
1660 UERROR(
"left model (%dx%d) has not the same size as the processed images (%dx%d)",
1667 UERROR(
"right model (%dx%d) has not the same size as the processed images (%dx%d)",
1677 #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)))
1678 bool fishEye = left.
D_raw().cols == 6;
1683 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));
1684 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));
1705 rms = cv::fisheye::stereoCalibrate(
1709 left.
K_raw(), D_left, right.
K_raw(), D_right,
1711 cv::fisheye::CALIB_FIX_INTRINSIC,
1712 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1713 UINFO(
"stereo calibration... done with RMS error=%f", rms);
1715 catch(
const cv::Exception &
e)
1717 UERROR(
"Error: %s (try restarting the calibration)",
e.what());
1721 std::cout <<
"R = " <<
R << std::endl;
1722 std::cout <<
"T = " << Tvec << std::endl;
1726 UINFO(
"Compute stereo rectification");
1728 cv::Mat
R1,
R2, P1, P2,
Q;
1730 left.
K_raw(), D_left,
1731 right.
K_raw(), D_right,
1732 imageSize,
R, Tvec,
R1,
R2, P1, P2,
Q,
1733 cv::CALIB_ZERO_DISPARITY, 0, imageSize);
1743 std::cout <<
"R1 = " <<
R1 << std::endl;
1744 std::cout <<
"R2 = " <<
R2 << std::endl;
1745 std::cout <<
"P1 = " << P1 << std::endl;
1746 std::cout <<
"P2 = " << P2 << std::endl;
1749 if(P1.at<
double>(0,0) < 0)
1751 P1.at<
double>(0,0) *= -1;
1752 P1.at<
double>(1,1) *= -1;
1754 if(P2.at<
double>(0,0) < 0)
1756 P2.at<
double>(0,0) *= -1;
1757 P2.at<
double>(1,1) *= -1;
1759 if(P2.at<
double>(0,3) > 0)
1761 P2.at<
double>(0,3) *= -1;
1763 P2.at<
double>(0,3) = P2.at<
double>(0,3) * left.
K_raw().at<
double>(0,0) / P2.at<
double>(0,0);
1764 P1.at<
double>(0,0) = P1.at<
double>(1,1) = left.
K_raw().at<
double>(0,0);
1765 P2.at<
double>(0,0) = P2.at<
double>(1,1) = left.
K_raw().at<
double>(0,0);
1767 std::cout <<
"P1n = " << P1 << std::endl;
1768 std::cout <<
"P2n = " << P2 << std::endl;
1771 cv::Mat
T(3,1,CV_64FC1);
1772 T.at <
double>(0,0) = Tvec[0];
1773 T.at <
double>(1,0) = Tvec[1];
1774 T.at <
double>(2,0) = Tvec[2];
1795 #if CV_MAJOR_VERSION < 3
1796 rms = cv::stereoCalibrate(
1802 imageSize,
R,
T,
E,
F,
1803 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5),
1804 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0));
1805 #elif CV_MAJOR_VERSION == 3 and (CV_MINOR_VERSION < 4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION < 1))
1807 rms = cv::stereoCalibrate(
1813 imageSize,
R,
T,
E,
F,
1814 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0),
1815 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1817 cv::Mat perViewErrorsMat;
1818 rms = cv::stereoCalibrate(
1824 imageSize,
R,
T,
E,
F,
1826 cv::CALIB_FIX_INTRINSIC | (
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0),
1827 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 100, 1
e-5));
1830 UINFO(
"Per stereo view errors: %dx%d", perViewErrorsMat.rows, perViewErrorsMat.cols);
1831 if(logStream) (*logStream) <<
"Per stereo view errors:" <<
ENDL;
1832 for(
int i=0;
i<perViewErrorsMat.rows; ++
i)
1834 UINFO(
"Image %d: %f <-> %f",
stereoImageIds_[
i], perViewErrorsMat.at<
double>(
i,0), perViewErrorsMat.at<
double>(
i,1));
1835 if(logStream) (*logStream) <<
"Image " <<
stereoImageIds_[
i] <<
": " << perViewErrorsMat.at<
double>(
i,0) <<
" <-> " << perViewErrorsMat.at<
double>(
i,0) <<
ENDL;
1839 UINFO(
"stereo calibration... done with RMS error=%f", rms);
1840 if(logStream) (*logStream) <<
"stereo calibration... done with RMS error=" << rms <<
ENDL;
1841 ui_->label_stereoError->setNum(rms);
1843 std::cout <<
"R = " <<
R << std::endl;
1844 std::cout <<
"T = " <<
T << std::endl;
1845 std::cout <<
"E = " <<
E << std::endl;
1846 std::cout <<
"F = " <<
F << std::endl;
1848 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1849 std::string strStream;
1850 if(logStream) (*logStream) <<
"R = " << (strStream<<
R).
c_str() <<
ENDL;
1852 if(logStream) (*logStream) <<
"T = " << (strStream<<
T).
c_str() <<
ENDL;
1854 if(logStream) (*logStream) <<
"E = " << (strStream<<
E).
c_str() <<
ENDL;
1856 if(logStream) (*logStream) <<
"F = " << (strStream<<
F).
c_str() <<
ENDL;
1862 UINFO(
"Compute stereo rectification");
1864 cv::Mat
R1,
R2, P1, P2,
Q;
1865 cv::stereoRectify(left.
K_raw(), left.
D_raw(),
1867 imageSize,
R,
T,
R1,
R2, P1, P2,
Q,
1868 cv::CALIB_ZERO_DISPARITY, 0, imageSize);
1870 std::cout <<
"R1 = " <<
R1 << std::endl;
1871 std::cout <<
"P1 = " << P1 << std::endl;
1872 std::cout <<
"R2 = " <<
R2 << std::endl;
1873 std::cout <<
"P2 = " << P2 << std::endl;
1875 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 2)
1876 if(logStream) (*logStream) <<
"R1 = " << (strStream<<
R1).
c_str() <<
ENDL;
1878 if(logStream) (*logStream) <<
"P1 = " << (strStream<<P1).
c_str() <<
ENDL;
1880 if(logStream) (*logStream) <<
"R2 = " << (strStream<<
R2).
c_str() <<
ENDL;
1882 if(logStream) (*logStream) <<
"P2 = " << (strStream<<P2).
c_str() <<
ENDL;
1887 std::vector<cv::Vec3f>
lines[2];
1888 UINFO(
"Computing re-projection errors...");
1889 if(logStream) (*logStream) <<
"Computing re-projection epipolar errors..." <<
ENDL;
1896 cv::undistortPoints(imgpt0, imgpt0, left.
K_raw(), left.
D_raw(),
R1, P1);
1897 cv::undistortPoints(imgpt1, imgpt1, right.
K_raw(), right.
D_raw(),
R2, P2);
1898 computeCorrespondEpilines(imgpt0, 1,
F,
lines[0]);
1899 computeCorrespondEpilines(imgpt1, 2,
F,
lines[1]);
1901 double sampleErr = 0.0;
1902 for(
int j = 0;
j < npt;
j++ )
1911 if(logStream) (*logStream) <<
"Stereo image " <<
stereoImageIds_[
i] <<
": " << sampleErr/npt <<
ENDL;
1915 double totalAvgErr = err/(double)npoints;
1916 UINFO(
"stereo avg re projection error = %f", totalAvgErr);
1917 if(logStream) (*logStream) <<
"stereo avg re projection error = " << totalAvgErr <<
ENDL;
1946 QString cameraName =
models_[0].name().c_str();
1947 QString filePath = QFileDialog::getSaveFileName(
this, tr(
"Export"),
savingDirectory_+
"/"+cameraName+
".yaml",
"*.yaml");
1949 if(!filePath.isEmpty())
1951 QString
name = QFileInfo(filePath).baseName();
1952 QString dir = QFileInfo(filePath).absoluteDir().absolutePath();
1956 QMessageBox::information(
this, tr(
"Export"), tr(
"Calibration file saved to \"%1\".").
arg(filePath));
1957 UINFO(
"Saved \"%s\"!", filePath.toStdString().c_str());
1963 UERROR(
"Error saving \"%s\"", filePath.toStdString().c_str());
1972 QString filePath = QFileDialog::getSaveFileName(
this, tr(
"Export"),
savingDirectory_ +
"/" + cameraName,
"*.yaml");
1973 QString
name = QFileInfo(filePath).baseName();
1974 QString dir = QFileInfo(filePath).absoluteDir().absolutePath();
1977 bool switched =
ui_->checkBox_switchImages->isChecked();
1979 std::string
base = (dir+QDir::separator()+
name).toStdString();
1982 std::string posePath =
base+
"_pose.yaml";
1985 QMessageBox::information(
this, tr(
"Export"), tr(
"Calibration files saved:\n \"%1\"\n \"%2\"\n \"%3\".").
1986 arg(leftPath.c_str()).
arg(rightPath.c_str()).arg(posePath.c_str()));
1987 UINFO(
"Saved \"%s\" and \"%s\"!", leftPath.c_str(), rightPath.c_str());
1993 UERROR(
"Error saving \"%s\" and \"%s\"", leftPath.c_str(), rightPath.c_str());
2007 cv::Point2f up_left;
2008 cv::Point2f up_right;
2009 cv::Point2f down_right;
2010 cv::Point2f down_left;
2011 if((
int)
corners.size() == (boardSize.width * boardSize.height))
2014 up_right =
corners[boardSize.width-1];
2020 cv::Rect rect = cv::boundingRect(
corners);
2021 up_left = cv::Point2f(rect.x, rect.y);
2022 up_right = cv::Point2f(rect.x+rect.width, rect.y);
2023 down_right = cv::Point2f(rect.x+rect.width, rect.y+rect.height);
2024 down_left = cv::Point2f(rect.x, rect.y+rect.height);
2026 cv::Point2f
a = up_right - up_left;
2027 cv::Point2f
b = down_right - up_right;
2028 cv::Point2f
c = down_left - down_right;
2029 cv::Point2f
p =
b +
c;
2030 cv::Point2f
q =
a +
b;
2031 return std::fabs(
p.x*
q.y -
p.y*
q.x) / 2.0f;
2036 UASSERT(fourCorners.size() == 4);
2037 std::vector<cv::Point2f>
corners = fourCorners;
2048 cv::Point2f up_left =
corners[0];
2049 cv::Point2f up_right =
corners[boardSize.width-1];
2053 cv::Point2f ab = up_left - up_right;
2054 cv::Point2f cb = down_right - up_right;
2055 float angle =
std::acos(ab.dot(cb) / (cv::norm(ab) * cv::norm(cb)));
2057 float r = 2.0f * std::fabs((CV_PI / 2.0
f) -
angle);
2058 return r > 1.0f?1.0f:r;
2066 float & x,
float & y,
float & size,
float & skew)
2069 size =
std::sqrt(area / (imageSize.width * imageSize.height));
2080 x = meanX / imageSize.width;
2081 y = meanY / imageSize.height;