diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2015-10-30 07:37:41 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2015-10-30 08:39:32 +0100 |
commit | aa066bdd4622d4f6824fee864f6be6806813f04d (patch) | |
tree | 3df328b8b364cba2373a85827191b259bd78d546 /tracker-ht | |
parent | d6a54431d178632a2bf466c9904f74abd143afe6 (diff) |
move to subdirectory-based build system
Closes #224
Diffstat (limited to 'tracker-ht')
-rw-r--r-- | tracker-ht/CMakeLists.txt | 11 | ||||
-rw-r--r-- | tracker-ht/ftnoir_tracker_ht.cpp | 225 | ||||
-rw-r--r-- | tracker-ht/ftnoir_tracker_ht.h | 90 | ||||
-rw-r--r-- | tracker-ht/headtracker-ftnoir.h | 24 | ||||
-rw-r--r-- | tracker-ht/ht-api.h | 51 | ||||
-rw-r--r-- | tracker-ht/ht-tracker.qrc | 5 | ||||
-rw-r--r-- | tracker-ht/ht-trackercontrols.ui | 157 | ||||
-rw-r--r-- | tracker-ht/ht_video_widget.cpp | 53 | ||||
-rw-r--r-- | tracker-ht/ht_video_widget.h | 39 | ||||
-rw-r--r-- | tracker-ht/images/ht.png | bin | 0 -> 2010 bytes |
10 files changed, 655 insertions, 0 deletions
diff --git a/tracker-ht/CMakeLists.txt b/tracker-ht/CMakeLists.txt new file mode 100644 index 00000000..74fd9056 --- /dev/null +++ b/tracker-ht/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SDK_HT "" CACHE FILEPATH "Path to headtracker library") +set(SDK_HT_FLANDMARK "" CACHE FILEPATH "Path to flandmark library for headtracker") +find_package(OpenCV 3.0) +if(OpenCV_FOUND) + if(SDK_HT AND SDK_HT_FLANDMARK) + opentrack_boilerplate(opentrack-tracker-ht) + target_link_libraries(opentrack-tracker-ht ${SDK_HT} ${SDK_HT_FLANDMARK} ${OpenCV_LIBS}) + link_with_dinput8(opentrack-tracker-ht) + target_include_directories(opentrack-tracker-ht SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) + endif() +endif() diff --git a/tracker-ht/ftnoir_tracker_ht.cpp b/tracker-ht/ftnoir_tracker_ht.cpp new file mode 100644 index 00000000..cc9d2ba1 --- /dev/null +++ b/tracker-ht/ftnoir_tracker_ht.cpp @@ -0,0 +1,225 @@ +#include "headtracker-ftnoir.h" +#include "ftnoir_tracker_ht.h" +#include "ui_ht-trackercontrols.h" +#include "opentrack/plugin-api.hpp" +#include <cmath> +#include "opentrack/camera-names.hpp" +#include "opentrack-compat/sleep.hpp" + +typedef struct { + int width; + int height; +} resolution_tuple; + +static resolution_tuple resolution_choices[] = { + { 640, 480 }, + { 320, 240 }, + { 320, 200 }, + { 0, 0 } +}; + +void Tracker::load_settings(ht_config_t* config) +{ + int nframes = 0; + switch (static_cast<int>(s.fps)) + { + default: + case 0: + nframes = 0; + break; + case 1: + nframes = 30; + break; + case 2: + nframes = 60; + break; + case 3: + nframes = 120; + break; + case 4: + nframes = 180; + break; + } + + config->classification_delay = 500; + config->field_of_view = s.fov; + config->max_keypoints = 150; + config->keypoint_distance = 3.5; + config->force_fps = nframes; + config->camera_index = camera_name_to_index(s.camera_name); + + config->ransac_max_reprojection_error = 25; + config->ransac_max_inlier_error = config->ransac_max_reprojection_error; + + config->pyrlk_pyramids = 0; + config->pyrlk_win_size_w = config->pyrlk_win_size_h = 21; + + config->ransac_max_mean_error = 999; + config->ransac_abs_max_mean_error = 999; + + config->debug = 1; + config->ransac_min_features = 0.95; + config->ransac_num_iters = 300; + + int res = s.resolution; + if (res < 0 || res >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple))) + res = 0; + resolution_tuple r = resolution_choices[res]; + config->force_width = r.width; + config->force_height = r.height; + config->flandmark_delay = 50; + for (int i = 0; i < 5; i++) + config->dist_coeffs[i] = 0; +} + +Tracker::Tracker() : + ht(nullptr), + ypr {0,0,0, 0,0,0}, + videoWidget(nullptr), + layout(nullptr), + should_stop(false) +{ +} + +Tracker::~Tracker() +{ + should_stop = true; + wait(); + ht_free_context(ht); + if (layout) + delete layout; + if (videoWidget) + delete videoWidget; +} + +void Tracker::start_tracker(QFrame* videoframe) +{ + videoframe->show(); + videoWidget = new HTVideoWidget(videoframe); + QHBoxLayout* layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(videoWidget); + if (videoframe->layout()) + delete videoframe->layout(); + videoframe->setLayout(layout); + videoWidget->show(); + this->layout = layout; + + load_settings(&conf); + ht = ht_make_context(&conf, nullptr); + start(); +} + +void Tracker::run() +{ + while (!should_stop) + { + ht_result_t euler; + euler.filled = false; + { + QMutexLocker l(&camera_mtx); + + if (!ht_cycle(ht, &euler)) + break; + } + if (euler.filled) + { + QMutexLocker l(&ypr_mtx); + ypr[TX] = euler.tx; + ypr[TY] = euler.ty; + ypr[TZ] = euler.tz; + ypr[Yaw] = euler.rotx; + ypr[Pitch] = euler.roty; + ypr[Roll] = euler.rotz; + } + { + const cv::Mat frame_ = ht_get_bgr_frame(ht); + if (frame_.cols <= HT_MAX_VIDEO_WIDTH && frame_.rows <= HT_MAX_VIDEO_HEIGHT && frame_.channels() <= HT_MAX_VIDEO_CHANNELS) + { + QMutexLocker l(&frame_mtx); + + const int cols = frame_.cols; + const int rows = frame_.rows; + const int pitch = cols * 3; + for (int y = 0; y < rows; y++) + { + for (int x = 0; x < cols; x++) + { + unsigned char* dest = &frame.frame[y * pitch + 3 * x]; + const cv::Vec3b& elt = frame_.at<cv::Vec3b>(y, x); + const cv::Scalar elt2 = static_cast<cv::Scalar>(elt); + dest[0] = elt2.val[0]; + dest[1] = elt2.val[1]; + dest[2] = elt2.val[2]; + } + } + frame.channels = frame_.channels(); + frame.width = frame_.cols; + frame.height = frame_.rows; + } + } + } + // give opencv time to exit camera threads, etc. + portable::sleep(500); +} + +void Tracker::data(double* data) +{ + { + QMutexLocker l(&frame_mtx); + + if (frame.width > 0) + { + videoWidget->update_image(frame.frame, frame.width, frame.height); + frame.width = 0; + } + } + + { + QMutexLocker l(&ypr_mtx); + + for (int i = 0; i < 6; i++) + data[i] = ypr[i]; + } +} + +TrackerControls::TrackerControls() : tracker(nullptr) +{ + ui.setupUi(this); + ui.cameraName->clear(); + QList<QString> names = get_camera_names(); + names.prepend("Any available"); + ui.cameraName->addItems(names); + tie_setting(s.camera_name, ui.cameraName); + tie_setting(s.fps, ui.cameraFPS); + tie_setting(s.fov, ui.cameraFOV); + tie_setting(s.resolution, ui.resolution); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); + connect(ui.camera_settings, SIGNAL(pressed()), this, SLOT(camera_settings())); +} + +void TrackerControls::doOK() +{ + s.b->save(); + this->close(); +} + +void TrackerControls::doCancel() +{ + s.b->reload(); + this->close(); +} + +void TrackerControls::camera_settings() +{ + if (tracker) + { + cv::VideoCapture* cap = ht_capture(tracker->ht); + open_camera_settings(cap, s.camera_name, &tracker->camera_mtx); + } + else + open_camera_settings(nullptr, s.camera_name, nullptr); +} + +OPENTRACK_DECLARE_TRACKER(Tracker, TrackerControls, TrackerDll) diff --git a/tracker-ht/ftnoir_tracker_ht.h b/tracker-ht/ftnoir_tracker_ht.h new file mode 100644 index 00000000..1e364456 --- /dev/null +++ b/tracker-ht/ftnoir_tracker_ht.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2013 Stanislaw Halik <sthalik@misaki.pl> + * + * 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 "headtracker-ftnoir.h" +#include "ui_ht-trackercontrols.h" +#include "ht_video_widget.h" +#include "opentrack-compat/shm.h" +#include <QObject> +#include "opentrack/options.hpp" +#include "opentrack/plugin-api.hpp" +#include "opentrack/opencv-camera-dialog.hpp" + +#include <QThread> +#include <QMutex> +#include <QMutexLocker> +#include <QHBoxLayout> +#include <QString> + +using namespace options; + +struct settings : opts { + value<double> fov; + value<QString> camera_name; + value<int> fps, resolution; + settings() : + opts("HT-Tracker"), + fov(b, "fov", 56), + camera_name(b, "camera-name", ""), + fps(b, "fps", 0), + resolution(b, "resolution", 0) + {} +}; + +class Tracker : public QThread, public ITracker +{ + Q_OBJECT +public: + Tracker(); + ~Tracker() override; + void run() override; + void start_tracker(QFrame* frame) override; + void data(double *data) override; + void load_settings(ht_config_t* config); + headtracker_t* ht; + QMutex camera_mtx; +private: + double ypr[6]; + settings s; + ht_config_t conf; + HTVideoWidget* videoWidget; + QHBoxLayout* layout; + QMutex ypr_mtx, frame_mtx; + ht_video_t frame; + volatile bool should_stop; +}; + +class TrackerControls : public ITrackerDialog, protected camera_dialog<Tracker> +{ + Q_OBJECT +public: + TrackerControls(); + void register_tracker(ITracker * t) override + { + tracker = static_cast<Tracker*>(t); + } + void unregister_tracker() override + { + tracker = nullptr; + } +private: + Ui::Form ui; + settings s; + Tracker* tracker; +private slots: + void doOK(); + void doCancel(); + void camera_settings(); +}; + +class TrackerDll : public Metadata +{ + QString name() { return QString("ht -- face tracker"); } + QIcon icon() { return QIcon(":/images/ht.png"); } +}; diff --git a/tracker-ht/headtracker-ftnoir.h b/tracker-ht/headtracker-ftnoir.h new file mode 100644 index 00000000..9a343bae --- /dev/null +++ b/tracker-ht/headtracker-ftnoir.h @@ -0,0 +1,24 @@ +#pragma once +#include <stdlib.h> +#include <stdio.h> +#include "ht-api.h" + +#define HT_SHM_NAME "ftnoir-tracker-ht-shm" +#define HT_MUTEX_NAME "ftnoir-tracker-ht-mutex" + +#define HT_MAX_VIDEO_WIDTH 640 +#define HT_MAX_VIDEO_HEIGHT 480 +#define HT_MAX_VIDEO_CHANNELS 3 + +typedef struct { + int width, height, channels; + unsigned char frame[HT_MAX_VIDEO_WIDTH * HT_MAX_VIDEO_HEIGHT * HT_MAX_VIDEO_CHANNELS]; +} ht_video_t; + +typedef struct { + ht_video_t frame; + ht_config_t config; + ht_result_t result; + volatile int timer; + volatile bool pause, terminate, running; +} ht_shm_t; diff --git a/tracker-ht/ht-api.h b/tracker-ht/ht-api.h new file mode 100644 index 00000000..4629a00b --- /dev/null +++ b/tracker-ht/ht-api.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef HT_API +# if defined(_WIN32) && !defined(MINGW) +# define HT_API(t) __declspec(dllexport) t __stdcall +# else +# define HT_API(t) t +# endif +#endif +#if !defined(_WIN32) && !defined(_isnan) +# define _isnan isnan +#endif +#include <opencv2/core.hpp> +#include <opencv2/highgui.hpp> +struct ht_context; +typedef struct ht_context headtracker_t; + +typedef struct ht_config { + float field_of_view; + float classification_delay; + int pyrlk_pyramids; + int pyrlk_win_size_w; + int pyrlk_win_size_h; + float ransac_max_inlier_error; + float ransac_max_reprojection_error; + int max_keypoints; + float keypoint_distance; + int force_width; + int force_height; + int force_fps; + int camera_index; + bool debug; + int ransac_num_iters; + float ransac_min_features; + float ransac_max_mean_error; + float ransac_abs_max_mean_error; + float flandmark_delay; + double dist_coeffs[5]; +} ht_config_t; + +typedef struct { + double rotx, roty, rotz; + double tx, ty, tz; + bool filled; +} ht_result_t; + +HT_API(headtracker_t*) ht_make_context(const ht_config_t* config, const char* filename); +HT_API(void) ht_free_context(headtracker_t* ctx); +HT_API(const cv::Mat) ht_get_bgr_frame(headtracker_t* ctx); +HT_API(bool) ht_cycle(headtracker_t* ctx, ht_result_t* euler); +HT_API(void) ht_reset(headtracker_t* ctx); +HT_API(cv::VideoCapture*) ht_capture(headtracker_t* ctx); diff --git a/tracker-ht/ht-tracker.qrc b/tracker-ht/ht-tracker.qrc new file mode 100644 index 00000000..b6af7a18 --- /dev/null +++ b/tracker-ht/ht-tracker.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>images/ht.png</file> + </qresource> +</RCC> diff --git a/tracker-ht/ht-trackercontrols.ui b/tracker-ht/ht-trackercontrols.ui new file mode 100644 index 00000000..29b80c8d --- /dev/null +++ b/tracker-ht/ht-trackercontrols.ui @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Form</class> + <widget class="QWidget" name="Form"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>354</width> + <height>179</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>750</width> + <height>280</height> + </size> + </property> + <property name="windowTitle"> + <string>HT tracker settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Frames per second</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDoubleSpinBox" name="cameraFOV"> + <property name="locale"> + <locale language="English" country="UnitedStates"/> + </property> + <property name="minimum"> + <double>35.000000000000000</double> + </property> + <property name="maximum"> + <double>180.000000000000000</double> + </property> + <property name="value"> + <double>52.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Diagonal FOV</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="cameraFPS"> + <item> + <property name="text"> + <string notr="true">Default</string> + </property> + </item> + <item> + <property name="text"> + <string>30</string> + </property> + </item> + <item> + <property name="text"> + <string>60</string> + </property> + </item> + <item> + <property name="text"> + <string>120</string> + </property> + </item> + <item> + <property name="text"> + <string>180</string> + </property> + </item> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="cameraName"/> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="resolution"> + <item> + <property name="text"> + <string>640x480</string> + </property> + </item> + <item> + <property name="text"> + <string>320x240</string> + </property> + </item> + <item> + <property name="text"> + <string>320x200</string> + </property> + </item> + <item> + <property name="text"> + <string>Default (not recommended!)</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Resolution</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Camera name</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Camera settings</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QPushButton" name="camera_settings"> + <property name="text"> + <string>Open</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tracker-ht/ht_video_widget.cpp b/tracker-ht/ht_video_widget.cpp new file mode 100644 index 00000000..02fe71d1 --- /dev/null +++ b/tracker-ht/ht_video_widget.cpp @@ -0,0 +1,53 @@ +/* Copyright (c) 2014 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 "ht_video_widget.h" + +void HTVideoWidget::update_image(unsigned char *frame, int width, int height) +{ + QMutexLocker foo(&mtx); + if (!fresh) + { + memcpy(fb, frame, width * height * 3); + this->width = width; + this->height = height; + fresh = true; + } +} + +void HTVideoWidget::update_and_repaint() +{ + QImage qframe; + { + QMutexLocker foo(&mtx); + if (width*height <= 0 || !fresh) + return; + fresh = false; + qframe = QImage(width, height, QImage::Format_RGB888); + uchar* data = qframe.bits(); + const int pitch = qframe.bytesPerLine(); + for (int y = 0; y < height; y++) + { + const int part = y*width; + for (int x = 0; x < width; x++) + { + const int pos = 3 * (part + x); + const int x_ = x * 3; + data[x_ + 0] = fb[pos + 2]; + data[x_ + 1] = fb[pos + 1]; + data[x_ + 2] = fb[pos + 0]; + } + data += pitch; + } + } + qframe = qframe.scaled(size(), Qt::IgnoreAspectRatio, Qt::FastTransformation); + { + QMutexLocker foo(&mtx); + texture = qframe; + } + update(); +} diff --git a/tracker-ht/ht_video_widget.h b/tracker-ht/ht_video_widget.h new file mode 100644 index 00000000..054b2cf4 --- /dev/null +++ b/tracker-ht/ht_video_widget.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2014 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 <QTimer> +#include <QWidget> +#include <QMutex> +#include <QMutexLocker> +#include <QPainter> +#include <QPaintEvent> + +class HTVideoWidget : public QWidget +{ + Q_OBJECT +public: + HTVideoWidget(QWidget *parent) : QWidget(parent), fb(), width(0), height(0), fresh(false) { + connect(&timer, SIGNAL(timeout()), this, SLOT(update_and_repaint())); + timer.start(60); + } + void update_image(unsigned char* frame, int width, int height); +protected slots: + void paintEvent( QPaintEvent* e ) { + QMutexLocker foo(&mtx); + QPainter painter(this); + painter.drawImage(e->rect(), texture); + } + void update_and_repaint(); +private: + QMutex mtx; + QImage texture; + QTimer timer; + unsigned char fb[2048*2048*3]; + int width,height; + bool fresh; +}; diff --git a/tracker-ht/images/ht.png b/tracker-ht/images/ht.png Binary files differnew file mode 100644 index 00000000..19c73d21 --- /dev/null +++ b/tracker-ht/images/ht.png |