프로젝트

일반

사용자정보

통계
| 개정판:

t1 / TFDContents / Assets / KinectScripts / BodySlicer.cs @ 3

이력 | 보기 | 이력해설 | 다운로드 (16.9 KB)

1
using UnityEngine;
2
//using Windows.Kinect;
3

    
4
using System.Collections;
5
using System.Collections.Generic;
6
using System.Runtime.InteropServices;
7
using System.Threading;
8
using System;
9
using System.IO;
10

    
11
public enum BodySlice
12
{
13
	HEIGHT = 0,
14
	WIDTH = 1,
15

    
16
	TORSO_1 = 2,
17
	TORSO_2 = 3,
18
	TORSO_3 = 4,
19
	TORSO_4 = 5,
20

    
21
	COUNT = 6
22
}
23

    
24
/// <summary>
25
/// Data structure used by the body slicer.
26
/// </summary>
27
public struct BodySliceData
28
{
29
	public bool isSliceValid;
30

    
31
	public float diameter;
32
	public int depthPointsLength;
33
	public int colorPointsLength;
34

    
35
//	public ushort[] depths;
36
	public Vector2 startDepthPoint;
37
	public Vector2 endDepthPoint;
38

    
39
	public Vector2 startColorPoint;
40
	public Vector2 endColorPoint;
41

    
42
	public Vector3 startKinectPoint;
43
	public Vector3 endKinectPoint;
44
}
45

    
46

    
47
/// <summary>
48
/// Body slicer is component that estimates the user height, as well as several other body measures, from the depth image data.
49
/// </summary>
50
public class BodySlicer : MonoBehaviour
51
{
52
	[Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")]
53
	public int playerIndex = 0;
54

    
55
	[Tooltip("Whether the body height should estimated or not.")]
56
	public bool estimateBodyHeight = true;
57

    
58
	[Tooltip("Whether the body width should estimated or not.")]
59
	public bool estimateBodyWidth = false;
60

    
61
	[Tooltip("Whether the body slices should estimated or not.")]
62
	public bool estimateBodySlices = false;
63

    
64
	[Tooltip("Whether the slicing should be done on all updates, or only after the user calibration.")]
65
	public bool continuousSlicing = false;
66

    
67
	[Tooltip("Whether the detected body slices should be displayed on the screen.")]
68
	public bool displayBodySlices = false;
69

    
70
//	// background image texture, if any
71
//	public GUITexture bgImage;
72

    
73
	private long calibratedUserId;
74
	private byte userBodyIndex;
75

    
76

    
77
	// The singleton instance of BodySlicer
78
	private static BodySlicer instance = null;
79
	private KinectManager manager;
80
	private KinectInterop.SensorData sensorData;
81
	private long lastDepthFrameTime;
82

    
83
	private BodySliceData[] bodySlices = new BodySliceData[(int)BodySlice.COUNT];
84
	private Texture2D depthImage;
85

    
86
	
87
	/// <summary>
88
	/// Gets the singleton BodySlicer instance.
89
	/// </summary>
90
	/// <value>The singleton BodySlicer instance.</value>
91
	public static BodySlicer Instance
92
	{
93
		get
94
		{
95
			return instance;
96
		}
97
	}
98

    
99

    
100
	/// <summary>
101
	/// Gets the height of the user.
102
	/// </summary>
103
	/// <returns>The user height.</returns>
104
	public float getUserHeight()
105
	{
106
		return getSliceWidth (BodySlice.HEIGHT);
107
	}
108

    
109

    
110
	/// <summary>
111
	/// Gets the slice width.
112
	/// </summary>
113
	/// <returns>The slice width.</returns>
114
	/// <param name="slice">Slice.</param>
115
	public float getSliceWidth(BodySlice slice)
116
	{
117
		int iSlice = (int)slice;
118

    
119
		if (bodySlices[iSlice].isSliceValid) 
120
		{
121
			return bodySlices[iSlice].diameter;
122
		}
123

    
124
		return 0f;
125
	}
126

    
127

    
128
	/// <summary>
129
	/// Gets the body slice count.
130
	/// </summary>
131
	/// <returns>The body slice count.</returns>
132
	public int getBodySliceCount()
133
	{
134
		return bodySlices != null ? bodySlices.Length : 0;
135
	}
136

    
137

    
138
	/// <summary>
139
	/// Gets the body slice data.
140
	/// </summary>
141
	/// <returns>The body slice data.</returns>
142
	/// <param name="slice">Slice.</param>
143
	public BodySliceData getBodySliceData(BodySlice slice)
144
	{
145
		return bodySlices[(int)slice];
146
	}
147

    
148

    
149
	/// <summary>
150
	/// Gets the calibrated user ID.
151
	/// </summary>
152
	/// <returns>The calibrated user ID.</returns>
153
	public long getCalibratedUserId() 
154
	{
155
		return calibratedUserId;				
156
	}
157

    
158

    
159
	/// <summary>
160
	/// Gets the last frame time.
161
	/// </summary>
162
	/// <returns>The last frame time.</returns>
163
	public long getLastFrameTime()
164
	{
165
		return lastDepthFrameTime;
166
	}
167

    
168

    
169
	////////////////////////////////////////////////////////////////////////
170

    
171

    
172
	void Awake()
173
	{
174
		instance = this;
175
	}
176

    
177
	void Start()
178
	{
179
		manager = KinectManager.Instance;
180
		sensorData = manager ? manager.GetSensorData() : null;
181
	}
182

    
183
	void Update()
184
	{
185
		if(!manager || !manager.IsInitialized())
186
			return;
187

    
188
		// get required player
189
		long userId = manager.GetUserIdByIndex (playerIndex);
190

    
191
		if(calibratedUserId == 0)
192
		{
193
			if(userId != 0)
194
			{
195
				OnCalibrationSuccess(userId);
196
			}
197
		}
198
		else
199
		{
200
			if (calibratedUserId != userId) 
201
			{
202
				OnUserLost(calibratedUserId);
203
			} 
204
			else if(continuousSlicing)
205
			{
206
				EstimateBodySlices(calibratedUserId);
207
			}
208
		}
209

    
210
//		// update color image
211
//		if (bgImage && !bgImage.texture) 
212
//		{
213
//			bgImage.texture = manager.GetUsersClrTex();
214
//		}
215
	}
216

    
217
	void OnGUI() 
218
	{
219
		if(displayBodySlices && depthImage)
220
		{
221
			Rect depthImageRect = new Rect(0, Screen.height, 256, -212);
222
			GUI.DrawTexture(depthImageRect, depthImage);
223
		}
224
	}
225

    
226
    public void OnCalibrationSuccess(long userId)
227
    {
228
		calibratedUserId = userId;
229

    
230
		// estimate body slices
231
		EstimateBodySlices(calibratedUserId);
232
    }
233

    
234
    void OnUserLost(long UserId)
235
    {
236
		calibratedUserId = 0;
237
    }
238
	
239
	public bool EstimateBodySlices(long userId)
240
	{
241
		if (userId <= 0) 
242
			userId = calibratedUserId;
243

    
244
		if(!manager || userId == 0)
245
			return false;
246

    
247
		userBodyIndex = (byte)manager.GetBodyIndexByUserId(userId);
248
		if (userBodyIndex == 255)
249
			return false;
250

    
251
		bool bSliceSuccess = false;
252

    
253
		if (sensorData.bodyIndexImage != null && sensorData.depthImage != null &&
254
		    sensorData.lastDepthFrameTime != lastDepthFrameTime) 
255
		{
256
			lastDepthFrameTime = sensorData.lastDepthFrameTime;
257
			bSliceSuccess = true;
258

    
259
			Vector2 pointSpineBase = manager.MapSpacePointToDepthCoords(manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.SpineBase));
260

    
261
			if (estimateBodyHeight) 
262
			{
263
				bodySlices[(int)BodySlice.HEIGHT] = GetUserHeightParams(pointSpineBase);
264
			}
265

    
266
			if (estimateBodyWidth) 
267
			{
268
				bodySlices[(int)BodySlice.WIDTH] = GetUserWidthParams(pointSpineBase);
269
			}
270

    
271
			if(estimateBodySlices && manager.IsJointTracked(userId, (int)KinectInterop.JointType.SpineBase) && manager.IsJointTracked(userId, (int)KinectInterop.JointType.Neck))
272
			{
273
				Vector2 point1 = pointSpineBase;
274
				Vector2 point2 = manager.MapSpacePointToDepthCoords(manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.Neck));
275
				Vector2 sliceDir = (point2 - point1) / 4f;
276

    
277
				Vector2 vSlicePoint = point1;
278
				bodySlices[(int)BodySlice.TORSO_1] = GetBodySliceParams(vSlicePoint, true, false, -1);
279

    
280
				vSlicePoint += sliceDir;
281
				bodySlices[(int)BodySlice.TORSO_2] = GetBodySliceParams(vSlicePoint, true, false, -1);
282

    
283
				vSlicePoint += sliceDir;
284
				bodySlices[(int)BodySlice.TORSO_3] = GetBodySliceParams(vSlicePoint, true, false, -1);
285

    
286
				vSlicePoint += sliceDir;
287
				bodySlices[(int)BodySlice.TORSO_4] = GetBodySliceParams(vSlicePoint, true, false, -1);
288
			}
289

    
290
			// display body slices
291
			if(displayBodySlices)
292
			{
293
				depthImage = manager.GetUsersLblTex();
294

    
295
				if(depthImage)
296
				{
297
					depthImage = GameObject.Instantiate(depthImage) as Texture2D;
298

    
299
					DrawBodySlice(bodySlices[(int)BodySlice.HEIGHT]);
300

    
301
					DrawBodySlice(bodySlices[(int)BodySlice.TORSO_1]);
302
					DrawBodySlice(bodySlices[(int)BodySlice.TORSO_2]);
303
					DrawBodySlice(bodySlices[(int)BodySlice.TORSO_3]);
304
					DrawBodySlice(bodySlices[(int)BodySlice.TORSO_4]);
305

    
306
					depthImage.Apply();
307
				}
308
			}
309
		}
310

    
311
		return bSliceSuccess;
312
	}
