summaryrefslogtreecommitdiffhomepage
path: root/ftnoir_tracker_ht
diff options
context:
space:
mode:
Diffstat (limited to 'ftnoir_tracker_ht')
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht.cpp291
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht.h78
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h19
-rw-r--r--ftnoir_tracker_ht/headtracker-ftnoir.h24
-rw-r--r--ftnoir_tracker_ht/ht-api.h49
-rw-r--r--ftnoir_tracker_ht/ht-tracker.qrc5
-rw-r--r--ftnoir_tracker_ht/ht-trackercontrols.ui228
-rw-r--r--ftnoir_tracker_ht/ht_video_widget.cpp44
-rw-r--r--ftnoir_tracker_ht/ht_video_widget.h47
-rw-r--r--ftnoir_tracker_ht/images/ht.pngbin0 -> 2010 bytes
-rw-r--r--ftnoir_tracker_ht/stdafx.h13
11 files changed, 798 insertions, 0 deletions
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp b/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp
new file mode 100644
index 00000000..76a6ba71
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp
@@ -0,0 +1,291 @@
+#include "stdafx.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "headtracker-ftnoir.h"
+#include "ftnoir_tracker_ht.h"
+#include "ftnoir_tracker_ht_dll.h"
+#include "ui_ht-trackercontrols.h"
+#include "facetracknoir/global-settings.h"
+#include <cmath>
+
+#if defined(_WIN32)
+#include <dshow.h>
+#else
+#include <unistd.h>
+#endif
+
+// delicious copypasta
+static QList<QString> get_camera_names(void) {
+ QList<QString> ret;
+#if defined(_WIN32)
+ // Create the System Device Enumerator.
+ HRESULT hr;
+ ICreateDevEnum *pSysDevEnum = NULL;
+ hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
+ if (FAILED(hr))
+ {
+ return ret;
+ }
+ // Obtain a class enumerator for the video compressor category.
+ IEnumMoniker *pEnumCat = NULL;
+ hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+ if (hr == S_OK) {
+ // Enumerate the monikers.
+ IMoniker *pMoniker = NULL;
+ ULONG cFetched;
+ while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
+ // To retrieve the filter's friendly name, do the following:
+ VARIANT varName;
+ VariantInit(&varName);
+ hr = pPropBag->Read(L"FriendlyName", &varName, 0);
+ if (SUCCEEDED(hr))
+ {
+ // Display the name in your UI somehow.
+ QString str((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
+ ret.append(str);
+ }
+ VariantClear(&varName);
+
+ ////// To create an instance of the filter, do the following:
+ ////IBaseFilter *pFilter;
+ ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
+ //// (void**)&pFilter);
+ // Now add the filter to the graph.
+ //Remember to release pFilter later.
+ pPropBag->Release();
+ }
+ pMoniker->Release();
+ }
+ pEnumCat->Release();
+ }
+ pSysDevEnum->Release();
+#elif !defined(__APPLE__)
+ for (int i = 0; i < 16; i++) {
+ char buf[128];
+ sprintf(buf, "/dev/video%d", i);
+ if (access(buf, R_OK | W_OK) == 0) {
+ ret.append(buf);
+ } else {
+ continue;
+ }
+ }
+#endif
+ return ret;
+}
+
+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->pyrlk_pyramids = 0;
+ config->pyrlk_win_size_w = config->pyrlk_win_size_h = 21;
+ config->max_keypoints = 150;
+ config->keypoint_distance = 6;
+ config->force_fps = nframes;
+ config->camera_index = s.camera_idx - 1;
+ config->ransac_num_iters = 100;
+ config->ransac_max_reprojection_error = 10;
+ config->ransac_max_inlier_error = 10;
+ config->ransac_abs_max_mean_error = 14;
+ config->ransac_max_mean_error = 8;
+ config->debug = 0;
+ config->ransac_min_features = 0.86;
+ 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 = 500;
+ for (int i = 0; i < 5; i++)
+ config->dist_coeffs[i] = 0;
+}
+
+Tracker::Tracker() :
+ lck_shm(HT_SHM_NAME, HT_MUTEX_NAME, sizeof(ht_shm_t)),
+ shm(reinterpret_cast<ht_shm_t*>(lck_shm.mem)),
+ videoWidget(nullptr),
+ layout(nullptr)
+{
+ shm->terminate = 0;
+ shm->result.filled = false;
+}
+
+Tracker::~Tracker()
+{
+ if (shm) {
+ shm->terminate = true;
+ subprocess.waitForFinished(5000);
+ }
+ subprocess.kill();
+ if (shm)
+ shm->terminate = true;
+ if (layout)
+ delete layout;
+ if (videoWidget)
+ delete videoWidget;
+}
+
+void Tracker::StartTracker(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(&shm->config);
+ shm->frame.channels = shm->frame.width = shm->frame.height = 0;
+ shm->pause = shm->terminate = shm->running = false;
+ shm->timer = 0;
+ subprocess.setWorkingDirectory(QCoreApplication::applicationDirPath() + "/tracker-ht");
+#if defined(_WIN32)
+ subprocess.start("\"" + QCoreApplication::applicationDirPath() + "/tracker-ht/headtracker-ftnoir" + "\"");
+#else
+ subprocess.start(QCoreApplication::applicationDirPath() + "/tracker-ht/headtracker-ftnoir");
+#endif
+}
+
+void Tracker::GetHeadPoseData(double *data)
+{
+ lck_shm.lock();
+ shm->timer = 0;
+ if (shm->frame.width > 0)
+ {
+ videoWidget->update_image(shm->frame.frame, shm->frame.width, shm->frame.height);
+ //memcpy(foo, shm->frame.frame, shm->frame.width * shm->frame.height * 3);
+ shm->frame.width = 0;
+ }
+ if (shm->result.filled) {
+ if (s.enableRX)
+ data[Yaw] = shm->result.rotx;
+ if (s.enableRY) {
+ data[Pitch] = shm->result.roty;
+ }
+ if (s.enableRZ) {
+ data[Roll] = shm->result.rotz;
+ }
+ if (s.enableTX)
+ data[TX] = shm->result.tx;
+ if (s.enableTY)
+ data[TY] = shm->result.ty;
+ if (s.enableTZ)
+ data[TZ] = shm->result.tz;
+ if (fabs(data[Yaw]) > 60 || fabs(data[Pitch]) > 50 || fabs(data[Roll]) > 40)
+ {
+ shm->pause = true;
+ }
+ } else {
+ shm->pause = false;
+ }
+ lck_shm.unlock();
+}
+
+//-----------------------------------------------------------------------------
+void TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "HT 1.0";
+}
+
+void TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "HT";
+}
+
+void TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "";
+}
+
+void TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/ht.png");
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new TrackerDll;
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new Tracker;
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
+
+TrackerControls::TrackerControls()
+{
+ ui.setupUi(this);
+ ui.cameraName->clear();
+ QList<QString> names = get_camera_names();
+ names.prepend("Any available");
+ ui.cameraName->addItems(names);
+ tie_setting(s.camera_idx, ui.cameraName);
+ tie_setting(s.fps, ui.cameraFPS);
+ tie_setting(s.fov, ui.cameraFOV);
+ tie_setting(s.enableTX, ui.tx);
+ tie_setting(s.enableTY, ui.ty);
+ tie_setting(s.enableTZ, ui.tz);
+ tie_setting(s.enableRX, ui.rx);
+ tie_setting(s.enableRY, ui.ry);
+ tie_setting(s.enableRZ, ui.rz);
+ tie_setting(s.resolution, ui.resolution);
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+}
+
+void TrackerControls::doOK()
+{
+ s.b->save();
+ this->close();
+}
+
+void TrackerControls::doCancel()
+{
+ s.b->revert();
+ this->close();
+}
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht.h b/ftnoir_tracker_ht/ftnoir_tracker_ht.h
new file mode 100644
index 00000000..583249dc
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht.h
@@ -0,0 +1,78 @@
+/* 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.
+ */
+
+#ifndef FTNOIR_TRACKER_HT_H
+#define FTNOIR_TRACKER_HT_H
+
+#include "stdafx.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "headtracker-ftnoir.h"
+#include "ui_ht-trackercontrols.h"
+#include "ht_video_widget.h"
+#include "compat/compat.h"
+#include <QObject>
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<bool> enableTX, enableTY, enableTZ, enableRX, enableRY, enableRZ;
+ value<double> fov;
+ value<int> fps, camera_idx, resolution;
+ settings() :
+ b(bundle("HT-Tracker")),
+ enableTX(b, "enable-tx", true),
+ enableTY(b, "enable-ty", true),
+ enableTZ(b, "enable-tz", true),
+ enableRX(b, "enable-rx", true),
+ enableRY(b, "enable-ry", true),
+ enableRZ(b, "enable-rz", true),
+ fov(b, "fov", 56),
+ fps(b, "fps", 0),
+ camera_idx(b, "camera-index", 0),
+ resolution(b, "resolution", 0)
+ {}
+};
+
+class Tracker : public QObject, public ITracker
+{
+ Q_OBJECT
+public:
+ Tracker();
+ virtual ~Tracker();
+ void StartTracker(QFrame* frame);
+ void GetHeadPoseData(double *data);
+ void load_settings(ht_config_t* config);
+private:
+ settings s;
+ PortableLockedShm lck_shm;
+ ht_shm_t* shm;
+ QProcess subprocess;
+ HTVideoWidget* videoWidget;
+ QHBoxLayout* layout;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class TrackerControls : public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ explicit TrackerControls();
+ void registerTracker(ITracker *) {}
+ void unRegisterTracker() {}
+
+private:
+ Ui::Form ui;
+ settings s;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+#endif
+
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h b/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h
new file mode 100644
index 00000000..ffdc5262
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "facetracknoir/global-settings.h"
+
+//-----------------------------------------------------------------------------
+class TrackerDll : public Metadata
+{
+ // ITrackerDll interface
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
diff --git a/ftnoir_tracker_ht/headtracker-ftnoir.h b/ftnoir_tracker_ht/headtracker-ftnoir.h
new file mode 100644
index 00000000..9a343bae
--- /dev/null
+++ b/ftnoir_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/ftnoir_tracker_ht/ht-api.h b/ftnoir_tracker_ht/ht-api.h
new file mode 100644
index 00000000..2ab2e840
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-api.h
@@ -0,0 +1,49 @@
+#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/core.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);
diff --git a/ftnoir_tracker_ht/ht-tracker.qrc b/ftnoir_tracker_ht/ht-tracker.qrc
new file mode 100644
index 00000000..b6af7a18
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-tracker.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/ht.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_tracker_ht/ht-trackercontrols.ui b/ftnoir_tracker_ht/ht-trackercontrols.ui
new file mode 100644
index 00000000..f57022c8
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-trackercontrols.ui
@@ -0,0 +1,228 @@
+<?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>531</width>
+ <height>166</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="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>Horizontal FOV</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" rowspan="3">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Enable axes</string>
+ </property>
+ <widget class="QCheckBox" name="rx">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RX</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ry">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>40</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="rz">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>60</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RZ</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tx">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>20</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ty">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>40</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tz">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>60</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Frames per second</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="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Camera name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="cameraName"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </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="4" column="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ftnoir_tracker_ht/ht_video_widget.cpp b/ftnoir_tracker_ht/ht_video_widget.cpp
new file mode 100644
index 00000000..c6d59b34
--- /dev/null
+++ b/ftnoir_tracker_ht/ht_video_widget.cpp
@@ -0,0 +1,44 @@
+/* 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 "ht_video_widget.h"
+
+#include <QDebug>
+
+using namespace std;
+
+void HTVideoWidget::update_image(unsigned char *frame, int width, int height)
+{
+ QMutexLocker foo(&mtx);
+ memcpy(fb, frame, width * height * 3);
+ this->width = width;
+ this->height = height;
+}
+
+void HTVideoWidget::update_and_repaint()
+{
+ QMutexLocker foo(&mtx);
+ if (width*height <= 0)
+ return;
+ QImage 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);
+ data[y * pitch + x * 3 + 0] = fb[pos + 2];
+ data[y * pitch + x * 3 + 1] = fb[pos + 1];
+ data[y * pitch + x * 3 + 2] = fb[pos + 0];
+ }
+ }
+ auto qframe2 = qframe.scaled(size(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
+ texture = qframe2;
+ update();
+}
diff --git a/ftnoir_tracker_ht/ht_video_widget.h b/ftnoir_tracker_ht/ht_video_widget.h
new file mode 100644
index 00000000..cbfe6ddc
--- /dev/null
+++ b/ftnoir_tracker_ht/ht_video_widget.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#ifndef VIDEOWIDGET_H
+#define VIDEOWIDGET_H
+
+#include <QTime>
+#include <QWidget>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QLabel>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QTimer>
+
+// ----------------------------------------------------------------------------
+class HTVideoWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ HTVideoWidget(QWidget *parent) : QWidget(parent), fb(), width(0), height(0) {
+ 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;
+ char fb[2048*2048*3];
+ int width,height;
+};
+
+#endif // VIDEOWIDGET_H
diff --git a/ftnoir_tracker_ht/images/ht.png b/ftnoir_tracker_ht/images/ht.png
new file mode 100644
index 00000000..19c73d21
--- /dev/null
+++ b/ftnoir_tracker_ht/images/ht.png
Binary files differ
diff --git a/ftnoir_tracker_ht/stdafx.h b/ftnoir_tracker_ht/stdafx.h
new file mode 100644
index 00000000..6f1539b7
--- /dev/null
+++ b/ftnoir_tracker_ht/stdafx.h
@@ -0,0 +1,13 @@
+#include <QWidget>
+#include <QMessageBox>
+#include <QProcess>
+#include <QImage>
+#include <QLabel>
+#include <QCoreApplication>
+#include <QIcon>
+#include <QHBoxLayout>
+#include <QTimer>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QList>
+#include <QString>