http://52coding.com/kinect-face-tracking
更新:
[2013-4-10]编译程序时遇到xcopy问题(error MSB3073),猪猪猪小航给出了他的解决办法(第4节)。
[2013-3-25]程序源代码由kexin_0311进行了改进,避免了人脸走出检测后窗体假死情况(第6节)。
自上篇Kinect SDK 1.5 Face Tracking文章出现后(2012-5-27),许多人阅读到了(CSDN,博客园,百度文库),相关代码也被上百次下载(百度文库、csdn)。已经过去很久了,但是国内依旧没有发现很好的关于这方面的博客(大家都藏着掖着吗)。当时我对于C++中的多线程还半知半解,如今多次使用有些熟悉。
之所以Kinect中的Face Tracking没有得到很多的关注,我想主要原因还是这个方法必须使用Kinect硬件(真不便宜啊,普通摄像头几十块ok了),只适合开发Kinect室内应用。其次这个Face Tracking也是很耗CPU的,一般的双核电脑一旦运行经常会占用90%多的CPU。题外话,微软有许多强大的算法,比如人脸跟踪(基于普通彩色摄像头),但是他们只给了Windows Phone下的API,却不给出PC下的。我也去天津参加过Kinect的会议,遇到一些微软的产品,确实做得很强大很稳健,人脸检测用改进的特征进行Adaboost,之后用AAM进行面部特征点跟踪,十分稳健。去年参加了不少面试,国内的很多公司都在做人脸识别,但真正有实力的只有大公司弄的好。但如果说你想靠这个Kinect的人脸识别混饭吃,有点困难,除非你研究AAM人脸跟踪算法再去研究人脸识别、表情识别、三维建模等算法才会有饭吃。
鉴于之前没有对Kinect的Face Tracking进行详细描述和探讨,直接暴力的给出相关代码,有必要给出更详细的文章和更好的代码。
------------------------------------------------------------------------------------
首先现在大部分开发都针对Kinect for windows(目前1900RMB,还是有点贵的)了,但这个face tracking SDK在xbox版的Kinect依旧可以运行,自然精度会不好。Kinect for windows的深度数据是640*480的,而xbox的是320*240。而彩色的RGB摄像头也清晰不少,这点官方没有具体介绍,但实际效果好很多。如果要做研究,自然使用贵很多的Kinect for windows了(这样也导致了个人一般不会去玩这个传感器),不过对于学校、公司这点钱不多。下面默认使用Kinect for windows,但代码稍加修改在xbox的kinect上也能运作。
--------------------------------------------------
Kinect for Windows有如下要求:
采用下列操作系统的一种:
Windows 7
嵌入式Windows 标准7
嵌入式WindowsPOSReady 7
Windows 8
硬件要求
32位(x86)或64位(x64) 处理器
双核2.66-GHz 或更快的处理器(建议使用i7)
专用USB 2.0总线
2 GB内存
从Kinect SDK 1.5开始的Kinect for windows开发工具包中含有人脸追踪模块,当前最新的是SDK1.6。在近几个星期里,将会有新的SDK会更新。新的SDK将会带来简单的动作识别(手握拳和张开)和Kinect Fusion三维实时建模。
--------------------------------------------------
依赖于你PC的CPU能力,人脸跟踪引擎使用4~8ms对一帧图像进行人脸跟踪,仅仅只依赖于CPU(没有使用GPU)。
将会影响跟踪准确率的因素:
A.光照:光线应该充足,没有太多阴影或者太强的侧光。也就是光照要均匀,并且足够。
B.距离:距离Kinect的距离,距离体感越近则跟踪效果越好,当小于1.5m时,跟踪的效果是最好的,因为越近深度数据越精确。当然也不能太近了,如果小于0.5m,深度数据都无法获取,或者人脸遮住摄像头了,也无法跟踪。
C.遮挡:戴厚眼镜或者Lincoln那样的胡子,人脸跟踪会有问题,这点还是需要改善的方面。
使用了Active Apperance Model作为二维特征跟踪器,然后我们把计算模型扩展到我们的Kinect深度数据上,然后它可以跟踪三维的人脸,这样使得它比二维特征点跟踪器稳健。AAM算法对于真实世界的情况不是很稳健。一些算法:算法AAM、AAM人脸跟踪,算法3.
--------------------------------------------------
坐标系统:
使用了Kinect的坐标系统来输出三维跟踪结果(x,y,z)。Z轴是传感器到用户的距离,Y轴是上下指向。数据都是以米为计量单位(这样一般会读出2.xxx,1.xxx之类的浮点数值),角度都是旋转角度(不是弧度)。
上图是输出的三维面具在kinect的坐标下情况。
输入图像:
彩色图像和深度图像。其实还需要一个人头和脖子的三维坐标。
--------------------------------------------------
人脸追踪的SDK是免注册的COM对象。主要有4个COM接口
IFTFaceTracker:人脸追踪主要接口。
IFTResult:人脸追踪运算的结果。
IFTImage:图像缓冲区,类似OpenCV的Mat。
IFTModel:三维人脸模型
还有一些结构:
FT_SENSOR_DATA:包含用于人脸追踪所有所需的输入数据。
FT_CAMERA_CONFIG:包含彩色或者深度传感器的信息。
FT_VECTOR2D:二维向量。也就是(x,y),(x,y)…
FT_VECTOR3D:三维向量。
FT_TRIANGLE:三维模型人脸角度。
FT_WEIGHTED_RECT:权重矩阵。
--------------------------------------------------
不论是1个人的还是2个人的追踪,都差不多。一般追踪距离摄像头比较近的人,骨骼数据可能有时无法获取(那就使用上一帧的就是,经验之谈,因为很随机)。
-----------------------------------------------------------------------------------
这里介绍一些实际开发时遇到的问题。
一般至少2个线程,微软给的例子(FaceTrackingVisualization)一个是获取彩色、深度、骨骼的线程,另一个线程直接调用使用这三个数据,把数据传给人脸跟踪模块,都没进行线程同步。我有点疑问,不同步会不会有问题,但怎么运行测试都木有问题。每次跟踪后它都Sleep(16)一下,这个数值自己可以修改,我在win32应用里使用Sleep(30)运行结果不错,而在MFC中也使用Sleep(16)。
这里还需要特别说明的是,千万小心对于原始数据的操作,也就是彩色、深度数据的操作。如果你想显示数据,请拷贝数据后另行处理,不要直接在数据上进行处理,否则会出现很麻烦的问题。你以为把数据传给跟踪模块后,就可以直接在彩色数据(IFTImage)上进行更改了,但在你更改后,人脸跟踪模块由于设置的是16ms,马上又调用人脸跟踪,丢失了人脸模型!这样会出现一种结果:人脸跟踪十分不稳定,面具随机在人脸附近跳动,人脸一旦运动快点,跟踪会发生失效。所以之后我学乖了,要对数据处理或者显示,另外拷贝一份,其实拷贝图像数据是瞬间的事情。
微软在face tracking介绍给了一张的人脸图像,这里我就不给图像了,因为那张人脸特征点图像不论是位置还是下标都是不符的。这里我给出我得到的图像
:
上图有121个特征点(有些点重复,有些不存在)微软的面部特征点符合Candide-3标准,我们可以在文献(Candide-3_-_an_updated_parameterised_face)中阅读到一张表Appendix A: The CANDIDE -3Vertices and the Corresponding MPEG -4 Facial Feature Points。它列出了113个顶点(下标从0开始),这些顶点的定义和程序中获取的数组一一对应,还多出来的部分可以无视掉。
这张图像是使用OpenCV显示人脸跟踪数组中的顶点结果,我们可以自己修改程序画出来,需要注意的就是把图像画大一些,否则字符串会重叠在一起。之所以我用线连起来,主要为了看上去更方便,如果一百多个数字显示在图片上,肯定没人想去研究。我把数字按顺序一次连接,发现某些点可以连接出如图所示的线段图。至于每个点到底什么意思,还是要对照表中定义。
显然这么多点,我们只取需要的点即可。这个仁者见仁智者见智吧。
下图是顶点定义的那张表:
获取人脸跟踪结果后,我们除了得到面部关键顶点还可以直接获取人脸朝向(Get3DPose函数)。下图是人脸朝向的定义:
Angle |
Value |
Pitch angle 0=neutral |
-90 = looking down towards the floor +90 = looking up towards the ceiling Face Tracking tracks when the user’s head pitch is less than 20 degrees, but works best when less than 10 degrees. |
Roll angle 0 = neutral |
-90 = horizontal parallel with right shoulder of subject +90 = horizontal parallel with left shoulder of the subject Face Tracking tracks when the user’s head roll is less than 90 degrees, but works best when less than 45 degrees. |
Yaw angle 0 = neutral |
-90 = turned towards the right shoulder of the subject +90 = turned towards the left shoulder of the subject Face Tracking tracks when the user’s head yaw is less than 45 degrees, but works best when less than 30 degrees |
自然如果你做简单的人头旋转头部动作识别,肯定得用到这块。如果你做人脸标表情、动作或者三维建模,也少不了使用这个旋转角度对人脸进行一些仿射变换。很显然,Roll方向上的人脸旋转可以消除掉(我在我的人脸识别中就这样)。如果你使用OpenCV进行放射旋转,要熟悉矩阵旋转公式,一旦人脸旋转,那么所有的顶点坐标都要跟着旋转,如果扣除人脸区域,那么相应的人脸顶点坐标也要减去x轴和y轴的数值。还有需要考虑一些临界情况,比如人脸从左侧移除,人脸从上方、下方走出,会不会导致程序崩溃?此时的人脸数据不可以使用。
之后还有动画单元等概念,也是可以通过函数直接获取结果,这部分我未进行研究,如果做表情或者人脸动画,则需要深入研究下。
----------------------------------------------------------------------------------
对于Win32程序。如果只想看效果,不想研究代码,安装好Kinect驱动后,可以直接点击Debug下的exe应用程序进行运行即可。如果想研究代码,需要配置OpenCV环境(文章链接),随便你使用OpenCV2.x某个版本,建议使用目前最新的OpenCV2.4.4。之所以使用OpenCV,是因为用它显示视频会十分简单,用到的代码也不多。
首先按照自己电脑上opencv的库文件地址、包含目录地址、库文件,将它们添加到相应的配置属性中
下方的OpenCV244\opencv\,,,,,配置在上方可添加路径区域效果是一样的,即:
包含目录配置:
或者
如果在编译运行的时候,遇见关于“xcopy”的问题
在配置属性中生成事件->后期生成事件中删除命令行的内容就可以了
-----------------------------------------------------------------------------------
博客:
[shi19871987]Kinect for windows SDK1.5人脸识别与跟踪
http://blog.csdn.net/shi19871987/article/details/7748094
[yangecnu, 杨洋]Kinect for Windows SDK开发入门(十六)面部追踪上【C#】
http://www.cnblogs.com/yangecnu/archive/2012/10/12/KinectSDK_FaceTracking.html
[箫鸣] Kinect SDK 1.5 Face Tracking ---> 使用opencv显示后的超级简化版本
http://blog.csdn.net/guoming0000/article/details/7607473
http://wenku.baidu.com/view/cf533d6858fafab069dc0256.html
一些官方资源:
Kinect官网
http://www.microsoft.com/zh-cn/kinectforwindows/
How To Use Kinect Face Tracking SDK
http://www.codeproject.com/Articles/394975/How-To-Use-Kinect-Face-Tracking-SDK
Candide标准
http://www.bk.isy.liu.se/candide/main.html
Face Tracking
http://msdn.microsoft.com/en-us/library/jj130970.aspx
【需要】
http://nsmoly.wordpress.com/2012/05/21/face-tracking-sdk-in-kinect-for-windows-1-5/
最后如果有任何问题,或者想一起讨论最好去我的CSDN留言,因为留言会自动发送到我的邮箱里面。也可以私信我的新浪微博。还有文章应该会不断更新,在我的网站(ilovecode.cn)上对应标题的文章会进行持续更新。欢迎大家分享相关知识,上篇kinect人脸跟踪文章写出后快一年了,中国这么大也未有人与我讨论。
-----------------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
|
// win32_KinectFaceTracking.cpp : 定义控制台应用程序的入口点。
/****************************************************
程序用途:KinectFace Tracking简单例子
开发环境:VisualStudio 2010 win32程序
OpenCV2.4.4 显示界面库
Kinect SDK v1.6 驱动版本
Windows 7 操作系统
开发人员:箫鸣
开发时间:2013-3-11~ 2013-3-12
联系方式:weibo.com/guoming0000
www.ilovecode.cn
备注:另有配套相关博客文章:
Kinect Face Tracking SDK[Kinect人脸跟踪]
******************************************************/
#include "stdafx.h"
#include <windows.h>
#include <opencv2\opencv.hpp>
#include <mmsystem.h>
#include <assert.h>
//#include <strsafe.h>
#include "NuiApi.h"
using
namespace
cv
;
using
namespace
std
;
//----------------------------------------------------
#define _WINDOWS
#include <FaceTrackLib.h>
//显示网状人脸,初始化人脸模型
HRESULT
VisualizeFaceModel
(
IFTImage
*
pColorImg
,
IFTModel
*
pModel
,
FT_CAMERA_CONFIG
const
*
pCameraConfig
,
FLOAT
const
*
pSUCoef
,
FLOAT
zoomFactor
,
POINT
viewOffset
,
IFTResult
*
pAAMRlt
,
UINT32
color
)
;
//pColorImg为图像缓冲区,pModel三维人脸模型
//---图像大小等参数--------------------------------------------
#define COLOR_WIDTH 640
#define COLOR_HIGHT 480
#define DEPTH_WIDTH 320
#define DEPTH_HIGHT 240
#define SKELETON_WIDTH 640
#define SKELETON_HIGHT 480
#define CHANNEL 3
BYTE
DepthBuf
[
DEPTH_WIDTH
*
DEPTH_HIGHT
*
CHANNEL
]
;
//---人脸跟踪用到的变量------------------------------------------
IFTImage
*
pColorFrame
,
*
pColorDisplay
;
//彩色图像数据,pColorDisplay是用于处理的深度数据
IFTImage
*
pDepthFrame
;
//深度图像数据
FT_VECTOR3D
m_hint3D
[
2
]
;
//头和肩膀中心的坐标
//----各种内核事件和句柄-----------------------------------------------------------------
HANDLE
m_hNextColorFrameEvent
;
HANDLE
m_hNextDepthFrameEvent
;
HANDLE
m_hNextSkeletonEvent
;
HANDLE
m_pColorStreamHandle
;
//保存图像数据流的句柄,用以提取数据
HANDLE
m_pDepthStreamHandle
;
HANDLE
m_hEvNuiProcessStop
;
//用于结束的事件对象
//-----------------------------------------------------------------------------------
//获取彩色图像数据,并进行显示
int
DrawColor
(
HANDLE
h
)
{
const
NUI_IMAGE_FRAME
*
pImageFrame
=
NULL
;
HRESULT
hr
=
NuiImageStreamGetNextFrame
(
h
,
0
,
&
;
pImageFrame
)
;
if
(
FAILED
(
hr
)
)
{
cout
<
;
<
;
"Get Color Image Frame Failed"
<
;
<
;
endl
;
return
-
1
;
}
INuiFrameTexture
*
pTexture
=
pImageFrame
-
>
;
pFrameTexture
;
NUI_LOCKED_RECT
LockedRect
;
pTexture
-
>
;
LockRect
(
0
,
&
;
LockedRect
,
NULL
,
0
)
;
//提取数据帧到LockedRect中,包括两个数据对象:pitch表示每行字节数,pBits第一个字节的地址
if
(
LockedRect
.
Pitch
!=
0
)
//如果每行字节数不为0
{
BYTE
*
pBuffer
=
(
BYTE
*
)
LockedRect
.
pBits
;
//pBuffer指向数据帧的第一个字节的地址
//该函数的作用是在LockedRect第一个字节开始的地址复制min(pColorFrame->GetBufferSize(), UINT(pTexture->BufferLen()))个字节到pColorFrame->GetBuffer()所指的缓冲区
memcpy
(
pColorFrame
-
>
;
GetBuffer
(
)
,
PBYTE
(
LockedRect
.
pBits
)
,
//PBYTE表示无符号单字节数值
min
(
pColorFrame
-
>
;
GetBufferSize
(
)
,
UINT
(
pTexture
-
>
;
BufferLen
(
)
)
)
)
;
//GetBuffer()它的作用是返回一个可写的缓冲指针
//OpenCV显示彩色视频
Mat
temp
(
COLOR_HIGHT
,
COLOR_WIDTH
,
CV_8UC4
,
pBuffer
)
;
imshow
(
"ColorVideo"
,
temp
)
;
int
c
=
waitKey
(
1
)
;
//按下ESC结束
//如果在视频界面按下ESC,q,Q都会导致整个程序退出
if
(
c
==
27
||
c
==
'q'
||
c
==
'Q'
)
{
SetEvent
(
m_hEvNuiProcessStop
)
;
}
}
NuiImageStreamReleaseFrame
(
h
,
pImageFrame
)
;
return
0
;
}
//获取深度图像数据,并进行显示
int
DrawDepth
(
HANDLE
h
)
{
const
NUI_IMAGE_FRAME
*
pImageFrame
=
NULL
;
HRESULT
hr
=
NuiImageStreamGetNextFrame
(
h
,
0
,
&
;
pImageFrame
)
;
if
(
FAILED
(
hr
)
)
{
cout
<
;
<
;
"Get Depth Image Frame Failed"
<
;
<
;
endl
;
return
-
1
;
}
INuiFrameTexture
*
pTexture
=
pImageFrame
-
>
;
pFrameTexture
;
NUI_LOCKED_RECT
LockedRect
;
pTexture
-
>
;
LockRect
(
0
,
&
;
LockedRect
,
NULL
,
0
)
;
if
(
LockedRect
.
Pitch
!=
0
)
{
USHORT
*
pBuff
=
(
USHORT
*
)
LockedRect
.
pBits
;
//注意这里需要转换,因为每个数据是2个字节,存储的同上面的颜色信息不一样,这里是2个字节一个信息,不能再用BYTE,转化为USHORT
// pDepthBuffer = pBuff;
memcpy
(
pDepthFrame
-
>
;
GetBuffer
(
)
,
PBYTE
(
LockedRect
.
pBits
)
,
min
(
pDepthFrame
-
>
;
GetBufferSize
(
)
,
UINT
(
pTexture
-
>
;
BufferLen
(
)
)
)
)
;
for
(
int
i
=
0
;
i
<
;
DEPTH_WIDTH
*
DEPTH_HIGHT
;
i
++
)
{
BYTE
index
=
pBuff
[
i
]
&
;
0x07
;
//提取ID信息
USHORT
realDepth
=
(
pBuff
[
i
]
&
;
0xFFF8
)
>
;
>
;
3
;
//提取距离信息
BYTE
scale
=
255
-
(
BYTE
)
(
256
*
realDepth
/
0x0fff
)
;
//因为提取的信息时距离信息
DepthBuf
[
CHANNEL
*
i
]
=
DepthBuf
[
CHANNEL
*
i
+
1
]
=
DepthBuf
[
CHANNEL
*
i
+
2
]
=
0
;
switch
(
index
)
{
case
0
:
DepthBuf
[
CHANNEL
*
i
]
=
scale
/
2
;
DepthBuf
[
CHANNEL
*
i
+
1
]
=
scale
/
2
;
DepthBuf
[
CHANNEL
*
i
+
2
]
=
scale
/
2
;
break
;
case
1
:
DepthBuf
[
CHANNEL
*
i
]
=
scale
;
break
;
case
2
:
DepthBuf
[
CHANNEL
*
i
+
1
]
=
scale
;
break
;
case
3
:
DepthBuf
[
CHANNEL
*
i
+
2
]
=
scale
;
break
;
case
4
:
DepthBuf
[
CHANNEL
*
i
]
=
scale
;
DepthBuf
[
CHANNEL
*
i
+
1
]
=
scale
;
break
;
case
5
:
DepthBuf
[
CHANNEL
*
i
]
=
scale
;
DepthBuf
[
CHANNEL
*
i
+
2
]
=
scale
;
break
;
case
6
:
DepthBuf
[
CHANNEL
*
i
+
1
]
=
scale
;
DepthBuf
[
CHANNEL
*
i
+
2
]
=
scale
;
break
;
case
7
:
DepthBuf
[
CHANNEL
*
i
]
=
255
-
scale
/
2
;
DepthBuf
[
CHANNEL
*
i
+
1
]
=
255
-
scale
/
2
;
DepthBuf
[
CHANNEL
*
i
+
2
]
=
255
-
scale
/
2
;
break
;
}
}
Mat
temp
(
DEPTH_HIGHT
,
DEPTH_WIDTH
,
CV_8UC3
,
DepthBuf
)
;
imshow
(
"DepthVideo"
,
temp
)
;
int
c
=
waitKey
(
1
)
;
//按下ESC结束
if
(
c
==
27
||
c
==
'q'
||
c
==
'Q'
)
{
SetEvent
(
m_hEvNuiProcessStop
)
;
}
}
NuiImageStreamReleaseFrame
(
h
,
pImageFrame
)
;
return
0
;
}
//获取骨骼数据,并进行显示
int
DrawSkeleton
(
)
{
NUI_SKELETON_FRAME
SkeletonFrame
;
//骨骼帧的定义
cv
::
Point
pt
[
20
]
;
Mat
skeletonMat
=
Mat
(
SKELETON_HIGHT
,
SKELETON_WIDTH
,
CV_8UC3
,
Scalar
(
0
,
0
,
0
)
)
;
//直接从kinect中提取骨骼帧
HRESULT
hr
=
NuiSkeletonGetNextFrame
(
0
,
&
;
SkeletonFrame
)
;
if
(
FAILED
(
hr
)
)
{
cout
<
;
<
;
"Get Skeleton Image Frame Failed"
<
;
<
;
endl
;
return
-
1
;
}
bool
bFoundSkeleton
=
false
;
for
(
int
i
=
0
;
i
<
;
NUI_SKELETON_COUNT
;
i
++
)
{
if
(
SkeletonFrame
.
SkeletonData
[
i
]
.
eTrackingState
==
NUI_SKELETON_TRACKED
)
{
bFoundSkeleton
=
true
;
}
}
// 跟踪到了骨架
if
(
bFoundSkeleton
)
{
NuiTransformSmooth
(
&
;
SkeletonFrame
,
NULL
)
;
for
(
int
i
=
0
;
i
<
;
NUI_SKELETON_COUNT
;
i
++
)
{
if
(
SkeletonFrame
.
SkeletonData
[
i
]
.
eTrackingState
==
NUI_SKELETON_TRACKED
)
{
for
(
int
j
=
0
;
j
<
;
NUI_SKELETON_POSITION_COUNT
;
j
++
)
{
float
fx
,
fy
;
NuiTransformSkeletonToDepthImage
(
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
j
]
,
&
;
fx
,
&
;
fy
)
;
pt
[
j
]
.
x
=
(
int
)
(
fx
*
SKELETON_WIDTH
)
/
320
;
pt
[
j
]
.
y
=
(
int
)
(
fy
*
SKELETON_HIGHT
)
/
240
;
circle
(
skeletonMat
,
pt
[
j
]
,
5
,
CV_RGB
(
255
,
0
,
0
)
)
;
}
// cout<<"one people"<<endl;
// cv::line(skeletonMat,pt[NUI_SKELETON_POSITION_SHOULDER_CENTER],pt[NUI_SKELETON_POSITION_SPINE],CV_RGB(0,255,0));
// cv::line(skeletonMat,pt[NUI_SKELETON_POSITION_SPINE],pt[NUI_SKELETON_POSITION_HIP_CENTER],CV_RGB(0,255,0));
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_HEAD
]
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_HAND_RIGHT
]
,
pt
[
NUI_SKELETON_POSITION_WRIST_RIGHT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_WRIST_RIGHT
]
,
pt
[
NUI_SKELETON_POSITION_ELBOW_RIGHT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_ELBOW_RIGHT
]
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_RIGHT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_RIGHT
]
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_LEFT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_SHOULDER_LEFT
]
,
pt
[
NUI_SKELETON_POSITION_ELBOW_LEFT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_ELBOW_LEFT
]
,
pt
[
NUI_SKELETON_POSITION_WRIST_LEFT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
cv
::
line
(
skeletonMat
,
pt
[
NUI_SKELETON_POSITION_WRIST_LEFT
]
,
pt
[
NUI_SKELETON_POSITION_HAND_LEFT
]
,
CV_RGB
(
0
,
255
,
0
)
)
;
m_hint3D
[
0
]
.
x
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
.
x
;
m_hint3D
[
0
]
.
y
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
.
y
;
m_hint3D
[
0
]
.
z
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_SHOULDER_CENTER
]
.
z
;
m_hint3D
[
1
]
.
x
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_HEAD
]
.
x
;
m_hint3D
[
1
]
.
y
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_HEAD
]
.
y
;
m_hint3D
[
1
]
.
z
=
SkeletonFrame
.
SkeletonData
[
i
]
.
SkeletonPositions
[
NUI_SKELETON_POSITION_HEAD
]
.
z
;
// cout<<"("<<m_hint3D[0].x<<","<<m_hint3D[0].y<<","<<m_hint3D[0].z<<")"<<endl;
// cout<<"("<<m_hint3D[1].x<<","<<m_hint3D[1].y<<","<<m_hint3D[1].z<<")"<<endl<<endl;
}
}
}
imshow
(
"SkeletonVideo"
,
skeletonMat
)
;
waitKey
(
1
)
;
int
c
=
waitKey
(
1
)
;
//按下ESC结束
if
(
c
==
27
||
c
==
'q'
||
c
==
'Q'
)
{
SetEvent
(
m_hEvNuiProcessStop
)
;
}
return
0
;
}
DWORD
WINAPI
KinectDataThread
(
LPVOID
pParam
)
//线程函数
{
HANDLE
hEvents
[
4
]
=
{
m_hEvNuiProcessStop
,
m_hNextColorFrameEvent
,
m_hNextDepthFrameEvent
,
m_hNextSkeletonEvent
}
;
//内核事件
while
(
1
)
{
int
nEventIdx
;
nEventIdx
=
WaitForMultipleObjects
(
sizeof
(
hEvents
)
/
sizeof
(
hEvents
[
0
]
)
,
hEvents
,
FALSE
,
100
)
;
if
(
WAIT_OBJECT_0
==
WaitForSingleObject
(
m_hEvNuiProcessStop
,
0
)
)
{
break
;
}
// Process signal events
if
(
WAIT_OBJECT_0
==
WaitForSingleObject
(
m_hNextColorFrameEvent
,
0
)
)
{
DrawColor
(
m_pColorStreamHandle
)
;
//获取彩色图像并进行显示
}
if
(
WAIT_OBJECT_0
==
WaitForSingleObject
(
m_hNextDepthFrameEvent
,
0
)
)
{
DrawDepth
(
m_pDepthStreamHandle
)
;
}
if
(
WAIT_OBJECT_0
==
WaitForSingleObject
(
m_hNextSkeletonEvent
,
0
)
)
{
DrawSkeleton
(
)
;
}
//这种方式关闭程序时可能出问题
// switch(nEventIdx)
// {
// case 0:
// break;
// case 1:
// DrawColor(m_pVideoStreamHandle);
// case 2:
// DrawDepth(m_pDepthStreamHandle);
// case 3:
// DrawSkeleton();
// }
}
CloseHandle
(
m_hEvNuiProcessStop
)
;
m_hEvNuiProcessStop
=
NULL
;
CloseHandle
(
m_hNextSkeletonEvent
)
;
CloseHandle
(
m_hNextDepthFrameEvent
)
;
CloseHandle
(
m_hNextColorFrameEvent
)
;
return
0
;
}
int
main
(
)
{
m_hint3D
[
0
]
.
x
=
0
;
//肩膀的中心坐标,三维向量
m_hint3D
[
0
]
.
y
=
0
;
|