Comme tout le monde depuis l'avènement de kinect, j'attends avec impatience un "EventClient" pour notre très chère Xbmc préféré mais malheureusement la seul version disponible en alpha est en c# et donc sous windows.
C'est pourquoi j'ai décider d'écrire ce tutorial.
Prés requis :
- Une Kinect
- Ubuntu (fonctionne aussi sous gentoo)
Tester :
Ubuntu 10.04 LTS "The Lucid Lynx" (ok) Ubuntu 10.10 "The Maverick Meerkat" (ok) Ubuntu 9.04 "The Jaunty Jackalope" (nok, manque une fonction dans lib usb ...) - Package :
build-essential
git-core
libglut3-dev
doxygen
graphviz
sudo apt-get install build-essential git-core libglut3-dev doxygen graphviz libusb-1.0-0-dev
Installation : Tuto original
ici (Anglais)
OpenNI :mkdir ~/kinect && cd ~/kinect
git clone [url]https://github.com/OpenNI/OpenNI.git[/url]
cd OpenNI/Platform/Linux-x86/Build
sed -ie '/^[\t]ALL_PROJS += $(MONO_FORMS_SAMPLES)/d' Makefile
make && sudo make install
Sensor :cd ~/kinect/
git clone [url]https://github.com/boilerbots/Sensor.git[/url]
cd Sensor
git checkout kinect
cd Platform/Linux-x86/Build
make && sudo make install
Nite :cd ~/kinect/
Téléchargement de Nite 32 ou 64 bits ici:
Télécharger Nite et extraire l'archive dans ~/kinect
cd ~/kinect/Nite-1.3.1.3
sudo niLicense PrimeSense 0KOIk2JeIBYClPWVnMoRKn5cdY4=
sed -ie '/^[\t]ALL_PROJS += Samples\/Boxes.net/d' Makefile
sed -i 's/\$(call MAKE_ALL_MAKEFILES,install)/\$(call MAKE_ALL_MAKEFILES,all)/' Makefile
sudo ./install.sh
La licence va vous être demandé, appuyer simplement sur la touche entréesudo make install
La dernière commande va se terminer avec une erreur du type : cp : opérande du fichier cible manquant après `/usr/local/include`
Pas de panique c'est juste un bug du script.(je n'ai pas eu le courage de le corriger
)Vous pouvez maintenant brancher votre Kinect et lancer les divers exemples fournis
~/kinect/NITE/Nite-1.3.0.17/Samples/Bin
Création d'un nouveau projet Xbmc:
Dossier Xbmc :cd ~/kinect/Nite-1.3.1.3/Samples
mkdir Xbmc && cd Xbmc
Copie projet PointViewer :cp ../PointViewer/* ./
chmod +w -R *
Édition fichier .mak :cat PointViewer.mak | sed s'/PointViewer/Xbmc/' > Xbmc.mak
rm PointViewer.mak
Édition fichier Makefile :cd ~/kinect/Nite-1.3.1.3/
Il faut éditer le fichier Makefile afin de rajouter le nouveau projet Xbmc,
en remplacant :# list all projects (in the order they should compile)
ALL_PROJS = \
Samples/PointServer \
Samples/SingleControl \
Samples/PointViewer \
Samples/Boxes \
Samples/TrackPad \
Samples/CircleControl \
Samples/SceneAnalysis \
Samples/Players \
# Samples/StickFigurePar :# list all projects (in the order they should compile)
ALL_PROJS = \
Samples/PointServer \
Samples/SingleControl \
Samples/PointViewer \
Samples/Boxes \
Samples/TrackPad \
Samples/CircleControl \
Samples/SceneAnalysis \
Samples/Players \
Samples/Xbmc \
# Samples/StickFigure
Compilation, Exécution :cd ~/kinect/Nite-1.3.1.3/
make
cd ~/kinect/Nite-1.3.1.3/Samples/Bin && ./Sample-Xbmc
Documentation OpenNI :
PDF téléchargeable ici
Bonus :
Petit exemple d'EventClient fait en 5 minutes a partir du projet PointViewer. (juste pour le fun
)
Cette exemple utilise le Keymap de la wiimote
Remplacer le main de Xbmc (~/kinect/Nite-1.3.1.3/Samples/Xbmc/main.cpp) par celui-ci :Main.cpp(C++):
#include <XnOpenNI.h>
#include <XnCppWrapper.h>
#include <XnHash.h>
#include <XnLog.h>
#include "XnVNite.h"
#include "PointDrawer.h"
#define CHECK_RC(rc, what) \
if (rc != XN_STATUS_OK) \
{ \
printf("%s failed: %s\n", what, xnGetStatusString(rc)); \
return rc; \
}
#ifdef USE_GLUT
#include <GL/glut.h>
#else
#endif
#include "xbmcclient.h"
#define KEYCODE_BUTTON_UP 1
#define KEYCODE_BUTTON_DOWN 2
#define KEYCODE_BUTTON_LEFT 3
#define KEYCODE_BUTTON_RIGHT 4
#define KEYCODE_BUTTON_ENTER 5
//Xbmc eventClient
CXBMCClient EventClient;
// OpenNI objects
xn::Context g_Context;
xn::DepthGenerator g_DepthGenerator;
xn::HandsGenerator g_HandsGenerator;
// NITE objects
XnVSessionManager* g_pSessionManager;
XnVFlowRouter* g_pFlowRouter;
XnVSteadyDetector* pNiteSteadyDetector;
XnVSwipeDetector* pNiteSwipeDetector;
XnVPushDetector* pNitePushDetector;
// the drawer
XnVPointDrawer* g_pDrawer;
#define GL_WIN_SIZE_X 720
#define GL_WIN_SIZE_Y 480
// Draw the depth map?
XnBool g_bDrawDepthMap = true;
XnBool g_bPrintFrameID = false;
// Use smoothing?
XnFloat g_fSmoothing = 0.0f;
XnBool g_bPause = false;
XnBool g_bQuit = false;
SessionState g_SessionState = NOT_IN_SESSION;
void CleanupExit()
{
if (NULL != g_pSessionManager){
delete g_pSessionManager;
g_pSessionManager = NULL;
}
delete pNiteSteadyDetector ;
delete pNiteSwipeDetector;
delete pNitePushDetector;
delete g_pDrawer;
g_Context.Shutdown();
exit (1);
}
// Callback for when the focus is in progress
void XN_CALLBACK_TYPE FocusProgress(const XnChar* strFocus, const XnPoint3D& ptPosition, XnFloat fProgress, void* UserCxt)
{
// printf("Focus progress: %s @(%f,%f,%f): %f\n", strFocus, ptPosition.X, ptPosition.Y, ptPosition.Z, fProgress);
}
// callback for session start
void XN_CALLBACK_TYPE SessionStarting(const XnPoint3D& ptPosition, void* UserCxt)
{
printf("Session start: (%f,%f,%f)\n", ptPosition.X, ptPosition.Y, ptPosition.Z);
g_SessionState = IN_SESSION;
EventClient.SendHELO("Kinect ON", ICON_PNG, NULL);
}
// Callback for session end
void XN_CALLBACK_TYPE SessionEnding(void* UserCxt)
{
printf("Session end\n");
g_SessionState = NOT_IN_SESSION;
EventClient.SendNOTIFICATION("Kinect OFF","", ICON_PNG, NULL);
}
void XN_CALLBACK_TYPE NoHands(void* UserCxt)
{
printf("Quick refocus\n");
g_SessionState = QUICK_REFOCUS;
EventClient.SendNOTIFICATION("Kinect Refocus","", ICON_PNG, NULL);
}
// callback for Steady detector
int timeout = 20;
static void XN_CALLBACK_TYPE steadyDetect(XnUInt32 fVelocity, XnFloat fAngle, void* cxt){
printf("Steady\n");
timeout--;
if(timeout<=0){
timeout = 20;
g_SessionState = NOT_IN_SESSION;
EventClient.SendNOTIFICATION("Kinect OFF","", ICON_PNG, NULL);
g_pSessionManager->EndSession();
}
}
// callback for Swipe detector
static void XN_CALLBACK_TYPE swipeUpDetect(XnFloat fVelocity, XnFloat fAngle, void* cxt){
printf("Up!\n");
timeout = 20;
EventClient.SendButton(KEYCODE_BUTTON_UP, "JS0:WiiRemote", BTN_QUEUE | BTN_NO_REPEAT);
}
static void XN_CALLBACK_TYPE swipeDownDetect(XnFloat fVelocity, XnFloat fAngle, void* cxt){
printf("Down!\n");
timeout = 20;
EventClient.SendButton(KEYCODE_BUTTON_DOWN, "JS0:WiiRemote", BTN_QUEUE | BTN_NO_REPEAT);
}
static void XN_CALLBACK_TYPE swipeLeftDetect(XnFloat fVelocity, XnFloat fAngle, void* cxt){
printf("Left!\n");
timeout = 20;
EventClient.SendButton(KEYCODE_BUTTON_LEFT, "JS0:WiiRemote", BTN_QUEUE | BTN_NO_REPEAT);
}
static void XN_CALLBACK_TYPE swipeRightDetect(XnFloat fVelocity, XnFloat fAngle, void* cxt){
printf("Right!\n");
timeout = 20;
EventClient.SendButton(KEYCODE_BUTTON_RIGHT, "JS0:WiiRemote", BTN_QUEUE | BTN_NO_REPEAT);
}
// callback for push detector
static void XN_CALLBACK_TYPE PushDetect(XnFloat fVelocity, XnFloat fAngle, void* cxt){
printf("Push!\n");
timeout = 20;
EventClient.SendButton(KEYCODE_BUTTON_ENTER, "JS0:WiiRemote", BTN_QUEUE | BTN_NO_REPEAT);
}
// this function is called each frame
void glutDisplay (void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Setup the OpenGL viewpoint
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
XnMapOutputMode mode;
g_DepthGenerator.GetMapOutputMode(mode);
#ifdef USE_GLUT
glOrtho(0, mode.nXRes, mode.nYRes, 0, -1.0, 1.0);
#else
glOrthof(0, mode.nXRes, mode.nYRes, 0, -1.0, 1.0);
#endif
glDisable(GL_TEXTURE_2D);
if (!g_bPause)
{
// Read next available data
g_Context.WaitAndUpdateAll();
// Update NITE tree
g_pSessionManager->Update(&g_Context);
PrintSessionState(g_SessionState);
}
#ifdef USE_GLUT
glutSwapBuffers();
#endif
}
#ifdef USE_GLUT
void glutIdle (void)
{
if (g_bQuit) {
CleanupExit();
}
// Display the frame
glutPostRedisplay();
}
void glutKeyboard (unsigned char key, int x, int y)
{
switch (key)
{
case 27:
// Exit
CleanupExit();
case'p':
// Toggle pause
g_bPause = !g_bPause;
break;
case 'd':
// Toggle drawing of the depth map
g_bDrawDepthMap = !g_bDrawDepthMap;
g_pDrawer->SetDepthMap(g_bDrawDepthMap);
break;
case 'f':
g_bPrintFrameID = !g_bPrintFrameID;
g_pDrawer->SetFrameID(g_bPrintFrameID);
break;
case 's':
// Toggle smoothing
if (g_fSmoothing == 0)
g_fSmoothing = 0.1;
else
g_fSmoothing = 0;
g_HandsGenerator.SetSmoothing(g_fSmoothing);
break;
case 'e':
// end current session
g_pSessionManager->EndSession();
break;
}
}
void glInit (int * pargc, char ** argv)
{
glutInit(pargc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(GL_WIN_SIZE_X, GL_WIN_SIZE_Y);
glutCreateWindow ("Xbmc Kinect Remote (nite+openni)");
//glutFullScreen();
glutSetCursor(GLUT_CURSOR_NONE);
glutKeyboardFunc(glutKeyboard);
glutDisplayFunc(glutDisplay);
glutIdleFunc(glutIdle);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
#endif
// xml to initialize OpenNI
#define SAMPLE_XML_PATH "../../Data/Sample-Tracking.xml"
int main(int argc, char ** argv)
{
XnStatus rc = XN_STATUS_OK;
// Initialize OpenNI
rc = g_Context.InitFromXmlFile(SAMPLE_XML_PATH);
CHECK_RC(rc, "InitFromXmlFile");
rc = g_Context.FindExistingNode(XN_NODE_TYPE_DEPTH, g_DepthGenerator);
CHECK_RC(rc, "Find depth generator");
rc = g_Context.FindExistingNode(XN_NODE_TYPE_HANDS, g_HandsGenerator);
CHECK_RC(rc, "Find hands generator");
// Create NITE objects
g_pSessionManager = new XnVSessionManager;
rc = g_pSessionManager->Initialize(&g_Context, "Wave", "RaiseHand");
CHECK_RC(rc, "SessionManager::Initialize");
g_pSessionManager->RegisterSession(NULL, SessionStarting, SessionEnding, FocusProgress);
g_pDrawer = new XnVPointDrawer(1, g_DepthGenerator);
g_pFlowRouter = new XnVFlowRouter;
pNiteSteadyDetector = new XnVSteadyDetector;
pNiteSwipeDetector = new XnVSwipeDetector;
pNitePushDetector = new XnVPushDetector;
g_pFlowRouter->SetActive(g_pDrawer);
g_pSessionManager->AddListener(g_pFlowRouter);
g_pSessionManager->AddListener(pNiteSwipeDetector);
g_pSessionManager->AddListener(pNiteSteadyDetector);
g_pSessionManager->AddListener(pNitePushDetector);
pNiteSwipeDetector->RegisterSwipeUp(NULL, &swipeUpDetect);
pNiteSwipeDetector->RegisterSwipeDown(NULL, &swipeDownDetect);
pNiteSwipeDetector->RegisterSwipeLeft(NULL, &swipeLeftDetect);
pNiteSwipeDetector->RegisterSwipeRight(NULL, &swipeRightDetect);
pNitePushDetector->RegisterPush(NULL, &PushDetect);
pNiteSteadyDetector->RegisterSteady(NULL, &steadyDetect);
g_pDrawer->RegisterNoPoints(NULL, NoHands);
g_pDrawer->SetDepthMap(g_bDrawDepthMap);
// Initialization done. Start generating
rc = g_Context.StartGeneratingAll();
CHECK_RC(rc, "StartGenerating");
// Mainloop
#ifdef USE_GLUT
glInit(&argc, argv);
glutMainLoop();
#else
#endif
}
Téléchargement de l'eventClient c++ xbmcclient.h :Pour ceux qui ne l'aurait pas : ici
Copier le dans ~/kinect/Nite-1.3.1.3/Samples/Xbmc Compilation , Execution :cd ~/kinect/Nite-1.3.1.3/
make
cd ~/kinect/Nite-1.3.1.3/Samples/Bin
./Sample-Xbmc &
xbmc &
Utilisation :Une fois xbmc et Sample-Xbmc lancer :Wave (coucou avec la main): active la reconnaissance de mouvement
Swipe up (claque avec la main vers le haut): aller vers le haut
Swipe Down (claque avec la main vers le bas): aller vers le bas
Swipe Left (claque avec la main vers la gauche): aller vers la gauche
Swipe right (claque avec la main vers la droite): aller vers la droite
Push (main d'arrière vers l'avant (Click)): valider
Au bout d'un certain temps ou si la main est perdu la reconnaissance ce désactive et donc pour la relancer il faut faire un mouvement "Wave"NB:Pour le mouvement "Swipe" au lieu de faire une "claque" on peut aussi laisser la main fasse a la kinect et la déplacer de gauche a droite et de haut en basEnjoy
