diff --git a/include/codeMarkerWidget.h b/include/codeMarkerWidget.h index 6e1fad9556908819e2fb589d99bd3dcb0a80d375..8fc06b18b4a4db7b2f6cf82db1405dc9231edadd 100644 --- a/include/codeMarkerWidget.h +++ b/include/codeMarkerWidget.h @@ -28,14 +28,14 @@ #include "petrack.h" #include "codeMarkerItem.h" - +#include "recognition.h" class CodeMarkerWidget: public QWidget, public Ui::CodeMarker { Q_OBJECT public: - CodeMarkerWidget(QWidget *parent = nullptr); + CodeMarkerWidget(QWidget *parent, reco::CodeMarkerOptions& opt); // store data in xml node void setXml(QDomElement &elem); @@ -43,153 +43,27 @@ public: // read data from xml node void getXml(QDomElement &elem); + reco::ArucoCodeParams packDetectorParams(); + private slots: - void on_showDetectedCandidates_stateChanged(int i) - { - - mMainWindow->getCodeMarkerItem()->setVisible(i); - if( !mMainWindow->isLoading() ) - mMainWindow->getScene()->update(); - } - - void on_moreInfosButton_clicked() - { - QDesktopServices::openUrl(QUrl("http://docs.opencv.org/trunk/d1/dcd/structcv_1_1aruco_1_1DetectorParameters.html#details", QUrl::TolerantMode)); - QDesktopServices::openUrl(QUrl("http://docs.opencv.org/3.1.0/d5/dae/tutorial_aruco_detection.html", QUrl::TolerantMode)); - } - void on_dictList_currentIndexChanged(int /*index*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_minMarkerPerimeter_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_maxMarkerPerimeter_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_adaptiveThreshWinSizeMin_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_adaptiveThreshWinSizeMax_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_adaptiveThreshWinSizeStep_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_adaptiveThreshConstant_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_polygonalApproxAccuracyRate_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_minCornerDistance_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_minDistanceToBorder_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_minMarkerDistance_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_doCornerRefinement_clicked() - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_cornerRefinementWinSize_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_cornerRefinementMaxIterations_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_cornerRefinementMinAccuracy_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_markerBorderBits_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_perspectiveRemovePixelPerCell_valueChanged(int /*i*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_perspectiveRemoveIgnoredMarginPerCell_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_maxErroneousBitsInBorderRate_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_errorCorrectionRate_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } - void on_minOtsuStdDev_valueChanged(double /*val*/) - { - mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens - if( !mMainWindow->isLoading() ) - mMainWindow->updateImage(); - } + void on_showDetectedCandidates_stateChanged(int i); + void on_moreInfosButton_clicked(); + void on_dictList_currentIndexChanged(int /*index*/); +private: + void notifyChanged(); + void sendDetectorParams(reco::ArucoCodeParams params); + +private slots: + void readDetectorParams(); + void readDictListIndex(); private: Petrack *mMainWindow; + reco::CodeMarkerOptions &mCodeMarkerOpt; }; #endif diff --git a/include/recognition.h b/include/recognition.h index 629fe0a558aac14ec89511903900f62cc182f732..4076c237b830f61e2057c3a302238d2cec32772e 100644 --- a/include/recognition.h +++ b/include/recognition.h @@ -32,6 +32,7 @@ class QRect; class BackgroundFilter; class Control; class ImageItem; +class CodeMarkerItem; namespace reco { /** @@ -50,12 +51,71 @@ namespace reco { Code = 6, }; + struct ArucoCodeParams { + double minMarkerPerimeter = 5; + double maxMarkerPerimeter = 15; + double minCornerDistance = 0.05; + double minMarkerDistance = 0.05; + int adaptiveThreshWinSizeMin = 3; + int adaptiveThreshWinSizeMax = 27; + int adaptiveThreshWinSizeStep = 10; + int adaptiveThreshConstant = 7; + double polygonalApproxAccuracyRate = 0.03; + int minDistanceToBorder = 3; + bool doCornerRefinement = false; + int cornerRefinementWinSize = 5; + int cornerRefinementMaxIterations = 30; + double cornerRefinementMinAccuracy = 0.1; + int markerBorderBits = 1; + int perspectiveRemovePixelPerCell = 4; + double perspectiveRemoveIgnoredMarginPerCell = 0.13; + double maxErroneousBitsInBorderRate = 0.35; + double minOtsuStdDev = 5; + double errorCorrectionRate = 0.6; + + friend inline constexpr bool operator==(const ArucoCodeParams & lhs, const ArucoCodeParams & rhs) noexcept + { + return (lhs.minMarkerPerimeter == rhs.minMarkerPerimeter) && ((lhs.maxMarkerPerimeter == rhs.maxMarkerPerimeter) && ((lhs.minCornerDistance == rhs.minCornerDistance) && ((lhs.minMarkerDistance == rhs.minMarkerDistance) && ((lhs.adaptiveThreshWinSizeMin == rhs.adaptiveThreshWinSizeMin) && ((lhs.adaptiveThreshWinSizeMax == rhs.adaptiveThreshWinSizeMax) && ((lhs.adaptiveThreshWinSizeStep == rhs.adaptiveThreshWinSizeStep) && ((lhs.adaptiveThreshConstant == rhs.adaptiveThreshConstant) && ((lhs.polygonalApproxAccuracyRate == rhs.polygonalApproxAccuracyRate) && ((lhs.minDistanceToBorder == rhs.minDistanceToBorder) && ((static_cast<int>(lhs.doCornerRefinement) == static_cast<int>(rhs.doCornerRefinement)) && ((lhs.cornerRefinementWinSize == rhs.cornerRefinementWinSize) && ((lhs.cornerRefinementMaxIterations == rhs.cornerRefinementMaxIterations) && ((lhs.cornerRefinementMinAccuracy == rhs.cornerRefinementMinAccuracy) && ((lhs.markerBorderBits == rhs.markerBorderBits) && ((lhs.perspectiveRemovePixelPerCell == rhs.perspectiveRemovePixelPerCell) && ((lhs.perspectiveRemoveIgnoredMarginPerCell == rhs.perspectiveRemoveIgnoredMarginPerCell) && ((lhs.maxErroneousBitsInBorderRate == rhs.maxErroneousBitsInBorderRate) && ((lhs.minOtsuStdDev == rhs.minOtsuStdDev) && (lhs.errorCorrectionRate == rhs.errorCorrectionRate))))))))))))))))))); + } + friend inline constexpr bool operator!=(const ArucoCodeParams& lhs, const ArucoCodeParams& rhs) + { + return !(lhs == rhs); + } + }; + + + struct CodeMarkerOptions : public QObject{ + Q_OBJECT + + public: + CodeMarkerItem *codeMarkerItem; + int indexOfMarkerDict = 16; + + ArucoCodeParams detectorParams; + + Control *controlWidget; + Vec2F offsetCropRect2Roi = Vec2F{0,0}; + + public: + ArucoCodeParams getDetectorParams() const {return detectorParams;} + int getIndexOfMarkerDict() const {return indexOfMarkerDict;} + + public: + void userChangedDetectorParams(ArucoCodeParams params); + void userChangedIndexOfMarkerDict(int idx); + + signals: + void detectorParamsChanged(); + void indexOfMarkerDictChanged(); + }; + class Recognizer : public QObject { Q_OBJECT private: // TODO add options for each marker type + CodeMarkerOptions mCodeMarkerOptions; // default multicolor marker (until 11/2016 hermes marker) RecognitionMethod mRecoMethod = RecognitionMethod::MultiColor; @@ -63,6 +123,7 @@ namespace reco { QList<TrackPoint> getMarkerPos(cv::Mat &img, QRect &roi, Control *controlWidget, int borderSize, BackgroundFilter *bgFilter); RecognitionMethod getRecoMethod() const {return mRecoMethod;} + CodeMarkerOptions& getCodeMarkerOptions() {return mCodeMarkerOptions;} public slots: void userChangedRecoMethod(RecognitionMethod method) @@ -130,6 +191,7 @@ namespace reco { bool autoCorrect = false; ///< should perspective correction be performed bool autoCorrectOnlyExport = false; ///< should perspective correction only be performed when exporting trajectories RecognitionMethod method = RecognitionMethod::Code; ///< Used recognition method; could be called from findMulticolorMarker + CodeMarkerOptions& codeOpt; }; std::vector<ColorBlob> findColorBlob(const ColorBlobDetectionParams &options); @@ -138,7 +200,7 @@ namespace reco { void refineWithBlackDot(std::vector<ColorBlob>& blobs, const cv::Mat& img, QList<TrackPoint>& crossList, const BlackDotOptions& options); void refineWithAruco(std::vector<ColorBlob> &blobs, const cv::Mat& img, QList<TrackPoint> &crossList, ArucoOptions& options); - void findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *controlWidget, RecognitionMethod recoMethod, Vec2F offsetCropRect2Roi=Vec2F{0,0}); + void findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, RecognitionMethod recoMethod, const CodeMarkerOptions &opt); cv::Ptr<cv::aruco::Dictionary> getDictMip36h12(); } } diff --git a/src/codeMarkerWidget.cpp b/src/codeMarkerWidget.cpp index 1ea23126830f81c382ec7204c8f090fbdc05bf92..741ff4e203cf1279320f794d783024b3b2b729f4 100644 --- a/src/codeMarkerWidget.cpp +++ b/src/codeMarkerWidget.cpp @@ -18,10 +18,13 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +#include <opencv2/core/version.hpp> + #include "codeMarkerWidget.h" +#include "recognition.h" -CodeMarkerWidget::CodeMarkerWidget(QWidget *parent) - : QWidget(parent) +CodeMarkerWidget::CodeMarkerWidget(QWidget *parent, reco::CodeMarkerOptions& codeMarkerOpt) + : QWidget(parent), mCodeMarkerOpt(codeMarkerOpt) { mMainWindow = (class Petrack*) parent; @@ -50,8 +53,45 @@ CodeMarkerWidget::CodeMarkerWidget(QWidget *parent) dictList->addItem("DICT_ARUCO_ORGINAL"); // 16 dictList->addItem("DICT_mip_36h12");//17 - dictList->setCurrentIndex(16); + connect(&mCodeMarkerOpt, &reco::CodeMarkerOptions::detectorParamsChanged, this, &CodeMarkerWidget::readDetectorParams); + connect(&mCodeMarkerOpt, &reco::CodeMarkerOptions::indexOfMarkerDictChanged, this, &CodeMarkerWidget::readDictListIndex); + + // get default values from Options + readDetectorParams(); + readDictListIndex(); + + + // NOTE: No parameter validation done here + // Can result in crash, when invalid params are chosen (e.g. min larger than max) + auto changedParams = [&](){ + sendDetectorParams(packDetectorParams()); + notifyChanged(); + }; + + connect(minMarkerPerimeter, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(maxMarkerPerimeter, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(adaptiveThreshWinSizeMin, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(adaptiveThreshWinSizeMax, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(adaptiveThreshWinSizeStep, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(adaptiveThreshConstant, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + + connect(polygonalApproxAccuracyRate, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(minCornerDistance, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(minDistanceToBorder, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(minMarkerDistance, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + + connect(doCornerRefinement, &QGroupBox::clicked, changedParams); + connect(cornerRefinementWinSize, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(cornerRefinementMaxIterations, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(cornerRefinementMinAccuracy, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + + connect(markerBorderBits, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(perspectiveRemovePixelPerCell, QOverload<int>::of(&QSpinBox::valueChanged), changedParams); + connect(perspectiveRemoveIgnoredMarginPerCell, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(maxErroneousBitsInBorderRate, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(errorCorrectionRate, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); + connect(minOtsuStdDev, QOverload<double>::of(&QDoubleSpinBox::valueChanged), changedParams); } //<COLOR_MARKER> @@ -155,10 +195,94 @@ void CodeMarkerWidget::getXml(QDomElement &elem) errorCorrectionRate->setValue(subElem.attribute("ERROR_CORRECTION_RATE").toDouble()); if (subElem.hasAttribute("SHOW_DETECTED_CANDIDATES")) showDetectedCandidates->setCheckState(subElem.attribute("SHOW_DETECTED_CANDIDATES").toInt() ? Qt::Checked : Qt::Unchecked); - - } } } +reco::ArucoCodeParams CodeMarkerWidget::packDetectorParams() +{ + reco::ArucoCodeParams params; + params.adaptiveThreshConstant = adaptiveThreshConstant->value(); + params.adaptiveThreshWinSizeMax = adaptiveThreshWinSizeMax->value(); + params.adaptiveThreshWinSizeMin = adaptiveThreshWinSizeMin->value(); + params.adaptiveThreshWinSizeStep = adaptiveThreshWinSizeStep->value(); + params.cornerRefinementMaxIterations = cornerRefinementMaxIterations->value(); + params.cornerRefinementMinAccuracy = cornerRefinementMinAccuracy->value(); + params.cornerRefinementWinSize = cornerRefinementWinSize->value(); + params.doCornerRefinement = doCornerRefinement->isChecked(); + params.errorCorrectionRate = errorCorrectionRate->value(); + params.markerBorderBits = markerBorderBits->value(); + params.maxErroneousBitsInBorderRate = maxErroneousBitsInBorderRate->value(); + params.minDistanceToBorder = minDistanceToBorder->value(); + params.minMarkerDistance = minMarkerDistance->value(); + params.minMarkerPerimeter = minMarkerPerimeter->value(); + params.minOtsuStdDev = minOtsuStdDev->value(); + params.perspectiveRemoveIgnoredMarginPerCell = perspectiveRemoveIgnoredMarginPerCell->value(); + params.perspectiveRemovePixelPerCell = perspectiveRemovePixelPerCell->value(); + params.polygonalApproxAccuracyRate = polygonalApproxAccuracyRate->value(); + + return params; +} + +void CodeMarkerWidget::on_showDetectedCandidates_stateChanged(int i) +{ + mMainWindow->getCodeMarkerItem()->setVisible(i); + if( !mMainWindow->isLoading() ) + mMainWindow->getScene()->update(); +} + +void CodeMarkerWidget::on_moreInfosButton_clicked() +{ +#define CV_NUMERIC_VERSION CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) "." CVAUX_STR(CV_VERSION_REVISION) + QDesktopServices::openUrl(QUrl("http://docs.opencv.org/" CV_NUMERIC_VERSION "/d1/dcd/structcv_1_1aruco_1_1DetectorParameters.html#details", QUrl::TolerantMode)); + QDesktopServices::openUrl(QUrl("http://docs.opencv.org/" CV_NUMERIC_VERSION "/d5/dae/tutorial_aruco_detection.html", QUrl::TolerantMode)); +} + +void CodeMarkerWidget::on_dictList_currentIndexChanged(int i) +{ + mCodeMarkerOpt.userChangedIndexOfMarkerDict(i); + notifyChanged(); +} + + +void CodeMarkerWidget::notifyChanged() +{ + mMainWindow->setRecognitionChanged(true);// flag indicates that changes of recognition parameters happens + if( !mMainWindow->isLoading() ) + mMainWindow->updateImage(); +} + +void CodeMarkerWidget::readDetectorParams() +{ + auto params = mCodeMarkerOpt.getDetectorParams(); + adaptiveThreshConstant->setValue(params.adaptiveThreshConstant); + adaptiveThreshWinSizeMax->setValue(params.adaptiveThreshWinSizeMax); + adaptiveThreshWinSizeMin->setValue(params.adaptiveThreshWinSizeMin); + adaptiveThreshWinSizeStep->setValue(params.adaptiveThreshWinSizeStep); + cornerRefinementMaxIterations->setValue(params.cornerRefinementMaxIterations); + cornerRefinementMinAccuracy->setValue(params.cornerRefinementMinAccuracy); + cornerRefinementWinSize->setValue(params.cornerRefinementWinSize); + doCornerRefinement->setChecked(params.doCornerRefinement); + errorCorrectionRate->setValue(params.errorCorrectionRate); + markerBorderBits->setValue(params.markerBorderBits); + maxErroneousBitsInBorderRate->setValue(params.maxErroneousBitsInBorderRate); + minDistanceToBorder->setValue(params.minDistanceToBorder); + minMarkerDistance->setValue(params.minMarkerDistance); + minMarkerPerimeter->setValue(params.minMarkerPerimeter); + minOtsuStdDev->setValue(params.minOtsuStdDev); + perspectiveRemoveIgnoredMarginPerCell->setValue(params.perspectiveRemoveIgnoredMarginPerCell); + perspectiveRemovePixelPerCell->setValue(params.perspectiveRemovePixelPerCell); + polygonalApproxAccuracyRate->setValue(params.polygonalApproxAccuracyRate); +} + +void CodeMarkerWidget::readDictListIndex() +{ + dictList->setCurrentIndex(mCodeMarkerOpt.getIndexOfMarkerDict()); +} + +void CodeMarkerWidget::sendDetectorParams(reco::ArucoCodeParams params) +{ + mCodeMarkerOpt.userChangedDetectorParams(params); +} + #include "moc_codeMarkerWidget.cpp" diff --git a/src/petrack.cpp b/src/petrack.cpp index 1a1ef1cb55c99383f4c164b4270490e73e259c1d..3c82cb8286f726b7e5e6e8da24b49a008bff02d7 100644 --- a/src/petrack.cpp +++ b/src/petrack.cpp @@ -125,7 +125,7 @@ Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPa mColorMarkerWidget->setWindowFlags(Qt::Window); mColorMarkerWidget->setWindowTitle("Color marker parameter"); - mCodeMarkerWidget = new CodeMarkerWidget(this); + mCodeMarkerWidget = new CodeMarkerWidget(this, mReco.getCodeMarkerOptions()); mCodeMarkerWidget->setWindowFlags(Qt::Window); mCodeMarkerWidget->setWindowTitle("Code marker parameter"); @@ -268,6 +268,10 @@ Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPa createMenus(); createStatusBar(); + // TODO delete once we get Options to be value only (i.e. no Pointer/Ref anymore) + mReco.getCodeMarkerOptions().controlWidget = mControlWidget; + mReco.getCodeMarkerOptions().codeMarkerItem = mCodeMarkerItem; + mSeqFileName = QDir::currentPath(); //fuer allerersten Aufruf des Programms readSettings(); diff --git a/src/recognition.cpp b/src/recognition.cpp index 133afb22efc73e4fbe179376ef71289fac9add45..023f3d47ce3e613f7de9b0f48b1c5b5c3c67f06a 100644 --- a/src/recognition.cpp +++ b/src/recognition.cpp @@ -559,6 +559,7 @@ void detail::refineWithAruco(std::vector<ColorBlob> &blobs, const cv::Mat& img, bool autoCorrect = options.autoCorrect; bool autoCorrectOnlyExport = options.autoCorrectOnlyExport; + CodeMarkerOptions& codeOpt = options.codeOpt; for(ColorBlob& blob : blobs) { // cropRect has coordinates of rechtangele around color blob with respect to lower left corner (as in the beginning of useBlackDot) @@ -579,7 +580,10 @@ void detail::refineWithAruco(std::vector<ColorBlob> &blobs, const cv::Mat& img, if (subImg.empty()) continue; - findCodeMarker(subImg, crossList, controlWidget, options.method, offsetCropRect2Roi); + // TODO: Use Reference to actual codeMarkerOptions in MulticolorMarkerOptions + // NOTE: For now, add as parameter of findMulticolorMarker + codeOpt.offsetCropRect2Roi = offsetCropRect2Roi; + findCodeMarker(subImg, crossList, options.method, codeOpt); // The next three statements each: // - set the offset of subImg with regards to ROI //(ROI to original image is archieved later in the code for all methods) @@ -632,6 +636,7 @@ void detail::refineWithAruco(std::vector<ColorBlob> &blobs, const cv::Mat& img, crossList.back() = crossList.back() + (Vec2F(cropRect.x, cropRect.y) + moveDir); } } + codeOpt.offsetCropRect2Roi = Vec2F{0,0}; } /** @@ -647,7 +652,7 @@ void detail::refineWithAruco(std::vector<ColorBlob> &blobs, const cv::Mat& img, * @param ignoreWithoutMarker (is ignored->overwritten by cmWidget->ignoreWithoutDot->isChecked()) * @param offset */ -void findMultiColorMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *controlWidget, bool ignoreWithoutMarker, Vec2F &offset, RecognitionMethod method) +void findMultiColorMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *controlWidget, bool ignoreWithoutMarker, Vec2F &offset, RecognitionMethod method, CodeMarkerOptions& codeOpt) { Petrack *mainWindow = controlWidget->getMainWindow(); MultiColorMarkerItem* cmItem = mainWindow->getMultiColorMarkerItem(); @@ -723,12 +728,14 @@ void findMultiColorMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *c refineWithBlackDot(blobs, img, crossList, options); }else if (useCodeMarker) { - ArucoOptions options; - options.autoCorrect = autoCorrect; - options.autoCorrectOnlyExport = autoCorrectOnlyExport; - options.ignoreWithoutMarker = ignoreWithoutMarker; - options.controlWidget = controlWidget; - options.method = method; + ArucoOptions options{ + controlWidget, + ignoreWithoutMarker, + autoCorrect, + autoCorrectOnlyExport, + method, + codeOpt + }; // adds to crosslist refineWithAruco(blobs, img, crossList, options); @@ -855,14 +862,14 @@ void findColorMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *contro * @param crossList[out] list of detected TrackPoints * @param controlWidget */ -void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control *controlWidget, RecognitionMethod recoMethod, Vec2F offsetCropRect2Roi /*=(0,0)*/) +void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, RecognitionMethod recoMethod, const CodeMarkerOptions& opt) { - CodeMarkerItem* codeMarkerItem = controlWidget->getMainWindow()->getCodeMarkerItem(); - CodeMarkerWidget* codeMarkerWidget = controlWidget->getMainWindow()->getCodeMarkerWidget(); + CodeMarkerItem *codeMarkerItem = opt.codeMarkerItem; + const auto& par = opt.detectorParams; - cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(codeMarkerWidget->dictList->currentIndex())); + cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(opt.indexOfMarkerDict)); - if (codeMarkerWidget->dictList->currentIndex() == 17) //for usage of DICT_mip_36h12 as it is not predifined in opencv + if (opt.indexOfMarkerDict == 17) //for usage of DICT_mip_36h12 as it is not predifined in opencv { dictionary = detail::getDictMip36h12(); } @@ -871,11 +878,11 @@ void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control double minMarkerPerimeterRate = 0.03, maxMarkerPerimeterRate = 4, minCornerDistanceRate = 0.05, minMarkerDistanceRate = 0.05; - Petrack *mainWindow = controlWidget->getMainWindow(); + Petrack *mainWindow = opt.controlWidget->getMainWindow(); int bS = mainWindow->getImageBorderSize(); - if (controlWidget->getCalibCoordDimension() == 0) // 3D + if (opt.controlWidget->getCalibCoordDimension() == 0) // 3D { if (recoMethod == RecognitionMethod::Code) // for usage of codemarker with CodeMarker-function (-> without MulticolorMarker) { @@ -883,18 +890,18 @@ void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control myRound(mainWindow->getRecoRoiItem()->rect().y()), myRound(mainWindow->getRecoRoiItem()->rect().width()), myRound(mainWindow->getRecoRoiItem()->rect().height())); - QPointF p1 = mainWindow->getImageItem()->getCmPerPixel(rect.x(),rect.y(),controlWidget->mapDefaultHeight->value()), - p2 = mainWindow->getImageItem()->getCmPerPixel(rect.x()+rect.width(),rect.y(),controlWidget->mapDefaultHeight->value()), - p3 = mainWindow->getImageItem()->getCmPerPixel(rect.x(),rect.y()+rect.height(),controlWidget->mapDefaultHeight->value()), - p4 = mainWindow->getImageItem()->getCmPerPixel(rect.x()+rect.width(),rect.y()+rect.height(),controlWidget->mapDefaultHeight->value()); + QPointF p1 = mainWindow->getImageItem()->getCmPerPixel(rect.x(),rect.y(),opt.controlWidget->mapDefaultHeight->value()), + p2 = mainWindow->getImageItem()->getCmPerPixel(rect.x()+rect.width(),rect.y(),opt.controlWidget->mapDefaultHeight->value()), + p3 = mainWindow->getImageItem()->getCmPerPixel(rect.x(),rect.y()+rect.height(),opt.controlWidget->mapDefaultHeight->value()), + p4 = mainWindow->getImageItem()->getCmPerPixel(rect.x()+rect.width(),rect.y()+rect.height(),opt.controlWidget->mapDefaultHeight->value()); double cmPerPixel_min = std::min(std::min(std::min(p1.x(), p1.y()), std::min(p2.x(), p2.y())), std::min(std::min(p3.x(), p3.y()), std::min(p4.x(), p4.y()))); double cmPerPixel_max = std::max(std::max(std::max(p1.x(), p1.y()), std::max(p2.x(), p2.y())), std::max(std::max(p3.x(), p3.y()), std::max(p4.x(), p4.y()))); - minMarkerPerimeterRate = (codeMarkerWidget->minMarkerPerimeter->value()*4./cmPerPixel_max)/std::max(rect.width(),rect.height()); - maxMarkerPerimeterRate = (codeMarkerWidget->maxMarkerPerimeter->value()*4./cmPerPixel_min)/std::max(rect.width(),rect.height()); + minMarkerPerimeterRate = (par.minMarkerPerimeter*4./cmPerPixel_max)/std::max(rect.width(),rect.height()); + maxMarkerPerimeterRate = (par.maxMarkerPerimeter*4./cmPerPixel_min)/std::max(rect.width(),rect.height()); } else if (recoMethod == RecognitionMethod::MultiColor) // for usage of codemarker with MulticolorMarker { QRect rect(0,0, img.rows, img.cols); @@ -904,44 +911,44 @@ void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control maxMarkerPerimeterRate = 4.; } - minCornerDistanceRate = codeMarkerWidget->minCornerDistance->value(); - minMarkerDistanceRate = codeMarkerWidget->minMarkerDistance->value(); + minCornerDistanceRate = par.minCornerDistance; + minMarkerDistanceRate = par.minMarkerDistance; } else // 2D { double cmPerPixel = mainWindow->getImageItem()->getCmPerPixel(); - minMarkerPerimeterRate = (codeMarkerWidget->minMarkerPerimeter->value()*4/cmPerPixel)/std::max(mainWindow->getImage()->width()-bS,mainWindow->getImage()->height()-bS); - maxMarkerPerimeterRate = (codeMarkerWidget->maxMarkerPerimeter->value()*4/cmPerPixel)/std::max(mainWindow->getImage()->width()-bS,mainWindow->getImage()->height()-bS); + minMarkerPerimeterRate = (par.minMarkerPerimeter*4/cmPerPixel)/std::max(mainWindow->getImage()->width()-bS,mainWindow->getImage()->height()-bS); + maxMarkerPerimeterRate = (par.maxMarkerPerimeter*4/cmPerPixel)/std::max(mainWindow->getImage()->width()-bS,mainWindow->getImage()->height()-bS); - minCornerDistanceRate = codeMarkerWidget->minCornerDistance->value(); - minMarkerDistanceRate = codeMarkerWidget->minMarkerDistance->value(); + minCornerDistanceRate = par.minCornerDistance; + minMarkerDistanceRate = par.minMarkerDistance; } - detectorParams->adaptiveThreshWinSizeMin = codeMarkerWidget->adaptiveThreshWinSizeMin->value(); - detectorParams->adaptiveThreshWinSizeMax = codeMarkerWidget->adaptiveThreshWinSizeMax->value(); - detectorParams->adaptiveThreshWinSizeStep = codeMarkerWidget->adaptiveThreshWinSizeStep->value(); - detectorParams->adaptiveThreshConstant = codeMarkerWidget->adaptiveThreshConstant->value(); + detectorParams->adaptiveThreshWinSizeMin = par.adaptiveThreshWinSizeMin; + detectorParams->adaptiveThreshWinSizeMax = par.adaptiveThreshWinSizeMax; + detectorParams->adaptiveThreshWinSizeStep = par.adaptiveThreshWinSizeStep; + detectorParams->adaptiveThreshConstant = par.adaptiveThreshConstant; detectorParams->minMarkerPerimeterRate = minMarkerPerimeterRate; detectorParams->maxMarkerPerimeterRate = maxMarkerPerimeterRate; - detectorParams->polygonalApproxAccuracyRate = codeMarkerWidget->polygonalApproxAccuracyRate->value(); + detectorParams->polygonalApproxAccuracyRate = par.polygonalApproxAccuracyRate; detectorParams->minCornerDistanceRate = minCornerDistanceRate; - detectorParams->minDistanceToBorder = codeMarkerWidget->minDistanceToBorder->value(); + detectorParams->minDistanceToBorder = par.minDistanceToBorder; detectorParams->minMarkerDistanceRate = minMarkerDistanceRate; // No refinement is default value // TODO Check if this is the best MEthod for our usecase - if(codeMarkerWidget->doCornerRefinement->isChecked()){ + if(par.doCornerRefinement){ detectorParams->cornerRefinementMethod = cv::aruco::CornerRefineMethod::CORNER_REFINE_SUBPIX; } - detectorParams->cornerRefinementWinSize = codeMarkerWidget->cornerRefinementWinSize->value(); - detectorParams->cornerRefinementMaxIterations = codeMarkerWidget->cornerRefinementMaxIterations->value(); - detectorParams->cornerRefinementMinAccuracy = codeMarkerWidget->cornerRefinementMinAccuracy->value(); - detectorParams->markerBorderBits = codeMarkerWidget->markerBorderBits->value(); - detectorParams->perspectiveRemovePixelPerCell = codeMarkerWidget->perspectiveRemovePixelPerCell->value(); - detectorParams->perspectiveRemoveIgnoredMarginPerCell = codeMarkerWidget->perspectiveRemoveIgnoredMarginPerCell->value(); - detectorParams->maxErroneousBitsInBorderRate = codeMarkerWidget->maxErroneousBitsInBorderRate->value(); - detectorParams->minOtsuStdDev = codeMarkerWidget->minOtsuStdDev->value(); - detectorParams->errorCorrectionRate = codeMarkerWidget->errorCorrectionRate->value(); + detectorParams->cornerRefinementWinSize = par.cornerRefinementWinSize; + detectorParams->cornerRefinementMaxIterations = par.cornerRefinementMaxIterations; + detectorParams->cornerRefinementMinAccuracy = par.cornerRefinementMinAccuracy; + detectorParams->markerBorderBits = par.markerBorderBits; + detectorParams->perspectiveRemovePixelPerCell = par.perspectiveRemovePixelPerCell; + detectorParams->perspectiveRemoveIgnoredMarginPerCell = par.perspectiveRemoveIgnoredMarginPerCell; + detectorParams->maxErroneousBitsInBorderRate = par.maxErroneousBitsInBorderRate; + detectorParams->minOtsuStdDev = par.minOtsuStdDev; + detectorParams->errorCorrectionRate = par.errorCorrectionRate; std::vector<int> ids; std::vector<std::vector<cv::Point2f> > corners, rejected; @@ -951,8 +958,8 @@ void detail::findCodeMarker(cv::Mat &img, QList<TrackPoint> &crossList, Control cv::aruco::detectMarkers(img, dictionary, corners, ids, detectorParams, rejected); - codeMarkerItem->addDetectedMarkers(corners,ids, offsetCropRect2Roi); - codeMarkerItem->addRejectedMarkers(rejected, offsetCropRect2Roi); + codeMarkerItem->addDetectedMarkers(corners,ids, opt.offsetCropRect2Roi); + codeMarkerItem->addRejectedMarkers(rejected, opt.offsetCropRect2Roi); // detected code markers for(size_t i = 0; i<ids.size(); i++) @@ -1100,13 +1107,13 @@ QList<TrackPoint> Recognizer::getMarkerPos(cv::Mat &img, QRect &roi, Control *co switch (mRecoMethod) { case RecognitionMethod::MultiColor: - findMultiColorMarker(tImg, crossList, controlWidget, ignoreWithoutMarker, v, mRecoMethod); + findMultiColorMarker(tImg, crossList, controlWidget, ignoreWithoutMarker, v, mRecoMethod, mCodeMarkerOptions); break; case RecognitionMethod::Color: findColorMarker(tImg, crossList, controlWidget); break; case RecognitionMethod::Code: - findCodeMarker(tImg, crossList, controlWidget, mRecoMethod); + findCodeMarker(tImg, crossList, mRecoMethod, mCodeMarkerOptions); break; case RecognitionMethod::Casern: [[fallthrough]]; case RecognitionMethod::Hermes: [[fallthrough]]; @@ -1142,6 +1149,21 @@ QList<TrackPoint> Recognizer::getMarkerPos(cv::Mat &img, QRect &roi, Control *co return crossList; } +void CodeMarkerOptions::userChangedDetectorParams(ArucoCodeParams params) +{ + if(params != detectorParams){ + detectorParams = params; + emit detectorParamsChanged(); + } +} + +void CodeMarkerOptions::userChangedIndexOfMarkerDict(int idx) +{ + if(idx != indexOfMarkerDict){ + indexOfMarkerDict = idx; + emit indexOfMarkerDictChanged(); + } +} /** * @brief getMip36h12Dict() overrides current dictionary with dictionary from 'aruco_mip_36h12_dict'