diff options
-rw-r--r-- | tracker-aruco/CMakeLists.txt | 14 | ||||
-rw-r--r-- | tracker-aruco/aruco-trackercontrols.ui | 13 | ||||
-rw-r--r-- | tracker-aruco/ftnoir_tracker_aruco.cpp | 165 | ||||
-rw-r--r-- | tracker-aruco/ftnoir_tracker_aruco.h | 56 | ||||
-rw-r--r-- | tracker-aruco/trans_calib.cpp | 41 | ||||
-rw-r--r-- | tracker-aruco/trans_calib.h | 39 |
6 files changed, 200 insertions, 128 deletions
diff --git a/tracker-aruco/CMakeLists.txt b/tracker-aruco/CMakeLists.txt index c0b50bee..d68b8f59 100644 --- a/tracker-aruco/CMakeLists.txt +++ b/tracker-aruco/CMakeLists.txt @@ -1,7 +1,9 @@ -find_package(OpenCV 3.0 QUIET COMPONENTS ${opencv-modules}) -set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Aruco paper marker tracker static library path") -if(SDK_ARUCO_LIBPATH AND OpenCV_FOUND) - opentrack_boilerplate(opentrack-tracker-aruco) - target_link_libraries(opentrack-tracker-aruco opentrack-cv ${SDK_ARUCO_LIBPATH} ${OpenCV_LIBS}) - target_include_directories(opentrack-tracker-aruco SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) +find_package(OpenCV 3.0 QUIET) +if(OpenCV_FOUND) + set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Aruco paper marker tracker static library path") + if(SDK_ARUCO_LIBPATH) + opentrack_boilerplate(opentrack-tracker-aruco) + target_link_libraries(opentrack-tracker-aruco opentrack-cv ${SDK_ARUCO_LIBPATH} ${OpenCV_LIBS}) + target_include_directories(opentrack-tracker-aruco SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) + endif() endif() diff --git a/tracker-aruco/aruco-trackercontrols.ui b/tracker-aruco/aruco-trackercontrols.ui index 1479b2e7..5f043098 100644 --- a/tracker-aruco/aruco-trackercontrols.ui +++ b/tracker-aruco/aruco-trackercontrols.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>dialog_aruco</class> - <widget class="QWidget" name="dialog_aruco"> + <class>Form</class> + <widget class="QWidget" name="Form"> <property name="windowModality"> <enum>Qt::NonModal</enum> </property> @@ -145,10 +145,10 @@ <locale language="English" country="UnitedStates"/> </property> <property name="minimum"> - <number>10</number> + <number>35</number> </property> <property name="maximum"> - <number>180</number> + <number>90</number> </property> </widget> </item> @@ -172,11 +172,6 @@ </item> <item> <property name="text"> - <string>320x200</string> - </property> - </item> - <item> - <property name="text"> <string>Default (not recommended!)</string> </property> </item> diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp index 8edb8cf5..0df89fcb 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.cpp +++ b/tracker-aruco/ftnoir_tracker_aruco.cpp @@ -5,13 +5,13 @@ * copyright notice and this permission notice appear in all copies. */ +#include "cv/video-widget.hpp" #include "ftnoir_tracker_aruco.h" -#include "api/plugin-api.hpp" -#include "compat/camera-names.hpp" #include "cv/video-property-page.hpp" +#include "compat/camera-names.hpp" +#include "compat/sleep.hpp" #include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/calib3d.hpp> @@ -30,34 +30,35 @@ struct resolution_tuple int height; }; -static resolution_tuple resolution_choices[] = +static constexpr const resolution_tuple resolution_choices[] = { { 640, 480 }, { 320, 240 }, - { 320, 200 }, { 0, 0 } }; -tracker_aruco::tracker_aruco() : +constexpr const double aruco_tracker::RC; +constexpr const double aruco_tracker::size_min; +constexpr const double aruco_tracker::size_max; + +aruco_tracker::aruco_tracker() : stop(false), layout(nullptr), videoWidget(nullptr), + fps(0), obj_points(4), - intrinsics(decltype(intrinsics)::eye()), - dist_coeffs(decltype(dist_coeffs)::zeros()), - rmat(decltype(rmat)::eye()), + intrinsics(cv::Matx33d::eye()), + rmat(cv::Matx33d::eye()), roi_points(4), - last_roi(65535, 65535, 0, 0), - freq(cv::getTickFrequency()), // XXX change to timer.hpp - cur_fps(0) + last_roi(65535, 65535, 0, 0) { // param 2 ignored for Otsu thresholding. it's required to use our fork of Aruco. - detector.setThresholdParams(5, -1); + detector.setThresholdParams(7, -1); detector.setDesiredSpeed(3); detector._thresMethod = aruco::MarkerDetector::FIXED_THRES; } -tracker_aruco::~tracker_aruco() +aruco_tracker::~aruco_tracker() { stop = true; wait(); @@ -65,10 +66,12 @@ tracker_aruco::~tracker_aruco() delete videoWidget; if(layout) delete layout; + // fast start/stop causes breakage + portable::sleep(1000); camera.release(); } -void tracker_aruco::start_tracker(QFrame* videoframe) +void aruco_tracker::start_tracker(QFrame* videoframe) { videoframe->show(); videoWidget = new cv_video_widget(videoframe); @@ -85,9 +88,7 @@ void tracker_aruco::start_tracker(QFrame* videoframe) layout = layout_; } -#define HT_PI M_PI - -void tracker_aruco::getRT(cv::Matx33d& r_, cv::Vec3d& t_) +void aruco_tracker::getRT(cv::Matx33d& r_, cv::Vec3d& t_) { QMutexLocker l(&mtx); @@ -95,17 +96,14 @@ void tracker_aruco::getRT(cv::Matx33d& r_, cv::Vec3d& t_) t_ = t; } -bool tracker_aruco::detect_with_roi() +bool aruco_tracker::detect_with_roi() { if (last_roi.width > 1 && last_roi.height > 1) { - float min = std::min(1.f, std::max(.01f, size_min * grayscale.cols / last_roi.width)); - float max = std::max(.01f, std::min(1.f, size_max * grayscale.cols / last_roi.width)); - detector.setMinMaxSize(min, max); + detector.setMinMaxSize(std::min(1., std::max(.01, size_min * grayscale.cols / last_roi.width)), + std::max(.01, std::min(1., size_max * grayscale.cols / last_roi.width))); - cv::Mat grayscale_ = grayscale(last_roi); - - detector.detect(grayscale_, markers, cv::Mat(), cv::Mat(), -1, false); + detector.detect(grayscale(last_roi), markers, cv::Mat(), cv::Mat(), -1, false); if (markers.size() == 1 && markers[0].size() == 4) { @@ -124,14 +122,14 @@ bool tracker_aruco::detect_with_roi() return false; } -bool tracker_aruco::detect_without_roi() +bool aruco_tracker::detect_without_roi() { detector.setMinMaxSize(size_min, size_max); detector.detect(grayscale, markers, cv::Mat(), cv::Mat(), -1, false); return markers.size() == 1 && markers[0].size() == 4; } -bool tracker_aruco::open_camera() +bool aruco_tracker::open_camera() { int rint = s.resolution; if (rint < 0 || rint >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple))) @@ -180,7 +178,7 @@ bool tracker_aruco::open_camera() return true; } -void tracker_aruco::set_intrinsics() +void aruco_tracker::set_intrinsics() { const int w = grayscale.cols, h = grayscale.rows; const double diag_fov = static_cast<int>(s.fov) * M_PI / 180.; @@ -195,16 +193,20 @@ void tracker_aruco::set_intrinsics() intrinsics(1, 2) = grayscale.rows/2; } -void tracker_aruco::update_fps(double alpha) +void aruco_tracker::update_fps() { - std::uint64_t time = std::uint64_t(cv::getTickCount()); - const double dt = std::max(0., (time - last_time) / freq); - last_time = time; + const double dt = fps_timer.elapsed_seconds(); + fps_timer.start(); + const double alpha = dt/(dt + RC); - cur_fps = cur_fps * alpha + (1-alpha) * (fabs(dt) < 1e-6 ? 0 : 1./dt); + if (dt > 1e-3) + { + fps *= 1 - alpha; + fps += alpha * (1./dt + .8); + } } -void tracker_aruco::draw_ar(bool ok) +void aruco_tracker::draw_ar(bool ok) { if (ok) { @@ -213,13 +215,13 @@ void tracker_aruco::draw_ar(bool ok) cv::line(frame, m[i], m[(i+1)%4], cv::Scalar(0, 0, 255), 2, 8); } - char buf[32]; - ::snprintf(buf, sizeof(buf)-1, "Hz: %d", (int)(unsigned short)cur_fps); + char buf[9]; + ::snprintf(buf, sizeof(buf)-1, "Hz: %d", clamp(int(fps), 0, 9999)); buf[sizeof(buf)-1] = '\0'; cv::putText(frame, buf, cv::Point(10, 32), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 255, 0), 1); } -void tracker_aruco::clamp_last_roi() +void aruco_tracker::clamp_last_roi() { if (last_roi.x < 0) last_roi.x = 0; @@ -242,7 +244,7 @@ void tracker_aruco::clamp_last_roi() last_roi.height -= last_roi.y; } -void tracker_aruco::set_points() +void aruco_tracker::set_points() { using f = float; const f hx = f(s.headpos_x), hy = f(s.headpos_y), hz = f(s.headpos_z); @@ -257,19 +259,18 @@ void tracker_aruco::set_points() obj_points[x4] = cv::Point3f(-size + hx, size + hy, 0 + hz); } -void tracker_aruco::draw_centroid() +void aruco_tracker::draw_centroid() { repr2.clear(); static const std::vector<cv::Point3f> centroid { cv::Point3f(0, 0, 0) }; - cv::projectPoints(centroid, rvec, tvec, intrinsics, dist_coeffs, repr2); + cv::projectPoints(centroid, rvec, tvec, intrinsics, cv::noArray(), repr2); - auto s = cv::Scalar(255, 0, 255); - cv::circle(frame, repr2[0], 4, s, -1); + cv::circle(frame, repr2[0], 4, cv::Scalar(255, 0, 255), -1); } -void tracker_aruco::set_last_roi() +void aruco_tracker::set_last_roi() { roi_projection.clear(); @@ -281,12 +282,12 @@ void tracker_aruco::set_last_roi() roi_points[i] = pt * c_search_window; } - cv::projectPoints(roi_points, rvec, tvec, intrinsics, dist_coeffs, roi_projection); + cv::projectPoints(roi_points, rvec, tvec, intrinsics, cv::noArray(), roi_projection); set_roi_from_projection(); } -void tracker_aruco::set_rmat() +void aruco_tracker::set_rmat() { cv::Rodrigues(rvec, rmat); @@ -305,7 +306,7 @@ void tracker_aruco::set_rmat() t = cv::Vec3d(tvec[0], -tvec[1], tvec[2]); } -void tracker_aruco::set_roi_from_projection() +void aruco_tracker::set_roi_from_projection() { last_roi = cv::Rect(color.cols-1, color.rows-1, 0, 0); @@ -328,7 +329,7 @@ void tracker_aruco::set_roi_from_projection() clamp_last_roi(); } -void tracker_aruco::run() +void aruco_tracker::run() { cv::setNumThreads(0); @@ -340,7 +341,7 @@ void tracker_aruco::run() if (!open_camera()) return; - last_time = std::uint64_t(cv::getTickCount()); + fps_timer.start(); while (!stop) { @@ -357,7 +358,7 @@ void tracker_aruco::run() set_intrinsics(); - update_fps(alpha_); + update_fps(); markers.clear(); @@ -367,10 +368,7 @@ void tracker_aruco::run() { set_points(); - if (!cv::solvePnP(obj_points, markers[0], intrinsics, dist_coeffs, rvec, tvec, false, cv::SOLVEPNP_DLS)) - goto fail; - - if (!cv::solvePnP(obj_points, markers[0], intrinsics, dist_coeffs, rvec, tvec, true, cv::SOLVEPNP_ITERATIVE)) + if (!cv::solvePnP(obj_points, markers[0], intrinsics, cv::noArray(), rvec, tvec, false, cv::SOLVEPNP_ITERATIVE)) goto fail; set_last_roi(); @@ -389,19 +387,19 @@ fail: } } -void tracker_aruco::data(double *data) +void aruco_tracker::data(double *data) { QMutexLocker lck(&mtx); data[Yaw] = pose[Yaw]; data[Pitch] = pose[Pitch]; data[Roll] = pose[Roll]; - data[TX] = pose[TX] * .5; - data[TY] = pose[TY] * .5; + data[TX] = pose[TX]; + data[TY] = pose[TY]; data[TZ] = pose[TZ]; } -dialog_aruco::dialog_aruco() +aruco_dialog::aruco_dialog() { tracker = nullptr; calib_timer.setInterval(250); @@ -419,34 +417,15 @@ dialog_aruco::dialog_aruco() connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); connect(ui.btn_calibrate, SIGNAL(clicked()), this, SLOT(toggleCalibrate())); connect(this, SIGNAL(destroyed()), this, SLOT(cleanupCalib())); - connect(&calib_timer, SIGNAL(timeout()), this, SLOT(update_tracker_calibration())); - connect(ui.cameraName, &QComboBox::currentTextChanged, this, &dialog_aruco::set_camera_settings_available); - set_camera_settings_available(ui.cameraName->currentText()); - connect(ui.camera_settings, &QPushButton::clicked, this, &dialog_aruco::show_camera_settings); -} + connect(ui.camera_settings, SIGNAL(clicked()), this, SLOT(camera_settings())); + connect(&s.camera_name, SIGNAL(valueChanged(const QString&)), this, SLOT(update_camera_settings_state(const QString&))); -void dialog_aruco::set_camera_settings_available(const QString& camera_name) -{ - const bool avail = video_property_page::should_show_dialog(camera_name); - ui.camera_settings->setEnabled(avail); + update_camera_settings_state(s.camera_name); } -void dialog_aruco::show_camera_settings() -{ - const int idx = ui.cameraName->currentIndex(); - if (tracker) - { - cv::VideoCapture& cap = tracker->camera; - if (cap.isOpened()) - video_property_page::show_from_capture(cap, idx); - } - else - video_property_page::show(idx); -} - -void dialog_aruco::toggleCalibrate() +void aruco_dialog::toggleCalibrate() { if (!calib_timer.isActive()) { @@ -467,13 +446,13 @@ void dialog_aruco::toggleCalibrate() } } -void dialog_aruco::cleanupCalib() +void aruco_dialog::cleanupCalib() { if (calib_timer.isActive()) calib_timer.stop(); } -void dialog_aruco::update_tracker_calibration() +void aruco_dialog::update_tracker_calibration() { if (calib_timer.isActive() && tracker) { @@ -484,15 +463,31 @@ void dialog_aruco::update_tracker_calibration() } } -void dialog_aruco::doOK() +void aruco_dialog::doOK() { s.b->save(); close(); } -void dialog_aruco::doCancel() +void aruco_dialog::doCancel() { close(); } -OPENTRACK_DECLARE_TRACKER(tracker_aruco, dialog_aruco, aruco_metadata) +void aruco_dialog::camera_settings() +{ + if (tracker) + { + QMutexLocker l(&tracker->camera_mtx); + video_property_page::show_from_capture(tracker->camera, camera_name_to_index(s.camera_name)); + } + else + video_property_page::show(camera_name_to_index(s.camera_name)); +} + +void aruco_dialog::update_camera_settings_state(const QString& name) +{ + ui.camera_settings->setEnabled(video_property_page::should_show_dialog(name)); +} + +OPENTRACK_DECLARE_TRACKER(aruco_tracker, aruco_dialog, aruco_metadata) diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h index ccb1ad21..54940d28 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.h +++ b/tracker-aruco/ftnoir_tracker_aruco.h @@ -8,14 +8,13 @@ #pragma once #include "ui_aruco-trackercontrols.h" +#include "options/options.hpp" +#include "trans_calib.h" #include "api/plugin-api.hpp" -#include "include/markerdetector.h" - #include "cv/video-widget.hpp" -#include "cv/translation-calibrator.hpp" +#include "compat/timer.hpp" -#include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> +#include "include/markerdetector.h" #include <QObject> #include <QThread> @@ -26,7 +25,9 @@ #include <cinttypes> -#include "options/options.hpp" +#include <opencv2/core.hpp> +#include <opencv2/videoio.hpp> + using namespace options; struct settings : opts { @@ -46,16 +47,16 @@ struct settings : opts { {} }; -class dialog_aruco; +class aruco_dialog; -class tracker_aruco : protected QThread, public ITracker +class aruco_tracker : protected QThread, public ITracker { Q_OBJECT - friend class dialog_aruco; + friend class aruco_dialog; static constexpr float c_search_window = 1.3f; public: - tracker_aruco(); - ~tracker_aruco() override; + aruco_tracker(); + ~aruco_tracker() override; void start_tracker(QFrame* frame) override; void data(double *data) override; void run() override; @@ -65,7 +66,7 @@ private: bool detect_without_roi(); bool open_camera(); void set_intrinsics(); - void update_fps(double dt); + void update_fps(); void draw_ar(bool ok); void clamp_last_roi(); void set_points(); @@ -77,16 +78,14 @@ private: cv::VideoCapture camera; QMutex camera_mtx; QMutex mtx; - volatile bool stop; QHBoxLayout* layout; cv_video_widget* videoWidget; settings s; - double pose[6]; + double pose[6], fps; cv::Mat frame, grayscale, color; cv::Matx33d r; std::vector<cv::Point3f> obj_points; cv::Matx33d intrinsics; - cv::Matx14f dist_coeffs; aruco::MarkerDetector detector; std::vector<aruco::Marker> markers; cv::Vec3d t; @@ -97,25 +96,26 @@ private: cv::Vec3d euler; std::vector<cv::Point3f> roi_points; cv::Rect last_roi; - double freq, cur_fps; - std::uint64_t last_time; + Timer fps_timer; + + volatile bool stop; - static constexpr float size_min = 0.05f; - static constexpr float size_max = 0.3f; + static constexpr const double size_min = 0.05f; + static constexpr const double size_max = 0.3f; - static constexpr double alpha_ = .95; + static constexpr const double RC = .25; }; -class dialog_aruco : public ITrackerDialog +class aruco_dialog : public ITrackerDialog { Q_OBJECT public: - dialog_aruco(); - void register_tracker(ITracker * x) override { tracker = static_cast<tracker_aruco*>(x); } + aruco_dialog(); + void register_tracker(ITracker * x) override { tracker = static_cast<aruco_tracker*>(x); } void unregister_tracker() override { tracker = nullptr; } private: - Ui::dialog_aruco ui; - tracker_aruco* tracker; + Ui::Form ui; + aruco_tracker* tracker; settings s; TranslationCalibrator calibrator; QTimer calib_timer; @@ -125,12 +125,12 @@ private slots: void toggleCalibrate(); void cleanupCalib(); void update_tracker_calibration(); - void set_camera_settings_available(const QString& camera_name); - void show_camera_settings(); + void camera_settings(); + void update_camera_settings_state(const QString& name); }; class aruco_metadata : public Metadata { - QString name() { return QString(QCoreApplication::translate("aruco_metadata", "aruco -- paper marker tracker")); } + QString name() { return QString("aruco -- paper marker tracker"); } QIcon icon() { return QIcon(":/images/aruco.png"); } }; diff --git a/tracker-aruco/trans_calib.cpp b/tracker-aruco/trans_calib.cpp new file mode 100644 index 00000000..b5148efd --- /dev/null +++ b/tracker-aruco/trans_calib.cpp @@ -0,0 +1,41 @@ +/* Copyright (c) 2012 Patrick Ruoff + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include "trans_calib.h" + +TranslationCalibrator::TranslationCalibrator() +{ + reset(); +} + +void TranslationCalibrator::reset() +{ + P = cv::Matx66f::zeros(); + y = cv::Vec6f(0,0,0, 0,0,0); +} + +void TranslationCalibrator::update(const cv::Matx33d& R_CM_k, const cv::Vec3d& t_CM_k) +{ + cv::Matx<double, 6,3> H_k_T = cv::Matx<double, 6,3>::zeros(); + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + H_k_T(i,j) = R_CM_k(j,i); + } + } + for (int i=0; i<3; ++i) + { + H_k_T(3+i,i) = 1.0; + } + P += H_k_T * H_k_T.t(); + y += H_k_T * t_CM_k; +} + +cv::Vec3f TranslationCalibrator::get_estimate() +{ + cv::Vec6f x = P.inv() * y; + return cv::Vec3f(x[0], x[1], x[2]); +} diff --git a/tracker-aruco/trans_calib.h b/tracker-aruco/trans_calib.h new file mode 100644 index 00000000..3d1e56fa --- /dev/null +++ b/tracker-aruco/trans_calib.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012 Patrick Ruoff + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifndef TRANSCALIB_H +#define TRANSCALIB_H + +#include <opencv2/core/core.hpp> + +//----------------------------------------------------------------------------- +// Calibrates the translation from head to model = t_MH +// by recursive least squares / +// kalman filter in information form with identity noise covariance +// measurement equation when head position = t_CH is fixed: +// (R_CM_k , Id)*(-t_MH, t_CH) = t_CM_k + +class TranslationCalibrator +{ +public: + TranslationCalibrator(); + + // reset the calibration process + void reset(); + + // update the current estimate + void update(const cv::Matx33d& R_CM_k, const cv::Vec3d& t_CM_k); + + // get the current estimate for t_MH + cv::Vec3f get_estimate(); + +private: + cv::Matx66f P; // normalized precision matrix = inverse covariance + cv::Vec6f y; // P*(-t_MH, t_CH) +}; + +#endif //TRANSCALIB_H |