313

    
314

    
315
	private void DrawBodySlice(BodySliceData bodySlice)
316
	{
317
		if(depthImage && bodySlice.isSliceValid && 
318
		   bodySlice.startDepthPoint != Vector2.zero && bodySlice.endDepthPoint != Vector2.zero)
319
		{
320
			KinectInterop.DrawLine(depthImage, (int)bodySlice.startDepthPoint.x, (int)bodySlice.startDepthPoint.y, 
321
			         (int)bodySlice.endDepthPoint.x, (int)bodySlice.endDepthPoint.y, Color.red);
322
		}
323
	}
324

    
325
	private BodySliceData GetUserHeightParams(Vector2 pointSpineBase)
326
	{
327
		int depthLength = sensorData.depthImage.Length;
328
		int depthWidth = sensorData.depthImageWidth;
329
		int depthHeight = sensorData.depthImageHeight;
330

    
331
		Vector2 posTop = new Vector2 (0, depthHeight);
332
		for (int i = 0, x = 0, y = 0; i < depthLength; i++) 
333
		{
334
			if (sensorData.bodyIndexImage [i] == userBodyIndex) 
335
			{
336
				//if (posTop.y > y)
337
					posTop = new Vector2(x, y);
338
				break;
339
			}
340

    
341
			x++;
342
			if (x >= depthWidth) 
343
			{
344
				x = 0;
345
				y++;
346
			}
347
		}
348

    
349
		Vector2 posBottom = new Vector2 (0, -1);
350
		for (int i = depthLength - 1, x = depthWidth - 1, y = depthHeight - 1; i >= 0; i--) 
351
		{
352
			if (sensorData.bodyIndexImage [i] == userBodyIndex) 
353
			{
354
				//if (posBottom.y < y)
355
					posBottom = new Vector2(x, y);
356
				break;
357
			}
358

    
359
			x--;
360
			if (x < 0) 
361
			{
362
				x = depthWidth - 1;
363
				y--;
364
			}
365
		}
366

    
367
		BodySliceData sliceData = new BodySliceData();
368
		sliceData.isSliceValid = false;
369

    
370
		if (posBottom.y >= 0) 
371
		{
372
			sliceData.startDepthPoint = posTop;
373
			sliceData.endDepthPoint = posBottom;
374
			sliceData.depthPointsLength = (int)posBottom.y - (int)posTop.y + 1;
375

    
376
			int index1 = (int)posTop.y * depthWidth + (int)posTop.x;
377
			ushort depth1 = sensorData.depthImage[index1];
378
			sliceData.startKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.startDepthPoint, depth1, true);
379

    
380
			int index2 = (int)posBottom.y * depthWidth + (int)posBottom.x;
381
			ushort depth2 = sensorData.depthImage[index2];
382
			sliceData.endKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.endDepthPoint, depth2, true);
