When dealing with nite::HandTracker
and nite::HandData
, there is no way to find out which hand belongs to which user. Actually, there is no direct way of finding this out in NiTE at all. In this recipe, we will show you how it's possible to use nite::UserTracker
and nite::UserMap
along with nite::HandTracker
and nite::HandData
to determine the user ID of each hand.
Create a project in Visual Studio and prepare it for working with OpenNI and NiTE using the Create a project in Visual Studio 2010 recipe in Chapter 2, OpenNI and C++.
ReadLastCharOfLine()
and HandleStatus()
from the first recipe of this chapter to the top of your source code (just below the #include
lines).int _tmain(int argc, _TCHAR* argv[]) {
nite::Status status = nite::STATUS_OK; status = nite::NiTE::initialize(); if (!HandleStatus(status)) return 1; printf("Creating user tracker ... "); nite::UserTracker uTracker; status = uTracker.create(); if (!HandleStatus(status)) return 1; printf("Creating hand tracker ... "); nite::HandTracker hTracker; status = hTracker.create(); if (!HandleStatus(status)) return 1; printf("Searching for Hand Raise gestures ... "); hTracker.startGestureDetection(nite::GESTURE_HAND_RAISE); printf("Reading data from hand/user trackers ... "); while(!_kbhit()) { nite::HandTrackerFrameRef handFrame; status = hTracker.readFrame(&handFrame); if (!HandleStatus(status) || !handFrame.isValid()) return 1; nite::UserTrackerFrameRef userFrame; status = uTracker.readFrame(&userFrame); if (!HandleStatus(status) || !userFrame.isValid()) return 1; nite::UserMap usersMap = userFrame.getUserMap(); const nite::Array<nite::GestureData>& gestures = handFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i){ if (gestures[i].isComplete()){ nite::HandId handId; status = hTracker.startHandTracking( gestures[i].getCurrentPosition(), &handId); } } const nite::Array<nite::HandData>& hands = handFrame.getHands(); for (int i = 0; i < hands.getSize(); ++i){ if (hands[i].isTracking()){ float posX, posY; status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &posX, &posY); if (status == nite::STATUS_OK) { nite::UserId* userId = (nite::UserId*)( (char*)usersMap.getPixels() + ((int)posY * usersMap.getStride()) ) + (int)posX; printf("User %d: Hand #%d @%g,%g,%g ", *userId, hands[i].getId(), hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z); } } } } hTracker.destroy(); uTracker.destroy(); nite::NiTE::shutdown(); return 0;
Again in the first step, we have the two ReadLastCharOfLine()
and
HandleStatus()
functions that are going to be used for handling the returned nite::Status
value and to read keys from the user.
Our main code starts at the second step, which is about the main function (starting point) of our project. In this function, we first initialize NiTE, as always, and then define and create a nite::HandTracker
object. But as we need to recognize each user in the scene as well as each hand, we need to define and create a nite::UserTracker
object too.
nite::UserTracker uTracker; status = uTracker.create(); . . . nite::HandTracker hTracker; status = hTracker.create();
Then, as we need a gesture to locate hands, we need to request a search for one. In this recipe, we prefer using the Hand Raise
gesture:
hTracker.startGestureDetection(nite::GESTURE_HAND_RAISE);
To read data from NiTE, we need to put our application almost in an infinite loop. So we used a while
loop to wait until the user presses a key to read the data.
In our while
loop, we must read a frame of data from both nite::HandTracker
and nite::UserTracker
. This can be done using the nite::HandTracker::readFrame()
and nite::UserTracker::readFrame()
methods:
nite::HandTrackerFrameRef handFrame; status = hTracker.readFrame(&handFrame); if (!HandleStatus(status) || !handFrame.isValid()) return 1; nite::UserTrackerFrameRef userFrame; status = uTracker.readFrame(&userFrame); if (!HandleStatus(status) || !userFrame.isValid()) return 1;
As you can clearly see, we checked whether the reading process has successfully been completed and also checked whether the returned frame is valid before doing anything.
We will use the
nite::UserMap
object associated with the returned nite::UserTrackerFrameRef
object later, so keeping it in another variable will help in enhancing the readability of our code.
nite::UserMap usersMap = userFrame.getUserMap();
Before you start looping through the hands being tracked and find the related user IDs of each, you need to take care of each recognized gesture by nite::HandTracker
and request hand tracking in the location of the gesture. To do this, we need to use the nite::HandTrackerFrameRef::getGestures()
method for retrieving an array of recognized gestures and the nite::HandTracker::startHandTracking()
method to start the tracking of a hand:
const nite::Array<nite::GestureData>& gestures = handFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i){ if (gestures[i].isComplete()){ nite::HandId handId; status = hTracker.startHandTracking( gestures[i].getCurrentPosition(), &handId); } }
Now is the time to loop through the list of hands being tracked and see which ones are retrievable using the nite::HandTrackerFrameRef::getHands()
method:
const nite::Array<nite::HandData>& hands = handFrame.getHands(); for (int i = 0; i < hands.getSize(); ++i){ if (hands[i].isTracking()){
nite::UserMap
is like a depth frame, only with different values stored in each frame instead of the depth value. So if we want to know the value of a pixel, we first need to convert the position of the tracked hand to the position of the pixel relative to the depth frame. As we did before, this can be done using the nite::HandTracker::convertHandCoordinatesToDepth()
method:
float posX, posY; status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition
If the process of converting ends without a problem, we will have the correct coordinates of the hand either relative to the depth frame or relative to nite::UserMap
in the posX
and posY
variables. Now we can use these variables to read the value of this pixel from nite::UserMap
:
if (status == nite::STATUS_OK) { nite::UserId* userId = (nite::UserId*)( (char*)usersMap.getPixels() + ((int)posY * usersMap.getStride()) ) + (int)posX;
After these lines of code, the
userId
variable contains the address of the related value that indicates the user ID of a hand. The value of this address is either 0
, which means no user or an unknown user, or a value other than 0
, which is the user ID of the user. As you can see in the preceding lines of code, reading from a nite::UserMap
object is no different than reading from an openni::VideoFrameRef
variable.
Now after having this info, we can print it to the console:
printf("User %d: Hand #%d @%g,%g,%g ", *userId, hands[i].getId(), hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z);
The output of our application is as follows:
18.223.170.63