From df4947220c74f86fceb7247f3904820228143bf4 Mon Sep 17 00:00:00 2001 From: "d.kilic" <d.kilic@fz-juelich.de> Date: Mon, 27 Mar 2023 10:35:30 +0200 Subject: [PATCH] Gray out calib (3/N) - Introduces `ExtrinsicParameters`-Struct - Extract `ExtrinsicBox` from `Control` --- CMakeLists.txt | 6 +- include/control.h | 36 +- include/extrCalibration.h | 51 +- include/extrinsicBox.h | 82 ++++ include/extrinsicParameters.h | 39 ++ src/borderFilter.cpp | 22 +- src/control.cpp | 319 ++---------- src/extrCalibration.cpp | 619 ++++++++++++------------ src/extrinsicBox.cpp | 301 ++++++++++++ src/imageItem.cpp | 7 +- src/petrack.cpp | 2 +- src/recognition.cpp | 5 +- src/trackerItem.cpp | 16 +- tests/unit_test/CMakeLists.txt | 1 + tests/unit_test/tst_extrCalibration.cpp | 45 +- tests/unit_test/tst_extrinsicBox.cpp | 115 +++++ ui/control.ui | 334 +------------ ui/extrinsicBox.ui | 354 ++++++++++++++ 18 files changed, 1318 insertions(+), 1036 deletions(-) create mode 100644 include/extrinsicBox.h create mode 100644 include/extrinsicParameters.h create mode 100644 src/extrinsicBox.cpp create mode 100644 tests/unit_test/tst_extrinsicBox.cpp create mode 100644 ui/extrinsicBox.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 97ab9fcb7..dbb91fbf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -390,7 +390,9 @@ target_sources(petrack_core PRIVATE include/filterBeforeBox.h include/intrinsicBox.h include/intrinsicCameraParams.h - ) + include/extrinsicBox.h + include/extrinsicParameters.h +) target_sources(petrack_core PRIVATE src/aboutDialog.cpp @@ -456,6 +458,7 @@ target_sources(petrack_core PRIVATE src/intrinsicBox.cpp src/filterBeforeBox.cpp src/intrinsicCameraParams.cpp + src/extrinsicBox.cpp ui/about.ui ui/codeMarker.ui ui/colorMarker.ui @@ -467,6 +470,7 @@ target_sources(petrack_core PRIVATE ui/moCapSelectionWidget.ui ui/filterBeforeBox.ui ui/intrinsicBox.ui + ui/extrinsicBox.ui ) target_sources(petrack PRIVATE diff --git a/include/control.h b/include/control.h index 01078b60a..1d060350d 100644 --- a/include/control.h +++ b/include/control.h @@ -21,6 +21,7 @@ #include "analysePlot.h" #include "colorPlot.h" +#include "extrinsicParameters.h" #include "intrinsicCameraParams.h" #include "petrack.h" #include "recognition.h" @@ -31,6 +32,7 @@ class QGraphicsScene; class QDomElement; class IntrinsicBox; +class ExtrinsicBox; class FilterBeforeBox; namespace Ui { @@ -152,19 +154,7 @@ public: void setCalibReprError(double d); void imageSizeChanged(int width, int height, int borderDiff); - double getCalibExtrRot1(); - void setCalibExtrRot1(double d); - double getCalibExtrRot2(); - void setCalibExtrRot2(double d); - double getCalibExtrRot3(); - void setCalibExtrRot3(double d); - double getCalibExtrTrans1(); - void setCalibExtrTrans1(double d); - double getCalibExtrTrans2(); - void setCalibExtrTrans2(double d); - double getCalibExtrTrans3(); - void setCalibExtrTrans3(double d); - void setEnabledExtrParams(bool enable); + const ExtrinsicParameters &getExtrinsicParameters() const; int getCalibCoordDimension(); bool getCalibExtrCalibPointsShow(); @@ -242,7 +232,7 @@ public: double getCameraAltitude() const; void setXml(QDomElement &elem); - void getXml(QDomElement &elem); + void getXml(const QDomElement &elem); bool isLoading() const { return mMainWindow->isLoading(); } ColorPlot *getColorPlot() const; void replotColorplot(); @@ -561,7 +551,6 @@ private slots: void on_coordAltitude_valueChanged(double d); void on_coordUnit_valueChanged(double d); void on_coordUseIntrinsic_stateChanged(int i); - void on_coordLoad3DCalibPoints_clicked(); void setMeasuredAltitude(); @@ -587,22 +576,10 @@ private slots: void on_coord3DTransZ_valueChanged(int value); void on_coord3DAxeLen_valueChanged(int value); - void on_rot1_valueChanged(double arg1); - void on_rot2_valueChanged(double arg1); - void on_rot3_valueChanged(double arg1); - void on_trans1_valueChanged(double arg1); - void on_trans2_valueChanged(double arg1); - void on_trans3_valueChanged(double arg1); - void on_coord3DSwapX_stateChanged(int arg1); void on_coord3DSwapY_stateChanged(int arg1); void on_coord3DSwapZ_stateChanged(int arg1); - void on_extrCalibSave_clicked(); - void on_extrCalibFetch_clicked(); - void on_extrCalibShowPoints_clicked(); - void on_extrCalibShowError_clicked(); - void on_trackPathColorButton_clicked(); void on_trackGroundPathColorButton_clicked(); void on_moCapColorButton_clicked(); @@ -635,11 +612,12 @@ private: Ui::Control *mUi; IntrinsicBox *mIntr; FilterBeforeBox *mFilterBefore; + ExtrinsicBox *mExtr; QGraphicsScene *mScene; bool mColorChanging; bool mIndexChanging; // shows, if the index of the color model is really changing; nor while constructor (initialer - // durchlauf) and may be while loading xml file - bool mLoading; // shows, if new project is just loading + // durchlauf) and may be while loading xml file + bool mLoading; // shows, if new project is just loading }; #endif diff --git a/include/extrCalibration.h b/include/extrCalibration.h index 3f811de80..a32071a3e 100644 --- a/include/extrCalibration.h +++ b/include/extrCalibration.h @@ -19,11 +19,14 @@ #ifndef EXTRCALIBRATION_H #define EXTRCALIBRATION_H +#include "extrinsicParameters.h" + #include <QDomElement> #include <QString> #include <array> #include <iostream> #include <opencv2/opencv.hpp> +#include <optional> #include <vector> class Petrack; @@ -165,30 +168,30 @@ private: public: ExtrCalibration(PersonStorage &storage); ~ExtrCalibration(); - void setMainWindow(Petrack *mw); - bool isEmptyExtrCalibFile(); - bool isSetExtrCalib(); - void setExtrCalibFile(const QString &f); - QString getExtrCalibFile(); - bool openExtrCalibFile(); - bool loadExtrCalibFile(); - bool saveExtrCalibPoints(); - bool fetch2DPoints(); - void calibExtrParams(); - bool calcReprojectionError(); - virtual cv::Point2f getImagePoint(cv::Point3f p3d); - cv::Point3f get3DPoint(const cv::Point2f &p2d, double h) const; - cv::Point3f transformRT(cv::Point3f p); - cv::Vec3d camToWorldRotation(const cv::Vec3d &vec) const; - bool isOutsideImage(cv::Point2f p2d); - inline bool isOutsideImage(cv::Point3f p3d) { return isOutsideImage(getImagePoint(p3d)); } - inline std::vector<cv::Point3f> get3DList() { return points3D; } - inline void set3DList(std::vector<cv::Point3f> list3D) { this->points3D = list3D; } - inline std::vector<cv::Point2f> get2DList() { return points2D; } - inline void set2DList(std::vector<cv::Point2f> list2D) { this->points2D = list2D; } - inline float getCamHeight() const { return camHeight; } - inline void setCamHeight(float cHeight) { this->camHeight = cHeight; } - inline ReprojectionError getReprojectionError() + void setMainWindow(Petrack *mw); + bool isEmptyExtrCalibFile(); + bool isSetExtrCalib(); + void setExtrCalibFile(const QString &f); + QString getExtrCalibFile(); + std::optional<ExtrinsicParameters> openExtrCalibFile(); + std::optional<ExtrinsicParameters> loadExtrCalibFile(); + bool saveExtrCalibPoints(); + std::optional<ExtrinsicParameters> fetch2DPoints(); + std::optional<ExtrinsicParameters> calibExtrParams(); + bool calcReprojectionError(); + virtual cv::Point2f getImagePoint(cv::Point3f p3d); + cv::Point3f get3DPoint(const cv::Point2f &p2d, double h) const; + cv::Point3f transformRT(cv::Point3f p); + cv::Vec3d camToWorldRotation(const cv::Vec3d &vec) const; + bool isOutsideImage(cv::Point2f p2d); + inline bool isOutsideImage(cv::Point3f p3d) { return isOutsideImage(getImagePoint(p3d)); } + inline std::vector<cv::Point3f> get3DList() { return points3D; } + inline void set3DList(std::vector<cv::Point3f> list3D) { this->points3D = list3D; } + inline std::vector<cv::Point2f> get2DList() { return points2D; } + inline void set2DList(std::vector<cv::Point2f> list2D) { this->points2D = list2D; } + inline float getCamHeight() const { return camHeight; } + inline void setCamHeight(float cHeight) { this->camHeight = cHeight; } + inline ReprojectionError getReprojectionError() { if(!reprojectionError.isValid()) { diff --git a/include/extrinsicBox.h b/include/extrinsicBox.h new file mode 100644 index 000000000..fd50a615d --- /dev/null +++ b/include/extrinsicBox.h @@ -0,0 +1,82 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef EXTRINSICBOX_H +#define EXTRINSICBOX_H + +#include "extrinsicParameters.h" + +#include <QGroupBox> + +namespace Ui +{ +class extr; +} +class QDomElement; +class ExtrCalibration; + + +class ExtrinsicBox : public QGroupBox +{ + Q_OBJECT + +public: + explicit ExtrinsicBox( + QWidget *parent, + Ui::extr *ui, + ExtrCalibration &extrCalib, + std::function<void()> updateCoordCallback); + explicit ExtrinsicBox(QWidget *parent, ExtrCalibration &extrCalib, std::function<void()> updateCoordCallback); + ExtrinsicBox(const ExtrinsicBox &other) = delete; + ExtrinsicBox(ExtrinsicBox &&other) = delete; + ExtrinsicBox &operator=(const ExtrinsicBox &other) = delete; + ExtrinsicBox &operator=(ExtrinsicBox &&other) = delete; + ~ExtrinsicBox() override; + + void setEnabledExtrParams(bool enable); + const ExtrinsicParameters &getExtrinsicParameters() const; + void setExtrinsicParameters(const ExtrinsicParameters ¶ms); + + bool getXml(QDomElement &subSubElem); + void setXml(QDomElement &subElem) const; + +private slots: + + void on_extrCalibFetch_clicked(); + void on_coordLoad3DCalibPoints_clicked(); + + void on_rot1_valueChanged(double newVal); + void on_rot2_valueChanged(double newVal); + void on_rot3_valueChanged(double newVal); + void on_trans1_valueChanged(double newVal); + void on_trans2_valueChanged(double newVal); + void on_trans3_valueChanged(double newVal); + + + void on_extrCalibSave_clicked(); + void on_extrCalibShowPoints_clicked(); + void on_extrCalibShowError_clicked(); + +private: + Ui::extr *mUi; + ExtrCalibration &mExtrCalibration; + ExtrinsicParameters mParams; + std::function<void()> mUpdateCoordCallback; +}; + +#endif // EXTRINSICBOX_H diff --git a/include/extrinsicParameters.h b/include/extrinsicParameters.h new file mode 100644 index 000000000..689b8a9a2 --- /dev/null +++ b/include/extrinsicParameters.h @@ -0,0 +1,39 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef EXTRINSICPARAMETERS_H +#define EXTRINSICPARAMETERS_H + +struct ExtrinsicParameters +{ + double trans1 = 0; + double trans2 = 0; + double trans3 = -500; + + double rot1 = 0; + double rot2 = 0; + double rot3 = 0; + + friend bool operator==(const ExtrinsicParameters &lhs, const ExtrinsicParameters &rhs) + { + return lhs.rot1 == rhs.rot1 && lhs.rot2 == rhs.rot2 && lhs.rot3 == rhs.rot3 && lhs.trans1 == rhs.trans1 && + lhs.trans2 == rhs.trans2 && lhs.trans3 == rhs.trans3; + } +}; + +#endif // EXTRINSICPARAMETERS_H diff --git a/src/borderFilter.cpp b/src/borderFilter.cpp index de1a98bc7..3417c3175 100644 --- a/src/borderFilter.cpp +++ b/src/borderFilter.cpp @@ -21,21 +21,21 @@ BorderFilter::BorderFilter() : Filter() { - mSize.setMinimum(0.); + mSize.setMinimum(0); mSize.setMaximum(750); - mSize.setValue(0.); + mSize.setValue(0); - mRed.setMinimum(0.); - mRed.setMaximum(200.); - mRed.setValue(0.); + mRed.setMinimum(0); + mRed.setMaximum(200); + mRed.setValue(0); - mGreen.setMinimum(0.); - mGreen.setMaximum(200.); - mGreen.setValue(0.); + mGreen.setMinimum(0); + mGreen.setMaximum(200); + mGreen.setValue(0); - mBlue.setMinimum(0.); - mBlue.setMaximum(200.); - mBlue.setValue(0.); + mBlue.setMinimum(0); + mBlue.setMaximum(200); + mBlue.setValue(0); setOnCopy(false); } diff --git a/src/control.cpp b/src/control.cpp index 8c0061d70..f346430b2 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -26,6 +26,7 @@ #include "colorMarkerWidget.h" #include "colorPlot.h" #include "colorRangeWidget.h" +#include "extrinsicBox.h" #include "filterBeforeBox.h" #include "imageItem.h" #include "intrinsicBox.h" @@ -120,10 +121,25 @@ Control::Control( ui->verticalLayout_13->insertWidget(1, mIntr); + mExtr = new ExtrinsicBox( + this, + *mMainWindow->getExtrCalibration(), + [this]() + { + if(!isLoading()) + { + mMainWindow->updateCoord(); + mScene->update(); + } + }); + mExtr->setObjectName(QString::fromUtf8("extr")); + ui->verticalLayout_13->insertWidget(2, mExtr); + // integrate new widgets in tabbing order - ui->calib->setFocusProxy(ui->rot1); + ui->calib->setFocusProxy(ui->coordShow); QWidget::setTabOrder(mFilterBefore, mIntr); - QWidget::setTabOrder(mIntr, ui->calib); + QWidget::setTabOrder(mIntr, mExtr); + QWidget::setTabOrder(mExtr, ui->calib); connect(mIntr, &IntrinsicBox::paramsChanged, this, &Control::on_intrinsicParamsChanged); @@ -652,64 +668,9 @@ void Control::imageSizeChanged(int width, int height, int borderDiff) mIntr->imageSizeChanged(width, height, borderDiff); } -double Control::getCalibExtrRot1() -{ - return mUi->rot1->value(); -} - -void Control::setCalibExtrRot1(double d) -{ - mUi->rot1->setValue(d); -} - -double Control::getCalibExtrRot2() -{ - return mUi->rot2->value(); -} - -void Control::setCalibExtrRot2(double d) -{ - mUi->rot2->setValue(d); -} - -double Control::getCalibExtrRot3() -{ - return mUi->rot3->value(); -} - -void Control::setCalibExtrRot3(double d) -{ - mUi->rot3->setValue(d); -} - -double Control::getCalibExtrTrans1() -{ - return mUi->trans1->value(); -} - -void Control::setCalibExtrTrans1(double d) -{ - mUi->trans1->setValue(d); -} - -double Control::getCalibExtrTrans2() -{ - return mUi->trans2->value(); -} - -void Control::setCalibExtrTrans2(double d) -{ - mUi->trans2->setValue(d); -} - -double Control::getCalibExtrTrans3() -{ - return mUi->trans3->value(); -} - -void Control::setCalibExtrTrans3(double d) +const ExtrinsicParameters &Control::getExtrinsicParameters() const { - mUi->trans3->setValue(d); + return mExtr->getExtrinsicParameters(); } @@ -778,16 +739,6 @@ void Control::setCalibGridScale(int i) mUi->gridScale->setValue(i); } -void Control::setEnabledExtrParams(bool enable) -{ - mUi->rot1->setEnabled(enable); - mUi->rot2->setEnabled(enable); - mUi->rot3->setEnabled(enable); - mUi->trans1->setEnabled(enable); - mUi->trans2->setEnabled(enable); - mUi->trans3->setEnabled(enable); -} - void Control::setGridMinMaxTranslation(int minx, int maxx, int miny, int maxy) { mUi->grid3DTransX->setMinimum(minx); @@ -1929,53 +1880,6 @@ void Control::on_intrinsicParamsChanged(IntrinsicCameraParams params) //--------------------------------------- -void Control::on_rot1_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} - -void Control::on_rot2_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} - -void Control::on_rot3_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} - -void Control::on_trans1_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} - -void Control::on_trans2_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} - -void Control::on_trans3_valueChanged(double /*arg1*/) -{ - if(!isLoading()) - { - mMainWindow->updateCoord(); - } -} void Control::on_extCalibPointsShow_stateChanged(int /*arg1*/) { @@ -1985,65 +1889,6 @@ void Control::on_extCalibPointsShow_stateChanged(int /*arg1*/) } } -void Control::on_extrCalibShowError_clicked() -{ - QString out; - QDialog msgBox; - QGridLayout *layout = new QGridLayout(); - msgBox.setLayout(layout); - QLabel *tableView = new QLabel(&msgBox); - layout->addWidget(tableView, 1, 1); - QLabel *titel = new QLabel(&msgBox); - titel->setText("<b>Reprojection error for extrinsic calibration:</b>"); - layout->addWidget(titel, 0, 1); - - if(!mMainWindow->getExtrCalibration()->getReprojectionError().isValid()) - { - out = QString("No File for extrinsic calibration found!"); - tableView->setText(out); - } - else - { - out = QString("<table>" - "<tr><th></th>" - "<th>average </th>" - "<th>std. deviation </th>" - "<th>variance </th>" - "<th>max </th></tr>" - "<tr><td>Point height: </td><td> %0 cm</td><td> %1 cm</td><td> %2 " - "cm</td><td> %3 cm</td></tr>" - "<tr><td>Default height: <small>[%12 cm]</small> </td><td> %4 cm</td><td> %5 cm</td><td> %6 " - "cm</td><td> %7 cm</td></tr>" - "<tr><td>Pixel error: </td><td> %8 px</td><td> %9 px</td><td> %10 " - "px</td><td> %11 px</td></tr>" - "</table>"); - const auto &reproError = mMainWindow->getExtrCalibration()->getReprojectionError().getData(); - for(double value : reproError) - { - if(value < 0) - { - out = out.arg("-"); - } - else - { - out = out.arg(value); - } - } - tableView->setText(out); - } - - msgBox.setWindowTitle("PeTrack"); - QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); - QLabel *infoIcon = new QLabel(&msgBox); - int iconSize = msgBox.style()->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, &msgBox); - infoIcon->setPixmap(icon.pixmap(iconSize, iconSize)); - layout->addWidget(infoIcon, 0, 0); - QDialogButtonBox *ok = new QDialogButtonBox(QDialogButtonBox::Ok); - layout->addWidget(ok, 2, 1); - connect(ok, &QDialogButtonBox::clicked, &msgBox, &QDialog::close); - msgBox.setFixedSize(msgBox.sizeHint()); - msgBox.exec(); -} void Control::on_extVanishPointsShow_stateChanged(int /*arg1*/) { @@ -2052,73 +1897,7 @@ void Control::on_extVanishPointsShow_stateChanged(int /*arg1*/) mScene->update(); } } -void Control::on_coordLoad3DCalibPoints_clicked() -{ - mMainWindow->getExtrCalibration()->openExtrCalibFile(); - - mMainWindow->updateCoord(); - mScene->update(); -} - -void Control::on_extrCalibSave_clicked() -{ - mMainWindow->getExtrCalibration()->saveExtrCalibPoints(); -} - -void Control::on_extrCalibFetch_clicked() -{ - mMainWindow->getExtrCalibration()->fetch2DPoints(); -} - -void Control::on_extrCalibShowPoints_clicked() -{ - QString out_str; - QTextStream out(&out_str); - unsigned int i; - - out << "<table><tr><th>Nr.</th><th>3D.x</th><th>3D.y</th><th>3D.z</th><th>2D.x</th><th>2D.y</th></tr>" << Qt::endl; - - - for(i = 0; i < std::max( - mMainWindow->getExtrCalibration()->get3DList().size(), - mMainWindow->getExtrCalibration()->get2DList().size()); - ++i) - { - out << "<tr>"; - if(i < mMainWindow->getExtrCalibration()->get3DList().size()) - { - out << "<td>[" << QString::number(i + 1, 'i', 0) << "]: </td><td>" - << QString::number(mMainWindow->getExtrCalibration()->get3DList().at(i).x, 'f', 1) << "</td><td>" - << QString::number(mMainWindow->getExtrCalibration()->get3DList().at(i).y, 'f', 1) << "</td><td>" - << QString::number(mMainWindow->getExtrCalibration()->get3DList().at(i).z, 'f', 1) << "</td><td>"; - } - else - { - out << "<td>-</td><td>-</td><td>-</td>"; - } - if(i < mMainWindow->getExtrCalibration()->get2DList().size()) - { - out << QString::number(mMainWindow->getExtrCalibration()->get2DList().at(i).x, 'f', 3) << "</td><td>" - << QString::number(mMainWindow->getExtrCalibration()->get2DList().at(i).y, 'f', 3) << "</td>"; - } - else - { - out << "<td>-</td><td>-</td>"; - } - out << "</tr>" << Qt::endl; - } - out << "</table>" << Qt::endl; - - QMessageBox msgBox; - msgBox.setWindowTitle("PeTrack"); - msgBox.setIcon(QMessageBox::Information); - msgBox.setText("Currently loaded point correspondences<br />for extrinsic calibration:"); - msgBox.setInformativeText(out_str); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); -} //--------------------------------------- void Control::on_gridTab_currentChanged(int /*index*/) @@ -2193,13 +1972,13 @@ void Control::on_coordTab_currentChanged(int index) { if(index == 1) { - setEnabledExtrParams(false); + mExtr->setEnabledExtrParams(false); mUi->trackShowGroundPosition->setEnabled(false); mUi->trackShowGroundPath->setEnabled(false); } else { - setEnabledExtrParams(true); + mExtr->setEnabledExtrParams(true); mUi->trackShowGroundPosition->setEnabled(true); mUi->trackShowGroundPath->setEnabled(true); } @@ -2378,14 +2157,8 @@ void Control::setXml(QDomElement &elem) // PATTERN and INTRINSIC_PARAMETERS elements mIntr->setXml(subElem); - subSubElem = (elem.ownerDocument()).createElement("EXTRINSIC_PARAMETERS"); - - subSubElem.setAttribute("EXTR_ROT_1", mUi->rot1->value()); - subSubElem.setAttribute("EXTR_ROT_2", mUi->rot2->value()); - subSubElem.setAttribute("EXTR_ROT_3", mUi->rot3->value()); - subSubElem.setAttribute("EXTR_TRANS_1", mUi->trans1->value()); - subSubElem.setAttribute("EXTR_TRANS_2", mUi->trans2->value()); - subSubElem.setAttribute("EXTR_TRANS_3", mUi->trans3->value()); + subSubElem = (subElem.ownerDocument()).createElement("EXTRINSIC_PARAMETERS"); + mExtr->setXml(subSubElem); subSubElem.setAttribute("SHOW_CALIB_POINTS", mUi->extCalibPointsShow->isChecked()); @@ -2688,7 +2461,7 @@ void Control::setXml(QDomElement &elem) } // read data from xml node -void Control::getXml(QDomElement &elem) +void Control::getXml(const QDomElement &elem) { QDomElement subElem, subSubElem, subSubSubElem; @@ -2711,6 +2484,10 @@ void Control::getXml(QDomElement &elem) { // intentionally left blank } + else if(mExtr->getXml(subSubElem)) + { + // intentionally left blank + } else if(subSubElem.tagName() == "BORDER") { if(subSubElem.hasAttribute("COLOR")) @@ -2742,30 +2519,6 @@ void Control::getXml(QDomElement &elem) } else if(subSubElem.tagName() == "EXTRINSIC_PARAMETERS") { - if(subSubElem.hasAttribute("EXTR_ROT_1")) - { - mUi->rot1->setValue(subSubElem.attribute("EXTR_ROT_1").toDouble()); - } - if(subSubElem.hasAttribute("EXTR_ROT_2")) - { - mUi->rot2->setValue(subSubElem.attribute("EXTR_ROT_2").toDouble()); - } - if(subSubElem.hasAttribute("EXTR_ROT_3")) - { - mUi->rot3->setValue(subSubElem.attribute("EXTR_ROT_3").toDouble()); - } - if(subSubElem.hasAttribute("EXTR_TRANS_1")) - { - mUi->trans1->setValue(subSubElem.attribute("EXTR_TRANS_1").toDouble()); - } - if(subSubElem.hasAttribute("EXTR_TRANS_2")) - { - mUi->trans2->setValue(subSubElem.attribute("EXTR_TRANS_2").toDouble()); - } - if(subSubElem.hasAttribute("EXTR_TRANS_3")) - { - mUi->trans3->setValue(subSubElem.attribute("EXTR_TRANS_3").toDouble()); - } if(subSubElem.hasAttribute("SHOW_CALIB_POINTS")) { mUi->extCalibPointsShow->setCheckState( @@ -2779,19 +2532,7 @@ void Control::getXml(QDomElement &elem) else { mUi->coordTab->setCurrentIndex(1); // = 2D - setEnabledExtrParams(false); - } - if(subSubElem.hasAttribute("EXTERNAL_CALIB_FILE")) - { - if(getExistingFile( - QString::fromStdString(subSubElem.attribute("EXTERNAL_CALIB_FILE").toStdString()), - mMainWindow->getProFileName()) != "") - { - mMainWindow->getExtrCalibration()->setExtrCalibFile(getExistingFile( - QString::fromStdString(subSubElem.attribute("EXTERNAL_CALIB_FILE").toStdString()), - mMainWindow->getProFileName())); - mMainWindow->getExtrCalibration()->loadExtrCalibFile(); - } + mExtr->setEnabledExtrParams(false); } if(subSubElem.hasAttribute("SHOW")) diff --git a/src/extrCalibration.cpp b/src/extrCalibration.cpp index 2706e6f1f..badbc37fe 100644 --- a/src/extrCalibration.cpp +++ b/src/extrCalibration.cpp @@ -75,7 +75,7 @@ QString ExtrCalibration::getExtrCalibFile() } } -bool ExtrCalibration::openExtrCalibFile() +std::optional<ExtrinsicParameters> ExtrCalibration::openExtrCalibFile() { if(mMainWindow) { @@ -97,7 +97,7 @@ bool ExtrCalibration::openExtrCalibFile() return loadExtrCalibFile(); } } - return false; + return std::nullopt; } // following function copied from OpenCV @@ -152,195 +152,190 @@ static bool isPlanarObjectPoints(cv::InputArray _objectPoints, double threshold * * @return */ -bool ExtrCalibration::loadExtrCalibFile() +std::optional<ExtrinsicParameters> ExtrCalibration::loadExtrCalibFile() { - bool all_ok = true; + if(mExtrCalibFile.isEmpty()) + { + return std::nullopt; + } - if(!mExtrCalibFile.isEmpty()) + if(mExtrCalibFile.right(4) != ".3dc" && mExtrCalibFile.right(4) != ".txt") { - if(mExtrCalibFile.right(4) == ".3dc" || mExtrCalibFile.right(4) == ".txt") - { - QFile file(mExtrCalibFile); - if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - PCritical( - mMainWindow, - QObject::tr("Petrack"), - QObject::tr("Error: Cannot open %1:\n%2.").arg(mExtrCalibFile, file.errorString())); - return false; - } + PWarning(nullptr, "Unsupported File Type", "Unsupported file extension (supported: .3dc, .txt)"); + return std::nullopt; + } + + QFile file(mExtrCalibFile); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + PCritical( + mMainWindow, + QObject::tr("Petrack"), + QObject::tr("Error: Cannot open %1:\n%2.").arg(mExtrCalibFile, file.errorString())); + return std::nullopt; + } + + SPDLOG_INFO("Reading 3D calibration data from {} ...", mExtrCalibFile); + + std::vector<cv::Point3f> points3D_tmp; + std::vector<cv::Point2f> points2D_tmp; - SPDLOG_INFO("Reading 3D calibration data from {} ...", mExtrCalibFile); + QTextStream in(&file); + QString line; + int line_counter = 0, counter; + float x, y, z, px, py; + float zahl; + bool with_2D_data = false, with_3D_data = false, end_loop = false; - std::vector<cv::Point3f> points3D_tmp; - std::vector<cv::Point2f> points2D_tmp; + // Exit loop when reaching the end of the file + while(!in.atEnd()) + { + // Neue Zeile einlesen + line = in.readLine(); + ++line_counter; + + // Kommentare ueberlesen + if(line.startsWith("#", Qt::CaseInsensitive) || line.startsWith(";;", Qt::CaseInsensitive) || + line.startsWith("//", Qt::CaseInsensitive) || line.startsWith("!", Qt::CaseInsensitive)) + { + continue; + } - QTextStream in(&file); - QString line; - int line_counter = 0, counter; - float x, y, z, px, py; - float zahl; - bool with_2D_data = false, with_3D_data = false, end_loop = false; + QTextStream stream(&line); + counter = 0; + end_loop = false; - // Exit loop when reaching the end of the file - while(!in.atEnd()) + while(!stream.atEnd() && !end_loop) + { + stream >> zahl; + ++counter; + + switch(counter) { - // Neue Zeile einlesen - line = in.readLine(); - ++line_counter; - - // Kommentare ueberlesen - if(line.startsWith("#", Qt::CaseInsensitive) || line.startsWith(";;", Qt::CaseInsensitive) || - line.startsWith("//", Qt::CaseInsensitive) || line.startsWith("!", Qt::CaseInsensitive)) - { - continue; - } - - QTextStream stream(&line); - counter = 0; - end_loop = false; - - while(!stream.atEnd() && !end_loop) - { - stream >> zahl; - ++counter; - - switch(counter) + case 1: + x = zahl; + if(!with_3D_data) { - case 1: - x = zahl; - if(!with_3D_data) - { - points3D_tmp.clear(); - with_3D_data = true; - } - break; - case 2: - y = zahl; - break; - case 3: - z = zahl; - break; - case 4: - px = zahl; - if(!with_2D_data) - { - points2D_tmp.clear(); - with_2D_data = true; - } - break; - case 5: - py = zahl; - break; - default: - end_loop = true; + points3D_tmp.clear(); + with_3D_data = true; } - } - if(counter == 1) - { - SPDLOG_INFO("Optional number of points in line {} ignored.", line_counter); - } - else if(counter != 3 && counter != 5) - { - SPDLOG_INFO("Something wrong in line {} ({})! Ignored. (counter={})", line_counter, line, counter); - } - - // 3D daten abspeichern - if(with_3D_data && (counter == 3 || counter == 5)) - { - points3D_tmp.push_back(cv::Point3f(x, y, z)); - } - // 2D daten abspeichern - if(with_2D_data && counter == 5) - { - points2D_tmp.push_back(cv::Point2f(px, py)); - } - } - // Check if there are more than 4 points for calibration in the file - if(points3D_tmp.size() < 4) - { - PCritical( - mMainWindow, - QObject::tr("PeTrack"), - QObject::tr("Error: Not enough points given: %1 (minimum 4 (coplanar) or 6 (not coplanar) " - "needed!). Please check your extrinsic " - "calibration file!") - .arg(points3D_tmp.size())); - all_ok = false; - } - else if(!isPlanarObjectPoints(points3D_tmp) && points3D_tmp.size() < 6) - { - // Non-planar points use DLT - we need at least 6 points; not only 4 - PCritical( - mMainWindow, - QObject::tr("PeTrack"), - QObject::tr("Error: Not enough points given: %1 (minimum 4 (coplanar) or 6 (not coplanar) " - "needed!). Please check your extrinsic " - "calibration file!") - .arg(points3D_tmp.size())); - all_ok = false; - } - // Check if 2D points delivered and if the number of 2D and 3D points agree - else if(points2D_tmp.size() > 0 && points2D_tmp.size() != points3D_tmp.size()) - { - PCritical( - mMainWindow, - QObject::tr("PeTrack"), - QObject::tr( - "Error: Unsupported File Format in: %1 (number of 3D (%2) and 2D (%3) points disagree!)") - .arg(mExtrCalibFile) - .arg(points3D_tmp.size()) - .arg(points2D_tmp.size())); - all_ok = false; - } - // Check if number of loaded 3D points agree with stored 2D points - else if(!with_2D_data && points2D.size() > 0 && points3D_tmp.size() != points2D.size()) - { - // ask if stored 2D points should be deleted? - int result = PWarning( - mMainWindow, - QObject::tr("PeTrack"), - QObject::tr("Number of 3D points (%1) disagree with number of stored 2D points (%2)!<br />The 2D " - "points will be deleted! You have to fetch new ones from the image!") - .arg(points3D_tmp.size()) - .arg(points2D.size()), - PMessageBox::StandardButton::Ok | PMessageBox::StandardButton::Abort); - if(result != PMessageBox::StandardButton::Ok) - { - all_ok = false; - } - else - { - points2D.clear(); - } - } - if(all_ok) - { - if(with_3D_data) - { - points3D = points3D_tmp; - } - if(with_2D_data) - { - points2D = points2D_tmp; - } + break; + case 2: + y = zahl; + break; + case 3: + z = zahl; + break; + case 4: + px = zahl; + if(!with_2D_data) + { + points2D_tmp.clear(); + with_2D_data = true; + } + break; + case 5: + py = zahl; + break; + default: + end_loop = true; } } + if(counter == 1) + { + SPDLOG_INFO("Optional number of points in line {} ignored.", line_counter); + } + else if(counter != 3 && counter != 5) + { + SPDLOG_INFO("Something wrong in line {} ({})! Ignored. (counter={})", line_counter, line, counter); + } + + // 3D daten abspeichern + if(with_3D_data && (counter == 3 || counter == 5)) + { + points3D_tmp.push_back(cv::Point3f(x, y, z)); + } + // 2D daten abspeichern + if(with_2D_data && counter == 5) + { + points2D_tmp.push_back(cv::Point2f(px, py)); + } + } + // Check if there are more than 4 points for calibration in the file + if(points3D_tmp.size() < 4) + { + PCritical( + mMainWindow, + QObject::tr("PeTrack"), + QObject::tr("Error: Not enough points given: %1 (minimum 4 (coplanar) or 6 (not coplanar) " + "needed!). Please check your extrinsic " + "calibration file!") + .arg(points3D_tmp.size())); + return std::nullopt; + } + + // Non-planar points use DLT - we need at least 6 points; not only 4 + if(!isPlanarObjectPoints(points3D_tmp) && points3D_tmp.size() < 6) + { + PCritical( + mMainWindow, + QObject::tr("PeTrack"), + QObject::tr("Error: Not enough points given: %1 (minimum 4 (coplanar) or 6 (not coplanar) " + "needed!). Please check your extrinsic " + "calibration file!") + .arg(points3D_tmp.size())); + return std::nullopt; + } + + // Check if 2D points delivered and if the number of 2D and 3D points agree + if(points2D_tmp.size() > 0 && points2D_tmp.size() != points3D_tmp.size()) + { + PCritical( + mMainWindow, + QObject::tr("PeTrack"), + QObject::tr("Error: Unsupported File Format in: %1 (number of 3D (%2) and 2D (%3) points disagree!)") + .arg(mExtrCalibFile) + .arg(points3D_tmp.size()) + .arg(points2D_tmp.size())); + return std::nullopt; + } + + // Check if number of loaded 3D points agree with stored 2D points + if(!with_2D_data && points2D.size() > 0 && points3D_tmp.size() != points2D.size()) + { + // ask if stored 2D points should be deleted? + int result = PWarning( + mMainWindow, + QObject::tr("PeTrack"), + QObject::tr("Number of 3D points (%1) disagree with number of stored 2D points (%2)!<br />The 2D " + "points will be deleted! You have to fetch new ones from the image!") + .arg(points3D_tmp.size()) + .arg(points2D.size()), + PMessageBox::StandardButton::Ok | PMessageBox::StandardButton::Abort); + if(result != PMessageBox::StandardButton::Ok) + { + return std::nullopt; + } else { - SPDLOG_WARN("unsupported file extension (supported: .3dc, .txt)"); + points2D.clear(); } } - else + + if(with_3D_data) { - // no calib_file - all_ok = false; + points3D = points3D_tmp; } - if(all_ok && !mMainWindow->isLoading()) + if(with_2D_data) { - calibExtrParams(); + points2D = points2D_tmp; } - return all_ok; + + if(!mMainWindow->isLoading()) + { + return calibExtrParams(); + } + return std::nullopt; } /** @@ -350,49 +345,37 @@ bool ExtrCalibration::loadExtrCalibFile() * * @return true if calibration did take place */ -bool ExtrCalibration::fetch2DPoints() +std::optional<ExtrinsicParameters> ExtrCalibration::fetch2DPoints() { - bool all_ok = true; if(!mMainWindow->getTracker() || mPersonStorage.nbPersons() < 4) { PCritical( mMainWindow, QObject::tr("Petrack"), QObject::tr("Error: At minimum four 3D calibration points needed for 3D calibration.")); - all_ok = false; + return std::nullopt; } - else - { - size_t sz_2d = mPersonStorage.nbPersons(); - if(points3D.size() > 0 && sz_2d != points3D.size()) - { - PCritical( - mMainWindow, - QObject::tr("Petrack"), - QObject::tr("Count of 2D-Points (%1) and 3D-Points (%2) disagree").arg(sz_2d).arg(points3D.size())); - all_ok = false; - } - // debout << "Marked 2D-Image-Points: " << endl; - if(all_ok) - { - points2D.clear(); + size_t sz_2d = mPersonStorage.nbPersons(); - for(int i = 0; i < static_cast<int>(sz_2d); i++) - { - // debout << "[" << i << "]: (" << mMainWindow->getTracker()->at(i).at(0).x() << ", " << - // mMainWindow->getTracker()->at(i).at(0).y() << ")" << endl; - // Info: Tracker->TrackPerson->TrackPoint->Vec2F - points2D.push_back(cv::Point2f(mPersonStorage.at(i).at(0).x(), mPersonStorage.at(i).at(0).y())); - } - } + if(points3D.size() > 0 && sz_2d != points3D.size()) + { + PCritical( + mMainWindow, + QObject::tr("Petrack"), + QObject::tr("Count of 2D-Points (%1) and 3D-Points (%2) disagree").arg(sz_2d).arg(points3D.size())); + return std::nullopt; } - if(all_ok) + + points2D.clear(); + for(int i = 0; i < static_cast<int>(sz_2d); i++) { - mPersonStorage.clear(); - calibExtrParams(); + // Info: Tracker->TrackPerson->TrackPoint->Vec2F + points2D.push_back(cv::Point2f(mPersonStorage.at(i).at(0).x(), mPersonStorage.at(i).at(0).y())); } - return all_ok; + + mPersonStorage.clear(); + return calibExtrParams(); } /** @@ -483,123 +466,122 @@ bool ExtrCalibration::isSetExtrCalib() /** * @brief Extrinsic calibration with help of cv::solvePnP */ -void ExtrCalibration::calibExtrParams() +std::optional<ExtrinsicParameters> ExtrCalibration::calibExtrParams() { - if(!points3D.empty() && !points2D.empty() && points2D.size() == points3D.size()) + if(points3D.empty() || points2D.empty() || points2D.size() != points3D.size()) { - int bS = mMainWindow->getImageBorderSize(); - /* Create Camera-Matrix form Camera-Params in the Petrack-GUI */ - cv::Mat camMat = mControlWidget->getIntrinsicCameraParams().cameraMatrix; - camMat.at<double>(0, 2) -= bS; - camMat.at<double>(1, 2) -= bS; - - cv::Mat distMat = cv::Mat::zeros(cv::Size(8, 1), CV_64F); - - /* Create Mat-objects of point correspondences */ - cv::Mat op(points3D); - cv::Mat ip(points2D); - - /* Mat-objects for result rotation and translation vectors */ - cv::Mat rvec(3, 1, CV_64F), /*,0),*/ tvec(3, 1, CV_64F); //,0); - - // Solve the PnP-Problem to calibrate the camera to its environment - - cv::solvePnP(op, ip, camMat, distMat, rvec, tvec, false, cv::SOLVEPNP_ITERATIVE); - - cv::Mat rot_mat(3, 3, CV_64F); //, 0); - // Transform the rotation vector into a rotation matrix with opencvs rodrigues method - Rodrigues(rvec, rot_mat); - - rotation_matrix[0] = rot_mat.at<double>(0, 0); - rotation_matrix[1] = rot_mat.at<double>(0, 1); - rotation_matrix[2] = rot_mat.at<double>(0, 2); - rotation_matrix[3] = rot_mat.at<double>(1, 0); - rotation_matrix[4] = rot_mat.at<double>(1, 1); - rotation_matrix[5] = rot_mat.at<double>(1, 2); - rotation_matrix[6] = rot_mat.at<double>(2, 0); - rotation_matrix[7] = rot_mat.at<double>(2, 1); - rotation_matrix[8] = rot_mat.at<double>(2, 2); - - translation_vector[0] = tvec.at<double>(0, 0); - translation_vector[1] = tvec.at<double>(0, 1); - translation_vector[2] = tvec.at<double>(0, 2); - - translation_vector2[0] = rotation_matrix[0] * translation_vector[0] + - rotation_matrix[3] * translation_vector[1] + - rotation_matrix[6] * translation_vector[2]; - translation_vector2[1] = rotation_matrix[1] * translation_vector[0] + - rotation_matrix[4] * translation_vector[1] + - rotation_matrix[7] * translation_vector[2]; - translation_vector2[2] = rotation_matrix[2] * translation_vector[0] + - rotation_matrix[5] * translation_vector[1] + - rotation_matrix[8] * translation_vector[2]; - - SPDLOG_INFO("-.- ESTIMATED ROTATION -.-"); - for(size_t p = 0; p < 3; p++) - { - SPDLOG_INFO("{}, {}, {}", rotation_matrix[p * 3], rotation_matrix[p * 3 + 1], rotation_matrix[p * 3] + 2); - } - SPDLOG_INFO("-.- ESTIMATED TRANSLATION -.-"); - SPDLOG_INFO("{}, {}, {}", translation_vector[0], translation_vector[1], translation_vector[2]); + QString msg = QString{"Invalid point correspondences for camera calibration\n" + "2D points: %1, 3D points %2"} + .arg(points2D.size()) + .arg(points3D.size()); + PWarning(nullptr, "Invalid point correspondences", msg); + return std::nullopt; + } - SPDLOG_INFO("-.- Translation vector -.-"); - SPDLOG_INFO("{}, {}, {}", translation_vector2[0], translation_vector2[1], translation_vector2[2]); + int bS = mMainWindow->getImageBorderSize(); + /* Create Camera-Matrix form Camera-Params in the Petrack-GUI */ + cv::Mat camMat = mControlWidget->getIntrinsicCameraParams().cameraMatrix; + camMat.at<double>(0, 2) -= bS; + camMat.at<double>(1, 2) -= bS; - SPDLOG_INFO("-.- Rotation vector -.-"); - SPDLOG_INFO("{}, {}, {}", rvec.at<double>(0, 0), rvec.at<double>(1, 0), rvec.at<double>(2, 0)); + cv::Mat distMat = cv::Mat::zeros(cv::Size(8, 1), CV_64F); - camHeight = translation_vector2[2] < 0 ? -translation_vector2[2] : translation_vector2[2]; + /* Create Mat-objects of point correspondences */ + cv::Mat op(points3D); + cv::Mat ip(points2D); - mControlWidget->setCalibExtrRot1(rvec.at<double>(0, 0)); - mControlWidget->setCalibExtrRot2(rvec.at<double>(1, 0)); - mControlWidget->setCalibExtrRot3(rvec.at<double>(2, 0)); + /* Mat-objects for result rotation and translation vectors */ + cv::Mat rvec(3, 1, CV_64F), /*,0),*/ tvec(3, 1, CV_64F); //,0); - mControlWidget->setCalibExtrTrans1(translation_vector2[0]); - mControlWidget->setCalibExtrTrans2(translation_vector2[1]); - mControlWidget->setCalibExtrTrans3(translation_vector2[2]); + // Solve the PnP-Problem to calibrate the camera to its environment + cv::solvePnP(op, ip, camMat, distMat, rvec, tvec, false, cv::SOLVEPNP_ITERATIVE); - if(!calcReprojectionError()) - { - SPDLOG_WARN("Extrinsic calibration not possible! Please select other 2D/3D points!"); - mControlWidget->setCalibExtrRot1(0); - mControlWidget->setCalibExtrRot2(0); - mControlWidget->setCalibExtrRot3(0); + cv::Mat rot_mat(3, 3, CV_64F); //, 0); + // Transform the rotation vector into a rotation matrix with opencvs rodrigues method + Rodrigues(rvec, rot_mat); - translation_vector2[0] = 0; - translation_vector2[1] = 0; - translation_vector2[2] = 0; + rotation_matrix[0] = rot_mat.at<double>(0, 0); + rotation_matrix[1] = rot_mat.at<double>(0, 1); + rotation_matrix[2] = rot_mat.at<double>(0, 2); + rotation_matrix[3] = rot_mat.at<double>(1, 0); + rotation_matrix[4] = rot_mat.at<double>(1, 1); + rotation_matrix[5] = rot_mat.at<double>(1, 2); + rotation_matrix[6] = rot_mat.at<double>(2, 0); + rotation_matrix[7] = rot_mat.at<double>(2, 1); + rotation_matrix[8] = rot_mat.at<double>(2, 2); + + translation_vector[0] = tvec.at<double>(0, 0); + translation_vector[1] = tvec.at<double>(0, 1); + translation_vector[2] = tvec.at<double>(0, 2); + + translation_vector2[0] = rotation_matrix[0] * translation_vector[0] + rotation_matrix[3] * translation_vector[1] + + rotation_matrix[6] * translation_vector[2]; + translation_vector2[1] = rotation_matrix[1] * translation_vector[0] + rotation_matrix[4] * translation_vector[1] + + rotation_matrix[7] * translation_vector[2]; + translation_vector2[2] = rotation_matrix[2] * translation_vector[0] + rotation_matrix[5] * translation_vector[1] + + rotation_matrix[8] * translation_vector[2]; + + SPDLOG_INFO("-.- ESTIMATED ROTATION -.-"); + for(size_t p = 0; p < 3; p++) + { + SPDLOG_INFO("{}, {}, {}", rotation_matrix[p * 3], rotation_matrix[p * 3 + 1], rotation_matrix[p * 3] + 2); + } + SPDLOG_INFO("-.- ESTIMATED TRANSLATION -.-"); + SPDLOG_INFO("{}, {}, {}", translation_vector[0], translation_vector[1], translation_vector[2]); - rotation_matrix[0] = 0; - rotation_matrix[1] = 0; - rotation_matrix[2] = 0; + SPDLOG_INFO("-.- Translation vector -.-"); + SPDLOG_INFO("{}, {}, {}", translation_vector2[0], translation_vector2[1], translation_vector2[2]); - mControlWidget->setCalibExtrTrans1(translation_vector2[0]); - mControlWidget->setCalibExtrTrans2(translation_vector2[1]); - mControlWidget->setCalibExtrTrans3(translation_vector2[2]); + SPDLOG_INFO("-.- Rotation vector -.-"); + SPDLOG_INFO("{}, {}, {}", rvec.at<double>(0, 0), rvec.at<double>(1, 0), rvec.at<double>(2, 0)); - reprojectionError = ReprojectionError{}; + camHeight = translation_vector2[2] < 0 ? -translation_vector2[2] : translation_vector2[2]; - PCritical( - mMainWindow, - QObject::tr("Petrack"), - QObject::tr("Error: Could not calculate extrinsic calibration. Please select other 2D/3D point " - "correspondences for extrinsic calibration!")); + ExtrinsicParameters results; - isExtCalib = false; + results.rot1 = rvec.at<double>(0, 0); + results.rot2 = rvec.at<double>(1, 0); + results.rot3 = rvec.at<double>(2, 0); - return; - } + results.trans1 = translation_vector2[0]; + results.trans2 = translation_vector2[1]; + results.trans3 = translation_vector2[2]; - isExtCalib = true; - - SPDLOG_INFO("End of extern calibration!"); - } - else + if(!calcReprojectionError()) { - std::cerr << "# Warning: invalid point correspondences for camera calibration." << std::endl; - std::cerr << "# 2D points:" << points2D.size() << ", 3D points: " << points3D.size() << std::endl; + SPDLOG_WARN("Extrinsic calibration not possible! Please select other 2D/3D points!"); + results.rot1 = 0; + results.rot2 = 0; + results.rot3 = 0; + + translation_vector2[0] = 0; + translation_vector2[1] = 0; + translation_vector2[2] = 0; + + rotation_matrix[0] = 0; + rotation_matrix[1] = 0; + rotation_matrix[2] = 0; + + results.trans1 = translation_vector2[0]; + results.trans2 = translation_vector2[1]; + results.trans3 = translation_vector2[2]; + + reprojectionError = ReprojectionError{}; + + PCritical( + mMainWindow, + QObject::tr("Petrack"), + QObject::tr("Error: Could not calculate extrinsic calibration. Please select other 2D/3D point " + "correspondences for extrinsic calibration!")); + + isExtCalib = false; + return results; } + + isExtCalib = true; + SPDLOG_INFO("End of extern calibration!"); mMainWindow->getScene()->update(); + return results; } /** @@ -779,11 +761,12 @@ cv::Point2f ExtrCalibration::getImagePoint(cv::Point3f p3d) // ToDo: use projectPoints(); int bS = mMainWindow->getImage() ? mMainWindow->getImageBorderSize() : 0; - double rvec_array[3], translation_vector[3]; + double rvec_array[3], translation_vector[3]; + const auto &extrParams = mControlWidget->getExtrinsicParameters(); - rvec_array[0] = mControlWidget->getCalibExtrRot1(); - rvec_array[1] = mControlWidget->getCalibExtrRot2(); - rvec_array[2] = mControlWidget->getCalibExtrRot3(); + rvec_array[0] = extrParams.rot1; + rvec_array[1] = extrParams.rot2; + rvec_array[2] = extrParams.rot3; cv::Mat rvec(3, 1, CV_64F, rvec_array), rot_inv; cv::Mat rot_mat(3, 3, CV_64F), e(3, 3, CV_64F); @@ -795,15 +778,12 @@ cv::Point2f ExtrCalibration::getImagePoint(cv::Point3f p3d) e = rot_inv * rot_mat; - translation_vector[0] = rot_mat.at<double>(0, 0) * mControlWidget->getCalibExtrTrans1() + - rot_mat.at<double>(0, 1) * mControlWidget->getCalibExtrTrans2() + - rot_mat.at<double>(0, 2) * mControlWidget->getCalibExtrTrans3(); - translation_vector[1] = rot_mat.at<double>(1, 0) * mControlWidget->getCalibExtrTrans1() + - rot_mat.at<double>(1, 1) * mControlWidget->getCalibExtrTrans2() + - rot_mat.at<double>(1, 2) * mControlWidget->getCalibExtrTrans3(); - translation_vector[2] = rot_mat.at<double>(2, 0) * mControlWidget->getCalibExtrTrans1() + - rot_mat.at<double>(2, 1) * mControlWidget->getCalibExtrTrans2() + - rot_mat.at<double>(2, 2) * mControlWidget->getCalibExtrTrans3(); + translation_vector[0] = rot_mat.at<double>(0, 0) * extrParams.trans1 + + rot_mat.at<double>(0, 1) * extrParams.trans2 + rot_mat.at<double>(0, 2) * extrParams.trans3; + translation_vector[1] = rot_mat.at<double>(1, 0) * extrParams.trans1 + + rot_mat.at<double>(1, 1) * extrParams.trans2 + rot_mat.at<double>(1, 2) * extrParams.trans3; + translation_vector[2] = rot_mat.at<double>(2, 0) * extrParams.trans1 + + rot_mat.at<double>(2, 1) * extrParams.trans2 + rot_mat.at<double>(2, 2) * extrParams.trans3; cv::Point3f point3D; @@ -846,8 +826,8 @@ cv::Vec3d ExtrCalibration::camToWorldRotation(const cv::Vec3d &camVec) const { // Transform the rotation vector into a rotation matrix with opencvs rodrigues method cv::Matx<double, 3, 3> rotMat(3, 3, CV_64F); - const auto rvec = cv::Vec3d( - mControlWidget->getCalibExtrRot1(), mControlWidget->getCalibExtrRot2(), mControlWidget->getCalibExtrRot3()); + const auto &extrParams = mControlWidget->getExtrinsicParameters(); + const auto rvec = cv::Vec3d(extrParams.rot1, extrParams.rot2, extrParams.rot3); Rodrigues(rvec, rotMat); auto rotInv = rotMat.inv(cv::DECOMP_LU); @@ -871,18 +851,13 @@ cv::Point3f ExtrCalibration::get3DPoint(const cv::Point2f &p2d, double h) const // Transform the rotation vector into a rotation matrix with opencvs rodrigues method cv::Matx<double, 3, 3> rot_inv; cv::Matx<double, 3, 3> rot_mat(3, 3, CV_64F); - const cv::Mat rvec = - (cv::Mat_<double>(3, 1) << mControlWidget->getCalibExtrRot1(), - mControlWidget->getCalibExtrRot2(), - mControlWidget->getCalibExtrRot3()); + const auto &extrParams = mControlWidget->getExtrinsicParameters(); + const cv::Mat rvec = (cv::Mat_<double>(3, 1) << extrParams.rot1, extrParams.rot2, extrParams.rot3); Rodrigues(rvec, rot_mat); rot_inv = rot_mat.inv(cv::DECOMP_LU, nullptr); // Create translation vector - cv::Vec3d translation{ - mControlWidget->getCalibExtrTrans1(), - mControlWidget->getCalibExtrTrans2(), - mControlWidget->getCalibExtrTrans3()}; + cv::Vec3d translation{extrParams.trans1, extrParams.trans2, extrParams.trans3}; const auto camMat = mControlWidget->getIntrinsicCameraParams(); const auto fx = camMat.getFx(); diff --git a/src/extrinsicBox.cpp b/src/extrinsicBox.cpp new file mode 100644 index 000000000..7d78de8b3 --- /dev/null +++ b/src/extrinsicBox.cpp @@ -0,0 +1,301 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "extrinsicBox.h" + +#include "extrCalibration.h" +#include "helper.h" +#include "ui_extrinsicBox.h" + +#include <QDialogButtonBox> +#include <QDomElement> +#include <QMessageBox> +#include <QStyle> +#include <utility> + +ExtrinsicBox::ExtrinsicBox( + QWidget *parent, + Ui::extr *ui, + ExtrCalibration &extrCalib, + std::function<void()> updateCoordCallback) : + QGroupBox(parent), mUi(ui), mExtrCalibration(extrCalib), mUpdateCoordCallback(std::move(updateCoordCallback)) +{ + mUi->setupUi(this); + setFocusProxy(mUi->rot1); + // use default values from struct as default for UI + setExtrinsicParameters(mParams); +} + +ExtrinsicBox::ExtrinsicBox(QWidget *parent, ExtrCalibration &extrCalib, std::function<void()> updateCoordCallback) : + ExtrinsicBox(parent, new Ui::extr, extrCalib, std::move(updateCoordCallback)) +{ +} + +void ExtrinsicBox::setEnabledExtrParams(bool enable) +{ + mUi->rot1->setEnabled(enable); + mUi->rot2->setEnabled(enable); + mUi->rot3->setEnabled(enable); + mUi->trans1->setEnabled(enable); + mUi->trans2->setEnabled(enable); + mUi->trans3->setEnabled(enable); +} + +const ExtrinsicParameters &ExtrinsicBox::getExtrinsicParameters() const +{ + return mParams; +} + +void ExtrinsicBox::setExtrinsicParameters(const ExtrinsicParameters ¶ms) +{ + setValue(mUi->trans1, params.trans1); + setValue(mUi->trans2, params.trans2); + setValue(mUi->trans3, params.trans3); + setValue(mUi->rot1, params.rot1); + setValue(mUi->rot2, params.rot2); + setValue(mUi->rot3, params.rot3); +} + +void ExtrinsicBox::on_extrCalibFetch_clicked() +{ + auto newCalib = mExtrCalibration.fetch2DPoints(); + if(newCalib) + { + setExtrinsicParameters(*newCalib); + } +} + +void ExtrinsicBox::on_coordLoad3DCalibPoints_clicked() +{ + auto newCalib = mExtrCalibration.openExtrCalibFile(); + if(newCalib) + { + setExtrinsicParameters(*newCalib); + } + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_rot1_valueChanged(double newVal) +{ + mParams.rot1 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_rot2_valueChanged(double newVal) +{ + mParams.rot2 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_rot3_valueChanged(double newVal) +{ + mParams.rot3 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_trans1_valueChanged(double newVal) +{ + mParams.trans1 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_trans2_valueChanged(double newVal) +{ + mParams.trans2 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_trans3_valueChanged(double newVal) +{ + mParams.trans3 = newVal; + mUpdateCoordCallback(); +} + +void ExtrinsicBox::on_extrCalibSave_clicked() +{ + mExtrCalibration.saveExtrCalibPoints(); +} + +void ExtrinsicBox::on_extrCalibShowError_clicked() +{ + QString out; + QDialog msgBox; + QGridLayout *layout = new QGridLayout(); + msgBox.setLayout(layout); + QLabel *tableView = new QLabel(&msgBox); + layout->addWidget(tableView, 1, 1); + QLabel *titel = new QLabel(&msgBox); + titel->setText("<b>Reprojection error for extrinsic calibration:</b>"); + layout->addWidget(titel, 0, 1); + + if(!mExtrCalibration.getReprojectionError().isValid()) + { + out = QString("No File for extrinsic calibration found!"); + tableView->setText(out); + } + else + { + out = QString("<table>" + "<tr><th></th>" + "<th>average </th>" + "<th>std. deviation </th>" + "<th>variance </th>" + "<th>max </th></tr>" + "<tr><td>Point height: </td><td> %0 cm</td><td> %1 cm</td><td> %2 " + "cm</td><td> %3 cm</td></tr>" + "<tr><td>Default height: <small>[%12 cm]</small> </td><td> %4 cm</td><td> %5 cm</td><td> %6 " + "cm</td><td> %7 cm</td></tr>" + "<tr><td>Pixel error: </td><td> %8 px</td><td> %9 px</td><td> %10 " + "px</td><td> %11 px</td></tr>" + "</table>"); + const auto &reproError = mExtrCalibration.getReprojectionError().getData(); + for(double value : reproError) + { + if(value < 0) + { + out = out.arg("-"); + } + else + { + out = out.arg(value); + } + } + tableView->setText(out); + } + + msgBox.setWindowTitle("PeTrack"); + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + QLabel *infoIcon = new QLabel(&msgBox); + int iconSize = msgBox.style()->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, &msgBox); + infoIcon->setPixmap(icon.pixmap(iconSize, iconSize)); + layout->addWidget(infoIcon, 0, 0); + QDialogButtonBox *ok = new QDialogButtonBox(QDialogButtonBox::Ok); + layout->addWidget(ok, 2, 1); + connect(ok, &QDialogButtonBox::clicked, &msgBox, &QDialog::close); + msgBox.setFixedSize(msgBox.sizeHint()); + msgBox.exec(); +} + +void ExtrinsicBox::on_extrCalibShowPoints_clicked() +{ + QString out_str; + QTextStream out(&out_str); + + unsigned int i; + + out << "<table><tr><th>Nr.</th><th>3D.x</th><th>3D.y</th><th>3D.z</th><th>2D.x</th><th>2D.y</th></tr>" << Qt::endl; + + + for(i = 0; i < std::max(mExtrCalibration.get3DList().size(), mExtrCalibration.get2DList().size()); ++i) + { + out << "<tr>"; + if(i < mExtrCalibration.get3DList().size()) + { + out << "<td>[" << QString::number(i + 1, 'i', 0) << "]: </td><td>" + << QString::number(mExtrCalibration.get3DList().at(i).x, 'f', 1) << "</td><td>" + << QString::number(mExtrCalibration.get3DList().at(i).y, 'f', 1) << "</td><td>" + << QString::number(mExtrCalibration.get3DList().at(i).z, 'f', 1) << "</td><td>"; + } + else + { + out << "<td>-</td><td>-</td><td>-</td>"; + } + if(i < mExtrCalibration.get2DList().size()) + { + out << QString::number(mExtrCalibration.get2DList().at(i).x, 'f', 3) << "</td><td>" + << QString::number(mExtrCalibration.get2DList().at(i).y, 'f', 3) << "</td>"; + } + else + { + out << "<td>-</td><td>-</td>"; + } + out << "</tr>" << Qt::endl; + } + out << "</table>" << Qt::endl; + + QMessageBox msgBox; + msgBox.setWindowTitle("PeTrack"); + msgBox.setIcon(QMessageBox::Information); + msgBox.setText("Currently loaded point correspondences<br />for extrinsic calibration:"); + msgBox.setInformativeText(out_str); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); +} + +/// returns whether the all attributes were consumed +bool ExtrinsicBox::getXml(QDomElement &subSubElem) +{ + if(subSubElem.tagName() == "EXTRINSIC_PARAMETERS") + { + ExtrinsicParameters params; + if(subSubElem.hasAttribute("EXTR_ROT_1")) + { + params.rot1 = subSubElem.attribute("EXTR_ROT_1").toDouble(); + } + if(subSubElem.hasAttribute("EXTR_ROT_2")) + { + params.rot2 = subSubElem.attribute("EXTR_ROT_2").toDouble(); + } + if(subSubElem.hasAttribute("EXTR_ROT_3")) + { + params.rot3 = subSubElem.attribute("EXTR_ROT_3").toDouble(); + } + if(subSubElem.hasAttribute("EXTR_TRANS_1")) + { + params.trans1 = subSubElem.attribute("EXTR_TRANS_1").toDouble(); + } + if(subSubElem.hasAttribute("EXTR_TRANS_2")) + { + params.trans2 = subSubElem.attribute("EXTR_TRANS_2").toDouble(); + } + if(subSubElem.hasAttribute("EXTR_TRANS_3")) + { + params.trans3 = subSubElem.attribute("EXTR_TRANS_3").toDouble(); + } + setExtrinsicParameters(params); + + if(subSubElem.hasAttribute("EXTERNAL_CALIB_FILE")) + { + if(getExistingFile(QString::fromStdString(subSubElem.attribute("EXTERNAL_CALIB_FILE").toStdString())) != "") + { + mExtrCalibration.setExtrCalibFile( + getExistingFile(QString::fromStdString(subSubElem.attribute("EXTERNAL_CALIB_FILE").toStdString()))); + // mMainWindow->isLoading() is true; doesn't perform calibration -> ignore return + mExtrCalibration.loadExtrCalibFile(); + } + } + } + return false; +} + +void ExtrinsicBox::setXml(QDomElement &subSubElem) const +{ + subSubElem.setAttribute("EXTR_ROT_1", mUi->rot1->value()); + subSubElem.setAttribute("EXTR_ROT_2", mUi->rot2->value()); + subSubElem.setAttribute("EXTR_ROT_3", mUi->rot3->value()); + subSubElem.setAttribute("EXTR_TRANS_1", mUi->trans1->value()); + subSubElem.setAttribute("EXTR_TRANS_2", mUi->trans2->value()); + subSubElem.setAttribute("EXTR_TRANS_3", mUi->trans3->value()); +} + + +ExtrinsicBox::~ExtrinsicBox() +{ + delete mUi; +} diff --git a/src/imageItem.cpp b/src/imageItem.cpp index 3999f23ad..83fe57481 100644 --- a/src/imageItem.cpp +++ b/src/imageItem.cpp @@ -146,10 +146,11 @@ QPointF ImageItem::getCmPerPixel(float px, float py, float h) ///* double ImageItem::getAngleToGround(float px, float py, float height) { + const auto &extrParams = mControlWidget->getExtrinsicParameters(); cv::Point3f cam( - -mControlWidget->getCalibCoord3DTransX() - mControlWidget->getCalibExtrTrans1(), - -mControlWidget->getCalibCoord3DTransY() - mControlWidget->getCalibExtrTrans2(), - -mControlWidget->getCalibCoord3DTransZ() - mControlWidget->getCalibExtrTrans3()); + -mControlWidget->getCalibCoord3DTransX() - extrParams.trans1, + -mControlWidget->getCalibCoord3DTransY() - extrParams.trans2, + -mControlWidget->getCalibCoord3DTransZ() - extrParams.trans3); cv::Point3f posInImage = mMainWindow->getExtrCalibration()->get3DPoint( cv::Point2f(px - mMainWindow->getImageBorderSize(), py - mMainWindow->getImageBorderSize()), height); diff --git a/src/petrack.cpp b/src/petrack.cpp index 02beaff12..ac259d06c 100644 --- a/src/petrack.cpp +++ b/src/petrack.cpp @@ -2646,7 +2646,7 @@ void Petrack::importTracker(QString dest) // default = "" tPoint.setSp( x, y, - -mControlWidget->getCalibExtrTrans3() - + -mControlWidget->getExtrinsicParameters().trans3 - z); // fuer den Abstand zur Kamera in z-Richtung wie bei einer Stereokamera // Neue ID ? ==> letzte Person beendet ==> abspeichern diff --git a/src/recognition.cpp b/src/recognition.cpp index 63a5062f5..e5a12b6d2 100644 --- a/src/recognition.cpp +++ b/src/recognition.cpp @@ -163,9 +163,10 @@ void setColorParameter(const QColor &fromColor, const QColor &toColor, bool inve Vec2F autoCorrectColorMarker(const Vec2F &boxImageCentre, Control *controlWidget) { Petrack *mainWindow = controlWidget->getMainWindow(); + auto extrParams = controlWidget->getExtrinsicParameters(); cv::Point2f tp = mainWindow->getExtrCalibration()->getImagePoint(cv::Point3f( - -controlWidget->getCalibCoord3DTransX() - controlWidget->getCalibExtrTrans1(), - -controlWidget->getCalibCoord3DTransY() - controlWidget->getCalibExtrTrans2(), + -controlWidget->getCalibCoord3DTransX() - extrParams.trans1, + -controlWidget->getCalibCoord3DTransY() - extrParams.trans2, 0)); Vec2F pixUnderCam(tp.x, tp.y); // CvPoint Vec2F boxImageCentreWithBorder = boxImageCentre; diff --git a/src/trackerItem.cpp b/src/trackerItem.cpp index bca4d97da..beabb5558 100644 --- a/src/trackerItem.cpp +++ b/src/trackerItem.cpp @@ -558,7 +558,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op rect, Qt::AlignHCenter, QString("-\n%2").arg( - -mControlWidget->getCalibExtrTrans3() - tp.sp().z(), 6, 'f', 1)); + -mControlWidget->getExtrinsicParameters().trans3 - tp.sp().z(), 6, 'f', 1)); } else { @@ -577,7 +577,8 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op Qt::AlignHCenter, QString("%1\n%2") .arg(height, 6, 'f', 1) - .arg(-mControlWidget->getCalibExtrTrans3() - tp.sp().z(), 6, 'f', 1)); + .arg( + -mControlWidget->getExtrinsicParameters().trans3 - tp.sp().z(), 6, 'f', 1)); } else { @@ -634,7 +635,8 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op if(tp.sp().z() > 0) { p3d_height = mMainWindow->getExtrCalibration()->get3DPoint( - cv::Point2f(tp.x(), tp.y()), -mControlWidget->getCalibExtrTrans3() - tp.sp().z()); + cv::Point2f(tp.x(), tp.y()), + -mControlWidget->getExtrinsicParameters().trans3 - tp.sp().z()); } else { @@ -676,7 +678,8 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op if(tp.sp().z() > 0) { p3d_height = mMainWindow->getExtrCalibration()->get3DPoint( - cv::Point2f(tp.x(), tp.y()), -mControlWidget->getCalibExtrTrans3() - tp.sp().z()); + cv::Point2f(tp.x(), tp.y()), + -mControlWidget->getExtrinsicParameters().trans3 - tp.sp().z()); } else { @@ -775,10 +778,11 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op { p3d_height_p1 = mMainWindow->getExtrCalibration()->get3DPoint( cv::Point2f(person.at(j - 1).x(), person.at(j - 1).y()), - -mControlWidget->getCalibExtrTrans3() - person.at(j - 1).sp().z()); + -mControlWidget->getExtrinsicParameters().trans3 - + person.at(j - 1).sp().z()); p3d_height_p2 = mMainWindow->getExtrCalibration()->get3DPoint( cv::Point2f(person.at(j).x(), person.at(j).y()), - -mControlWidget->getCalibExtrTrans3() - person.at(j).sp().z()); + -mControlWidget->getExtrinsicParameters().trans3 - person.at(j).sp().z()); } else { diff --git a/tests/unit_test/CMakeLists.txt b/tests/unit_test/CMakeLists.txt index 2ec2351cb..af74b8370 100644 --- a/tests/unit_test/CMakeLists.txt +++ b/tests/unit_test/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources(petrack_tests PRIVATE tst_filter.cpp tst_intrinsicBox.cpp util.h + tst_extrinsicBox.cpp ) diff --git a/tests/unit_test/tst_extrCalibration.cpp b/tests/unit_test/tst_extrCalibration.cpp index 2ed83b12b..44b6c09e0 100644 --- a/tests/unit_test/tst_extrCalibration.cpp +++ b/tests/unit_test/tst_extrCalibration.cpp @@ -19,21 +19,29 @@ #include "extrCalibration.h" #include "petrack.h" +#include <QDomDocument> #include <catch2/catch.hpp> // use margin for absolute difference, as epsilon would be relative which is useless when comparing to 0 -constexpr float VEC_MARGIN = 0.01; +constexpr float VEC_MARGIN = 0.01f; TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") { Petrack petrack{"Unknown"}; - auto calib = petrack.getExtrCalibration(); + auto *calib = petrack.getExtrCalibration(); Control *control = petrack.getControlWidget(); - control->setCalibExtrRot1(0); - control->setCalibExtrRot2(0); - control->setCalibExtrRot3(0); + const QString testConfig{ + R"(<CONTROL> + <CALIBRATION> + <EXTRINSIC_PARAMETERS EXTR_ROT_1="%1" EXTR_ROT_2="%2" EXTR_ROT_3="%3" EXTR_TRANS_1="0" EXTR_TRANS_2="0" EXTR_TRANS_3="0" /> + </CALIBRATION> + </CONTROL>)"}; + + QDomDocument doc; + doc.setContent(testConfig.arg("0", "0", "0")); + control->getXml(doc.documentElement()); SECTION("Identity Coordinate System") { @@ -49,7 +57,8 @@ TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") SECTION("Rotated around z-axis") { // rotate 90 degrees - control->setCalibExtrRot3(PI / 2); + doc.setContent(testConfig.arg("0", "0", QString::number(PI / 2))); + control->getXml(doc.documentElement()); REQUIRE( cv::norm(calib->camToWorldRotation(cv::Vec3d(1, 0, 0)) - cv::Vec3d(0, -1, 0)) == Approx(0).margin(VEC_MARGIN)); @@ -67,7 +76,8 @@ TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") Approx(0).margin(VEC_MARGIN)); // negative rotation - control->setCalibExtrRot3(-PI); + doc.setContent(testConfig.arg("0", "0", QString::number(-PI))); + control->getXml(doc.documentElement()); REQUIRE( cv::norm(calib->camToWorldRotation(cv::Vec3d(1, 0, 0)) - cv::Vec3d(-1, 0, 0)) == Approx(0).margin(VEC_MARGIN)); @@ -80,9 +90,8 @@ TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") SECTION("Wild rotation") { // vector (1, 1, 1) with length pi/2 - control->setCalibExtrRot1(0.9067); - control->setCalibExtrRot2(0.9067); - control->setCalibExtrRot3(0.9067); + doc.setContent(testConfig.arg("0.9067", "0.9067", "0.9067")); + control->getXml(doc.documentElement()); REQUIRE( cv::norm(calib->camToWorldRotation(cv::Vec3d(1, 1, 1)) - cv::Vec3d(1, 1, 1)) == @@ -98,9 +107,14 @@ TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") SECTION("Translation should not matter") { - control->setCalibExtrTrans1(10); - control->setCalibExtrTrans2(-20); - control->setCalibExtrTrans3(-500); + const QString testConfig{ + R"(<CONTROL> + <CALIBRATION> + <EXTRINSIC_PARAMETERS EXTR_ROT_1="%1" EXTR_ROT_2="%2" EXTR_ROT_3="%3" EXTR_TRANS_1="10" EXTR_TRANS_2="-20" EXTR_TRANS_3="-500" /> + </CALIBRATION> + </CONTROL>)"}; + doc.setContent(testConfig.arg("0.9067", "0.9067", "0.9067")); + control->getXml(doc.documentElement()); REQUIRE( cv::norm(calib->camToWorldRotation(cv::Vec3d(1, 0, 0)) - cv::Vec3d(0.33, -0.24, 0.91)) == Approx(0).margin(VEC_MARGIN)); @@ -113,9 +127,8 @@ TEST_CASE("src/extrCalibration/camToWorldRotation", "[extrCalibration]") SECTION("Another Wild Rotation") { - control->setCalibExtrRot1(0.5); - control->setCalibExtrRot2(-2); - control->setCalibExtrRot3(1.1); + doc.setContent(testConfig.arg("0.5", "-2", "1.1")); + control->getXml(doc.documentElement()); REQUIRE( cv::norm(calib->camToWorldRotation(cv::Vec3d(1, 1, 1)) - cv::Vec3d(0.2, -0.63, -1.6)) == diff --git a/tests/unit_test/tst_extrinsicBox.cpp b/tests/unit_test/tst_extrinsicBox.cpp new file mode 100644 index 000000000..fd5d1cb9f --- /dev/null +++ b/tests/unit_test/tst_extrinsicBox.cpp @@ -0,0 +1,115 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://cdwww.gnu.org/licenses/>. + */ + +#include "extrCalibration.h" +#include "extrinsicBox.h" +#include "personStorage.h" +#include "petrack.h" +#include "ui_extrinsicBox.h" + +#include <QDomElement> +#include <catch2/catch.hpp> + +TEST_CASE("ExtrinsicBox: display of values in UI") +{ + Petrack petrack{"Unkown"}; + Autosave autosave(petrack); + PersonStorage storage(petrack, autosave); + ExtrCalibration calib(storage); + Ui::extr *ui = new Ui::extr(); + ExtrinsicBox extrBox(nullptr, ui, calib, []() {}); + + ExtrinsicParameters params; + params.rot1 = 0.5; + params.rot2 = 0.2; + params.rot3 = -0.5; + params.trans1 = 234; + params.trans2 = 420; + params.trans3 = 699; + + WHEN("We change the values of the extrinsic parameters") + { + extrBox.setExtrinsicParameters(params); + + THEN("The values are displayed accordingly") + { + CHECK(ui->rot1->value() == params.rot1); + CHECK(ui->rot2->value() == params.rot2); + CHECK(ui->rot3->value() == params.rot3); + CHECK(ui->trans1->value() == params.trans1); + CHECK(ui->trans2->value() == params.trans2); + CHECK(ui->trans3->value() == params.trans3); + } + } + + WHEN("We change the values in the UI") + { + ui->rot1->setValue(params.rot1); + ui->rot2->setValue(params.rot2); + ui->rot3->setValue(params.rot3); + ui->trans1->setValue(params.trans1); + ui->trans2->setValue(params.trans2); + ui->trans3->setValue(params.trans3); + + THEN("The changes are propagated accordingly") + { + CHECK(extrBox.getExtrinsicParameters() == params); + } + } +} + +TEST_CASE("ExtrinsicBox: reading/writing xml") +{ + Petrack petrack{"Unkown"}; + Autosave autosave(petrack); + PersonStorage storage(petrack, autosave); + ExtrCalibration calib(storage); + ExtrinsicBox extrBox(nullptr, calib, []() {}); + + WHEN("We change the extrinsic parameters") + { + ExtrinsicParameters params; + params.rot1 = 0.5; + params.rot2 = 0.2; + params.rot3 = -0.5; + params.trans1 = 234; + params.trans2 = 420; + params.trans3 = 699; + + extrBox.setExtrinsicParameters(params); + + AND_WHEN("We save that state into an xml-document") + { + QDomDocument doc; + QDomElement elem = doc.createElement("EXTRINSIC_PARAMETERS"); + extrBox.setXml(elem); + + THEN("We can read that state in again") + { + Petrack petrack{"Unkown"}; + Autosave autosave(petrack); + PersonStorage storage(petrack, autosave); + ExtrCalibration calib(storage); + ExtrinsicBox extrBox(nullptr, calib, []() {}); + + extrBox.getXml(elem); + CHECK(extrBox.getExtrinsicParameters() == params); + } + } + } +} diff --git a/ui/control.ui b/ui/control.ui index a502857e4..d484430ee 100644 --- a/ui/control.ui +++ b/ui/control.ui @@ -139,8 +139,8 @@ <rect> <x>0</x> <y>0</y> - <width>477</width> - <height>619</height> + <width>494</width> + <height>555</height> </rect> </property> <property name="sizePolicy"> @@ -150,325 +150,6 @@ </sizepolicy> </property> <layout class="QVBoxLayout" name="verticalLayout_13"> - <item> - <widget class="QGroupBox" name="extr"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>300</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="title"> - <string>extrinsic parameters</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_7"> - <property name="leftMargin"> - <number>5</number> - </property> - <property name="topMargin"> - <number>5</number> - </property> - <property name="rightMargin"> - <number>5</number> - </property> - <property name="bottomMargin"> - <number>5</number> - </property> - <item> - <layout class="QGridLayout" name="gridLayout_6" columnstretch="0,0,0,0"> - <item row="0" column="0"> - <widget class="QLabel" name="label_58"> - <property name="toolTip"> - <string>Translate the coordinate system in x-direction.</string> - </property> - <property name="text"> - <string>rotation:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_59"> - <property name="toolTip"> - <string>Translate the coordinate system in x-direction.</string> - </property> - <property name="text"> - <string>translation:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="PDoubleSpinBox" name="trans1"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="PDoubleSpinBox" name="rot1"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-100.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.001000000000000</double> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="PDoubleSpinBox" name="trans2"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - </widget> - </item> - <item row="1" column="3"> - <widget class="PDoubleSpinBox" name="trans3"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - <property name="value"> - <double>-500.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="PDoubleSpinBox" name="rot3"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-100.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.001000000000000</double> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="PDoubleSpinBox" name="rot2"> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>-100.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.001000000000000</double> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QLabel" name="label_62"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>3D/2D Points:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="coordLoad3DCalibPoints"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>30</width> - <height>18</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>18</height> - </size> - </property> - <property name="toolTip"> - <string>Load 3D points to corresponding 2D image points</string> - </property> - <property name="text"> - <string>load</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="extrCalibFetch"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>30</width> - <height>18</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>18</height> - </size> - </property> - <property name="toolTip"> - <string>Fetch marked 2D Points to loaded 3D points</string> - </property> - <property name="text"> - <string>fetch</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="extrCalibSave"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>30</width> - <height>18</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>18</height> - </size> - </property> - <property name="toolTip"> - <string>Save 3D and/or 2D points to extrinisc calib file</string> - </property> - <property name="text"> - <string>save</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="extrCalibShowPoints"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>30</width> - <height>18</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>18</height> - </size> - </property> - <property name="toolTip"> - <string>Show the saved 2D/3D point correspondences in the file</string> - </property> - <property name="text"> - <string>show</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="extrCalibShowError"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>30</width> - <height>18</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>18</height> - </size> - </property> - <property name="toolTip"> - <string>Show the reprojection error of extrinsic calibration</string> - </property> - <property name="text"> - <string>error</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> <item> <widget class="QGroupBox" name="align_2"> <property name="sizePolicy"> @@ -6465,17 +6146,6 @@ </customwidget> </customwidgets> <tabstops> - <tabstop>rot1</tabstop> - <tabstop>rot2</tabstop> - <tabstop>rot3</tabstop> - <tabstop>trans1</tabstop> - <tabstop>trans2</tabstop> - <tabstop>trans3</tabstop> - <tabstop>coordLoad3DCalibPoints</tabstop> - <tabstop>extrCalibFetch</tabstop> - <tabstop>extrCalibSave</tabstop> - <tabstop>extrCalibShowPoints</tabstop> - <tabstop>extrCalibShowError</tabstop> <tabstop>coordShow</tabstop> <tabstop>coordFix</tabstop> <tabstop>coordTab</tabstop> diff --git a/ui/extrinsicBox.ui b/ui/extrinsicBox.ui new file mode 100644 index 000000000..6b7c1d14f --- /dev/null +++ b/ui/extrinsicBox.ui @@ -0,0 +1,354 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>extr</class> + <widget class="QGroupBox" name="extr"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>322</width> + <height>106</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="focusPolicy"> + <enum>Qt::TabFocus</enum> + </property> + <property name="title"> + <string>extrinsic parameters</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> + <item> + <layout class="QGridLayout" name="gridLayout_6" columnstretch="0,0,0,0"> + <item row="0" column="0"> + <widget class="QLabel" name="label_58"> + <property name="toolTip"> + <string>Translate the coordinate system in x-direction.</string> + </property> + <property name="text"> + <string>rotation:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_59"> + <property name="toolTip"> + <string>Translate the coordinate system in x-direction.</string> + </property> + <property name="text"> + <string>translation:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="PDoubleSpinBox" name="trans1"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="PDoubleSpinBox" name="rot1"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-100.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.001000000000000</double> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="PDoubleSpinBox" name="trans2"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="PDoubleSpinBox" name="trans3"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + <property name="value"> + <double>-500.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="PDoubleSpinBox" name="rot3"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-100.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.001000000000000</double> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="PDoubleSpinBox" name="rot2"> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>-100.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.001000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <item> + <widget class="QLabel" name="label_62"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>3D/2D Points:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="coordLoad3DCalibPoints"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>18</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>18</height> + </size> + </property> + <property name="toolTip"> + <string>Load 3D points to corresponding 2D image points</string> + </property> + <property name="text"> + <string>load</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="extrCalibFetch"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>18</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>18</height> + </size> + </property> + <property name="toolTip"> + <string>Fetch marked 2D Points to loaded 3D points</string> + </property> + <property name="text"> + <string>fetch</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="extrCalibSave"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>18</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>18</height> + </size> + </property> + <property name="toolTip"> + <string>Save 3D and/or 2D points to extrinisc calib file</string> + </property> + <property name="text"> + <string>save</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="extrCalibShowPoints"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>18</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>18</height> + </size> + </property> + <property name="toolTip"> + <string>Show the saved 2D/3D point correspondences in the file</string> + </property> + <property name="text"> + <string>show</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="extrCalibShowError"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>18</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>18</height> + </size> + </property> + <property name="toolTip"> + <string>Show the reprojection error of extrinsic calibration</string> + </property> + <property name="text"> + <string>error</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>PDoubleSpinBox</class> + <extends>QDoubleSpinBox</extends> + <header>pdoublespinbox.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>rot1</tabstop> + <tabstop>rot2</tabstop> + <tabstop>rot3</tabstop> + <tabstop>trans1</tabstop> + <tabstop>trans2</tabstop> + <tabstop>trans3</tabstop> + <tabstop>coordLoad3DCalibPoints</tabstop> + <tabstop>extrCalibFetch</tabstop> + <tabstop>extrCalibSave</tabstop> + <tabstop>extrCalibShowPoints</tabstop> + <tabstop>extrCalibShowError</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> -- GitLab