383

    
384
			sliceData.startColorPoint = manager.MapDepthPointToColorCoords(sliceData.startDepthPoint, depth1);
385
			sliceData.endColorPoint = manager.MapDepthPointToColorCoords(sliceData.endDepthPoint, depth2);
386

    
387
			if (sliceData.startColorPoint.y < 0)
388
				sliceData.startColorPoint.y = 0;
389
			if (sliceData.endColorPoint.y >= manager.GetColorImageHeight())
390
				sliceData.endColorPoint.y = manager.GetColorImageHeight() - 1;
391
			sliceData.colorPointsLength = (int)sliceData.endColorPoint.y - (int)sliceData.startColorPoint.y + 1;
392

    
393
			// correct x-positions of depth points
394
			sliceData.startDepthPoint.x = pointSpineBase.x;
395
			sliceData.endDepthPoint.x = pointSpineBase.x;
396

    
397
			sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude;
398
			sliceData.isSliceValid = true;
399
		} 
400

    
401
		return sliceData;
402
	}
403

    
404
	private BodySliceData GetUserWidthParams(Vector2 pointSpineBase)
405
	{
406
		int depthLength = sensorData.depthImage.Length;
407
		int depthWidth = sensorData.depthImageWidth;
408
		//int depthHeight = sensorData.depthImageHeight;
409

    
410
		Vector2 posLeft = new Vector2 (depthWidth, 0);
411
		Vector2 posRight = new Vector2 (-1, 0);
412

    
413
		for (int i = 0, x = 0, y = 0; i < depthLength; i++) 
414
		{
415
			if (sensorData.bodyIndexImage [i] == userBodyIndex) 
416
			{
417
				if (posLeft.x > x)
418
					posLeft = new Vector2(x, y);
419
				if (posRight.x < x)
420
					posRight = new Vector2(x, y);
421
			}
422

    
423
			x++;
424
			if (x >= depthWidth) 
425
			{
426
				x = 0;
427
				y++;
428
			}
429
		}
430

    
431
		BodySliceData sliceData = new BodySliceData();
432
		sliceData.isSliceValid = false;
433

    
434
		if (posRight.x >= 0) 
435
		{
436
			sliceData.startDepthPoint = posLeft;
437
			sliceData.endDepthPoint = posRight;
438
			sliceData.depthPointsLength = (int)posRight.x - (int)posLeft.x + 1;
439

    
440
			int index1 = (int)posLeft.y * depthWidth + (int)posLeft.x;
441
			ushort depth1 = sensorData.depthImage[index1];
442
			sliceData.startKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.startDepthPoint, depth1, true);
443

    
444
			int index2 = (int)posRight.y * depthWidth + (int)posRight.x;
445
			ushort depth2 = sensorData.depthImage[index2];
446
			sliceData.endKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.endDepthPoint, depth2, true);
