summaryrefslogtreecommitdiffhomepage
path: root/tracker-ht
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2015-10-30 07:37:41 +0100
committerStanislaw Halik <sthalik@misaki.pl>2015-10-30 08:39:32 +0100
commitaa066bdd4622d4f6824fee864f6be6806813f04d (patch)
tree3df328b8b364cba2373a85827191b259bd78d546 /tracker-ht
parentd6a54431d178632a2bf466c9904f74abd143afe6 (diff)
move to subdirectory-based build system
Closes #224
Diffstat (limited to 'tracker-ht')
-rw-r--r--tracker-ht/CMakeLists.txt11
-rw-r--r--tracker-ht/ftnoir_tracker_ht.cpp225
-rw-r--r--tracker-ht/ftnoir_tracker_ht.h90
-rw-r--r--tracker-ht/headtracker-ftnoir.h24
-rw-r--r--tracker-ht/ht-api.h51
-rw-r--r--tracker-ht/ht-tracker.qrc5
-rw-r--r--tracker-ht/ht-trackercontrols.ui157
-rw-r--r--tracker-ht/ht_video_widget.cpp53
-rw-r--r--tracker-ht/ht_video_widget.h39
-rw-r--r--tracker-ht/images/ht.pngbin0 -> 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
new file mode 100644
index 00000000..19c73d21
--- /dev/null
+++ b/tracker-ht/images/ht.png
Binary files differ