Fawkes API Fawkes Development Version
usertracker_thread.cpp
1
2/***************************************************************************
3 * usertracker_thread.cpp - OpenNI user tracker thread
4 *
5 * Created: Sun Feb 27 17:53:38 2011
6 * Copyright 2006-2011 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * Read the full text in the LICENSE.GPL file in the doc directory.
21 */
22
23#include "usertracker_thread.h"
24
25#include "utils/setup.h"
26
27#include <core/threading/mutex_locker.h>
28#include <fvutils/ipc/shm_image.h>
29#include <interfaces/HumanSkeletonInterface.h>
30#include <interfaces/HumanSkeletonProjectionInterface.h>
31
32#include <memory>
33
34using namespace fawkes;
35using namespace firevision;
36
37/** @class OpenNiUserTrackerThread "usertracker_thread.h"
38 * OpenNI User Tracker Thread.
39 * This thread requests a user tracker node from OpenNI and publishes the
40 * retrieved information via the blackboard.
41 *
42 * @author Tim Niemueller
43 */
44
45/** Constructor. */
47: Thread("OpenNiUserTrackerThread", Thread::OPMODE_WAITFORWAKEUP),
48 BlockedTimingAspect(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PROCESS)
49{
50}
51
52/** Destructor. */
54{
55}
56
57static void XN_CALLBACK_TYPE
58cb_new_user(xn::UserGenerator &generator, XnUserID id, void *cookie)
59{
60 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
61 t->new_user(id);
62}
63
64static void XN_CALLBACK_TYPE
65cb_lost_user(xn::UserGenerator &generator, XnUserID id, void *cookie)
66{
67 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
68 t->lost_user(id);
69}
70
71static void XN_CALLBACK_TYPE
72cb_pose_start(xn::PoseDetectionCapability &capability,
73 const XnChar * pose_name,
74 XnUserID id,
75 void * cookie)
76{
77 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
78 t->pose_start(id, pose_name);
79}
80
81static void XN_CALLBACK_TYPE
82cb_pose_end(xn::PoseDetectionCapability &capability,
83 const XnChar * pose_name,
84 XnUserID id,
85 void * cookie)
86{
87 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
88 t->pose_end(id, pose_name);
89}
90
91static void XN_CALLBACK_TYPE
92cb_calibration_start(xn::SkeletonCapability &capability, XnUserID id, void *cookie)
93{
94 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
95 t->calibration_start(id);
96}
97
98#if XN_VERSION_GE(1, 3, 2, 0)
99static void XN_CALLBACK_TYPE
100cb_calibration_complete(xn::SkeletonCapability &capability,
101 XnUserID id,
102 XnCalibrationStatus status,
103 void * cookie)
104{
105 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
106 t->calibration_end(id, status == XN_CALIBRATION_STATUS_OK);
107}
108#else
109static void XN_CALLBACK_TYPE
110cb_calibration_end(xn::SkeletonCapability &capability, XnUserID id, XnBool success, void *cookie)
111{
112 OpenNiUserTrackerThread *t = static_cast<OpenNiUserTrackerThread *>(cookie);
113 t->calibration_end(id, success);
114}
115#endif
116
117void
119{
121
122 user_gen_ = new xn::UserGenerator();
123 std::auto_ptr<xn::UserGenerator> usergen_autoptr(user_gen_);
124
125 depth_gen_ = new xn::DepthGenerator();
126 std::auto_ptr<xn::DepthGenerator> depthgen_autoptr(depth_gen_);
127
128 XnStatus st;
129
130 fawkes::openni::find_or_create_node(openni, XN_NODE_TYPE_DEPTH, depth_gen_);
131 fawkes::openni::setup_map_generator(*depth_gen_, config);
132 fawkes::openni::find_or_create_node(openni, XN_NODE_TYPE_USER, user_gen_);
133
134 if (!user_gen_->IsCapabilitySupported(XN_CAPABILITY_SKELETON)) {
135 throw Exception("User generator does not support skeleton capability");
136 }
137
138 scene_md_ = new xn::SceneMetaData();
139 std::auto_ptr<xn::SceneMetaData> scenemd_autoptr(scene_md_);
140 if ((st = user_gen_->GetUserPixels(0, *scene_md_)) != XN_STATUS_OK) {
141 throw Exception("Failed to get scene meta data (%s)", xnGetStatusString(st));
142 }
143
144 st = user_gen_->RegisterUserCallbacks(cb_new_user, cb_lost_user, this, user_cb_handle_);
145 if (st != XN_STATUS_OK) {
146 throw Exception("Failed to register user callbacks (%s)", xnGetStatusString(st));
147 }
148
149 skelcap_ = new xn::SkeletonCapability(user_gen_->GetSkeletonCap());
150
151#if XN_VERSION_GE(1, 3, 2, 0)
152 st = skelcap_->RegisterToCalibrationStart(cb_calibration_start, this, calib_start_cb_handle_);
153 if (st != XN_STATUS_OK) {
154 throw Exception("Failed to register calibration start event (%s)", xnGetStatusString(st));
155 }
156 st = skelcap_->RegisterToCalibrationComplete(cb_calibration_complete,
157 this,
158 calib_complete_cb_handle_);
159#else
160 st = skelcap_->RegisterCalibrationCallbacks(cb_calibration_start,
161 cb_calibration_end,
162 this,
163 calib_cb_handle_);
164#endif
165
166 if (st != XN_STATUS_OK) {
167 throw Exception("Failed to register calibration callback (%s)", xnGetStatusString(st));
168 }
169
170 skel_need_calib_pose_ = skelcap_->NeedPoseForCalibration();
171
172 if (skel_need_calib_pose_) {
173 if (!user_gen_->IsCapabilitySupported(XN_CAPABILITY_POSE_DETECTION)) {
174 throw Exception("Calibration requires pose, but not supported by node");
175 }
176 skelcap_->GetCalibrationPose(calib_pose_name_);
177
178 xn::PoseDetectionCapability posecap = user_gen_->GetPoseDetectionCap();
179
180#if XN_VERSION_GE(1, 3, 2, 0)
181 st = posecap.RegisterToPoseDetected(cb_pose_start, this, pose_start_cb_handle_);
182 if (st != XN_STATUS_OK) {
183 throw Exception("Failed to register pose detect event (%s)", xnGetStatusString(st));
184 }
185 st = posecap.RegisterToOutOfPose(cb_pose_end, this, pose_end_cb_handle_);
186#else
187 st = posecap.RegisterToPoseCallbacks(cb_pose_start, cb_pose_end, this, pose_cb_handle_);
188#endif
189 if (st != XN_STATUS_OK) {
190 throw Exception("Failed to register pose callbacks (%s)", xnGetStatusString(st));
191 }
192 }
193
194 skelcap_->SetSkeletonProfile(XN_SKEL_PROFILE_ALL);
195
196 depth_gen_->StartGenerating();
197 user_gen_->StartGenerating();
198
199 label_buf_ =
200 new SharedMemoryImageBuffer("openni-labels", RAW16, scene_md_->XRes(), scene_md_->YRes());
201 label_bufsize_ = colorspace_buffer_size(RAW16, scene_md_->XRes(), scene_md_->YRes());
202
203 usergen_autoptr.release();
204 depthgen_autoptr.release();
205 scenemd_autoptr.release();
206}
207
208void
210{
211 // we do not stop generating, we don't know if there is no other plugin
212 // using the node.
213 delete user_gen_;
214 delete scene_md_;
215 delete skelcap_;
216 delete label_buf_;
217
218 UserMap::iterator i;
219 for (i = users_.begin(); i != users_.end(); ++i) {
220 blackboard->close(i->second.skel_if);
221 blackboard->close(i->second.proj_if);
222 }
223}
224
225void
227{
228 // we do not lock here, we are only operating on our user generator copy
229 // and the update happens in a different main loop hook
230
231 if (!user_gen_->IsDataNew())
232 return;
233
234 UserMap::iterator i;
235 for (i = users_.begin(); i != users_.end(); ++i) {
236 if (!i->second.valid)
237 continue;
238
239 bool needs_write = false;
240
241 HumanSkeletonInterface::State new_state = i->second.skel_if->state();
242 if (skelcap_->IsTracking(i->first)) {
243 new_state = HumanSkeletonInterface::STATE_TRACKING;
244 } else if (skelcap_->IsCalibrating(i->first)) {
245 new_state = HumanSkeletonInterface::STATE_CALIBRATING;
246 } else {
247 new_state = HumanSkeletonInterface::STATE_DETECTING_POSE;
248 }
249
250 if (new_state != i->second.skel_if->state()) {
251 i->second.skel_if->set_state(new_state);
252 needs_write = true;
253 }
254
255 if (new_state == HumanSkeletonInterface::STATE_TRACKING) {
256 // update skeleton information
257 try {
258 update_user(i->first, i->second);
259 update_com(i->first, i->second);
260 needs_write = true;
261 } catch (Exception &e) {
263 "Failed to update skeleton data for %u, "
264 "exception follows",
265 i->first);
266 logger->log_warn(name(), e);
267 }
268 } else if (new_state == HumanSkeletonInterface::STATE_DETECTING_POSE) {
269 update_com(i->first, i->second);
270 needs_write = true;
271 } else if (new_state == HumanSkeletonInterface::STATE_CALIBRATING) {
272 update_com(i->first, i->second);
273 needs_write = true;
274 }
275
276 if (needs_write) {
277 i->second.skel_if->write();
278 i->second.proj_if->write();
279 }
280 }
281
282 if (label_buf_->num_attached() > 1) {
283 memcpy(label_buf_->buffer(), scene_md_->Data(), label_bufsize_);
284 }
285}
286
287// Very noisy when added to st != XN_STATUS_OK case
288//logger->log_warn(name(), "Failed to get joint transformation for "
289// "%s joint (%s)",
290// joint_name, xnGetStatusString(st));
291
292// change from mm to m
293// translating to Fawkes coordinates, empirically verified
294// permute ori columns to match our coordinate system, empirically verified
295#define SET_JTF(id, joint, joint_name, bbfield) \
296 st = skelcap_->GetSkeletonJoint(id, joint, jtf); \
297 if (st != XN_STATUS_OK) { \
298 ori[0] = ori[1] = ori[2] = ori[3] = ori[4] = ori[5] = 0.; \
299 ori[6] = ori[7] = ori[8] = ori_confidence = pos_confidence = 0.; \
300 proj[0] = proj[1] = 0; \
301 } else { \
302 pos[0] = jtf.position.position.Z * 0.001; \
303 pos[1] = -jtf.position.position.X * 0.001; \
304 pos[2] = jtf.position.position.Y * 0.001; \
305 pos_confidence = jtf.position.fConfidence; \
306 \
307 ori[0] = jtf.orientation.orientation.elements[2]; \
308 ori[1] = -jtf.orientation.orientation.elements[0]; \
309 ori[2] = jtf.orientation.orientation.elements[1]; \
310 ori[3] = jtf.orientation.orientation.elements[5]; \
311 ori[4] = -jtf.orientation.orientation.elements[3]; \
312 ori[5] = jtf.orientation.orientation.elements[4]; \
313 ori[6] = jtf.orientation.orientation.elements[8]; \
314 ori[7] = -jtf.orientation.orientation.elements[6]; \
315 ori[8] = jtf.orientation.orientation.elements[7]; \
316 ori_confidence = jtf.orientation.fConfidence; \
317 \
318 XnPoint3D pt; \
319 pt = jtf.position.position; \
320 depth_gen_->ConvertRealWorldToProjective(1, &pt, &pt); \
321 proj[0] = pt.X; \
322 proj[1] = pt.Y; \
323 } \
324 user.skel_if->set_pos_##bbfield(pos); \
325 user.skel_if->set_pos_##bbfield##_confidence(pos_confidence); \
326 user.skel_if->set_ori_##bbfield(ori); \
327 user.skel_if->set_ori_##bbfield##_confidence(ori_confidence); \
328 \
329 user.proj_if->set_proj_##bbfield(proj);
330
331void
332OpenNiUserTrackerThread::update_user(XnUserID id, UserInfo &user)
333{
334 XnSkeletonJointTransformation jtf;
335 XnStatus st;
336
337 float pos[3], ori[9], proj[2], pos_confidence, ori_confidence;
338
339 SET_JTF(id, XN_SKEL_HEAD, "head", head);
340 SET_JTF(id, XN_SKEL_NECK, "neck", neck);
341 SET_JTF(id, XN_SKEL_TORSO, "torso", torso);
342 SET_JTF(id, XN_SKEL_WAIST, "waist", waist);
343 SET_JTF(id, XN_SKEL_LEFT_COLLAR, "left collar", left_collar);
344 SET_JTF(id, XN_SKEL_LEFT_SHOULDER, "left shoulder", left_shoulder);
345 SET_JTF(id, XN_SKEL_LEFT_ELBOW, "left elbow", left_elbow);
346 SET_JTF(id, XN_SKEL_LEFT_WRIST, "left wrist", left_wrist);
347 SET_JTF(id, XN_SKEL_LEFT_HAND, "left hand", left_hand);
348 SET_JTF(id, XN_SKEL_LEFT_FINGERTIP, "left finger tip", left_fingertip);
349 SET_JTF(id, XN_SKEL_RIGHT_COLLAR, "right collar", right_collar);
350 SET_JTF(id, XN_SKEL_RIGHT_SHOULDER, "right shoulder", right_shoulder);
351 SET_JTF(id, XN_SKEL_RIGHT_ELBOW, "right elbow", right_elbow);
352 SET_JTF(id, XN_SKEL_RIGHT_WRIST, "right wrist", right_wrist);
353 SET_JTF(id, XN_SKEL_RIGHT_HAND, "right hand", right_hand);
354 SET_JTF(id, XN_SKEL_RIGHT_FINGERTIP, "right finger tip", right_fingertip);
355 SET_JTF(id, XN_SKEL_LEFT_HIP, "left hip", left_hip);
356 SET_JTF(id, XN_SKEL_LEFT_KNEE, "left knee", left_knee);
357 SET_JTF(id, XN_SKEL_LEFT_ANKLE, "left ankle", left_ankle);
358 SET_JTF(id, XN_SKEL_LEFT_FOOT, "left foot", left_foot);
359 SET_JTF(id, XN_SKEL_RIGHT_HIP, "right hip", right_hip);
360 SET_JTF(id, XN_SKEL_RIGHT_KNEE, "right knee", right_knee);
361 SET_JTF(id, XN_SKEL_RIGHT_ANKLE, "right ankle", right_ankle);
362 SET_JTF(id, XN_SKEL_RIGHT_FOOT, "right foot", right_foot);
363}
364
365void
366OpenNiUserTrackerThread::update_com(XnUserID id, UserInfo &user)
367{
368 XnPoint3D compt, compt_proj;
369 XnStatus st;
370 float com[3], com_proj[2];
371 com[0] = com[1] = com[2] = com_proj[0] = com_proj[1] = 0.;
372 if ((st = user_gen_->GetCoM(id, compt)) == XN_STATUS_OK) {
373 // translating to Fawkes coordinates, empirically verified
374 com[0] = compt.Z * 0.001;
375 com[1] = -compt.X * 0.001;
376 com[2] = compt.Y * 0.001;
377
378 depth_gen_->ConvertRealWorldToProjective(1, &compt, &compt_proj);
379 com_proj[0] = compt_proj.X;
380 com_proj[1] = compt_proj.Y;
381 } else {
382 logger->log_warn(name(), "GetCoM failed: %s", xnGetStatusString(st));
383 }
384
385 user.skel_if->set_com(com);
386 user.proj_if->set_proj_com(com_proj);
387
388 int current_vishist = user.skel_if->visibility_history();
389 if ((com[0] == 0.) && (com[1] == 0.) && (com[2] == 0.)) {
390 if (current_vishist < 0) {
391 user.skel_if->set_visibility_history(--current_vishist);
392 } else {
393 user.skel_if->set_visibility_history(-1);
394 }
395 } else {
396 if (current_vishist > 0) {
397 user.skel_if->set_visibility_history(++current_vishist);
398 } else {
399 user.skel_if->set_visibility_history(1);
400 }
401 }
402}
403
404/** Notify of new user.
405 * This is called by the OpenNI callback when a new user has been detected.
406 * @param id new user's ID
407 */
408void
410{
411 if (users_.find(id) != users_.end()) {
412 logger->log_error(name(), "New user ID %u, interface already exists", id);
413 } else {
414 char *ifid;
415 if (asprintf(&ifid, "OpenNI Human %u", id) == -1) {
417 "New user ID %u, but cannot generate "
418 "interface ID",
419 id);
420 return;
421 }
422 try {
423 logger->log_debug(name(), "Opening interface 'HumanSkeletonInterface::%s'", ifid);
424 users_[id].skel_if = blackboard->open_for_writing<HumanSkeletonInterface>(ifid);
425 users_[id].skel_if->set_user_id(id);
426 users_[id].skel_if->write();
427 } catch (Exception &e) {
428 logger->log_warn(name(), "Failed to open interface, exception follows");
429 logger->log_warn(name(), e);
430 }
431
432 try {
433 logger->log_debug(name(), "Opening interface 'HumanSkeletonProjectionInterface::%s'", ifid);
435 XnFieldOfView fov;
436 XnStatus st;
437 if ((st = depth_gen_->GetFieldOfView(fov)) != XN_STATUS_OK) {
439 "Failed to get field of view, ignoring. (%s)",
440 xnGetStatusString(st));
441 } else {
442 users_[id].proj_if->set_horizontal_fov(fov.fHFOV);
443 users_[id].proj_if->set_vertical_fov(fov.fVFOV);
444 }
445
446 xn::DepthMetaData dmd;
447 depth_gen_->GetMetaData(dmd);
448 users_[id].proj_if->set_res_x(dmd.XRes());
449 users_[id].proj_if->set_res_y(dmd.YRes());
450 users_[id].proj_if->set_max_depth(depth_gen_->GetDeviceMaxDepth());
451 users_[id].proj_if->write();
452 } catch (Exception &e) {
453 blackboard->close(users_[id].proj_if);
454 users_.erase(id);
455 logger->log_warn(name(), "Failed to open interface, exception follows");
456 logger->log_warn(name(), e);
457 }
458
459 free(ifid);
460 }
461
462 users_[id].valid = true;
463
464 if (skel_need_calib_pose_) {
465 user_gen_->GetPoseDetectionCap().StartPoseDetection(calib_pose_name_, id);
466 } else {
467 user_gen_->GetSkeletonCap().RequestCalibration(id, TRUE);
468 }
469}
470
471/** Notify of lost user.
472 * This is called by the OpenNI callback when a user has been lost,
473 * i.e. it has not been visible for some time.
474 * @param id lost user's ID
475 */
476void
478{
479 if (users_.find(id) == users_.end()) {
480 logger->log_error(name(), "Lost user ID %u, but interface does not exist", id);
481 return;
482 }
483
485 "Lost user ID %u, setting interface '%s' to invalid",
486 id,
487 users_[id].skel_if->uid());
488 // write invalid, a reader might still be open
489 users_[id].skel_if->set_state(HumanSkeletonInterface::STATE_INVALID);
490 users_[id].skel_if->write();
491 users_[id].valid = false;
492 //blackboard->close(users_[id].skel_if);
493 //blackboard->close(users_[id].proj_if);
494 //users_.erase(id);
495}
496
497/** Notify of detected pose.
498 * This is called if a pose has been detected.
499 * @param id ID of user who is in the pose
500 * @param pose_name name of the detected pose
501 */
502void
503OpenNiUserTrackerThread::pose_start(XnUserID id, const char *pose_name)
504{
505 if (users_.find(id) == users_.end()) {
507 "Pose start for user ID %u, "
508 "but interface does not exist",
509 id);
510 return;
511 }
512
513 logger->log_info(name(), "Pose %s detected for user %u", pose_name, id);
514
515 users_[id].skel_if->set_pose(pose_name);
516 user_gen_->GetPoseDetectionCap().StopPoseDetection(id);
517 user_gen_->GetSkeletonCap().RequestCalibration(id, TRUE);
518}
519
520/** Notify of pose detection end.
521 * This is called if a pose is no longer detected. The NITE middleware seems
522 * not to call this.
523 * @param id ID of user who is in the pose
524 * @param pose_name name of the no longer detected pose
525 */
526void
527OpenNiUserTrackerThread::pose_end(XnUserID id, const char *pose_name)
528{
529 if (users_.find(id) == users_.end()) {
531 "Pose end for user ID %u, "
532 "but interface does not exist",
533 id);
534 return;
535 }
536
537 users_[id].skel_if->set_pose("");
538}
539
540/** Notify of calibration start.
541 * This is called when tracking for a user has been started.
542 * @param id ID of user who is being calibrated.
543 */
544void
546{
547 if (users_.find(id) == users_.end()) {
549 "Pose end for user ID %u, "
550 "but interface does not exist",
551 id);
552 return;
553 }
554
555 logger->log_info(name(), "Calibration started for user %u", id);
556}
557
558/** Notify of calibration end.
559 * This is called when tracking for a user has finished.
560 * @param id ID of user who was being calibrated
561 * @param success true if the calibration was successful, false otherwise
562 */
563void
565{
566 if (users_.find(id) == users_.end()) {
568 "Pose end for user ID %u, "
569 "but interface does not exist",
570 id);
571 return;
572 }
573
574 users_[id].skel_if->set_pose("");
575
576 if (success) {
578 "Calibration successful for user %u, "
579 "starting tracking",
580 id);
581 user_gen_->GetSkeletonCap().StartTracking(id);
582 } else {
583 logger->log_info(name(), "Calibration failed for user %u, restarting", id);
584 if (skel_need_calib_pose_) {
585 user_gen_->GetPoseDetectionCap().StartPoseDetection(calib_pose_name_, id);
586 } else {
587 user_gen_->GetSkeletonCap().RequestCalibration(id, TRUE);
588 }
589 }
590}
OpenNI User Tracker Thread.
void calibration_end(XnUserID id, bool success)
Notify of calibration end.
virtual void init()
Initialize the thread.
void new_user(XnUserID id)
Notify of new user.
virtual ~OpenNiUserTrackerThread()
Destructor.
virtual void loop()
Code to execute in the thread.
void pose_end(XnUserID id, const char *pose_name)
Notify of pose detection end.
OpenNiUserTrackerThread()
Constructor.
void pose_start(XnUserID id, const char *pose_name)
Notify of detected pose.
void calibration_start(XnUserID id)
Notify of calibration start.
void lost_user(XnUserID id)
Notify of lost user.
virtual void finalize()
Finalize the thread.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual void close(Interface *interface)=0
Close interface.
Thread aspect to use blocked timing.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
Base class for exceptions in Fawkes.
Definition: exception.h:36
HumanSkeletonInterface Fawkes BlackBoard Interface.
State
Current tracking state for the skeleton.
HumanSkeletonProjectionInterface Fawkes BlackBoard Interface.
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
Mutex locking helper.
Definition: mutex_locker.h:34
LockPtr< xn::Context > openni
Central OpenNI context.
Definition: openni.h:47
unsigned int num_attached() const
Get number of attached processes.
Definition: shm.cpp:763
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
Shared memory image buffer.
Definition: shm_image.h:184
unsigned char * buffer() const
Get image buffer.
Definition: shm_image.cpp:228
Fawkes library namespace.