447

    
448
			sliceData.startColorPoint = manager.MapDepthPointToColorCoords(sliceData.startDepthPoint, depth1);
449
			sliceData.endColorPoint = manager.MapDepthPointToColorCoords(sliceData.endDepthPoint, depth2);
450

    
451
			if (sliceData.startColorPoint.x < 0)
452
				sliceData.startColorPoint.x = 0;
453
			if (sliceData.endColorPoint.x >= manager.GetColorImageWidth())
454
				sliceData.endColorPoint.x = manager.GetColorImageWidth() - 1;
455
			sliceData.colorPointsLength = (int)sliceData.endColorPoint.x - (int)sliceData.startColorPoint.x + 1;
456

    
457
			// correct y-positions of depth points
458
			sliceData.startDepthPoint.y = pointSpineBase.y;
459
			sliceData.endDepthPoint.y = pointSpineBase.y;
460

    
461
			sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude;
462
			sliceData.isSliceValid = true;
463
		} 
464

    
465
		return sliceData;
466
	}
467

    
468
	private BodySliceData GetBodySliceParams(Vector2 middlePoint, bool bSliceOnX, bool bSliceOnY, int maxDepthLength)
469
	{
470
		BodySliceData sliceData = new BodySliceData();
471
		sliceData.isSliceValid = false;
472
		sliceData.depthPointsLength  = 0;
473

    
474
		if(!manager || middlePoint == Vector2.zero)
475
			return sliceData;
476
		if(!bSliceOnX && !bSliceOnY)
477
			return sliceData;
478

    
479
		middlePoint.x = Mathf.FloorToInt(middlePoint.x + 0.5f);
480
		middlePoint.y = Mathf.FloorToInt(middlePoint.y + 0.5f);
481

    
482
		int depthWidth = sensorData.depthImageWidth;
483
		int depthHeight = sensorData.depthImageHeight;
484

    
485
		int indexMid = (int)middlePoint.y * depthWidth + (int)middlePoint.x;
486
		byte userIndex = sensorData.bodyIndexImage[indexMid];
487

    
488
		if(userIndex != userBodyIndex)
489
			return sliceData;
490

    
491
		sliceData.startDepthPoint = middlePoint;
492
		sliceData.endDepthPoint = middlePoint;
493

    
494
		int indexDiff1 = 0;
495
		int indexDiff2 = 0;
496

    
497
		if(bSliceOnX)
498
		{
499
			// min-max
500
			int minIndex = (int)middlePoint.y * depthWidth;
501
			int maxIndex = (int)(middlePoint.y + 1) * depthWidth;
502

    
503
			// horizontal left
504
			int stepIndex = -1;
505
			indexDiff1 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex);
506

    
507
			// horizontal right
508
			stepIndex = 1;
509
			indexDiff2 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex);
