From 0657dc14e07ee705a8ab0bba67dfbe04c603db49 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Thu, 18 Jan 2018 23:45:42 +0100 Subject: tracker/pt: move impl bits away from pt-base --- tracker-pt/CMakeLists.txt | 4 +- tracker-pt/camera.cpp | 171 --------------- tracker-pt/camera.h | 69 ------ tracker-pt/frame.cpp | 83 ------- tracker-pt/frame.hpp | 36 --- tracker-pt/ftnoir_tracker_pt.qrc | 1 - tracker-pt/ftnoir_tracker_pt_dialog.h | 2 +- tracker-pt/module.cxx | 69 ------ tracker-pt/module/CMakeLists.txt | 6 + tracker-pt/module/Resources/Logo_IR.png | Bin 0 -> 10386 bytes tracker-pt/module/camera.cpp | 171 +++++++++++++++ tracker-pt/module/camera.h | 69 ++++++ tracker-pt/module/frame.cpp | 83 +++++++ tracker-pt/module/frame.hpp | 36 +++ tracker-pt/module/ftnoir_tracker_pt.qrc | 5 + tracker-pt/module/lang/nl_NL.ts | 4 + tracker-pt/module/lang/ru_RU.ts | 4 + tracker-pt/module/lang/stub.ts | 4 + tracker-pt/module/module.cpp | 69 ++++++ tracker-pt/module/point_extractor.cpp | 377 ++++++++++++++++++++++++++++++++ tracker-pt/module/point_extractor.h | 59 +++++ tracker-pt/point_extractor.cpp | 377 -------------------------------- tracker-pt/point_extractor.h | 59 ----- 23 files changed, 889 insertions(+), 869 deletions(-) delete mode 100644 tracker-pt/camera.cpp delete mode 100644 tracker-pt/camera.h delete mode 100644 tracker-pt/frame.cpp delete mode 100644 tracker-pt/frame.hpp delete mode 100644 tracker-pt/module.cxx create mode 100644 tracker-pt/module/CMakeLists.txt create mode 100644 tracker-pt/module/Resources/Logo_IR.png create mode 100644 tracker-pt/module/camera.cpp create mode 100644 tracker-pt/module/camera.h create mode 100644 tracker-pt/module/frame.cpp create mode 100644 tracker-pt/module/frame.hpp create mode 100644 tracker-pt/module/ftnoir_tracker_pt.qrc create mode 100644 tracker-pt/module/lang/nl_NL.ts create mode 100644 tracker-pt/module/lang/ru_RU.ts create mode 100644 tracker-pt/module/lang/stub.ts create mode 100644 tracker-pt/module/module.cpp create mode 100644 tracker-pt/module/point_extractor.cpp create mode 100644 tracker-pt/module/point_extractor.h delete mode 100644 tracker-pt/point_extractor.cpp delete mode 100644 tracker-pt/point_extractor.h (limited to 'tracker-pt') diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt index 53d86320..ebabd364 100644 --- a/tracker-pt/CMakeLists.txt +++ b/tracker-pt/CMakeLists.txt @@ -4,7 +4,5 @@ if(OpenCV_FOUND) otr_module(tracker-pt-base STATIC) target_include_directories(opentrack-tracker-pt-base SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) target_link_libraries(opentrack-tracker-pt-base opentrack-cv ${modules}) - - otr_module(tracker-pt SOURCES module.cxx) - target_link_libraries(opentrack-tracker-pt opentrack-tracker-pt-base) endif() +add_subdirectory(module) diff --git a/tracker-pt/camera.cpp b/tracker-pt/camera.cpp deleted file mode 100644 index ba4583da..00000000 --- a/tracker-pt/camera.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* 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 "camera.h" -#include "frame.hpp" - -#include "compat/sleep.hpp" -#include "compat/camera-names.hpp" -#include "compat/math-imports.hpp" - -#include - -#include "cv/video-property-page.hpp" - -using namespace pt_module; - -Camera::Camera(const QString& module_name) : s { module_name } -{ -} - -QString Camera::get_desired_name() const -{ - return desired_name; -} - -QString Camera::get_active_name() const -{ - return active_name; -} - -void Camera::show_camera_settings() -{ - const int idx = camera_name_to_index(s.camera_name); - - if (bool(*this)) - video_property_page::show_from_capture(*cap, idx); - else - { - video_property_page::show(idx); - } -} - -Camera::result Camera::get_info() const -{ - if (cam_info.res_x == 0 || cam_info.res_y == 0) - return result(false, pt_camera_info()); - return result(true, cam_info); -} - -Camera::result Camera::get_frame(pt_frame& frame_) -{ - cv::Mat& frame = frame_.as()->mat; - - const bool new_frame = _get_frame(frame); - - if (new_frame) - { - const double dt = t.elapsed_seconds(); - t.start(); - - // measure fps of valid frames - constexpr double RC = .1; // seconds - const double alpha = dt/(dt + RC); - - if (dt_mean < dt_eps) - dt_mean = dt; - else - dt_mean = (1-alpha) * dt_mean + alpha * dt; - - cam_info.fps = dt_mean > dt_eps ? 1 / dt_mean : 0; - cam_info.res_x = frame.cols; - cam_info.res_y = frame.rows; - cam_info.fov = fov; - - return result(true, cam_info); - } - else - return result(false, pt_camera_info()); -} - -pt_camera_open_status Camera::start(int idx, int fps, int res_x, int res_y) -{ - if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0) - { - if (cam_desired.idx != idx || - cam_desired.fps != fps || - cam_desired.res_x != res_x || - cam_desired.res_y != res_y || - !cap || !cap->isOpened() || !cap->grab()) - { - stop(); - - desired_name = get_camera_names().value(idx); - cam_desired.idx = idx; - cam_desired.fps = fps; - cam_desired.res_x = res_x; - cam_desired.res_y = res_y; - cam_desired.fov = fov; - - cap = camera_ptr(new cv::VideoCapture(cam_desired.idx)); - - if (cam_desired.res_x) - cap->set(cv::CAP_PROP_FRAME_WIDTH, cam_desired.res_x); - if (cam_desired.res_y) - cap->set(cv::CAP_PROP_FRAME_HEIGHT, cam_desired.res_y); - if (cam_desired.fps) - cap->set(cv::CAP_PROP_FPS, cam_desired.fps); - - if (cap->isOpened() && cap->grab()) - { - cam_info = pt_camera_info(); - active_name = QString(); - cam_info.idx = idx; - dt_mean = 0; - active_name = desired_name; - - t.start(); - - return cam_open_ok_change; - } - else - { - stop(); - return cam_open_error; - } - } - - return cam_open_ok_no_change; - } - - stop(); - return cam_open_error; -} - -void Camera::stop() -{ - cap = nullptr; - desired_name = QString(); - active_name = QString(); - cam_info = pt_camera_info(); - cam_desired = pt_camera_info(); -} - -bool Camera::_get_frame(cv::Mat& frame) -{ - if (cap && cap->isOpened()) - { - for (int i = 0; i < 5; i++) - { - if (cap->read(frame)) - return true; - portable::sleep(1); - } - } - return false; -} - -void Camera::camera_deleter::operator()(cv::VideoCapture* cap) -{ - if (cap) - { - if (cap->isOpened()) - cap->release(); - delete cap; - } -} - diff --git a/tracker-pt/camera.h b/tracker-pt/camera.h deleted file mode 100644 index 96234840..00000000 --- a/tracker-pt/camera.h +++ /dev/null @@ -1,69 +0,0 @@ -/* 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. - */ - -#pragma once - -#include "pt-api.hpp" - -#include "compat/timer.hpp" - -#include -#include -#include - -#include -#include - -#include - -namespace pt_module { - -struct Camera final : pt_camera -{ - Camera(const QString& module_name); - - pt_camera_open_status start(int idx, int fps, int res_x, int res_y) override; - void stop() override; - - result get_frame(pt_frame& Frame) override; - result get_info() const override; - - pt_camera_info get_desired() const override { return cam_desired; } - QString get_desired_name() const override; - QString get_active_name() const override; - - operator bool() const override { return cap && cap->isOpened(); } - - void set_fov(double value) override { fov = value; } - void show_camera_settings() override; - -private: - warn_result_unused bool _get_frame(cv::Mat& Frame); - - double dt_mean = 0, fov = 30; - - Timer t; - - pt_camera_info cam_info; - pt_camera_info cam_desired; - QString desired_name, active_name; - - struct camera_deleter final - { - void operator()(cv::VideoCapture* cap); - }; - - using camera_ptr = std::unique_ptr; - - camera_ptr cap; - - pt_settings s; - - static constexpr inline double dt_eps = 1./384; -}; - -} // ns pt_module diff --git a/tracker-pt/frame.cpp b/tracker-pt/frame.cpp deleted file mode 100644 index e403af07..00000000 --- a/tracker-pt/frame.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "frame.hpp" - -#include "compat/math.hpp" - -#include -#include - -#include - -using namespace pt_module; - -Preview& Preview::operator=(const pt_frame& frame_) -{ - const cv::Mat& frame = frame_.as_const()->mat; - ensure_size(frame_copy, frame_out.cols, frame_out.rows, CV_8UC3); - - if (frame.channels() != 3) - { - once_only(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels()); - return *this; - } - - const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows; - if (need_resize) - cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST); - else - frame.copyTo(frame_copy); - - return *this; -} - -Preview::Preview(int w, int h) -{ - ensure_size(frame_out, w, h, CV_8UC4); - - frame_out.setTo(cv::Scalar(0, 0, 0, 0)); -} - -QImage Preview::get_bitmap() -{ - int stride = frame_out.step.p[0]; - - if (stride < 64 || stride < frame_out.cols * 4) - { - once_only(qDebug() << "bad stride" << stride - << "for bitmap size" << frame_copy.cols << frame_copy.rows); - return QImage(); - } - - cv::cvtColor(frame_copy, frame_out, cv::COLOR_BGR2BGRA); - - return QImage((const unsigned char*) frame_out.data, - frame_out.cols, frame_out.rows, - stride, - QImage::Format_ARGB32); -} - -void Preview::draw_head_center(double x, double y) -{ - double px_, py_; - - std::tie(px_, py_) = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows); - - int px = iround(px_), py = iround(py_); - - constexpr int len = 9; - - static const cv::Scalar color(0, 255, 255); - cv::line(frame_copy, - cv::Point(px - len, py), - cv::Point(px + len, py), - color, 1); - cv::line(frame_copy, - cv::Point(px, py - len), - cv::Point(px, py + len), - color, 1); -} - -void Preview::ensure_size(cv::Mat& frame, int w, int h, int type) -{ - if (frame.cols != w || frame.rows != h) - frame = cv::Mat(h, w, type); -} diff --git a/tracker-pt/frame.hpp b/tracker-pt/frame.hpp deleted file mode 100644 index 9e4f809a..00000000 --- a/tracker-pt/frame.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "pt-api.hpp" - -#include -#include - -namespace pt_module { - -struct Frame final : pt_frame -{ - cv::Mat mat; - - operator const cv::Mat&() const& { return mat; } - operator cv::Mat&() & { return mat; } -}; - -struct Preview final : pt_preview -{ - Preview(int w, int h); - - Preview& operator=(const pt_frame& frame) override; - QImage get_bitmap() override; - void draw_head_center(double x, double y) override; - - operator cv::Mat&() { return frame_copy; } - operator cv::Mat const&() const { return frame_copy; } - -private: - static void ensure_size(cv::Mat& frame, int w, int h, int type); - - bool fresh = true; - cv::Mat frame_copy, frame_color, frame_out; -}; - -} // ns pt_module diff --git a/tracker-pt/ftnoir_tracker_pt.qrc b/tracker-pt/ftnoir_tracker_pt.qrc index a8f9a1af..8c270540 100644 --- a/tracker-pt/ftnoir_tracker_pt.qrc +++ b/tracker-pt/ftnoir_tracker_pt.qrc @@ -4,6 +4,5 @@ Resources/cap_side.png Resources/clip_front.png Resources/clip_side.png - Resources/Logo_IR.png diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.h b/tracker-pt/ftnoir_tracker_pt_dialog.h index 655e52ca..66835c10 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.h +++ b/tracker-pt/ftnoir_tracker_pt_dialog.h @@ -10,7 +10,7 @@ #include "pt-api.hpp" #include "ftnoir_tracker_pt.h" -#include "ui_FTNoIR_PT_Controls.h" +#include "tracker-pt/ui_FTNoIR_PT_Controls.h" #include "cv/translation-calibrator.hpp" #include "cv/video-widget.hpp" #include "compat/correlation-calibrator.hpp" diff --git a/tracker-pt/module.cxx b/tracker-pt/module.cxx deleted file mode 100644 index 5c298ca5..00000000 --- a/tracker-pt/module.cxx +++ /dev/null @@ -1,69 +0,0 @@ -#include "ftnoir_tracker_pt.h" -#include "api/plugin-api.hpp" - -#include "camera.h" -#include "frame.hpp" -#include "point_extractor.h" -#include "ftnoir_tracker_pt_dialog.h" - -#include "pt-api.hpp" - -#include - -static const QString module_name = "tracker-pt"; - -using namespace pt_module; - -struct pt_module_traits final : pt_runtime_traits -{ - pointer make_camera() const override - { - return pointer(new Camera(module_name)); - } - - pointer make_point_extractor() const override - { - return pointer(new PointExtractor(module_name)); - } - - QString get_module_name() const override - { - return module_name; - } - - pointer make_frame() const override - { - return pointer(new Frame); - } - - pointer make_preview(int w, int h) const override - { - return pointer(new Preview(w, h)); - } -}; - -struct tracker_pt : Tracker_PT -{ - tracker_pt() : Tracker_PT(pointer(new pt_module_traits)) - { - } -}; - -struct dialog_pt : TrackerDialog_PT -{ - dialog_pt(); -}; - -class metadata_pt : public Metadata -{ - QString name() { return _("PointTracker 1.1"); } - QIcon icon() { return QIcon(":/Resources/Logo_IR.png"); } -}; - -// ns pt_module - -using namespace pt_module; - -dialog_pt::dialog_pt() : TrackerDialog_PT(module_name) {} - -OPENTRACK_DECLARE_TRACKER(tracker_pt, dialog_pt, metadata_pt) diff --git a/tracker-pt/module/CMakeLists.txt b/tracker-pt/module/CMakeLists.txt new file mode 100644 index 00000000..1d1b4458 --- /dev/null +++ b/tracker-pt/module/CMakeLists.txt @@ -0,0 +1,6 @@ +find_package(OpenCV 3.0 QUIET) +if(OpenCV_FOUND) + otr_module(tracker-pt) + target_link_libraries(opentrack-tracker-pt opentrack-tracker-pt-base) + target_include_directories(opentrack-tracker-pt PRIVATE "${CMAKE_SOURCE_DIR}/tracker-pt") +endif() diff --git a/tracker-pt/module/Resources/Logo_IR.png b/tracker-pt/module/Resources/Logo_IR.png new file mode 100644 index 00000000..95032a25 Binary files /dev/null and b/tracker-pt/module/Resources/Logo_IR.png differ diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp new file mode 100644 index 00000000..ba4583da --- /dev/null +++ b/tracker-pt/module/camera.cpp @@ -0,0 +1,171 @@ +/* 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 "camera.h" +#include "frame.hpp" + +#include "compat/sleep.hpp" +#include "compat/camera-names.hpp" +#include "compat/math-imports.hpp" + +#include + +#include "cv/video-property-page.hpp" + +using namespace pt_module; + +Camera::Camera(const QString& module_name) : s { module_name } +{ +} + +QString Camera::get_desired_name() const +{ + return desired_name; +} + +QString Camera::get_active_name() const +{ + return active_name; +} + +void Camera::show_camera_settings() +{ + const int idx = camera_name_to_index(s.camera_name); + + if (bool(*this)) + video_property_page::show_from_capture(*cap, idx); + else + { + video_property_page::show(idx); + } +} + +Camera::result Camera::get_info() const +{ + if (cam_info.res_x == 0 || cam_info.res_y == 0) + return result(false, pt_camera_info()); + return result(true, cam_info); +} + +Camera::result Camera::get_frame(pt_frame& frame_) +{ + cv::Mat& frame = frame_.as()->mat; + + const bool new_frame = _get_frame(frame); + + if (new_frame) + { + const double dt = t.elapsed_seconds(); + t.start(); + + // measure fps of valid frames + constexpr double RC = .1; // seconds + const double alpha = dt/(dt + RC); + + if (dt_mean < dt_eps) + dt_mean = dt; + else + dt_mean = (1-alpha) * dt_mean + alpha * dt; + + cam_info.fps = dt_mean > dt_eps ? 1 / dt_mean : 0; + cam_info.res_x = frame.cols; + cam_info.res_y = frame.rows; + cam_info.fov = fov; + + return result(true, cam_info); + } + else + return result(false, pt_camera_info()); +} + +pt_camera_open_status Camera::start(int idx, int fps, int res_x, int res_y) +{ + if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0) + { + if (cam_desired.idx != idx || + cam_desired.fps != fps || + cam_desired.res_x != res_x || + cam_desired.res_y != res_y || + !cap || !cap->isOpened() || !cap->grab()) + { + stop(); + + desired_name = get_camera_names().value(idx); + cam_desired.idx = idx; + cam_desired.fps = fps; + cam_desired.res_x = res_x; + cam_desired.res_y = res_y; + cam_desired.fov = fov; + + cap = camera_ptr(new cv::VideoCapture(cam_desired.idx)); + + if (cam_desired.res_x) + cap->set(cv::CAP_PROP_FRAME_WIDTH, cam_desired.res_x); + if (cam_desired.res_y) + cap->set(cv::CAP_PROP_FRAME_HEIGHT, cam_desired.res_y); + if (cam_desired.fps) + cap->set(cv::CAP_PROP_FPS, cam_desired.fps); + + if (cap->isOpened() && cap->grab()) + { + cam_info = pt_camera_info(); + active_name = QString(); + cam_info.idx = idx; + dt_mean = 0; + active_name = desired_name; + + t.start(); + + return cam_open_ok_change; + } + else + { + stop(); + return cam_open_error; + } + } + + return cam_open_ok_no_change; + } + + stop(); + return cam_open_error; +} + +void Camera::stop() +{ + cap = nullptr; + desired_name = QString(); + active_name = QString(); + cam_info = pt_camera_info(); + cam_desired = pt_camera_info(); +} + +bool Camera::_get_frame(cv::Mat& frame) +{ + if (cap && cap->isOpened()) + { + for (int i = 0; i < 5; i++) + { + if (cap->read(frame)) + return true; + portable::sleep(1); + } + } + return false; +} + +void Camera::camera_deleter::operator()(cv::VideoCapture* cap) +{ + if (cap) + { + if (cap->isOpened()) + cap->release(); + delete cap; + } +} + diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h new file mode 100644 index 00000000..96234840 --- /dev/null +++ b/tracker-pt/module/camera.h @@ -0,0 +1,69 @@ +/* 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. + */ + +#pragma once + +#include "pt-api.hpp" + +#include "compat/timer.hpp" + +#include +#include +#include + +#include +#include + +#include + +namespace pt_module { + +struct Camera final : pt_camera +{ + Camera(const QString& module_name); + + pt_camera_open_status start(int idx, int fps, int res_x, int res_y) override; + void stop() override; + + result get_frame(pt_frame& Frame) override; + result get_info() const override; + + pt_camera_info get_desired() const override { return cam_desired; } + QString get_desired_name() const override; + QString get_active_name() const override; + + operator bool() const override { return cap && cap->isOpened(); } + + void set_fov(double value) override { fov = value; } + void show_camera_settings() override; + +private: + warn_result_unused bool _get_frame(cv::Mat& Frame); + + double dt_mean = 0, fov = 30; + + Timer t; + + pt_camera_info cam_info; + pt_camera_info cam_desired; + QString desired_name, active_name; + + struct camera_deleter final + { + void operator()(cv::VideoCapture* cap); + }; + + using camera_ptr = std::unique_ptr; + + camera_ptr cap; + + pt_settings s; + + static constexpr inline double dt_eps = 1./384; +}; + +} // ns pt_module diff --git a/tracker-pt/module/frame.cpp b/tracker-pt/module/frame.cpp new file mode 100644 index 00000000..e403af07 --- /dev/null +++ b/tracker-pt/module/frame.cpp @@ -0,0 +1,83 @@ +#include "frame.hpp" + +#include "compat/math.hpp" + +#include +#include + +#include + +using namespace pt_module; + +Preview& Preview::operator=(const pt_frame& frame_) +{ + const cv::Mat& frame = frame_.as_const()->mat; + ensure_size(frame_copy, frame_out.cols, frame_out.rows, CV_8UC3); + + if (frame.channels() != 3) + { + once_only(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels()); + return *this; + } + + const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows; + if (need_resize) + cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST); + else + frame.copyTo(frame_copy); + + return *this; +} + +Preview::Preview(int w, int h) +{ + ensure_size(frame_out, w, h, CV_8UC4); + + frame_out.setTo(cv::Scalar(0, 0, 0, 0)); +} + +QImage Preview::get_bitmap() +{ + int stride = frame_out.step.p[0]; + + if (stride < 64 || stride < frame_out.cols * 4) + { + once_only(qDebug() << "bad stride" << stride + << "for bitmap size" << frame_copy.cols << frame_copy.rows); + return QImage(); + } + + cv::cvtColor(frame_copy, frame_out, cv::COLOR_BGR2BGRA); + + return QImage((const unsigned char*) frame_out.data, + frame_out.cols, frame_out.rows, + stride, + QImage::Format_ARGB32); +} + +void Preview::draw_head_center(double x, double y) +{ + double px_, py_; + + std::tie(px_, py_) = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows); + + int px = iround(px_), py = iround(py_); + + constexpr int len = 9; + + static const cv::Scalar color(0, 255, 255); + cv::line(frame_copy, + cv::Point(px - len, py), + cv::Point(px + len, py), + color, 1); + cv::line(frame_copy, + cv::Point(px, py - len), + cv::Point(px, py + len), + color, 1); +} + +void Preview::ensure_size(cv::Mat& frame, int w, int h, int type) +{ + if (frame.cols != w || frame.rows != h) + frame = cv::Mat(h, w, type); +} diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp new file mode 100644 index 00000000..9e4f809a --- /dev/null +++ b/tracker-pt/module/frame.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "pt-api.hpp" + +#include +#include + +namespace pt_module { + +struct Frame final : pt_frame +{ + cv::Mat mat; + + operator const cv::Mat&() const& { return mat; } + operator cv::Mat&() & { return mat; } +}; + +struct Preview final : pt_preview +{ + Preview(int w, int h); + + Preview& operator=(const pt_frame& frame) override; + QImage get_bitmap() override; + void draw_head_center(double x, double y) override; + + operator cv::Mat&() { return frame_copy; } + operator cv::Mat const&() const { return frame_copy; } + +private: + static void ensure_size(cv::Mat& frame, int w, int h, int type); + + bool fresh = true; + cv::Mat frame_copy, frame_color, frame_out; +}; + +} // ns pt_module diff --git a/tracker-pt/module/ftnoir_tracker_pt.qrc b/tracker-pt/module/ftnoir_tracker_pt.qrc new file mode 100644 index 00000000..dfeb7369 --- /dev/null +++ b/tracker-pt/module/ftnoir_tracker_pt.qrc @@ -0,0 +1,5 @@ + + + Resources/Logo_IR.png + + diff --git a/tracker-pt/module/lang/nl_NL.ts b/tracker-pt/module/lang/nl_NL.ts new file mode 100644 index 00000000..9e739505 --- /dev/null +++ b/tracker-pt/module/lang/nl_NL.ts @@ -0,0 +1,4 @@ + + + + diff --git a/tracker-pt/module/lang/ru_RU.ts b/tracker-pt/module/lang/ru_RU.ts new file mode 100644 index 00000000..f62cf2e1 --- /dev/null +++ b/tracker-pt/module/lang/ru_RU.ts @@ -0,0 +1,4 @@ + + + + diff --git a/tracker-pt/module/lang/stub.ts b/tracker-pt/module/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/tracker-pt/module/lang/stub.ts @@ -0,0 +1,4 @@ + + + + diff --git a/tracker-pt/module/module.cpp b/tracker-pt/module/module.cpp new file mode 100644 index 00000000..5c298ca5 --- /dev/null +++ b/tracker-pt/module/module.cpp @@ -0,0 +1,69 @@ +#include "ftnoir_tracker_pt.h" +#include "api/plugin-api.hpp" + +#include "camera.h" +#include "frame.hpp" +#include "point_extractor.h" +#include "ftnoir_tracker_pt_dialog.h" + +#include "pt-api.hpp" + +#include + +static const QString module_name = "tracker-pt"; + +using namespace pt_module; + +struct pt_module_traits final : pt_runtime_traits +{ + pointer make_camera() const override + { + return pointer(new Camera(module_name)); + } + + pointer make_point_extractor() const override + { + return pointer(new PointExtractor(module_name)); + } + + QString get_module_name() const override + { + return module_name; + } + + pointer make_frame() const override + { + return pointer(new Frame); + } + + pointer make_preview(int w, int h) const override + { + return pointer(new Preview(w, h)); + } +}; + +struct tracker_pt : Tracker_PT +{ + tracker_pt() : Tracker_PT(pointer(new pt_module_traits)) + { + } +}; + +struct dialog_pt : TrackerDialog_PT +{ + dialog_pt(); +}; + +class metadata_pt : public Metadata +{ + QString name() { return _("PointTracker 1.1"); } + QIcon icon() { return QIcon(":/Resources/Logo_IR.png"); } +}; + +// ns pt_module + +using namespace pt_module; + +dialog_pt::dialog_pt() : TrackerDialog_PT(module_name) {} + +OPENTRACK_DECLARE_TRACKER(tracker_pt, dialog_pt, metadata_pt) diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp new file mode 100644 index 00000000..b67c4036 --- /dev/null +++ b/tracker-pt/module/point_extractor.cpp @@ -0,0 +1,377 @@ +/* Copyright (c) 2012 Patrick Ruoff + * Copyright (c) 2015-2017 Stanislaw Halik + * + * 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 "point_extractor.h" +#include "point_tracker.h" +#include "frame.hpp" + +#include "cv/numeric.hpp" +#include "compat/math.hpp" + +#include + +#undef PREVIEW +//#define PREVIEW + +#if defined PREVIEW +# include +#endif + +#include +#include +#include +#include + +#include + +using namespace types; +using namespace pt_module; + +/* +http://en.wikipedia.org/wiki/Mean-shift +In this application the idea, is to eliminate any bias of the point estimate +which is introduced by the rather arbitrary thresholded area. One must recognize +that the thresholded area can only move in one pixel increments since it is +binary. Thus, its center of mass might make "jumps" as pixels are added/removed +from the thresholded area. +With mean-shift, a moving "window" or kernel is multiplied with the gray-scale +image, and the COM is calculated of the result. This is iterated where the +kernel center is set the previously computed COM. Thus, peaks in the image intensity +distribution "pull" the kernel towards themselves. Eventually it stops moving, i.e. +then the computed COM coincides with the kernel center. We hope that the +corresponding location is a good candidate for the extracted point. +The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free +algorithm for tracking single particles with variable size and shape." (2008). +*/ +static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 ¤t_center, f filter_width) +{ + // Most amazingly this function runs faster with doubles than with floats. + const f s = 1.0 / filter_width; + + f m = 0; + vec2 com { 0, 0 }; + for (int i = 0; i < frame_gray.rows; i++) + { + auto frame_ptr = (uint8_t const* restrict_ptr)frame_gray.ptr(i); + for (int j = 0; j < frame_gray.cols; j++) + { + f val = frame_ptr[j]; + val = val * val; // taking the square wights brighter parts of the image stronger. + { + f dx = (j - current_center[0])*s; + f dy = (i - current_center[1])*s; + f f = std::fmax(0, 1 - dx*dx - dy*dy); + val *= f; + } + m += val; + com[0] += j * val; + com[1] += i * val; + } + } + if (m > f(.1)) + { + com *= f(1) / m; + return com; + } + else + return current_center; +} + +PointExtractor::PointExtractor(const QString& module_name) : s(module_name) +{ + blobs.reserve(max_blobs); +} + +void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame) +{ + if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols) + for (unsigned k = 0; k < 3; k++) + ch[k] = cv::Mat1b(orig_frame.rows, orig_frame.cols); +} + +void PointExtractor::ensure_buffers(const cv::Mat& frame) +{ + const int W = frame.cols, H = frame.rows; + + if (frame_gray.rows != W || frame_gray.cols != H) + { + frame_gray = cv::Mat1b(H, W); + frame_bin = cv::Mat1b(H, W); + frame_blobs = cv::Mat1b(H, W); + } +} + +void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest) +{ + ensure_channel_buffers(orig_frame); + + const int from_to[] = { + idx, 0, + }; + + cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1); +} + +void PointExtractor::extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs) +{ + ensure_channel_buffers(orig_frame); + + cv::mixChannels(&orig_frame, 1, (cv::Mat*) ch, order_npairs, order, order_npairs); +} + +void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output) +{ + switch (s.blob_color) + { + case pt_color_blue_only: + { + extract_single_channel(frame, 0, output); + break; + } + case pt_color_red_only: + { + extract_single_channel(frame, 2, output); + break; + } + case pt_color_average: + { + const int W = frame.cols, H = frame.rows; + const cv::Mat tmp = frame.reshape(1, W * H); + cv::Mat output_ = output.reshape(1, W * H); + cv::reduce(tmp, output_, 1, cv::REDUCE_AVG); + break; + } + default: + once_only(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color)); + /*FALLTHROUGH*/ + case pt_color_natural: + cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY); + break; + } +} + +void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output) +{ + const int threshold_slider_value = s.threshold_slider.to(); + + if (!s.auto_threshold) + { + cv::threshold(frame_gray, output, threshold_slider_value, 255, cv::THRESH_BINARY); + } + else + { + const int hist_size = 256; + const float ranges_[] = { 0, 256 }; + float const* ranges = (const float*) ranges_; + + cv::calcHist(&frame_gray, + 1, + nullptr, + cv::noArray(), + hist, + 1, + (int const*) &hist_size, + &ranges); + + const f radius = (f) threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value); + + auto ptr = (float const* const restrict_ptr) hist.ptr(0); + const unsigned area = uround(3 * M_PI * radius*radius); + const unsigned sz = unsigned(hist.cols * hist.rows); + unsigned thres = 32; + for (unsigned i = sz-1, cnt = 0; i > 32; i--) + { + cnt += ptr[i]; + if (cnt >= area) + break; + thres = i; + } + + cv::threshold(frame_gray, output, thres, 255, CV_THRESH_BINARY); + } +} + +void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector& points) +{ + const cv::Mat& frame = frame_.as_const()->mat; + cv::Mat& preview_frame = *preview_frame_.as(); + + ensure_buffers(frame); + color_to_grayscale(frame, frame_gray); + +#if defined PREVIEW + cv::imshow("capture", frame_gray); + cv::waitKey(1); +#endif + + threshold_image(frame_gray, frame_bin); + + blobs.clear(); + frame_bin.copyTo(frame_blobs); + + const f region_size_min = s.min_point_size; + const f region_size_max = s.max_point_size; + + unsigned idx = 0; + for (int y=0; y < frame_blobs.rows; y++) + { + const unsigned char* ptr_bin = frame_blobs.ptr(y); + for (int x=0; x < frame_blobs.cols; x++) + { + if (ptr_bin[x] != 255) + continue; + idx = blobs.size() + 1; + + cv::Rect rect; + cv::floodFill(frame_blobs, + cv::Point(x,y), + cv::Scalar(idx), + &rect, + cv::Scalar(0), + cv::Scalar(0), + 4 | cv::FLOODFILL_FIXED_RANGE); + + unsigned cnt = 0; + unsigned norm = 0; + + const int ymax = rect.y+rect.height, + xmax = rect.x+rect.width; + + for (int i=rect.y; i < ymax; i++) + { + unsigned char const* const restrict_ptr ptr_blobs = frame_blobs.ptr(i); + unsigned char const* const restrict_ptr ptr_gray = frame_gray.ptr(i); + for (int j=rect.x; j < xmax; j++) + { + if (ptr_blobs[j] != idx) + continue; + + //ptr_blobs[j] = 0; + norm += ptr_gray[j]; + cnt++; + } + } + + const double radius = std::sqrt(cnt / M_PI); + if (radius > region_size_max || radius < region_size_min) + continue; + + blobs.emplace_back(radius, + vec2(rect.width/2., rect.height/2.), + std::pow(f(norm), f(1.1))/cnt, + rect); + + if (idx >= max_blobs) + goto end; + + // XXX we could go to the next scanline unless the points are really small. + // i'd expect each point being present on at least one unique scanline + // but it turns out some people are using 2px points -sh 20180110 +#if BROKEN && 0 + break; +#endif + } + } +end: + + const int W = frame_gray.cols; + const int H = frame_gray.rows; + + const unsigned sz = blobs.size(); + + std::sort(blobs.begin(), blobs.end(), [](const blob& b1, const blob& b2) { return b2.brightness < b1.brightness; }); + + for (idx = 0; idx < sz; ++idx) + { + blob &b = blobs[idx]; + cv::Rect rect = b.rect; + + rect.x -= rect.width / 2; + rect.y -= rect.height / 2; + rect.width *= 2; + rect.height *= 2; + rect &= cv::Rect(0, 0, W, H); // crop at frame boundaries + + cv::Mat frame_roi = frame_gray(rect); + + // smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1 + static constexpr f radius_c = f(1.75); + + const f kernel_radius = b.radius * radius_c; + vec2 pos(rect.width/2., rect.height/2.); // position relative to ROI. + + for (int iter = 0; iter < 10; ++iter) + { + vec2 com_new = MeanShiftIteration(frame_roi, pos, kernel_radius); + vec2 delta = com_new - pos; + pos = com_new; + if (delta.dot(delta) < 1e-2) + break; + } + + b.pos[0] = pos[0] + rect.x; + b.pos[1] = pos[1] + rect.y; + } + + for (unsigned k = 0; k < blobs.size(); k++) + { + blob& b = blobs[k]; + + const f dpi = preview_frame.cols / f(320); + const f offx = 10 * dpi, offy = 7.5 * dpi; + + const f cx = preview_frame.cols / f(frame.cols), + cy = preview_frame.rows / f(frame.rows), + c_ = (cx+cy)/2; + + static constexpr unsigned fract_bits = 16; + static constexpr double c_fract(1 << fract_bits); + + cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract)); + + auto circle_color = k >= PointModel::N_POINTS + ? cv::Scalar(192, 192, 192) + : cv::Scalar(255, 255, 0); + + const f overlay_size = dpi > 1.5 ? 2 : 1; + + cv::circle(preview_frame, p, iround((b.radius + 3.3) * c_ * c_fract), circle_color, overlay_size, cv::LINE_AA, fract_bits); + + char buf[16]; + buf[sizeof(buf)-1] = '\0'; + std::snprintf(buf, sizeof(buf) - 1, "%.2fpx", b.radius); + + auto text_color = k >= PointModel::N_POINTS + ? cv::Scalar(160, 160, 160) + : cv::Scalar(0, 0, 255); + + cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)); + cv::putText(preview_frame, buf, pos, + cv::FONT_HERSHEY_PLAIN, overlay_size, text_color, + 1); + } + + // End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values. + points.reserve(max_blobs); + points.clear(); + + for (const auto& b : blobs) + { + // note: H/W is equal to fx/fy + + vec2 p; + std::tie(p[0], p[1]) = to_screen_pos(b.pos[0], b.pos[1], W, H); + points.push_back(p); + } +} + +blob::blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect) : + radius(radius), brightness(brightness), pos(pos), rect(rect) +{ + //qDebug() << "radius" << radius << "pos" << pos[0] << pos[1]; +} diff --git a/tracker-pt/module/point_extractor.h b/tracker-pt/module/point_extractor.h new file mode 100644 index 00000000..1b6f55a2 --- /dev/null +++ b/tracker-pt/module/point_extractor.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2012 Patrick Ruoff + * Copyright (c) 2015-2016 Stanislaw Halik + * + * 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. + */ + +#pragma once + +#include "pt-api.hpp" + +#include + +#include +#include + +namespace pt_module { + +using namespace types; + +struct blob +{ + f radius, brightness; + vec2 pos; + cv::Rect rect; + + blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect); +}; + +class PointExtractor final : public pt_point_extractor +{ +public: + // extracts points from frame and draws some processing info into frame, if draw_output is set + // dt: time since last call in seconds + void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector& points) override; + PointExtractor(const QString& module_name); +private: + static constexpr int max_blobs = 16; + + pt_settings s; + + cv::Mat1b frame_gray, frame_bin, frame_blobs; + cv::Mat1f hist; + std::vector blobs; + cv::Mat1b ch[3]; + + void ensure_channel_buffers(const cv::Mat& orig_frame); + void ensure_buffers(const cv::Mat& frame); + + void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest); + void extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs); + + void color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output); + void threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output); +}; + +} // ns impl + diff --git a/tracker-pt/point_extractor.cpp b/tracker-pt/point_extractor.cpp deleted file mode 100644 index b67c4036..00000000 --- a/tracker-pt/point_extractor.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* Copyright (c) 2012 Patrick Ruoff - * Copyright (c) 2015-2017 Stanislaw Halik - * - * 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 "point_extractor.h" -#include "point_tracker.h" -#include "frame.hpp" - -#include "cv/numeric.hpp" -#include "compat/math.hpp" - -#include - -#undef PREVIEW -//#define PREVIEW - -#if defined PREVIEW -# include -#endif - -#include -#include -#include -#include - -#include - -using namespace types; -using namespace pt_module; - -/* -http://en.wikipedia.org/wiki/Mean-shift -In this application the idea, is to eliminate any bias of the point estimate -which is introduced by the rather arbitrary thresholded area. One must recognize -that the thresholded area can only move in one pixel increments since it is -binary. Thus, its center of mass might make "jumps" as pixels are added/removed -from the thresholded area. -With mean-shift, a moving "window" or kernel is multiplied with the gray-scale -image, and the COM is calculated of the result. This is iterated where the -kernel center is set the previously computed COM. Thus, peaks in the image intensity -distribution "pull" the kernel towards themselves. Eventually it stops moving, i.e. -then the computed COM coincides with the kernel center. We hope that the -corresponding location is a good candidate for the extracted point. -The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free -algorithm for tracking single particles with variable size and shape." (2008). -*/ -static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 ¤t_center, f filter_width) -{ - // Most amazingly this function runs faster with doubles than with floats. - const f s = 1.0 / filter_width; - - f m = 0; - vec2 com { 0, 0 }; - for (int i = 0; i < frame_gray.rows; i++) - { - auto frame_ptr = (uint8_t const* restrict_ptr)frame_gray.ptr(i); - for (int j = 0; j < frame_gray.cols; j++) - { - f val = frame_ptr[j]; - val = val * val; // taking the square wights brighter parts of the image stronger. - { - f dx = (j - current_center[0])*s; - f dy = (i - current_center[1])*s; - f f = std::fmax(0, 1 - dx*dx - dy*dy); - val *= f; - } - m += val; - com[0] += j * val; - com[1] += i * val; - } - } - if (m > f(.1)) - { - com *= f(1) / m; - return com; - } - else - return current_center; -} - -PointExtractor::PointExtractor(const QString& module_name) : s(module_name) -{ - blobs.reserve(max_blobs); -} - -void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame) -{ - if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols) - for (unsigned k = 0; k < 3; k++) - ch[k] = cv::Mat1b(orig_frame.rows, orig_frame.cols); -} - -void PointExtractor::ensure_buffers(const cv::Mat& frame) -{ - const int W = frame.cols, H = frame.rows; - - if (frame_gray.rows != W || frame_gray.cols != H) - { - frame_gray = cv::Mat1b(H, W); - frame_bin = cv::Mat1b(H, W); - frame_blobs = cv::Mat1b(H, W); - } -} - -void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest) -{ - ensure_channel_buffers(orig_frame); - - const int from_to[] = { - idx, 0, - }; - - cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1); -} - -void PointExtractor::extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs) -{ - ensure_channel_buffers(orig_frame); - - cv::mixChannels(&orig_frame, 1, (cv::Mat*) ch, order_npairs, order, order_npairs); -} - -void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output) -{ - switch (s.blob_color) - { - case pt_color_blue_only: - { - extract_single_channel(frame, 0, output); - break; - } - case pt_color_red_only: - { - extract_single_channel(frame, 2, output); - break; - } - case pt_color_average: - { - const int W = frame.cols, H = frame.rows; - const cv::Mat tmp = frame.reshape(1, W * H); - cv::Mat output_ = output.reshape(1, W * H); - cv::reduce(tmp, output_, 1, cv::REDUCE_AVG); - break; - } - default: - once_only(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color)); - /*FALLTHROUGH*/ - case pt_color_natural: - cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY); - break; - } -} - -void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output) -{ - const int threshold_slider_value = s.threshold_slider.to(); - - if (!s.auto_threshold) - { - cv::threshold(frame_gray, output, threshold_slider_value, 255, cv::THRESH_BINARY); - } - else - { - const int hist_size = 256; - const float ranges_[] = { 0, 256 }; - float const* ranges = (const float*) ranges_; - - cv::calcHist(&frame_gray, - 1, - nullptr, - cv::noArray(), - hist, - 1, - (int const*) &hist_size, - &ranges); - - const f radius = (f) threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value); - - auto ptr = (float const* const restrict_ptr) hist.ptr(0); - const unsigned area = uround(3 * M_PI * radius*radius); - const unsigned sz = unsigned(hist.cols * hist.rows); - unsigned thres = 32; - for (unsigned i = sz-1, cnt = 0; i > 32; i--) - { - cnt += ptr[i]; - if (cnt >= area) - break; - thres = i; - } - - cv::threshold(frame_gray, output, thres, 255, CV_THRESH_BINARY); - } -} - -void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector& points) -{ - const cv::Mat& frame = frame_.as_const()->mat; - cv::Mat& preview_frame = *preview_frame_.as(); - - ensure_buffers(frame); - color_to_grayscale(frame, frame_gray); - -#if defined PREVIEW - cv::imshow("capture", frame_gray); - cv::waitKey(1); -#endif - - threshold_image(frame_gray, frame_bin); - - blobs.clear(); - frame_bin.copyTo(frame_blobs); - - const f region_size_min = s.min_point_size; - const f region_size_max = s.max_point_size; - - unsigned idx = 0; - for (int y=0; y < frame_blobs.rows; y++) - { - const unsigned char* ptr_bin = frame_blobs.ptr(y); - for (int x=0; x < frame_blobs.cols; x++) - { - if (ptr_bin[x] != 255) - continue; - idx = blobs.size() + 1; - - cv::Rect rect; - cv::floodFill(frame_blobs, - cv::Point(x,y), - cv::Scalar(idx), - &rect, - cv::Scalar(0), - cv::Scalar(0), - 4 | cv::FLOODFILL_FIXED_RANGE); - - unsigned cnt = 0; - unsigned norm = 0; - - const int ymax = rect.y+rect.height, - xmax = rect.x+rect.width; - - for (int i=rect.y; i < ymax; i++) - { - unsigned char const* const restrict_ptr ptr_blobs = frame_blobs.ptr(i); - unsigned char const* const restrict_ptr ptr_gray = frame_gray.ptr(i); - for (int j=rect.x; j < xmax; j++) - { - if (ptr_blobs[j] != idx) - continue; - - //ptr_blobs[j] = 0; - norm += ptr_gray[j]; - cnt++; - } - } - - const double radius = std::sqrt(cnt / M_PI); - if (radius > region_size_max || radius < region_size_min) - continue; - - blobs.emplace_back(radius, - vec2(rect.width/2., rect.height/2.), - std::pow(f(norm), f(1.1))/cnt, - rect); - - if (idx >= max_blobs) - goto end; - - // XXX we could go to the next scanline unless the points are really small. - // i'd expect each point being present on at least one unique scanline - // but it turns out some people are using 2px points -sh 20180110 -#if BROKEN && 0 - break; -#endif - } - } -end: - - const int W = frame_gray.cols; - const int H = frame_gray.rows; - - const unsigned sz = blobs.size(); - - std::sort(blobs.begin(), blobs.end(), [](const blob& b1, const blob& b2) { return b2.brightness < b1.brightness; }); - - for (idx = 0; idx < sz; ++idx) - { - blob &b = blobs[idx]; - cv::Rect rect = b.rect; - - rect.x -= rect.width / 2; - rect.y -= rect.height / 2; - rect.width *= 2; - rect.height *= 2; - rect &= cv::Rect(0, 0, W, H); // crop at frame boundaries - - cv::Mat frame_roi = frame_gray(rect); - - // smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1 - static constexpr f radius_c = f(1.75); - - const f kernel_radius = b.radius * radius_c; - vec2 pos(rect.width/2., rect.height/2.); // position relative to ROI. - - for (int iter = 0; iter < 10; ++iter) - { - vec2 com_new = MeanShiftIteration(frame_roi, pos, kernel_radius); - vec2 delta = com_new - pos; - pos = com_new; - if (delta.dot(delta) < 1e-2) - break; - } - - b.pos[0] = pos[0] + rect.x; - b.pos[1] = pos[1] + rect.y; - } - - for (unsigned k = 0; k < blobs.size(); k++) - { - blob& b = blobs[k]; - - const f dpi = preview_frame.cols / f(320); - const f offx = 10 * dpi, offy = 7.5 * dpi; - - const f cx = preview_frame.cols / f(frame.cols), - cy = preview_frame.rows / f(frame.rows), - c_ = (cx+cy)/2; - - static constexpr unsigned fract_bits = 16; - static constexpr double c_fract(1 << fract_bits); - - cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract)); - - auto circle_color = k >= PointModel::N_POINTS - ? cv::Scalar(192, 192, 192) - : cv::Scalar(255, 255, 0); - - const f overlay_size = dpi > 1.5 ? 2 : 1; - - cv::circle(preview_frame, p, iround((b.radius + 3.3) * c_ * c_fract), circle_color, overlay_size, cv::LINE_AA, fract_bits); - - char buf[16]; - buf[sizeof(buf)-1] = '\0'; - std::snprintf(buf, sizeof(buf) - 1, "%.2fpx", b.radius); - - auto text_color = k >= PointModel::N_POINTS - ? cv::Scalar(160, 160, 160) - : cv::Scalar(0, 0, 255); - - cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)); - cv::putText(preview_frame, buf, pos, - cv::FONT_HERSHEY_PLAIN, overlay_size, text_color, - 1); - } - - // End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values. - points.reserve(max_blobs); - points.clear(); - - for (const auto& b : blobs) - { - // note: H/W is equal to fx/fy - - vec2 p; - std::tie(p[0], p[1]) = to_screen_pos(b.pos[0], b.pos[1], W, H); - points.push_back(p); - } -} - -blob::blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect) : - radius(radius), brightness(brightness), pos(pos), rect(rect) -{ - //qDebug() << "radius" << radius << "pos" << pos[0] << pos[1]; -} diff --git a/tracker-pt/point_extractor.h b/tracker-pt/point_extractor.h deleted file mode 100644 index 1b6f55a2..00000000 --- a/tracker-pt/point_extractor.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright (c) 2012 Patrick Ruoff - * Copyright (c) 2015-2016 Stanislaw Halik - * - * 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. - */ - -#pragma once - -#include "pt-api.hpp" - -#include - -#include -#include - -namespace pt_module { - -using namespace types; - -struct blob -{ - f radius, brightness; - vec2 pos; - cv::Rect rect; - - blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect); -}; - -class PointExtractor final : public pt_point_extractor -{ -public: - // extracts points from frame and draws some processing info into frame, if draw_output is set - // dt: time since last call in seconds - void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector& points) override; - PointExtractor(const QString& module_name); -private: - static constexpr int max_blobs = 16; - - pt_settings s; - - cv::Mat1b frame_gray, frame_bin, frame_blobs; - cv::Mat1f hist; - std::vector blobs; - cv::Mat1b ch[3]; - - void ensure_channel_buffers(const cv::Mat& orig_frame); - void ensure_buffers(const cv::Mat& frame); - - void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest); - void extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs); - - void color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output); - void threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output); -}; - -} // ns impl - -- cgit v1.2.3