From: Thomas Pietrzak Date: Fri, 15 Feb 2013 14:01:57 +0000 (+0000) Subject: Initial import tactile pictures and Immersion touchscreen demo X-Git-Url: https://git.thomaspietrzak.com/?a=commitdiff_plain;h=acb3d9bea36665b4ad60949a1534c39c6a8a3666;p=dwell.git Initial import tactile pictures and Immersion touchscreen demo git-svn-id: svn+ssh://thomaspietrzak.com/var/svn/rep@104 47cf9a05-e0a8-4ed5-9e9b-101a649bc004 --- diff --git a/DwellCursor/GeneratedFiles/qrc_dwellcursor.cpp b/DwellCursor/GeneratedFiles/qrc_dwellcursor.cpp index 913cbe3..e5cc9d1 100644 --- a/DwellCursor/GeneratedFiles/qrc_dwellcursor.cpp +++ b/DwellCursor/GeneratedFiles/qrc_dwellcursor.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** Resource object code ** -** Created: Wed 1. Aug 17:16:43 2012 +** Created: Mon 28. Jan 16:21:26 2013 ** by: The Resource Compiler for Qt version 4.8.2 ** ** WARNING! All changes made in this file will be lost! diff --git a/DwellCursor/GeneratedFiles/ui_ExperimentSetup.h b/DwellCursor/GeneratedFiles/ui_ExperimentSetup.h index 8513b83..cbc427d 100644 --- a/DwellCursor/GeneratedFiles/ui_ExperimentSetup.h +++ b/DwellCursor/GeneratedFiles/ui_ExperimentSetup.h @@ -1,7 +1,7 @@ /******************************************************************************** ** Form generated from reading UI file 'ExperimentSetup.ui' ** -** Created: Wed 1. Aug 17:16:43 2012 +** Created: Mon 28. Jan 16:21:26 2013 ** by: Qt User Interface Compiler version 4.8.2 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! diff --git a/DwellCursor/GeneratedFiles/ui_dwellcursor.h b/DwellCursor/GeneratedFiles/ui_dwellcursor.h index 5f9f9ff..79bbb94 100644 --- a/DwellCursor/GeneratedFiles/ui_dwellcursor.h +++ b/DwellCursor/GeneratedFiles/ui_dwellcursor.h @@ -1,7 +1,7 @@ /******************************************************************************** ** Form generated from reading UI file 'dwellcursor.ui' ** -** Created: Wed 1. Aug 17:16:42 2012 +** Created: Mon 28. Jan 16:21:22 2013 ** by: Qt User Interface Compiler version 4.8.2 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! diff --git a/PushButtons/KinectInput.cpp b/PushButtons/KinectInput.cpp index 15bcd9d..9fc8f37 100644 --- a/PushButtons/KinectInput.cpp +++ b/PushButtons/KinectInput.cpp @@ -1,11 +1,12 @@ #include "KinectInput.h" #include +#include #define TIMEOUT 100 KinectInput::KinectInput() -:_running(false), _skeletonid(-1) +:_running(false), _skeletonid(-1), _skeletonEvent(NULL), _xfilter(50, 0.05), _yfilter(50, 0.05), _zfilter(50, 0.01) { //creates the kinect sensor (id 0) if(FAILED(NuiCreateSensorByIndex(0, &_kinect))) @@ -76,8 +77,9 @@ void KinectInput::run() /* if (data.eTrackingState != NUI_SKELETON_TRACKED) continue;*/ + //Let's use the 1 euro instead, see below //smooth out the skeleton data - _kinect->NuiTransformSmooth(&SkeletonFrame, NULL); + //_kinect->NuiTransformSmooth(&SkeletonFrame, NULL); if (data.eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_HAND_RIGHT] == NUI_SKELETON_NOT_TRACKED) @@ -88,7 +90,14 @@ void KinectInput::run() NuiTransformSkeletonToDepthImageF(data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT], &posx, &posy); emit rightHandMove(posx, posy,0);*/ Vector4 &skelpos = data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT]; - emit rightHandMove(skelpos.x, skelpos.y, skelpos.z); + + //Filter + int timestamp = QDateTime::currentMSecsSinceEpoch(); + float fx = _xfilter.filter(skelpos.x, timestamp); + float fy = _yfilter.filter(skelpos.y, timestamp); + float fz = _zfilter.filter(skelpos.z, timestamp); + + emit rightHandMove(fx, fy, fz); } } } diff --git a/PushButtons/KinectInput.h b/PushButtons/KinectInput.h index b4bf077..855fb7b 100644 --- a/PushButtons/KinectInput.h +++ b/PushButtons/KinectInput.h @@ -6,6 +6,8 @@ #include #include +#include "OneEuroFilter.h" + class KinectInput: public QThread { Q_OBJECT @@ -22,6 +24,8 @@ class KinectInput: public QThread bool _running; int _skeletonid; + OneEuroFilter _xfilter, _yfilter, _zfilter; + signals: void rightHandMove(float x, float y, float z); }; diff --git a/PushButtons/OneEuroFilter.cpp b/PushButtons/OneEuroFilter.cpp new file mode 100644 index 0000000..ee1754c --- /dev/null +++ b/PushButtons/OneEuroFilter.cpp @@ -0,0 +1,118 @@ +#include "OneEuroFilter.h" + +#define _USE_MATH_DEFINES +#include +#include + +LowPassFilter::LowPassFilter(double alpha, double initval) +:_y(initval), _s(initval), _initialized(false) +{ + setAlpha(alpha); +} + +double LowPassFilter::filter(double value) +{ + double result; + if (_initialized) + result = _a * value + (1.0 - _a) * _s; + else + { + result = value; + _initialized = true; + } + _y = value; + _s = result; + return result; +} + +double LowPassFilter::filterWithAlpha(double value, double alpha) +{ + setAlpha(alpha); + return filter(value); +} + +bool LowPassFilter::hasLastRawValue(void) +{ + return _initialized; +} + +double LowPassFilter::lastRawValue(void) +{ + return _y ; +} + +void LowPassFilter::setAlpha(double alpha) +{ + if (alpha <= 0.0 || alpha > 1.0) + throw std::range_error("alpha should be in ]0.0., 1.0]"); + _a = alpha; +} + + +OneEuroFilter::OneEuroFilter(double freq, double mincutoff, double beta, double dcutoff) +:_lasttime(UndefinedTime) +{ + setFrequency(freq); + + //don't do this earlier, frequency is required to compute alpha + _x = new LowPassFilter(alpha(mincutoff)); + _dx = new LowPassFilter(alpha(dcutoff)); + + setMinCutoff(mincutoff); + setBeta(beta); + setDerivateCutoff(dcutoff); +} + +OneEuroFilter::~OneEuroFilter(void) +{ + delete _x; + delete _dx; +} + +double OneEuroFilter::filter(double value, TimeStamp timestamp) +{ + // update the sampling frequency based on timestamps + if (_lasttime != UndefinedTime && timestamp != UndefinedTime) + _freq = 1.0 / (timestamp - _lasttime); + _lasttime = timestamp; + // estimate the current variation per second + double dvalue = _x->hasLastRawValue() ? (value - _x->lastRawValue()) * _freq : 0.0; // FIXME: 0.0 or value? + double edvalue = _dx->filterWithAlpha(dvalue, alpha(_dcutoff)); + // use it to update the cutoff frequency + double cutoff = _mincutoff + _beta * fabs(edvalue); + // filter the given value + return _x->filterWithAlpha(value, alpha(cutoff)); +} + +double OneEuroFilter::alpha(double cutoff) +{ + double te = 1.0 / _freq; + double tau = 1.0 / (2 * M_PI * cutoff); + return 1.0 / (1.0 + tau/te); +} + +void OneEuroFilter::setFrequency(double f) +{ + if (f <= 0) + throw std::range_error("freq should be >0"); + _freq = f; +} + +void OneEuroFilter::setMinCutoff(double mc) +{ + if (mc <= 0) + throw std::range_error("mincutoff should be >0"); + _mincutoff = mc; +} + +void OneEuroFilter::setBeta(double b) +{ + _beta = b; +} + +void OneEuroFilter::setDerivateCutoff(double dc) +{ + if (dc <= 0) + throw std::range_error("dcutoff should be >0"); + _dcutoff = dc; +} diff --git a/PushButtons/OneEuroFilter.h b/PushButtons/OneEuroFilter.h new file mode 100644 index 0000000..7d24ffa --- /dev/null +++ b/PushButtons/OneEuroFilter.h @@ -0,0 +1,62 @@ +/* + * OneEuroFilter.h/.cpp - + * + * Author: Thomas Pietrzak (thomas.pietrzak@lifl.fr) + * based on code from + * Nicolas Roussel (nicolas.roussel@inria.fr) + * + */ + + +#ifndef __ONEEURO__ +#define __ONEEURO__ + + +typedef double TimeStamp ; // in seconds + +static const TimeStamp UndefinedTime = -1.0 ; + + +class LowPassFilter +{ + public: + LowPassFilter(double alpha, double initval=0.0); + + double filterWithAlpha(double value, double alpha); + inline bool hasLastRawValue(void); + inline double lastRawValue(void); + + private: + double _y, _a, _s; + bool _initialized; + + void setAlpha(double alpha); + double filter(double value); +} ; + +class OneEuroFilter +{ + public: + OneEuroFilter( + double freq, + double mincutoff=1.0, + double beta_=0.0, + double dcutoff=1.0); + + ~OneEuroFilter(); + + double filter(double value, TimeStamp timestamp=UndefinedTime); + + private: + double _freq, _mincutoff, _beta, _dcutoff; + LowPassFilter *_x, *_dx; + TimeStamp _lasttime; + + double alpha(double cutoff); + inline void setFrequency(double f); + inline void setMinCutoff(double mc); + inline void setBeta(double b); + inline void setDerivateCutoff(double dc); +} ; + +#endif diff --git a/TactileButtons.sln b/TactileButtons.sln index b12eb95..4ac9046 100644 --- a/TactileButtons.sln +++ b/TactileButtons.sln @@ -7,6 +7,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DwellCursor", "DwellCursor\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TactileFluids", "TactileFluids\TactileFluids.vcxproj", "{014A41B9-3E4D-48F0-B5D7-8C6FE42A142E}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TactilePictures", "TactilePictures\TactilePictures.vcxproj", "{B219D272-E4C1-46D8-9263-3B4462606CC6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TouchscreenButton", "TouchscreenButton\TouchscreenButton.vcxproj", "{3484A961-9B00-4197-85ED-99303909089C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -25,6 +29,14 @@ Global {014A41B9-3E4D-48F0-B5D7-8C6FE42A142E}.Debug|Win32.Build.0 = Debug|Win32 {014A41B9-3E4D-48F0-B5D7-8C6FE42A142E}.Release|Win32.ActiveCfg = Release|Win32 {014A41B9-3E4D-48F0-B5D7-8C6FE42A142E}.Release|Win32.Build.0 = Release|Win32 + {B219D272-E4C1-46D8-9263-3B4462606CC6}.Debug|Win32.ActiveCfg = Debug|Win32 + {B219D272-E4C1-46D8-9263-3B4462606CC6}.Debug|Win32.Build.0 = Debug|Win32 + {B219D272-E4C1-46D8-9263-3B4462606CC6}.Release|Win32.ActiveCfg = Release|Win32 + {B219D272-E4C1-46D8-9263-3B4462606CC6}.Release|Win32.Build.0 = Release|Win32 + {3484A961-9B00-4197-85ED-99303909089C}.Debug|Win32.ActiveCfg = Debug|Win32 + {3484A961-9B00-4197-85ED-99303909089C}.Debug|Win32.Build.0 = Debug|Win32 + {3484A961-9B00-4197-85ED-99303909089C}.Release|Win32.ActiveCfg = Release|Win32 + {3484A961-9B00-4197-85ED-99303909089C}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TactileButtons.suo b/TactileButtons.suo index 95b9ff6..8297b58 100644 Binary files a/TactileButtons.suo and b/TactileButtons.suo differ diff --git a/TactileFluids/KinectInput.cpp b/TactileFluids/KinectInput.cpp index e908518..cc9512f 100644 --- a/TactileFluids/KinectInput.cpp +++ b/TactileFluids/KinectInput.cpp @@ -1,11 +1,12 @@ #include "KinectInput.h" #include +#include #define TIMEOUT 100 KinectInput::KinectInput() -:_running(false), _skeletonid(-1), _skeletonEvent(NULL) +:_running(false), _skeletonid(-1), _skeletonEvent(NULL), _xfilter(50, 1.0), _yfilter(50, 1.0), _zfilter(50, 1.0) { //creates the kinect sensor (id 0) if(FAILED(NuiCreateSensorByIndex(0, &_kinect))) @@ -76,8 +77,9 @@ void KinectInput::run() /* if (data.eTrackingState != NUI_SKELETON_TRACKED) continue;*/ + //Let's use the 1 euro instead, see below //smooth out the skeleton data - _kinect->NuiTransformSmooth(&SkeletonFrame, NULL); + //_kinect->NuiTransformSmooth(&SkeletonFrame, NULL); if (data.eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_HAND_RIGHT] == NUI_SKELETON_NOT_TRACKED) @@ -88,7 +90,14 @@ void KinectInput::run() NuiTransformSkeletonToDepthImageF(data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT], &posx, &posy); emit rightHandMove(posx, posy,0);*/ Vector4 &skelpos = data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT]; - emit rightHandMove(skelpos.x, skelpos.y, skelpos.z); + + //Filter + int timestamp = QDateTime::currentMSecsSinceEpoch(); + float fx = _xfilter.filter(skelpos.x, timestamp); + float fy = _yfilter.filter(skelpos.y, timestamp); + float fz = _zfilter.filter(skelpos.z, timestamp); + + emit rightHandMove(fx, fy, fz); } } } diff --git a/TactileFluids/KinectInput.h b/TactileFluids/KinectInput.h index b4bf077..855fb7b 100644 --- a/TactileFluids/KinectInput.h +++ b/TactileFluids/KinectInput.h @@ -6,6 +6,8 @@ #include #include +#include "OneEuroFilter.h" + class KinectInput: public QThread { Q_OBJECT @@ -22,6 +24,8 @@ class KinectInput: public QThread bool _running; int _skeletonid; + OneEuroFilter _xfilter, _yfilter, _zfilter; + signals: void rightHandMove(float x, float y, float z); }; diff --git a/TactileFluids/OneEuroFilter.cpp b/TactileFluids/OneEuroFilter.cpp index 02377c4..ee1754c 100644 --- a/TactileFluids/OneEuroFilter.cpp +++ b/TactileFluids/OneEuroFilter.cpp @@ -50,9 +50,14 @@ void LowPassFilter::setAlpha(double alpha) OneEuroFilter::OneEuroFilter(double freq, double mincutoff, double beta, double dcutoff) -:_x(new LowPassFilter(alpha(mincutoff))), _dx(new LowPassFilter(alpha(dcutoff))), _lasttime(UndefinedTime) +:_lasttime(UndefinedTime) { setFrequency(freq); + + //don't do this earlier, frequency is required to compute alpha + _x = new LowPassFilter(alpha(mincutoff)); + _dx = new LowPassFilter(alpha(dcutoff)); + setMinCutoff(mincutoff); setBeta(beta); setDerivateCutoff(dcutoff); diff --git a/TactileFluids/OneEuroFilter.h b/TactileFluids/OneEuroFilter.h index 100a5ae..7d24ffa 100644 --- a/TactileFluids/OneEuroFilter.h +++ b/TactileFluids/OneEuroFilter.h @@ -48,7 +48,7 @@ class OneEuroFilter double filter(double value, TimeStamp timestamp=UndefinedTime); private: - double _freq, _mincutoff, _beta;, _dcutoff; + double _freq, _mincutoff, _beta, _dcutoff; LowPassFilter *_x, *_dx; TimeStamp _lasttime; diff --git a/TactileFluids/TactileFluids.vcxproj b/TactileFluids/TactileFluids.vcxproj index 1ba5787..15b4a3e 100644 --- a/TactileFluids/TactileFluids.vcxproj +++ b/TactileFluids/TactileFluids.vcxproj @@ -54,7 +54,7 @@ Windows $(OutDir)\$(ProjectName).exe - $(KINECTSDK10_DIR)\lib\x86;;$(QTDIR)\lib;%(AdditionalLibraryDirectories) + $(KINECTSDK10_DIR)\lib\x86;$(QTDIR)\lib;%(AdditionalLibraryDirectories) true TactonPlayerd.lib;Kinect10.lib;qtmaind.lib;QtCored4.lib;QtGuid4.lib;%(AdditionalDependencies) diff --git a/TactileFluids/tactilefluids.ui b/TactileFluids/tactilefluids.ui index 22abb01..c91f830 100644 --- a/TactileFluids/tactilefluids.ui +++ b/TactileFluids/tactilefluids.ui @@ -67,7 +67,7 @@ 10 - 100 + 500 diff --git a/TactilePictures/Canvas.cpp b/TactilePictures/Canvas.cpp new file mode 100644 index 0000000..903d908 --- /dev/null +++ b/TactilePictures/Canvas.cpp @@ -0,0 +1,286 @@ +/* + * + * Adapted from Pixel based by Nicolas Roussel and Paolo Olivo + * Authors: Thomas Pietrzak + * Copyright © Inria + * + */ + +#include "Canvas.h" + +#include +#include +#include +#include + +#include +#include + +const float Canvas::KinectWidth = 1.6; +const float Canvas::KinectHeight = 1.0; +const float Canvas::CursorSize = 10; + + +// ############################### CLASS Canvas ################################ +Canvas::Canvas(QWidget *parent) +: QGraphicsView(parent), _tactonplayer(NULL), _image(new QGraphicsPixmapItem), data(new QImage) , _cursor(new QGraphicsEllipseItem(0, 0, CursorSize, CursorSize)) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui.setupUi(this) ; + ui.infoWidget->setVisible(false); + + //feedback = NULL ; + //this->_image = new QGraphicsPixmapItem ; + setScene(&_scene); + _scene.addItem(_image); + _scene.addItem(_cursor); + _cursor->setPen(QPen(QBrush(Qt::black), 5)); + + //this->data = new QImage ; + + showData = false ; + invFeedback = false ; + paintUpFeedback = true ; + + connect(&_kinectinput, SIGNAL(rightHandMove(float,float,float)), + this, SLOT(updateFeedback(float,float,float))); + + _kinectinput.start(); +} + +Canvas::~Canvas() { + delete data; + delete _image; + + _kinectinput.stop(); + if(_tactonplayer) + { + unsigned char amp[] = {0, 0, 0, 0}; + _tactonplayer->setAmplitudes(4, amp); + //May be dangerous, new in main + delete _tactonplayer; + } +} +/* +void +Canvas::setFeedback(Feedback *feedback) { + this->feedback = feedback ; +}*/ +void +Canvas::setTactonPlayer(TactonPlayer *player) { + this->_tactonplayer = player; + if (_tactonplayer) + _tactonplayer->setFrequency(250); +} + +int +Canvas::setImage(const char *imagePath, const char *dataPath) { +// if (image->load(imagePath)) { + QPixmap im; + if (im.load(imagePath)) + { + //pixels copied? + _image->setPixmap(im); + ui.imagePath->setText(QString(imagePath)) ; + if (!data->load(dataPath)) { + // If the data image cannot be found, delete the old data image + // and use image as data. + delete data; + QPixmap greydata(imagePath, 0, Qt::MonoOnly); + data = new QImage(greydata.toImage()); + qDebug("No tactile map. Using %s", imagePath) ; + ui.dataPath->setText(QString("No tactile map")) ; + } else { + QPixmap greydata; + greydata.convertFromImage(*data, Qt::MonoOnly); + delete data; + data = new QImage(greydata.toImage()); + ui.dataPath->setText(QString(dataPath)) ; + } + // check if the file requires to invert the feedback + std::string imageStr(imagePath) ; + if (imageStr.find("-BW-") < imageStr.length() ) { + // BW means "black on white", i.e. dark areas are the one with the strongest feedback + data->invertPixels() ; + } + // check if the user has required to invert the feedback + if (invFeedback) + data->invertPixels() ; + + setMinimumSize(_image->pixmap().size()); + fitInView(_scene.sceneRect(), Qt::KeepAspectRatio); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + //adjustSize() ; + /*setFixedSize(sizeHint()) ; + update() ; + updateGeometry() ;*/ + + qDebug() << "Image w=" << _image->pixmap().width() << " h=" << _image->pixmap().height(); + qDebug() << "Widget w=" << width() << " h=" << height(); + + paintUpFeedback = true; + // Cut the string manually at beginning since is the end part the most + // important one. + QFontMetrics metrics(ui.imagePath->font()); + int width = metrics.width(ui.imagePath->text()); + if (width > sizeHint().width() - 28) { + ui.imagePath->setText(metrics.elidedText(ui.imagePath->text(), Qt::ElideLeft, sizeHint().width() - 28)) ; + ui.imagePath->setAlignment(Qt::AlignRight) ; + } else { + ui.imagePath->setAlignment(Qt::AlignLeft) ; + } + // Same thing for the dataPath + width = metrics.width(ui.dataPath->text()); + if (width > sizeHint().width() - 28) { + ui.dataPath->setText(metrics.elidedText(ui.dataPath->text(), Qt::ElideLeft, sizeHint().width() - 28)) ; + ui.dataPath->setAlignment(Qt::AlignRight) ; + } else { + ui.dataPath->setAlignment(Qt::AlignLeft) ; + } + return 0 ; + } else { + qDebug("Image %s cannot be loaded.", imagePath) ; + return 1; + } +} + +void +Canvas::updateFeedback() { + QPoint pos = mapFromGlobal(QCursor::pos()) ; + updateFeedback(_currentpos.x(), _currentpos.y(), 0.0) ; +} + +void +Canvas::updateFeedback(float x, float y, float z) { + qDebug() << "Hand at x=" << x << " y=" << y << " z=" << z; + + if (!_tactonplayer) return ; + + //map Kinect Coordinates to image + int px = (x / KinectWidth + 0.5) * data->width(); + int py = (0.5 - y / KinectHeight) * data->height(); + if (px < 0) + px = 0; + else if (px > data->width()) + px = data->width(); + if (py < 0) + py = 0; + else if (py > data->width()) + py = data->width(); + + _currentpos = QPoint(px, py); + _cursor->setPos(px - CursorSize / 2, py - CursorSize / 2); + qDebug() << "Screen x=" << px << " y=" << py << "\n"; + + + if (data->valid(px, py)) { + int gray = qGray(data->pixel(px, py)) ; + //TODO: try 4 pixels around + unsigned char amp[] = {gray, gray, gray, gray}; + _tactonplayer->setAmplitudes(4, amp); + } else { + unsigned char amp[] = {0, 0, 0, 0}; + _tactonplayer->setAmplitudes(4, amp); + } +} + +// <<<<<<<<<<<<<<<<<<<<<<<<<<<< OVERRIDDEN METHODS >>>>>>>>>>>>>>>>>>>>>>>>>>>>> +/*QSize +Canvas::minimumSizeHint() const { + return QSize(_image->pixmap().width(), _image->pixmap().height()) ; +} + +QSize +Canvas::sizeHint() const { + return QSize(_image->pixmap().width(), _image->pixmap().height()) ; +}*/ +/* +void +Canvas::paintEvent(QPaintEvent * event) { + QRect dirtyRect = event->rect(); + + QPainter painter(this) ; + painter.setRenderHint(QPainter::Antialiasing, true) ; + if (showData) + painter.drawImage(dirtyRect, *data, dirtyRect) ; + else + painter.drawImage(dirtyRect, *image, dirtyRect) ; + painter.end() ; + + if (paintUpFeedback) { + updateFeedback() ; + paintUpFeedback = false ; + } + + //draw Kinect cursor + painter.drawLine(_currentpos - QPoint(-5, 0), _currentpos - QPoint(+5, 0)); + painter.drawLine(_currentpos - QPoint(0, -5), _currentpos - QPoint(0, +5)); +}*/ +/* +void +Canvas::mouseMoveEvent(QMouseEvent * event) { + updateFeedback(event->pos()) ; +} + +void +Canvas::leaveEvent(QEvent * event) { + if (feedback) feedback->setModalities(Feedback::NONE) ; +}*/ + +// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SLOTS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +void +Canvas::showFeedback(bool value) { + showData = value ; + if (!showData) ui.imageShown->setText("Visible image") ; + else ui.imageShown->setText("Friction map") ; + update() ; +} + +void +Canvas::invertFeedback(bool value) { + invFeedback = value ; + data->invertPixels() ; + if (invFeedback) ui.feedbackInfo->setText("Inverted") ; + else ui.feedbackInfo->setText("Normal") ; + if (showData) + update() ; + updateFeedback() ; +} + +void +Canvas::showInfo(bool value) { + ui.infoWidget->setVisible(value) ; +} + +void +Canvas::notifyImageNum(int id, int num) { + char label[64]; + sprintf(label,"%d/%d", id, num); + ui.imageNum->setText(label) ; +} + +/* +QImage * +Canvas::toGrayscale(QImage *image) { + // Initialize color table + QVector colorTable(256); + for (int i = 0; i<256; ++i) + colorTable[i] = qRgb(i, i, i) ; + + QSize size = image->size() ; + QImage * grayImage = new QImage(size, QImage::Format_Indexed8) ; + grayImage->setColorTable(colorTable) ; + for (int i = 0; isetPixel(j, i, qGray(image->pixel(j,i))) ; + return grayImage ; +}*/ +/* +void +Canvas::touchEvent(STIMTAC_input_200::TouchEvent event) { + if (event.activecount==0 && feedback) feedback->setModalities(Feedback::NONE) ; +} + +*/ \ No newline at end of file diff --git a/TactilePictures/Canvas.h b/TactilePictures/Canvas.h new file mode 100644 index 0000000..f3579be --- /dev/null +++ b/TactilePictures/Canvas.h @@ -0,0 +1,79 @@ +/* + * + * Adapted from Pixel based by Nicolas Roussel and Paolo Olivo + * Authors: Thomas Pietrzak + * Copyright © Inria + * + */ + +#ifndef CANVAS_H +#define CANVAS_H + +#include "ui_Canvas.h" + +#include +#include +#include +#include + +#include "KinectInput.h" +#include + +class Canvas : public QGraphicsView { + + Q_OBJECT ; + + Ui::Canvas ui; + /** When showdata is true that it will be shown the data image and not + * the front image. */ + bool showData; + bool invFeedback ; + /** If true then the feedback must be updated at the end of next paint event. + * This is necessary when the image is changed since the new + * image get place only at the end of the paint method. */ + bool paintUpFeedback ; + +protected: +// Feedback *feedback ; + TactonPlayer *_tactonplayer; + //QImage *image; + QImage *data; + QGraphicsScene _scene; + QGraphicsPixmapItem *_image; + QGraphicsEllipseItem *_cursor; + static const float CursorSize; + + KinectInput _kinectinput; + QPoint _currentpos; + static const float KinectWidth, KinectHeight; + +public: + Canvas(QWidget *parent=0) ; + virtual ~Canvas() ; + +// void setFeedback(Feedback *feedback) ; + void setTactonPlayer(TactonPlayer *player) ; + virtual int setImage(const char *img, const char *data = 0) ; + + void showFeedback(bool value) ; + void invertFeedback(bool value) ; + void showInfo(bool value) ; + void notifyImageNum(int id, int num) ; + +protected: +// QSize minimumSizeHint() const ; +// QSize sizeHint() const ; + //virtual void paintEvent(QPaintEvent *event) ; + //virtual void mouseMoveEvent(QMouseEvent *event) ; + //void leaveEvent(QEvent *event) ; + + virtual void updateFeedback() ; + + QImage * toGrayscale(QImage *image) ; + +public slots: +// void touchEvent(STIMTAC_input_200::TouchEvent event) ; + virtual void updateFeedback(float x, float y, float z) ; +} ; + +#endif diff --git a/TactilePictures/Canvas.ui b/TactilePictures/Canvas.ui new file mode 100644 index 0000000..89e784b --- /dev/null +++ b/TactilePictures/Canvas.ui @@ -0,0 +1,326 @@ + + + Canvas + + + + 0 + 0 + 300 + 300 + + + + + 0 + 0 + + + + + 300 + 300 + + + + true + + + Canvas + + + + 0 + + + 0 + + + + + true + + + color: rgb(255, 0, 0); + + + + + + 0 + + + + + + 75 + true + + + + true + + + Qt::LeftToRight + + + + + + Image: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + true + + + + + + TextLabel + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + 5 + + + 0 + + + + + + + + 75 + true + + + + true + + + + + + Friction map + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + true + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + 5 + + + 0 + + + + + + + + + Qt::Vertical + + + + 279 + 208 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 75 + true + + + + true + + + + + + Image shown: + + + + + + + + 0 + 0 + + + + true + + + + + + Visible image + + + + + + + + 75 + true + + + + true + + + + + + Feedback: + + + + + + + + 0 + 0 + + + + true + + + + + + Normal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + true + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + 1/1 + + + + + + + + + + + + + + + diff --git a/TactilePictures/ImageDemo.cpp b/TactilePictures/ImageDemo.cpp new file mode 100644 index 0000000..72288d7 --- /dev/null +++ b/TactilePictures/ImageDemo.cpp @@ -0,0 +1,300 @@ +/* -*- mode: c++ -*- + * + * Adapted from Pixel based by Paolo Olivo + * Authors: Thomas Pietrzak + * Copyright © Inria + * + */ + +#include + +#include "ImageDemo.h" + +// ############################### CLASS ImageRef ############################## +ImageRef::ImageRef(const char *img, const char *data) : + _img(img), _data(data) { +} + +ImageRef::ImageRef(const ImageRef ©) : + _img(copy.img()), _data(copy.data()) { +} + +// ############################## CLASS ImageDemo ############################## +ImageDemo::ImageDemo(/*Feedback *feedback*/ TactonPlayer *player, const char *path, QWidget *parent) : QMainWindow(parent) { + ui.setupUi(this); +// ui.canvas->setFeedback(feedback) ; + ui.canvas->setTactonPlayer(player) ; + showRepository(false); + showMenuBar(true) ; + showCursor(true) ; + QString sPath ; + if (path) sPath = path ; + else sPath = ":/images/" ; + QFileInfo pathInfo(sPath) ; + if (pathInfo.isDir()) { + addDir(sPath.toLocal8Bit().data()) ; + } else if (pathInfo.isFile()) { + QListWidgetItem *item = addImage(sPath.toLocal8Bit().data()) ; + if (item) { + QVariant var = item->data(Qt::UserRole) ; + ImageRef ref = var.value() ; + showImage(item) ; + } + } + connect(ui.repository, SIGNAL(itemClicked(QListWidgetItem *)), + SLOT(showImage(QListWidgetItem *))) ; + connect(ui.openImage, SIGNAL(triggered(void)), SLOT(loadImagesDialog(void))) ; + connect(ui.openDir, SIGNAL(triggered(void)), SLOT(loadDirectoryDialog(void))) ; + connect(ui.showRepository, SIGNAL(triggered(bool)), SLOT(showRepository(bool))) ; + connect(ui.showMenuBar, SIGNAL(triggered(bool)), SLOT(showMenuBar(bool))) ; + connect(ui.showCursor, SIGNAL(triggered(bool)), SLOT(showCursor(bool))) ; + connect(ui.showOverlay, SIGNAL(triggered(bool)), SLOT(showOverlay(bool))) ; + connect(ui.showFeedback, SIGNAL(triggered(bool)), SLOT(showFeedback(bool))) ; + connect(ui.invertFeedback, SIGNAL(triggered(bool)), SLOT(invertFeedback(bool))) ; + connect(ui.showInfo, SIGNAL(triggered(bool)), SLOT(showInfo(bool))) ; +} + +// <<<<<<<<<<<<<<<<<<<<<<<<<<<< OVERRIDDEN METHODS >>>>>>>>>>>>>>>>>>>>>>>>>>>>> +void +ImageDemo::enterEvent(QEvent *) { + activateWindow() ; +} + +void +ImageDemo::dragEnterEvent(QDragEnterEvent *event) { + if (event->mimeData()->hasUrls()) { + event->setDropAction(Qt::CopyAction) ; + event->accept() ; + } +} + +void +ImageDemo::dragMoveEvent(QDragMoveEvent *event) { + if (event->mimeData()->hasUrls()) { + event->setDropAction(Qt::CopyAction) ; + event->accept() ; + } +} + +void +ImageDemo::dropEvent(QDropEvent *event) { + if (event->mimeData()->hasUrls()) { + QList urls = event->mimeData()->urls() ; + QStringList files ; + for (int i = 0; i < urls.size(); ++i) { + files.append(urls[i].toLocalFile()) ; + } + addFiles(files) ; + } + // Set current window as active + activateWindow() ; +} + +void +ImageDemo::keyPressEvent(QKeyEvent *event) { + bool handled = false; + if (!ui.menuBar->isVisible()) { + if (event->key() == Qt::Key_O && (event->modifiers() & Qt::CTRL)) { + loadImagesDialog() ; + handled = true ; + } else if (event->key() == Qt::Key_D && (event->modifiers() & Qt::CTRL)) { + loadDirectoryDialog() ; + handled = true ; + } else if (event->key() == Qt::Key_S && (event->modifiers() & Qt::CTRL)) { + showInfo(!ui.showInfo->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_Q && (event->modifiers() & Qt::CTRL)) { + close() ; + handled = true ; + } else if (event->key() == Qt::Key_F && (event->modifiers() & Qt::CTRL)) { + showFeedback(!ui.showFeedback->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_I && (event->modifiers() & Qt::CTRL)) { + invertFeedback(!ui.showFeedback->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_R && (event->modifiers() & Qt::CTRL)) { + showRepository(!ui.showRepository->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_M && (event->modifiers() & Qt::CTRL)) { + showMenuBar(!ui.showMenuBar->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_C && (event->modifiers() & Qt::CTRL)) { + showCursor(!ui.showCursor->isChecked()) ; + handled = true ; + } else if (event->key() == Qt::Key_V && (event->modifiers() & Qt::CTRL)) { + showOverlay(!ui.showOverlay->isChecked()) ; + handled = true ; + } + } + if (event->key()==Qt::Key_Up) { + int row = ui.repository->row(currItem) ; + if (row > 0) + showImage(ui.repository->item(row-1)) ; + handled = true ; + } else if (event->key()==Qt::Key_Down) { + int row = ui.repository->row(currItem) ; + if (row < ui.repository->count()) + showImage(ui.repository->item(row+1)) ; + handled = true ; + } + if (!handled) QMainWindow::keyPressEvent(event) ; +} + +void +ImageDemo::addFiles(QStringList &files) { + bool set = false ; + for (int i = 0; i < files.size(); ++i) { + if (!set) { + QListWidgetItem *item = addImage(files[i].toLocal8Bit().data()) ; + if (showImage(item) == 0) + set = true; + } else { + addImage(files[i].toLocal8Bit().data()) ; + } + } +} + +void +ImageDemo::addDir(const char *path) { + QDir dir(path) ; + if (!dir.exists()) { + qWarning("Cannot find the example directory"); + } else { + dir.setFilter(QDir::Files | QDir::NoSymLinks); + + QFileInfoList list = dir.entryInfoList(); + QStringList files ; + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + files.append(fileInfo.absoluteFilePath()) ; + } + addFiles(files) ; + } +} + +QListWidgetItem * +ImageDemo::addImage(const char * img) { + if (img) { + std::string file(img) ; + std::size_t pos = file.rfind('/') ; + file = file.substr(pos + 1) ; + + if (file.find(".data") < file.length() ) { + qDebug("%s not added to the list", file.data()) ; + return NULL ; + } + + std::string dataImg(img) ; + pos = dataImg.rfind('.') ; + if (pos > 0 && pos < dataImg.length()) { + dataImg.insert(pos,".data") ; + } + + ImageRef imgRef(img, dataImg.data()) ; + QListWidgetItem *item = new QListWidgetItem(QString(file.data())) ; + item->setData(Qt::UserRole, QVariant::fromValue(imgRef)) ; + ui.repository->addItem(item) ; + + ui.canvas->notifyImageNum(ui.repository->row(item) + 1, ui.repository->count()) ; + return item ; + } else + return NULL ; +} + +// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SLOTS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +int +ImageDemo::showImage(QListWidgetItem *item) { + if (item) { + QVariant var = item->data(Qt::UserRole) ; + ImageRef ref = var.value() ; + if (ui.canvas->setImage(ref.img(), ref.data())) { + return 1 ; + } + currItem = item ; + ui.repository->setCurrentItem(item) ; + ui.canvas->notifyImageNum(ui.repository->row(item) + 1, ui.repository->count()) ; + return 0 ; + } else + return 1 ; +} + +void +ImageDemo::loadImagesDialog(void) { + QFileDialog fileDialog ; + fileDialog.setFileMode(QFileDialog::ExistingFiles); + fileDialog.setViewMode(QFileDialog::List); + fileDialog.exec() ; + QStringList files = fileDialog.selectedFiles() ; + addFiles(files) ; + // Set current window as active + activateWindow() ; +} + +void +ImageDemo::loadDirectoryDialog(void) { + QFileDialog fileDialog ; + fileDialog.setFileMode(QFileDialog::Directory); + fileDialog.setViewMode(QFileDialog::List); + fileDialog.exec() ; + QStringList files = fileDialog.selectedFiles() ; + addDir(files[0].toLocal8Bit().data()) ; + // Set current window as active + activateWindow() ; +} + +void +ImageDemo::showRepository(bool value) { + ui.repository->setVisible(value) ; + if (value && isFullScreen()) + { + ui.repositorySpacer->setVisible(true) ; + ui.repositorySpacer->setMinimumWidth(250); + } + else + { + ui.repositorySpacer->setMinimumWidth(0); + ui.repositorySpacer->setVisible(false) ; + } + ui.showRepository->setChecked(value) ; +} + +void +ImageDemo::showMenuBar(bool value) { + ui.menuBar->setVisible(value) ; + ui.showMenuBar->setChecked(value) ; +} + +void +ImageDemo::showCursor(bool value) { + if (value) + this->setCursor(Qt::ArrowCursor) ; + else + this->setCursor(Qt::BlankCursor) ; + ui.showCursor->setChecked(value) ; +} + +void +ImageDemo::showOverlay(bool value) { + ui.showOverlay->setChecked(value) ; + emit overlayRequest(value) ; +} + +void +ImageDemo::showFeedback(bool value) { + ui.canvas->showFeedback(value) ; + ui.showFeedback->setChecked(value) ; +} + +void +ImageDemo::invertFeedback(bool value) { + ui.canvas->invertFeedback(value) ; + ui.invertFeedback->setChecked(value) ; +} + +void +ImageDemo::showInfo(bool value) { + ui.canvas->showInfo(value) ; + ui.showInfo->setChecked(value) ; +} + + diff --git a/TactilePictures/ImageDemo.h b/TactilePictures/ImageDemo.h new file mode 100644 index 0000000..9a14fe8 --- /dev/null +++ b/TactilePictures/ImageDemo.h @@ -0,0 +1,81 @@ +/* -*- mode: c++ -*- + * + * Adapted from Pixel based by Paolo Olivo + * Authors: Thomas Pietrzak + * Copyright © Inria + * + */ + +#ifndef IMAGEDEMO_H +#define IMAGEDEMO_H + +#include "Canvas.h" +#include "ui_ImageDemo.h" + +#include + +class ImageRef { + + std::string _img ; + std::string _data ; + +public: + + ImageRef() {} + ImageRef(const ImageRef ©) ; + ImageRef(const char *img, const char *data) ; + ~ImageRef(void) {} + + const char *img() const { return _img.data() ; } + const char *data() const { return _data.data() ; } + +}; + +// This MACRO is necessary to use ImageRef as a QVariant. +Q_DECLARE_METATYPE(ImageRef) + +class ImageDemo : public QMainWindow { + + Q_OBJECT ; + + Ui::ImageDemo ui; + QListWidgetItem *currItem ; + +public: + ImageDemo(/*Feedback *feedback*/TactonPlayer *player, const char *path=0, QWidget *parent=0) ; + //void setFeedback(Feedback *feedback) { ui.canvas->setFeedback(feedback) ; } + void setTactonPlayer(TactonPlayer *player) { ui.canvas->setTactonPlayer(player) ; } + QWidget* canvas(void) { return ui.canvas ; } + +protected: + void enterEvent(QEvent *event) ; + void dragEnterEvent(QDragEnterEvent *event) ; + void dragMoveEvent(QDragMoveEvent *event) ; + void dropEvent(QDropEvent *event) ; + void keyPressEvent(QKeyEvent *event) ; + +private: + void addFiles(QStringList &files) ; + void addDir(const char *path) ; + QListWidgetItem * addImage(const char * img) ; + +public slots: + void showMenuBar(bool value) ; + void showCursor(bool value) ; + void showOverlay(bool value) ; + +protected slots: + int showImage(QListWidgetItem * item) ; + void loadImagesDialog(void) ; + void loadDirectoryDialog(void) ; + void showRepository(bool value) ; + void showFeedback(bool value) ; + void invertFeedback(bool value) ; + void showInfo(bool value) ; + +signals: + void overlayRequest(bool value) ; + +}; + +#endif diff --git a/TactilePictures/ImageDemo.ui b/TactilePictures/ImageDemo.ui new file mode 100644 index 0000000..1297ec1 --- /dev/null +++ b/TactilePictures/ImageDemo.ui @@ -0,0 +1,351 @@ + + + ImageDemo + + + + 0 + 0 + 861 + 477 + + + + + 0 + 0 + + + + true + + + ImageDemo + + + + true + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + + 250 + 16777215 + + + + Qt::NoFocus + + + true + + + true + + + + + + + + + 0 + 0 + 861 + 21 + + + + + &File + + + + + + + + + &Canvas + + + + + + &Show + + + + + + + + + + + + + + + + &Open Image + + + Ctrl+O + + + + + true + + + &Toggle images + + + Ctrl+T + + + + + true + + + &Invert feedback + + + Ctrl+I + + + + + true + + + &Repository + + + Ctrl+R + + + + + true + + + &Image Info + + + Show Image Info + + + Ctrl+S + + + + + true + + + &Feedback Image + + + Ctrl+F + + + + + true + + + &Menubar + + + Ctrl+M + + + + + Open &Directory + + + Ctrl+D + + + + + &Quit + + + Ctrl+Q + + + + + true + + + false + + + &Cursor + + + Ctrl+C + + + + + true + + + O&verlay + + + Ctrl+V + + + + + + Canvas + QWidget +
Canvas.h
+ 1 +
+
+ + + + + + quit + triggered() + ImageDemo + close() + + + -1 + -1 + + + 283 + 238 + + + + +
diff --git a/TactilePictures/KinectInput.cpp b/TactilePictures/KinectInput.cpp new file mode 100644 index 0000000..9fc8f37 --- /dev/null +++ b/TactilePictures/KinectInput.cpp @@ -0,0 +1,108 @@ +#include "KinectInput.h" + +#include +#include + +#define TIMEOUT 100 + +KinectInput::KinectInput() +:_running(false), _skeletonid(-1), _skeletonEvent(NULL), _xfilter(50, 0.05), _yfilter(50, 0.05), _zfilter(50, 0.01) +{ + //creates the kinect sensor (id 0) + if(FAILED(NuiCreateSensorByIndex(0, &_kinect))) + { + qDebug() << "Cannot find a KINECT sensor" << endl; + //I should do something else, sucha as an exception + return; + } + + if (_kinect->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON) == E_NUI_SKELETAL_ENGINE_BUSY) + { + qDebug() << "Cannot initialize the KINECT sensor" << endl; + return; + } + + //initialize the skeleton tracking + _skeletonEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!HasSkeletalEngine(_kinect) || FAILED(_kinect->NuiSkeletonTrackingEnable(_skeletonEvent, 0))) + { + qDebug() << "Cannot Initialize skeleton tracking" << endl; + return; + } +} + +KinectInput::~KinectInput() +{ + NuiShutdown(); + if( _skeletonEvent && ( _skeletonEvent != INVALID_HANDLE_VALUE ) ) + CloseHandle( _skeletonEvent ); +} + +//thread that manages the kinect events +void KinectInput::run() +{ + _running = true; + while(_running) + { + //wait for an event + if (WaitForSingleObject(_skeletonEvent, TIMEOUT) != 0) + continue; + + //get skeleton values + NUI_SKELETON_FRAME SkeletonFrame; + HRESULT hr = _kinect->NuiSkeletonGetNextFrame( 0, &SkeletonFrame ); +// qDebug() << "Kinect frame " << SkeletonFrame.dwFrameNumber; + + //if no skeleton already identified or not tracked anymore, search for one + if (_skeletonid < 0 || SkeletonFrame.SkeletonData[_skeletonid].eTrackingState != NUI_SKELETON_TRACKED) + { + for( int i = 0 ; i < NUI_SKELETON_COUNT ; i++ ) + { + if(SkeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED) + { + _skeletonid = i; + break; + } + } + } + + //no skeleton found + if (_skeletonid < 0) + continue; + + //retrieve information on the skeleton + NUI_SKELETON_DATA &data = SkeletonFrame.SkeletonData[_skeletonid]; + + //checking if we have a skeleton +/* if (data.eTrackingState != NUI_SKELETON_TRACKED) + continue;*/ + + //Let's use the 1 euro instead, see below + //smooth out the skeleton data + //_kinect->NuiTransformSmooth(&SkeletonFrame, NULL); + + + if (data.eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_HAND_RIGHT] == NUI_SKELETON_NOT_TRACKED) + continue; //right hand not detected + else + { +/* float posx, posy; + NuiTransformSkeletonToDepthImageF(data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT], &posx, &posy); + emit rightHandMove(posx, posy,0);*/ + Vector4 &skelpos = data.SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT]; + + //Filter + int timestamp = QDateTime::currentMSecsSinceEpoch(); + float fx = _xfilter.filter(skelpos.x, timestamp); + float fy = _yfilter.filter(skelpos.y, timestamp); + float fz = _zfilter.filter(skelpos.z, timestamp); + + emit rightHandMove(fx, fy, fz); + } + } +} + +void KinectInput::stop() +{ + _running = false; +} diff --git a/TactilePictures/KinectInput.h b/TactilePictures/KinectInput.h new file mode 100644 index 0000000..855fb7b --- /dev/null +++ b/TactilePictures/KinectInput.h @@ -0,0 +1,33 @@ +#ifndef __KINECTINPUT__ +#define __KINECTINPUT__ + +#include +#include +#include +#include + +#include "OneEuroFilter.h" + +class KinectInput: public QThread +{ + Q_OBJECT + public: + KinectInput(); + ~KinectInput(); + + void run(); + void stop(); + + private: + INuiSensor* _kinect; + HANDLE _skeletonEvent; + bool _running; + int _skeletonid; + + OneEuroFilter _xfilter, _yfilter, _zfilter; + + signals: + void rightHandMove(float x, float y, float z); +}; + +#endif diff --git a/TactilePictures/OneEuroFilter.cpp b/TactilePictures/OneEuroFilter.cpp new file mode 100644 index 0000000..ee1754c --- /dev/null +++ b/TactilePictures/OneEuroFilter.cpp @@ -0,0 +1,118 @@ +#include "OneEuroFilter.h" + +#define _USE_MATH_DEFINES +#include +#include + +LowPassFilter::LowPassFilter(double alpha, double initval) +:_y(initval), _s(initval), _initialized(false) +{ + setAlpha(alpha); +} + +double LowPassFilter::filter(double value) +{ + double result; + if (_initialized) + result = _a * value + (1.0 - _a) * _s; + else + { + result = value; + _initialized = true; + } + _y = value; + _s = result; + return result; +} + +double LowPassFilter::filterWithAlpha(double value, double alpha) +{ + setAlpha(alpha); + return filter(value); +} + +bool LowPassFilter::hasLastRawValue(void) +{ + return _initialized; +} + +double LowPassFilter::lastRawValue(void) +{ + return _y ; +} + +void LowPassFilter::setAlpha(double alpha) +{ + if (alpha <= 0.0 || alpha > 1.0) + throw std::range_error("alpha should be in ]0.0., 1.0]"); + _a = alpha; +} + + +OneEuroFilter::OneEuroFilter(double freq, double mincutoff, double beta, double dcutoff) +:_lasttime(UndefinedTime) +{ + setFrequency(freq); + + //don't do this earlier, frequency is required to compute alpha + _x = new LowPassFilter(alpha(mincutoff)); + _dx = new LowPassFilter(alpha(dcutoff)); + + setMinCutoff(mincutoff); + setBeta(beta); + setDerivateCutoff(dcutoff); +} + +OneEuroFilter::~OneEuroFilter(void) +{ + delete _x; + delete _dx; +} + +double OneEuroFilter::filter(double value, TimeStamp timestamp) +{ + // update the sampling frequency based on timestamps + if (_lasttime != UndefinedTime && timestamp != UndefinedTime) + _freq = 1.0 / (timestamp - _lasttime); + _lasttime = timestamp; + // estimate the current variation per second + double dvalue = _x->hasLastRawValue() ? (value - _x->lastRawValue()) * _freq : 0.0; // FIXME: 0.0 or value? + double edvalue = _dx->filterWithAlpha(dvalue, alpha(_dcutoff)); + // use it to update the cutoff frequency + double cutoff = _mincutoff + _beta * fabs(edvalue); + // filter the given value + return _x->filterWithAlpha(value, alpha(cutoff)); +} + +double OneEuroFilter::alpha(double cutoff) +{ + double te = 1.0 / _freq; + double tau = 1.0 / (2 * M_PI * cutoff); + return 1.0 / (1.0 + tau/te); +} + +void OneEuroFilter::setFrequency(double f) +{ + if (f <= 0) + throw std::range_error("freq should be >0"); + _freq = f; +} + +void OneEuroFilter::setMinCutoff(double mc) +{ + if (mc <= 0) + throw std::range_error("mincutoff should be >0"); + _mincutoff = mc; +} + +void OneEuroFilter::setBeta(double b) +{ + _beta = b; +} + +void OneEuroFilter::setDerivateCutoff(double dc) +{ + if (dc <= 0) + throw std::range_error("dcutoff should be >0"); + _dcutoff = dc; +} diff --git a/TactilePictures/OneEuroFilter.h b/TactilePictures/OneEuroFilter.h new file mode 100644 index 0000000..7d24ffa --- /dev/null +++ b/TactilePictures/OneEuroFilter.h @@ -0,0 +1,62 @@ +/* + * OneEuroFilter.h/.cpp - + * + * Author: Thomas Pietrzak (thomas.pietrzak@lifl.fr) + * based on code from + * Nicolas Roussel (nicolas.roussel@inria.fr) + * + */ + + +#ifndef __ONEEURO__ +#define __ONEEURO__ + + +typedef double TimeStamp ; // in seconds + +static const TimeStamp UndefinedTime = -1.0 ; + + +class LowPassFilter +{ + public: + LowPassFilter(double alpha, double initval=0.0); + + double filterWithAlpha(double value, double alpha); + inline bool hasLastRawValue(void); + inline double lastRawValue(void); + + private: + double _y, _a, _s; + bool _initialized; + + void setAlpha(double alpha); + double filter(double value); +} ; + +class OneEuroFilter +{ + public: + OneEuroFilter( + double freq, + double mincutoff=1.0, + double beta_=0.0, + double dcutoff=1.0); + + ~OneEuroFilter(); + + double filter(double value, TimeStamp timestamp=UndefinedTime); + + private: + double _freq, _mincutoff, _beta, _dcutoff; + LowPassFilter *_x, *_dx; + TimeStamp _lasttime; + + double alpha(double cutoff); + inline void setFrequency(double f); + inline void setMinCutoff(double mc); + inline void setBeta(double b); + inline void setDerivateCutoff(double dc); +} ; + +#endif diff --git a/TactilePictures/PictureView.cpp b/TactilePictures/PictureView.cpp new file mode 100644 index 0000000..b6f2e3e --- /dev/null +++ b/TactilePictures/PictureView.cpp @@ -0,0 +1,25 @@ +#include "PictureView.h" + +#include +#include + +PictureView::PictureView(void) +{ + setScene(&_scene); + _scene.addItem(&_image); +// this->setMaximumSize(0xffffffff, 0xffffffff); + //setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +// resize(200, 200); + +} + + +PictureView::~PictureView(void) +{ +} + +void PictureView::setImage(QString fileName) +{ + _image.setPixmap(QPixmap(fileName)); + fitInView(_scene.sceneRect(),Qt::KeepAspectRatio); +} diff --git a/TactilePictures/PictureView.h b/TactilePictures/PictureView.h new file mode 100644 index 0000000..330a6d4 --- /dev/null +++ b/TactilePictures/PictureView.h @@ -0,0 +1,25 @@ +#ifndef __PICTUREVIEW__ +#define __PICTUREVIEW__ + +#include +#include +#include +#include + +class PictureView : + public QGraphicsView +{ + Q_OBJECT + + public: + PictureView(void); + ~PictureView(void); + + void setImage(QString); + + private: + QGraphicsScene _scene; + QGraphicsPixmapItem _image; +}; + +#endif diff --git a/TactilePictures/TactilePictures.vcxproj b/TactilePictures/TactilePictures.vcxproj new file mode 100644 index 0000000..05e6b03 --- /dev/null +++ b/TactilePictures/TactilePictures.vcxproj @@ -0,0 +1,242 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B219D272-E4C1-46D8-9263-3B4462606CC6} + Qt4VSv1.0 + + + + Application + + + Application + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + AllRules.ruleset + + + AllRules.ruleset + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + UNICODE;WIN32;QT_LARGEFILE_SUPPORT;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;%(PreprocessorDefinitions) + $(KINECTSDK10_DIR)\inc;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebugDLL + false + + + Windows + $(OutDir)\$(ProjectName).exe + $(KINECTSDK10_DIR)\lib\x86;$(QTDIR)\lib;%(AdditionalLibraryDirectories) + true + TactonPlayerd.lib;Kinect10.lib;qtmaind.lib;QtCored4.lib;QtGuid4.lib;%(AdditionalDependencies) + + + + + UNICODE;WIN32;QT_LARGEFILE_SUPPORT;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;%(PreprocessorDefinitions) + $(KINECTSDK10_DIR)\inc;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + + + MultiThreadedDLL + false + + + Windows + $(OutDir)\$(ProjectName).exe + $(KINECTSDK10_DIR)\lib\x86;$(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + TactonPlayer.lib;Kinect10.lib;qtmain.lib;QtCore4.lib;QtGui4.lib;%(AdditionalDependencies) + + + + + + true + + + true + + + true + + + true + + + true + + + + + + + + + true + + + true + + + true + + + true + + + true + + + + + + + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing tactilepictures.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing tactilepictures.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + + + Document + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing Canvas.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing Canvas.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing ImageDemo.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing ImageDemo.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing PictureView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing PictureView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing KinectInput.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing KinectInput.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I$(KINECTSDK10_DIR)\inc" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + + + Document + %(FullPath);%(AdditionalInputs) + Rcc%27ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs) + "$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp + %(FullPath);%(AdditionalInputs) + Rcc%27ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs) + "$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp + + + + + Document + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + + + + Document + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + + + + + + + + + + \ No newline at end of file diff --git a/TactilePictures/images/00-VH07.PNG b/TactilePictures/images/00-VH07.PNG new file mode 100644 index 0000000..129e219 Binary files /dev/null and b/TactilePictures/images/00-VH07.PNG differ diff --git a/TactilePictures/images/00-VS07.PNG b/TactilePictures/images/00-VS07.PNG new file mode 100644 index 0000000..8424ad9 Binary files /dev/null and b/TactilePictures/images/00-VS07.PNG differ diff --git a/TactilePictures/images/01-VH03.PNG b/TactilePictures/images/01-VH03.PNG new file mode 100644 index 0000000..0fa6176 Binary files /dev/null and b/TactilePictures/images/01-VH03.PNG differ diff --git a/TactilePictures/images/01-VS03.PNG b/TactilePictures/images/01-VS03.PNG new file mode 100644 index 0000000..d556cbd Binary files /dev/null and b/TactilePictures/images/01-VS03.PNG differ diff --git a/TactilePictures/images/02-VH01.PNG b/TactilePictures/images/02-VH01.PNG new file mode 100644 index 0000000..b71d168 Binary files /dev/null and b/TactilePictures/images/02-VH01.PNG differ diff --git a/TactilePictures/images/02-VS01.PNG b/TactilePictures/images/02-VS01.PNG new file mode 100644 index 0000000..d546ede Binary files /dev/null and b/TactilePictures/images/02-VS01.PNG differ diff --git a/TactilePictures/images/03-OH07.PNG b/TactilePictures/images/03-OH07.PNG new file mode 100644 index 0000000..5d3ca93 Binary files /dev/null and b/TactilePictures/images/03-OH07.PNG differ diff --git a/TactilePictures/images/03-OS07.PNG b/TactilePictures/images/03-OS07.PNG new file mode 100644 index 0000000..c225248 Binary files /dev/null and b/TactilePictures/images/03-OS07.PNG differ diff --git a/TactilePictures/images/10-BW-eiffel.png b/TactilePictures/images/10-BW-eiffel.png new file mode 100644 index 0000000..a03ba46 Binary files /dev/null and b/TactilePictures/images/10-BW-eiffel.png differ diff --git a/TactilePictures/images/11-BW-patterns.png b/TactilePictures/images/11-BW-patterns.png new file mode 100644 index 0000000..a25f09b Binary files /dev/null and b/TactilePictures/images/11-BW-patterns.png differ diff --git a/TactilePictures/images/20-BW-teapot.png b/TactilePictures/images/20-BW-teapot.png new file mode 100644 index 0000000..1e77f01 Binary files /dev/null and b/TactilePictures/images/20-BW-teapot.png differ diff --git a/TactilePictures/images/21-BW-teapot.png b/TactilePictures/images/21-BW-teapot.png new file mode 100644 index 0000000..70fe955 Binary files /dev/null and b/TactilePictures/images/21-BW-teapot.png differ diff --git a/TactilePictures/images/22-BW-teapot.png b/TactilePictures/images/22-BW-teapot.png new file mode 100644 index 0000000..b68ece8 Binary files /dev/null and b/TactilePictures/images/22-BW-teapot.png differ diff --git a/TactilePictures/images/23-BW-teapot.data.png b/TactilePictures/images/23-BW-teapot.data.png new file mode 100644 index 0000000..b68ece8 Binary files /dev/null and b/TactilePictures/images/23-BW-teapot.data.png differ diff --git a/TactilePictures/images/23-BW-teapot.png b/TactilePictures/images/23-BW-teapot.png new file mode 100644 index 0000000..70fe955 Binary files /dev/null and b/TactilePictures/images/23-BW-teapot.png differ diff --git a/TactilePictures/images/30-BW-map.png b/TactilePictures/images/30-BW-map.png new file mode 100644 index 0000000..a2b4c18 Binary files /dev/null and b/TactilePictures/images/30-BW-map.png differ diff --git a/TactilePictures/images/31-BW-map.data.png b/TactilePictures/images/31-BW-map.data.png new file mode 100644 index 0000000..afdcbc3 Binary files /dev/null and b/TactilePictures/images/31-BW-map.data.png differ diff --git a/TactilePictures/images/31-BW-map.png b/TactilePictures/images/31-BW-map.png new file mode 100644 index 0000000..a2b4c18 Binary files /dev/null and b/TactilePictures/images/31-BW-map.png differ diff --git a/TactilePictures/main.cpp b/TactilePictures/main.cpp new file mode 100644 index 0000000..5873344 --- /dev/null +++ b/TactilePictures/main.cpp @@ -0,0 +1,29 @@ +#include "tactilepictures.h" +#include + +#include +#include "ImageDemo.h" + +#include + +int main(int argc, char *argv[]) +{ + TactonPlayer *player; + try + { + player = new TactonPlayer("COM8"); + } + catch (...) + { + player = NULL; + qDebug() << "No wristband found"; + return 0; + } + + QApplication a(argc, argv); + ImageDemo demo(player); + demo.show(); + //TactilePictures w; + //w.show(); + return a.exec(); +} diff --git a/TactilePictures/pixelbased.cpp b/TactilePictures/pixelbased.cpp new file mode 100644 index 0000000..cec056e --- /dev/null +++ b/TactilePictures/pixelbased.cpp @@ -0,0 +1,164 @@ +/* + * + * demos/pixelbased/pixelbased.cpp -- + * + * Initial software + * Authors: Nicolas Roussel, Paolo Olivo + * Copyright © Inria + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ImageDemo.h" + +int +main(int argc, char* argv[]) { + QApplication::setStyle(QStyleFactory::create("plastique")) ; + QApplication app(argc, argv) ; + // Parse args + int ch, overlaysize=-1 ; + bool absolute=false, fullscreen=false, hidemenubar=false, showoverlay=false; + QString device, rotation, screenstr, calibstr ; + while ((ch = getopt(argc, argv, "d:r:f::maho::c:")) != -1) { + switch (ch) { + case 'f': fullscreen = true ; if (optarg) screenstr = optarg ; break ; + case 'r': rotation = optarg ; break ; + case 'd': device = optarg ; break ; + case 'm': hidemenubar = true ; break ; + case 'a': absolute = true ; break ; + case 'c': calibstr = optarg ; break ; + case 'o': showoverlay = true ; if (optarg) overlaysize = atoi(optarg) ; break ; + case 'h': + qDebug() << "Usage:" << argv[0] << "[OPTION]... [DIRECTORY]" ; + qDebug(); + qDebug() << "Options:" ; + qDebug() << " -d select device between: standalone, stimtac200, stm32ts60" ; + qDebug() << " (default=standalone)" ; + qDebug() << " -c MATRIX set 3x3 calibration matrix (e.g. -c 0.865,0,0.074,0,1.1,0,0,0,1)" ; + qDebug() << " (standalone only)" ; + qDebug() << " -r select rotation between: normal, left, inverted, right" ; + qDebug() << " (standalone only, default=normal)" ; + qDebug() << " -f[SCREEN-NUMBER] set demo fullscreen and select screen (e.g. -f0)" ; + qDebug() << " -m hide menubar" ; + qDebug() << " -o[SIZE] show overlay (e.g. -o60)" ; + qDebug() << " -a set absolute pointing (standalone only)" ; + qDebug() << " -h display this help and exit" ; + return 0 ; + default: break ; + } + } + char* path = argc - optind > 0 ? argv[optind] : NULL ; + // Init demo and stimtac device + Q_INIT_RESOURCE(pixelbased) ; + ImageDemo* demo = new ImageDemo(NULL, path) ; + Overlay *overlay = new Overlay(demo) ; + overlay->setRadius(overlaysize) ; + QObject::connect(demo, SIGNAL(overlayRequest(bool)), + overlay, SLOT(setEnabled(bool))) ; + Feedback* feedback = NULL; + STM32TS60CursorControl* stm32ts60Cursor = NULL ; + StandaloneCursorControl* standaloneCursor = NULL ; + STIMTAC200CursorControl* stimtac200Cursor = NULL ; + if (device.isEmpty()) { + device = "standalone" ; + qDebug() << "Using default device:" << device ; + } + if (device=="standalone") { + standaloneCursor = new StandaloneCursorControl(demo) ; + feedback = new Feedback(standaloneCursor->standalone()) ; + standaloneCursor->setAbsolute(absolute) ; + if (!calibstr.isEmpty()) { + bool ok ; + int i = 0 ; + qreal data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0} ; + QStringList list = calibstr.remove(QChar(' ')).split(",") ; + ok = list.size()==9 ; + while (i<9 && ok) { + data[i] = list.at(i).toFloat(&ok) ; + ++i ; + } + if (ok) { + QMatrix3x3 matrix(data) ; + QMatrix3x3 result = matrix * standaloneCursor->transformationMatrix() ; + standaloneCursor->setTransformationMatrix(result) ; + } else { + qDebug() << "WARNING: invalid calibration matrix" << calibstr ; + } + } + if (!rotation.isEmpty()) { + QMatrix3x3 matrix = StandaloneCursorControl::orientationMatrix(rotation) ; + QMatrix3x3 result = matrix * standaloneCursor->transformationMatrix() ; + standaloneCursor->setTransformationMatrix(result) ; + } + if (absolute) demo->showCursor(false) ; + + } else if (device=="stimtac200") { + stimtac200Cursor = new STIMTAC200CursorControl(demo, 0.0) ; + feedback = new Feedback(stimtac200Cursor->stimtac()) ; + demo->connect(stimtac200Cursor->stimtac(), + SIGNAL(touchEvent(STIMTAC_input_200::TouchEvent)), + demo->canvas(), + SLOT(touchEvent(STIMTAC_input_200::TouchEvent))) ; + if (!calibstr.isEmpty()) qDebug() + << "WARNING: calibration matrix cannot be set for requested device:" + << device ; + if (!rotation.isEmpty()) qDebug() + << "WARNING: rotation cannot be set for requested device:" + << device ; + if (absolute) qDebug() + << "WARNING: absolute pointing not available for requested device:" + << device ; + + } else if (device=="stm32ts60") { + stm32ts60Cursor = new STM32TS60CursorControl(demo, 0.0) ; + feedback = new Feedback ; + if (!calibstr.isEmpty()) qDebug() + << "WARNING: calibration matrix cannot be set for requested device:" + << device ; + if (!rotation.isEmpty()) qDebug() + << "WARNING: rotation cannot be set for requested device:" + << device ; + if (absolute) qDebug() + << "WARNING: absolute pointing not available for requested device:" + << device ; + } else { + qDebug() << "ERROR! Device not recognized: " << device ; + return -1 ; + } + demo->setFeedback(feedback) ; + demo->showOverlay(showoverlay) ; + demo->showMenuBar(!hidemenubar) ; + if (!screenstr.isEmpty()) { + // Check fullscreen and screen selection + QDesktopWidget* desktop = app.desktop() ; + bool ok ; + int screennumber = screenstr.toInt(&ok) ; + if (ok && screennumber>=0 && screennumberscreenCount()) { + const QRect screen = desktop->screenGeometry(screennumber) ; + demo->move(screen.x(), screen.y()) ; + } else { + qDebug() << "Cannot select screen:" << screenstr ; + } + } + fullscreen ? demo->showFullScreen() : demo->show() ; + demo->raise() ; + // Run + int value = app.exec() ; + // Closing + if (stm32ts60Cursor) delete stm32ts60Cursor ; + if (standaloneCursor) delete standaloneCursor ; + if (stimtac200Cursor) delete stimtac200Cursor ; + delete feedback ; + delete demo ; + return value ; +} diff --git a/TactilePictures/tactilepictures.cpp b/TactilePictures/tactilepictures.cpp new file mode 100644 index 0000000..3bb4f3f --- /dev/null +++ b/TactilePictures/tactilepictures.cpp @@ -0,0 +1,24 @@ +#include "tactilepictures.h" + +#include + +TactilePictures::TactilePictures(QWidget *parent, Qt::WFlags flags) +: QMainWindow(parent, flags) +{ + setupUi(this); + + _pictureview.setParent(_pictureframe); + + connect(_load, SIGNAL(pressed()), this, SLOT(pressLoad())); +} + +TactilePictures::~TactilePictures() +{ + +} + +void TactilePictures::pressLoad() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), ".", tr("Image Files (*.png *.jpg *.bmp)")); + _pictureview.setImage(fileName); +} \ No newline at end of file diff --git a/TactilePictures/tactilepictures.h b/TactilePictures/tactilepictures.h new file mode 100644 index 0000000..4e0f8ef --- /dev/null +++ b/TactilePictures/tactilepictures.h @@ -0,0 +1,27 @@ +#ifndef TACTILEPICTURES_H +#define TACTILEPICTURES_H + +#include +#include "ui_tactilepictures.h" + +#include + +#include "PictureView.h" + + +class TactilePictures : public QMainWindow, public Ui::TactilePicturesClass +{ + Q_OBJECT + + public: + TactilePictures(QWidget *parent = 0, Qt::WFlags flags = 0); + ~TactilePictures(); + + public slots: + void pressLoad(); + + private: + PictureView _pictureview; +}; + +#endif // TACTILEPICTURES_H diff --git a/TactilePictures/tactilepictures.ui b/TactilePictures/tactilepictures.ui new file mode 100644 index 0000000..ffbb963 --- /dev/null +++ b/TactilePictures/tactilepictures.ui @@ -0,0 +1,90 @@ + + + TactilePicturesClass + + + + 0 + 0 + 919 + 676 + + + + TactilePictures + + + + Qt::LeftToRight + + + + + + + 0 + 0 + + + + + + + + + QLayout::SetMinimumSize + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Load + + + + + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + diff --git a/TouchscreenButton/TouchscreenButton.cpp b/TouchscreenButton/TouchscreenButton.cpp new file mode 100644 index 0000000..5471467 --- /dev/null +++ b/TouchscreenButton/TouchscreenButton.cpp @@ -0,0 +1,39 @@ +#include "TouchscreenButton.h" + +#include + +TouchscreenButton::TouchscreenButton(QWidget *parent, Qt::WFlags flags) +: QMainWindow(parent, flags) +{ + setupUi(this); + + try + { + _touchscreen = new USBHIDImmersionTouchscreen(); + _wristband = new TactonPlayer("COM11"); + } + catch(...) + { + qDebug() << "Cannot find the touchscreen or the wristband"; + } + + connect(_play, SIGNAL(pressed()), this, SLOT(play())); + connect(_stop, SIGNAL(pressed()), this, SLOT(stop())); +} + +TouchscreenButton::~TouchscreenButton() +{ + if (_touchscreen) + delete _touchscreen; + hid_exit(); +} + +void TouchscreenButton::play() +{ + _touchscreen->playEffect((unsigned char)(_effect->value()), (unsigned char)(_repeats->value())); +} + +void TouchscreenButton::stop() +{ + _touchscreen->playEffect(0, 1); +} diff --git a/TouchscreenButton/TouchscreenButton.h b/TouchscreenButton/TouchscreenButton.h new file mode 100644 index 0000000..79fec7d --- /dev/null +++ b/TouchscreenButton/TouchscreenButton.h @@ -0,0 +1,27 @@ +#ifndef TOUCHSCREENBUTTON_H +#define TOUCHSCREENBUTTON_H + +#include +#include "ui_touchscreenbutton.h" + +#include +#include "USBHIDImmersionTouchscreen.h" + +class TouchscreenButton : public QMainWindow, public Ui::TouchscreenButtonClass +{ + Q_OBJECT + + public: + TouchscreenButton(QWidget *parent = 0, Qt::WFlags flags = 0); + ~TouchscreenButton(); + + public slots: + void play(); + void stop(); + + private: + USBHIDImmersionTouchscreen *_touchscreen; + TactonPlayer *_wristband; +}; + +#endif // TOUCHSCREENBUTTON_H diff --git a/TouchscreenButton/TouchscreenButton.vcxproj b/TouchscreenButton/TouchscreenButton.vcxproj new file mode 100644 index 0000000..55d5fc0 --- /dev/null +++ b/TouchscreenButton/TouchscreenButton.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {3484A961-9B00-4197-85ED-99303909089C} + Qt4VSv1.0 + + + + Application + + + Application + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + AllRules.ruleset + + + AllRules.ruleset + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + UNICODE;WIN32;QT_LARGEFILE_SUPPORT;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;%(PreprocessorDefinitions) + ArduinoSerial;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebugDLL + false + + + Windows + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + true + hidapid.lib;qtmaind.lib;QtCored4.lib;QtGuid4.lib;%(AdditionalDependencies) + + + + + UNICODE;WIN32;QT_LARGEFILE_SUPPORT;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;%(PreprocessorDefinitions) + ArduinoSerial;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + + + MultiThreadedDLL + false + + + Windows + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + hidapi.lib;qtmain.lib;QtCore4.lib;QtGui4.lib;%(AdditionalDependencies) + + + + + true + + + + + + + + + true + + + + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing TouchscreenButton.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB "-I.\ArduinoSerial" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing TouchscreenButton.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB "-I.\ArduinoSerial" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" + + + + + Document + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + + + + + %(AdditionalInputs) + + + + + + + %(AdditionalInputs) + + + + + + + + + + + Document + %(FullPath);%(AdditionalInputs) + Rcc%27ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs) + "$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp + %(FullPath);%(AdditionalInputs) + Rcc%27ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs) + "$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp + + + + + + + + + + + \ No newline at end of file diff --git a/TouchscreenButton/USBHIDImmersionTouchscreen.cpp b/TouchscreenButton/USBHIDImmersionTouchscreen.cpp new file mode 100644 index 0000000..75d2aa4 --- /dev/null +++ b/TouchscreenButton/USBHIDImmersionTouchscreen.cpp @@ -0,0 +1,48 @@ +#include "USBHIDImmersionTouchscreen.h" + + +// Vendir ID and Product ID (see lsusb) +int USBHIDImmersionTouchscreen::VID = 0x0563; +int USBHIDImmersionTouchscreen::PID = 0x0423; + +#define TIMEOUT 100 + +#define PLAYEFFECTREPREAT_BYTE 90 +#define SETNONVOLATILEEFFECTS_BYTE 88 +#define STATUS_BYTE 96 +#define STRING_BYTE 2 + +hid_device *USBHIDImmersionTouchscreen::_devicehandle = NULL; + +USBHIDImmersionTouchscreen::USBHIDImmersionTouchscreen(void) +{ + if ((_devicehandle = hid_open(VID, PID, NULL)) == NULL) + throw "Cannot claim the device"; +} + + +USBHIDImmersionTouchscreen::~USBHIDImmersionTouchscreen(void) +{ + if (_devicehandle) + { + hid_close(_devicehandle); + _devicehandle = NULL; + } +} + +bool USBHIDImmersionTouchscreen::playEffect(unsigned char effect, unsigned char nbrepeat) const +{ + unsigned char buffer[35]; + buffer[0] = PLAYEFFECTREPREAT_BYTE; + buffer[1] = effect; + buffer[2] = nbrepeat; + if (_devicehandle) + return hid_write(_devicehandle, buffer, 35); + else + return -1; +} + +char * USBHIDImmersionTouchscreen::getStatus() const +{ + return NULL; +} diff --git a/TouchscreenButton/USBHIDImmersionTouchscreen.h b/TouchscreenButton/USBHIDImmersionTouchscreen.h new file mode 100644 index 0000000..f40bb6a --- /dev/null +++ b/TouchscreenButton/USBHIDImmersionTouchscreen.h @@ -0,0 +1,29 @@ +#ifndef __USBHIDINNERSIONTOUCHSCREEN__ +#define __USBHIDINNERSIONTOUCHSCREEN__ + +#include + +class USBHIDImmersionTouchscreen +{ + public: + USBHIDImmersionTouchscreen(void); + ~USBHIDImmersionTouchscreen(void); + + /* + See documentation for list of effects (0-46), 255 for no effect + nbrepeats: 255 for infinite until other effect or no effect + */ + bool playEffect(unsigned char effect, unsigned char nbrepeat) const; + char *getStatus() const; + //bool setNonVolatileEffects() const; + + private: + // Vendor ID and Product ID + static int VID; + static int PID; + //handle on the device + static hid_device *_devicehandle; +}; + +#endif + diff --git a/TouchscreenButton/Wristband.cpp b/TouchscreenButton/Wristband.cpp new file mode 100644 index 0000000..8e7af34 --- /dev/null +++ b/TouchscreenButton/Wristband.cpp @@ -0,0 +1,37 @@ +#include "Wristband.h" + +#include + +Wristband::Wristband() +{ + try + { + _tactonplayer = new TactonPlayer("COM11"); + } + catch(...) + { + qDebug() << "Cannot find the touchscreen or the wristband"; + _tactonplayer = NULL; + } + +} + + +Wristband::~Wristband() +{ + if (_tactonplayer) + delete _tactonplayer; +} + +bool Wristband::playEffect(unsigned char effect, unsigned char nbrepeat) const +{ + for (int i = 0 ; i < nbrepeat ; i++) + { + _tactonplayer->play(_effects[effect]); + ///TODO: add a pause + } +} + +char *Wristband::getStatus() const +{ +} diff --git a/TouchscreenButton/Wristband.h b/TouchscreenButton/Wristband.h new file mode 100644 index 0000000..aa6f0ce --- /dev/null +++ b/TouchscreenButton/Wristband.h @@ -0,0 +1,25 @@ +#ifndef __WRISTBAND__ +#define __WRISTBAND__ + +#include + +class Wristband : + public TactonPlayer +{ + public: + Wristband(void); + ~Wristband(void); + + /* + Mimics Immersion touchscreen effects (0-46), 255 for no effect + nbrepeats: 255 for infinite until other effect or no effect + */ + bool playEffect(unsigned char effect, unsigned char nbrepeat) const; + char *getStatus() const; + + private: + TactonPlayer *_tactonplayer; + Tacton _effects[46]; +}; + +#endif diff --git a/TouchscreenButton/main.cpp b/TouchscreenButton/main.cpp new file mode 100644 index 0000000..fcc30a6 --- /dev/null +++ b/TouchscreenButton/main.cpp @@ -0,0 +1,10 @@ +#include "TouchscreenButton.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + TouchscreenButton w; + w.show(); + return a.exec(); +} diff --git a/TouchscreenButton/touchscreenbutton.ui b/TouchscreenButton/touchscreenbutton.ui new file mode 100644 index 0000000..7dfcac8 --- /dev/null +++ b/TouchscreenButton/touchscreenbutton.ui @@ -0,0 +1,85 @@ + + + TouchscreenButtonClass + + + + 0 + 0 + 345 + 191 + + + + TouchscreenButton + + + + + + + + + 46 + + + + + + + Effect: + + + + + + + 254 + + + 1 + + + + + + + Repeats + + + + + + + + + + 20 + + + + Play + + + + + + + + 20 + + + + Stop + + + + + + + + + + + +