510
		}
511
		else if(bSliceOnY)
512
		{
513
			// min-max
514
			int minIndex = 0;
515
			int maxIndex = depthHeight * depthWidth;
516

    
517
			// vertical up
518
			int stepIndex = -depthWidth;
519
			indexDiff1 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex);
520

    
521
			// vertical down
522
			stepIndex = depthWidth;
523
			indexDiff2 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex);
524
		}
525

    
526
		// calculate depth length
527
		sliceData.depthPointsLength = indexDiff1 + indexDiff2 + 1;
528

    
529
		// check for max length (used by upper legs)
530
		if(maxDepthLength > 0 && sliceData.depthPointsLength > maxDepthLength)
531
		{
532
//			indexDiff1 = (int)((float)indexDiff1 * maxDepthLength / sliceData.depthsLength);
533
//			indexDiff2 = (int)((float)indexDiff2 * maxDepthLength / sliceData.depthsLength);
534

    
535
			if(indexDiff1 > indexDiff2)
536
				indexDiff1 = indexDiff2;
537
			else
538
				indexDiff2 = indexDiff1;
539

    
540
			sliceData.depthPointsLength = indexDiff1 + indexDiff2 + 1;
541
		}
542

    
543
		// set start and end depth points
544
		if(bSliceOnX)
