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.pngBinary files differ new file mode 100644 index 00000000..19c73d21 --- /dev/null +++ b/tracker-ht/images/ht.png | 
