In an earlier post I explained how to get PrimeSense's NITE up and running and how to use the samples they provided. Now some people might be thinking "cool, but how can I use this?" I thought using NITE hand tracking to control the cursor would be a good and simple demonstration.
The linux kernel provides a means to create userspace input drivers using a feature called uinput. If you compile your kernel with uinput enabled as a module you can then simply:
modprobe uinputto load the uinput module. Once the module is loaded you can use the piece of code I've embedded below to convert the coordinates output by the NITE code into actual mouse/cursor movement. In short:
(1) download the code below
(2) save it as ~/kinect/NITE/Nite-1.3.0.17/Samples/SingleControl/main.cpp (you might want to back up the original)
(3)
cd ~/kinect/NITE/Nite-1.3.0.17 && make(4)Note: do the following as root or using sudo
~/kinect/NITE/Nite-1.3.0.17/Samples/Bin/Sample-SingleControl (5)Perform a focus gesture to start the hand tracking (check out my video above to see how to do that)
At this point you should be able to do what I do in the video above. You can also extend the code to generate mouse clicks, keystrokes, etc. Have fun.
The formatting of the code below got a bit mangled, so here's a direct download link:
http://dl.dropbox.com/u/11217419/main.cpp
If I wind up spending more time on the code then I might bother to put it up on github or something, but for now sorry about the dropbox only.
/****************************************************************************
* *
* Nite 1.3 - Single Control Sample *
* *
* Author: Oz Magal *
* *
****************************************************************************/
/****************************************************************************
* *
* Nite 1.3 *
* Copyright (C) 2006 PrimeSense Ltd. All Rights Reserved. *
* *
* This file has been provided pursuant to a License Agreement containing *
* restrictions on its use. This data contains valuable trade secrets *
* and proprietary information of PrimeSense Ltd. and is protected by law. *
* *
****************************************************************************/
//-----------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------
// General headers
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// OpenNI headers
#include
// NITE headers
#include
#include "XnVMultiProcessFlowClient.h"
#include
#include "kbhit.h"
#include "signal_catch.h"
// xml to initialize OpenNI
#define SAMPLE_XML_FILE "../../Data/Sample-Tracking.xml"
static int uinp_fd = -1;
struct uinput_user_dev uinp; // uInput device structure
struct input_event event; // Input device structure
XnBool g_bQuit = false;
int setup_uinput_device()
{
int i=0;
uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
if (!uinp_fd)
{
printf("Unable to open /dev/uinput\n");
return -1;
}
memset(&uinp,0,sizeof(uinp)); // Intialize the uInput device to NULL
strncpy(uinp.name, "Kinect Mouse", UINPUT_MAX_NAME_SIZE);
uinp.id.version = 4;
uinp.id.bustype = BUS_USB;
ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);
ioctl(uinp_fd, UI_SET_RELBIT, REL_X);
ioctl(uinp_fd, UI_SET_RELBIT, REL_Y);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_TOUCH);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MIDDLE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_FORWARD);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_BACK);
write(uinp_fd, &uinp, sizeof(uinp));
if (ioctl(uinp_fd, UI_DEV_CREATE))
{
printf("Unable to create UINPUT device.");
return -1;
}
return 1;
}
void move_cursor(int x, int y )
{
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
event.code = REL_X;
event.value = x;
write(uinp_fd, &event, sizeof(event));
event.type = EV_REL;
event.code = REL_Y;
event.value = y;
write(uinp_fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}
//-----------------------------------------------------------------------------
// Callbacks
//-----------------------------------------------------------------------------
// Callback for when the focus is in progress
void XN_CALLBACK_TYPE SessionProgress(const XnChar* strFocus, const XnPoint3D& ptFocusPoint, XnFloat fProgress, void* UserCxt)
{
printf("Session progress (%6.2f,%6.2f,%6.2f) - %6.2f [%s]\n", ptFocusPoint.X, ptFocusPoint.Y, ptFocusPoint.Z, fProgress, strFocus);
}
// callback for session start
void XN_CALLBACK_TYPE SessionStart(const XnPoint3D& ptFocusPoint, void* UserCxt)
{
printf("Session started. Please wave (%6.2f,%6.2f,%6.2f)...\n", ptFocusPoint.X, ptFocusPoint.Y, ptFocusPoint.Z);
}
// Callback for session end
void XN_CALLBACK_TYPE SessionEnd(void* UserCxt)
{
printf("Session ended. Please perform focus gesture to start session\n");
}
// Callback for wave detection
void XN_CALLBACK_TYPE OnWaveCB(void* cxt)
{
printf("Wave!\n");
}
// callback for a new position of any hand
void XN_CALLBACK_TYPE OnPointUpdate(const XnVHandPointContext* pContext, void* cxt)
{
//printf("%d: (%f,%f,%f) [%f]\n", pContext->nID, pContext->ptPosition.X, pContext->ptPosition.Y, pContext->ptPosition.Z, pContext->f
Time);
//printf("%f,%f,%f\n", pContext->ptPosition.X, pContext->ptPosition.Y, pContext->ptPosition.Z);
move_cursor((int)(pContext->ptPosition.X/4),(int) -(pContext->ptPosition.Y/4));
}
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
// this sample can run either as a regular sample, or as a client for multi-process (remote mode)
int main(int argc, char** argv)
{
xn::Context context;
XnVSessionGenerator* pSessionGenerator;
XnBool bRemoting = FALSE;
if (argc > 1)
{
// remote mode
context.Init();
printf("Running in 'Remoting' mode (Section name: %s)\n", argv[1]);
bRemoting = TRUE;
// Create multi-process client
pSessionGenerator = new XnVMultiProcessFlowClient(argv[1]);
XnStatus rc = ((XnVMultiProcessFlowClient*)pSessionGenerator)->Initialize();
if (rc != XN_STATUS_OK)
{
printf("Initialize failed: %s\n", xnGetStatusString(rc));
delete pSessionGenerator;
return 1;
}
}
else
{
// Local mode
// Create context
XnStatus rc = context.InitFromXmlFile(SAMPLE_XML_FILE);
if (rc != XN_STATUS_OK)
{
printf("Couldn't initialize: %s\n", xnGetStatusString(rc));
return 1;
}
// Create the Session Manager
pSessionGenerator = new XnVSessionManager();
rc = ((XnVSessionManager*)pSessionGenerator)->Initialize(&context, "Click", "RaiseHand");
if (rc != XN_STATUS_OK)
{
printf("Session Manager couldn't initialize: %s\n", xnGetStatusString(rc));
delete pSessionGenerator;
return 1;
}
// Initialization done. Start generating
context.StartGeneratingAll();
}
// Register session callbacks
pSessionGenerator->RegisterSession(NULL, &SessionStart, &SessionEnd, &SessionProgress);
// Start catching signals for quit indications
CatchSignals(&g_bQuit);
// init & register wave control
XnVWaveDetector wc;
wc.RegisterWave(NULL, OnWaveCB);
wc.RegisterPointUpdate(NULL, OnPointUpdate);
pSessionGenerator->AddListener(&wc);
printf("Please perform focus gesture to start session\n");
printf("Hit any key to exit\n");
setup_uinput_device();
// Main loop
while ((!_kbhit()) && (!g_bQuit))
{
if (bRemoting)
{
((XnVMultiProcessFlowClient*)pSessionGenerator)->ReadState();
}
else
{
context.WaitAndUpdateAll();
((XnVSessionManager*)pSessionGenerator)->Update(&context);
}
}
delete pSessionGenerator;
context.Shutdown();
ioctl(uinp_fd, UI_DEV_DESTROY);
close(uinp_fd);
return 0;
}