545
		{
546
			sliceData.startDepthPoint.x -= indexDiff1;
547
			sliceData.endDepthPoint.x += indexDiff2;
548
		}
549
		else if(bSliceOnY)
550
		{
551
			sliceData.startDepthPoint.y -= indexDiff1;
552
			sliceData.endDepthPoint.y += indexDiff2;
553
		}
554

    
555
		// start point
556
		int index1 = (int)sliceData.startDepthPoint.y * depthWidth + (int)sliceData.startDepthPoint.x;
557
		ushort depth1 = sensorData.depthImage[index1];
558
		sliceData.startKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.startDepthPoint, depth1, true);
559

    
560
		// end point
561
		int index2 = (int)sliceData.endDepthPoint.y * depthWidth + (int)sliceData.endDepthPoint.x;
562
		ushort depth2 = sensorData.depthImage[index2];
563
		sliceData.endKinectPoint = manager.MapDepthPointToSpaceCoords(sliceData.endDepthPoint, depth2, true);
564

    
565
		sliceData.startColorPoint = manager.MapDepthPointToColorCoords(sliceData.startDepthPoint, depth1);
566
		sliceData.endColorPoint = manager.MapDepthPointToColorCoords(sliceData.endDepthPoint, depth2);
567

    
568
		if (sliceData.startColorPoint.x < 0)
569
			sliceData.startColorPoint.x = 0;
570
		if (sliceData.endColorPoint.x >= manager.GetColorImageWidth())
571
			sliceData.endColorPoint.x = manager.GetColorImageWidth() - 1;
572
		sliceData.colorPointsLength = (int)sliceData.endColorPoint.x - (int)sliceData.startColorPoint.x + 1;
573

    
574
		// diameter
575
		sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude;
576
		sliceData.isSliceValid = true;
577

    
578
//		// get depths
579
//		sliceData.depths = new ushort[sliceData.depthsLength];
580
//		int stepDepthIndex = 1;
581
//
582
//		if(bSliceOnX)
583
//		{
584
//			stepDepthIndex = 1;
585
//		}
586
//		else if(bSliceOnY)
587
//		{
588
//			stepDepthIndex = depthWidth;
589
//		}
590
//		
591
//		for(int i = index1, d = 0; i <= index2; i+= stepDepthIndex, d++)
592
//		{
593
//			sliceData.depths[d] = sensorData.depthImage[i];
594
//		}
595

    
596
		return sliceData;
597
	}
598

    
599
	private int TrackSliceInDirection(int index, int stepIndex, int minIndex, int maxIndex, byte userIndex)
600
	{
601
		int indexDiff = 0;
602
		int errCount = 0;
603

    
604
		index += stepIndex;
605
		while(index >= minIndex && index < maxIndex)
606
		{
607
			if(sensorData.bodyIndexImage[index] != userIndex)
608
			{
609
				errCount++;
610
				if(errCount > 0) // allow 0 error(s)
611
					break;
612
			}
613
			else
614
			{
615
				errCount = 0;
616
			}
617
			
618
			index += stepIndex;
619
			indexDiff++;
620
		}
621

    
622
		return indexDiff;
623
	}
624

    
625
}
626