鸟语天空
Kinect
post by:追风剑情 2019-9-19 13:11

官方文档
Xbox NUI Camera Drivers Download
Microsoft WDF KinectSensor Interface Driver
Kinect-v2 Examples with MS-SDK(译文二)
Kinect-v2 Examples with MS-SDK Doc(Chinese documents)

Kinect驱动安装目录
C:\Program Files\Microsoft Kinect Drivers\Drivers

1111.png

一、安装开发环境

1、官方下载 Kinect for Windows SDK 2.0,并安装。

2、测试Kinect设备是否正常工作。

1111.png

1111.png

绿勾代表正常工作

2222.png

有时Kinect connected显示叉,尝试让kinect断开电源10秒以上,再插上。

Kinect V2必须插USB 3.0才能正常工作。USB 2.0、USB 3.1都不行。

设备管理器查看USB接口版本

1111.png

确保麦克风阵列处于启用状态,否则 Kinect 可能出现工作不稳定

1111.png


3、下载示例并导入到Unity2017或更高版本中
百度网盘下载 (Kinect v2 Examples with_2.13.unitypackage) 提取码: 5yjs
百度网盘下载 (Kinect v2 Examples with MS-SDK 2.20.unitypackage) 提取码: aj05
注意: Kinect v2 Examples with MS-SDK 2.20.unitypackage 需要Unity2019.1.0或更高版本、Direct3D11。

1111.png

=====================================================================================

»KinectManager接口功能

1.检查Kinect是否完成了初始化
if ( KinectManager.IsKinectInitialized() ) { }

2.获取摄像头画面(一般作为背景)
Texture2D background = manager.GetUsersClrTex();

3.判断指定用户的某关节是否正处于跟踪中
long userId = manager.GetUserIdByIndex(0);//0代表第1个用户
int joint = (int)KinectInterop.JointType.HandRight;//判断右手
if (manager.IsJointTracked (userId, joint)) {}

4.获取关节坐标及旋转角

//关节坐标
long userId = manager.GetUserIdByIndex(0);
int joint = (int)KinectInterop.JointType.HandRight;//右手
Rect backgroundRect = foregroundCamera.pixelRect;
Vector3 posJoint = manager.GetJointPosColorOverlay(userId, joint, foregroundCamera, backgroundRect);
//关节旋转角度
Vector3 vForward = foregroundCamera ? foregroundCamera.transform.forward : Vector3.forward;
bool objFlipped = (Vector3.Dot(overlayObject.forward, vForward) < 0);//夹角为锐角
Quaternion rotJoint = manager.GetJointOrientation(userId, joint, !objFlipped);

5.获取父关节
KinectInterop.JointType jointParent = manager.GetParentJoint(KinectInterop.JointType.HandRight);

6.是否至少有1个用户被跟踪
if (!manager.IsUserDetected()) {
//没用户时,可以暂停或停止游戏
}

7.判断指定用户ID是否在跟踪用户列表中
if (manager.IsUserTracked(userId)) {}

8.清除所有被跟踪的用户
manager.ClearKinectUsers();//通常在游戏结束时调用

9.骨骼枚举
Kinect中的定义:KinectInterop.JointType
Unity中的定义: HumanBodyBones
PS: 注意定义中的对应关系
例如:KinectInterop.JointType.ShoulderLeft对应的是HumanBodyBones.LeftUpperArm

10.获取骨骼
//获取KinectInterop.JointType.ShoulderLeft
animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperArm)

11.改变人类化身姿态

//确保对象不为null,且为人类
if (animatorComponent && animatorComponent.avatar && animatorComponent.avatar.isHuman)
{
	HumanPoseHandler humanPoseHandler = new HumanPoseHandler(animatorComponent.avatar, rootTransform);
	HumanPose humanPose = new HumanPose();
	humanPoseHandler.GetHumanPose(ref humanPose);
	humanPose.bodyPosition = 新值;
	humanPose.bodyRotation = 新值;
	humanPose.muscles = 新值;//当前姿势的肌肉值的数组
	humanPoseHandler.SetHumanPose(ref humanPose);
}

12.刷新姿态监听器列表(通常进入场景后调用一次)
找出场景中实现了KinectGestures.GestureListenerInterface的脚本,并加入到监听列表,同时找出场景中
KinectGestures脚本。
manager.refreshGestureListeners();

13.刷新化身控制器列表(通常进入场景后调用一次)
找出场景中的AvatarController脚本并加入avatarControllers列表
manager.refreshAvatarControllers()

14.获取主用户ID
获取主用户(第一个或最近的用户)的userid,如果没有检测到用户,则获取0
manager.GetPrimaryUserID();

15.获取前景图像(通常指用户)

backManager = BackgroundRemovalManager.Instance;
if (backManager && backManager.IsBackgroundRemovalInitialized()) {
	foregroundTex = (RenderTexture)backManager.GetForegroundTex ();
}

前景图像使用的渲染shader为Color2DepthShader.shader,修改此shader可以调整渲染效果。

在 BackgroundRemovalManager 类的 Start() 中替换 sensorData.color2DepthMaterial 可自定义前景渲染材质。


Compute User Map: 必须设置为Body Texture
Compute Color Map: 必须勾选

16.获取坐标点深度值

//获取头部关节坐标
Vector3 posHeadRaw = kinectManager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.Head);
if(posHeadRaw != Vector3.zero)
{
	//转到深度坐标系
	Vector2 posDepthHead = kinectManager.MapSpacePointToDepthCoords(posHeadRaw);
	//得到深度值
	ushort depthHead = kinectManager.GetDepthForPixel((int)posDepthHead.x, (int)posDepthHead.y);
}

17.获取面部矩形区域

if (faceManager.IsFaceTrackingInitialized() && faceManager.IsTrackingFace(userId)) 
{
    Rect faceJointRect = faceManager.GetFaceColorRect(userId);
}

18.调整前景裁剪边缘
禾.png
Erode Iterations: 缩小裁剪边缘
Dilate Iterations: 扩大裁剪边缘
2222.png

19.获取面部模型数据

KinectInterop.SensorData sensorData = kinectManager.GetSensorData();
//获取面部模型顶点数
int iNumVertices = sensorData.sensorInterface.GetFaceModelVerticesCount(0);
if (iNumVertices <= 0)
	return;
Vector3[] avModelVertices = new Vector3[iNumVertices];
//获取面部模型顶点
bool bGotModelVertices = sensorData.sensorInterface.GetFaceModelVertices(0, ref avModelVertices);

//获取三角形数量
int iNumTriangles = sensorData.sensorInterface.GetFaceModelTrianglesCount();
if(iNumTriangles <= 0)
	return;
//获取三角形顶点索引
int[] avModelTriangles = new int[iNumTriangles];
bool bGotModelTriangles = sensorData.sensorInterface.GetFaceModelTriangles(mirroredModelMesh, ref avModelTriangles);

//获取头在kinect中的坐标
Vector3 headPos = Vector3.zero;
bGotHeadPos = sensorData.sensorInterface.GetHeadPosition(primaryUserID, ref headPos);
//从kinect坐标系转到Unity世界坐标系
Matrix4x4 kinectToWorld = kinectManager ? kinectManager.GetKinectToWorldMatrix() : Matrix4x4.identity;
Vector3 headPosWorld = kinectToWorld.MultiplyPoint3x4(headPos);

//将顶点坐标从kinect坐标系转到Unity世界坐标
Vector3[] vMeshVertices = new Vector3[avModelVertices.Length];
for(int i = 0; i < avModelVertices.Length; i++)
{
	//世界坐标转面部模型的本地坐标
	vMeshVertices[i] = kinectToWorld.MultiplyPoint3x4(avModelVertices[i]) - headPosWorld;
}

//创建Mesh
if (faceModelMesh) 
{
	Mesh mesh = new Mesh();
	mesh.name = "FaceMesh";
	faceModelMesh.GetComponent().mesh = mesh;
	mesh.vertices = vMeshVertices;
	mesh.triangles = avModelTriangles;
	mesh.RecalculateNormals();
}

//为面部Mesh设置贴图
Texture texColorMap = kinectManager ? kinectManager.GetUsersClrTex() : null;
RenderTexture faceMeshTexture = new RenderTexture(texColorMap.width, texColorMap.height, 0);
faceModelMesh.GetComponent().material.mainTexture = faceMeshTexture;
if (faceMeshTexture && texColorMap) 
{
	// update the color texture
	Graphics.Blit(texColorMap, faceMeshTexture);
}

20.获取采集图像尺寸

//摄像头采集的图像尺寸
kinectManager.GetColorImageWidth();
kinectManager.GetColorImageHeight();
//红外摄像头采集的深度图像尺寸
kinectManager.GetDepthImageWidth();
kinectManager.GetDepthImageHeight();

21.获取面部模型特征点在Unity中的世界坐标

FacetrackingManager faceManager = FacetrackingManager.Instance;
int iVertCount = faceManager.GetUserFaceVertexCount(userId);
Vector3[] faceVertices = new Vector3[iVertCount];
//这里的顶点坐标系是Kinect设备
if (faceManager.GetUserFaceVertices(userId, ref faceVertices))
{
	//Kinect坐标到Unity世界坐标系的转换矩阵
	Matrix4x4 kinectToWorld = kinectManager.GetKinectToWorldMatrix();
	//面部关键点枚举数组
	HighDetailFacePoints[] facePoints = (HighDetailFacePoints[])System.Enum.GetValues(typeof(HighDetailFacePoints));

	Dictionary dictFacePoints = new Dictionary();
	for (int i = 0; i < facePoints.Length; i++) 
	{
		HighDetailFacePoints point = facePoints[i];
		//转换到Unity世界坐标系
		dictFacePoints[point] = kinectToWorld.MultiplyPoint3x4(faceVertices[(int)point]);
	}
	
	//让面具模型坐标跟随鼻尖(即,实现面具跟随面部)
	//NoseTip: 鼻尖
	HighDetailFacePoints facePoint = HighDetailFacePoints.NoseTip;
	Vector3 facePointPos = faceVertices[(int)facePoint];
	facePointTransform.position = facePointPos;
}

22.调整背景画面显示大小及位置 
//显示摄像头画面
KinectManager manager = KinectManager.Instance;
mGUITexture.texture = manager.GetUsersClrTex();
2222.png1111.png

23.设置前景像机(渲染3D物体)与Kinect摄像头的视场角相等 
KinectManager manager = KinectManager.Instance;
KinectInterop.SensorData sensorData = manager.GetSensorData();
foregroundCamera.fieldOfView = sensorData.colorCameraFOV;

24.获取关节在Camera坐标系中的坐标

Rect backgroundRect = foregroundCamera.pixelRect;
ortraitBackground portraitBack = PortraitBackground.Instance;
if(portraitBack && portraitBack.enabled)
{
	backgroundRect = portraitBack.GetBackgroundRect();
}
//返回关节在Unity中的世界坐标
Vector3 posColorOverlay = kinectManager.GetJointPosColorOverlay(
primaryUserID, (int)KinectInterop.JointType.Head, foregroundCamera, backgroundRect);
//让目标3D对象跟随关节
faceModelMesh.transform.position = posColorOverlay;

25.计算面部模型顶点对应的UV贴图坐标

//获取模型顶点坐标
KinectInterop.SensorData sensorData = kinectManager.GetSensorData();
int iNumVertices = sensorData.sensorInterface.GetFaceModelVerticesCount(primaryUserID);
Vector3[] avModelVertices = new Vector3[iNumVertices];
sensorData.sensorInterface.GetFaceModelVertices(primaryUserID, ref avModelVertices);
//Kinect摄像头采集的画面宽高
float colorWidth = (float)kinectManager.GetColorImageWidth();
float colorHeight = (float)kinectManager.GetColorImageHeight();
//计算模型顶点对应的UV坐标
for(int i = 0; i < avModelVertices.Length; i++)
{
	//顶点对应的深度图坐标
	Vector2 posDepth = kinectManager.MapSpacePointToDepthCoords(avModelVertices[i]);
	if(posDepth != Vector2.zero)
	{
		//得到深度值
		ushort depth = kinectManager.GetDepthForPixel((int)posDepth.x, (int)posDepth.y);
		//得到顶点在Color图像中的坐标
		Vector2 posColor = kinectManager.MapDepthPointToColorCoords(posDepth, depth);
		if(posColor != Vector2.zero && !float.IsInfinity(posColor.x) && !float.IsInfinity(posColor.y))
		{
			//转成[0,1]区间的UV坐标
			avModelUV[i] = new Vector2(posColor.x / colorWidth, posColor.y / colorHeight);
		}
	}
}
//设置uv
mesh.uv = avModelUV;
//如果顶点有变化,别忘了重新计算下
mesh.RecalculateNormals();
mesh.RecalculateBounds();

26.获取面部细节点坐标

int NoseTip = (int)Microsoft.Kinect.Face.HighDetailFacePoints.NoseTip;
Vector3 NoseTipPos = avModelVertices[NoseTip];//鼻尖坐标


27.表情动画

//获取动画单元(AU)权重值,Shape各动画表情权重值
//AU是中性形状的增量,您可以使用它来对动画化身模型上的目标进行变形,
//以使化身像被跟踪的用户一样起作用。例如,AU定义是否张口,抬起眉毛以及其他面部表情细节。
Dictionary dictAU = 
	new Dictionary();
sensorData.sensorInterface.GetAnimUnits(userId, ref dictAU);

//获取形状单位(SU)权重
//SU估计用户头部的特定形状:眉毛,鼻子,脸颊,嘴巴或下巴等特征的形状。
Dictionary dictSU =
	new Dictionary();
ensorData.sensorInterface.GetShapeUnits(userId, ref dictSU);


»需要开发人员自己实现的接口:

public class MyGestureListener : KinectGestures.GestureListenerInterface 
{
	//用户进入
	void UserDetected(long userId, int userIndex)
	{
		KinectManager manager = KinectManager.Instance;
		// 添加希望追踪的用户手势
		manager.DetectGesture(userId, KinectGestures.Gestures.SwipeLeft);
		manager.DetectGesture(userId, KinectGestures.Gestures.SwipeRight);
	}
	
	//用户离开
	void UserLost(long userId, int userIndex)
	{
	}
	
	//返回手势进度
	void GestureInProgress(long userId, int userIndex, Gestures gesture, float progress, 
		                       KinectInterop.JointType joint, Vector3 screenPos)
	{
	}
	
	//手势完成
	//return true(手势必须重新开始)
	bool GestureCompleted(long userId, int userIndex, Gestures gesture,
		                      KinectInterop.JointType joint, Vector3 screenPos)
	{
		// 判断手势类型并做出相应处理
		switch (gesture)
		{
			case KinectGestures.Gestures.SwipeLeft:
			
				break;
			case KinectGestures.Gestures.SwipeRight:
			
				break;
		}
		//返回true,KinectManager会调用ResetPlayerGestures();意味着手势将重新开始
		return true;
	}
	
	//手势取消
	bool GestureCancelled(long userId, int userIndex, Gestures gesture, 
		                      KinectInterop.JointType joint)
	{
		//返回true,KinectManager会调用ResetGesture();意味着手势将重新开始
		return true;
	}
}

public class MyInteractionListener : InteractionListenerInterface 
{
	//手握住
	void HandGripDetected(long userId, int userIndex, bool isRightHand, 
                              bool isHandInteracting, Vector3 handScreenPos)
	{
	
	}
	
	//手释放
	void HandReleaseDetected(long userId, int userIndex, bool isRightHand, 
                                 bool isHandInteracting, Vector3 handScreenPos)
	{
	
	}
	
	//左手或右手在适当的位置停留至少2.5秒
	//return true(手势必须重新开始)
	bool HandClickDetected(long userId, int userIndex, bool isRightHand, Vector3 handScreenPos)
	{
	
	}
}

»手势识别
Kinect手势类型定义在KinectGestures.cs中 KinectGestures.Gestures

public enum Gestures
{
	None = 0,
	RaiseRightHand,//右手举起过肩并保持至少一秒
	RaiseLeftHand,//左手举起过肩并保持至少一秒
	Psi,//双手举起过肩并保持至少一秒
	Tpose,//触摸
	Stop,//双手下垂
	Wave,//左手或右手举起来回摆动
	Click,//左手或右手在适当的位置停留至少2.5秒
	SwipeLeft,//右手向左挥
	SwipeRight,//左手向右挥
	SwipeUp,//左手或者右手向上挥
	SwipeDown,//左手或者右手向下挥
	RightHandCursor,//假手势,用来使光标随着手移动
	LeftHandCursor,//假手势,用来使光标随着手移动
	ZoomIn,//手肘向下,两手掌相聚至少0.7米,然后慢慢合在一起
	ZoomOut,//手肘向下,左右手掌合在一起(求佛的手势),然后慢慢分开
	Wheel,//想象一下你双手握着方向盘,然后左右转动
	Jump,//在1.5秒内髋关节中心至少上升10厘米 (跳)
	Squat,//在1.5秒内髋关节中心至少下降10厘米 (下蹲)
	Push,//在1.5秒内将左手或右手向外推
	Pull,//在1.5秒内将左手或右手向里拉
	ShoulderLeftFront,//左肩前倾
	ShoulderRightFront,//右肩前倾
	LeanLeft, //身体向左倾斜
	LeanRight, //身体向右倾斜
	LeanForward,//身体向前倾斜
	LeanBack,//身体向后倾斜
	KickLeft,//踢左脚
	KickRight,//踢右脚
	Run,//跑
	RaisedRightHorizontalLeftHand,//左手平举
	RaisedLeftHorizontalRightHand,//右手平举
	
	//自定义手势
	UserGesture1 = 101,
	UserGesture2 = 102,
	UserGesture3 = 103,
	UserGesture4 = 104,
	UserGesture5 = 105,
	UserGesture6 = 106,
	UserGesture7 = 107,
	UserGesture8 = 108,
	UserGesture9 = 109,
	UserGesture10 = 110,
}

每种手势的识别处理在KinectGestures.CheckForGesture()方法中,可以在这个方法中扩展自定义手势识别。

Gestures.Push
右手向前推的姿态定义:
1、右手.y - 左肘.y > -0.1米
2、Mathf.Abs(右手.x - 右肩.x) < 0.2米
3、右手.z - 左肘.z < -0.2米

»关节
每个姿态由手、肘、肩、脊柱、臀部、膝、踝关节共同组成,参见KinectGestures.GetNeededJointIndexes()方法:

/// <summary>
/// 获取组成姿态的关节索引列表
/// </summary>
public virtual int[] GetNeededJointIndexes(KinectManager manager)
{
	//手
	leftHandIndex = manager.GetJointIndex(KinectInterop.JointType.HandLeft);
	rightHandIndex = manager.GetJointIndex(KinectInterop.JointType.HandRight);
	//肘
	leftElbowIndex = manager.GetJointIndex(KinectInterop.JointType.ElbowLeft);
	rightElbowIndex = manager.GetJointIndex(KinectInterop.JointType.ElbowRight);
	//肩
	leftShoulderIndex = manager.GetJointIndex(KinectInterop.JointType.ShoulderLeft);
	rightShoulderIndex = manager.GetJointIndex(KinectInterop.JointType.ShoulderRight);
	//脊柱
	hipCenterIndex = manager.GetJointIndex(KinectInterop.JointType.SpineBase);
	shoulderCenterIndex = manager.GetJointIndex(KinectInterop.JointType.SpineShoulder);
	//臀部
	leftHipIndex = manager.GetJointIndex(KinectInterop.JointType.HipLeft);
	rightHipIndex = manager.GetJointIndex(KinectInterop.JointType.HipRight);
	//膝
	leftKneeIndex = manager.GetJointIndex(KinectInterop.JointType.KneeLeft);
	rightKneeIndex = manager.GetJointIndex(KinectInterop.JointType.KneeRight);
	//踝
	leftAnkleIndex = manager.GetJointIndex(KinectInterop.JointType.AnkleLeft);
	rightAnkleIndex = manager.GetJointIndex(KinectInterop.JointType.AnkleRight);
	
	int[] neededJointIndexes = {
		leftHandIndex, rightHandIndex, leftElbowIndex, rightElbowIndex, leftShoulderIndex, 
                rightShoulderIndex, hipCenterIndex, shoulderCenterIndex, leftHipIndex, rightHipIndex, 
                leftKneeIndex, rightKneeIndex, leftAnkleIndex, rightAnkleIndex
	};

	return neededJointIndexes;
}


»只跟踪指定关节
创建XXXAvatarController继承自AvatarController,并重写基类的MapBones()方法。
protected virtual void MapBones()

AvatarController中声明了Kinect关节与Mecanim关节之间的映射关系

//kinect-joints to mecanim-bones mapping
//Kinect关节到Mecanim动画系统中的关节映射
protected static readonly Dictionary<int, HumanBodyBones> boneIndex2MecanimMap = new Dictionary<int, HumanBodyBones>
{
	{0, HumanBodyBones.Hips},
	{1, HumanBodyBones.Spine},
	//{2, HumanBodyBones.Chest},
	{3, HumanBodyBones.Neck},
	//{4, HumanBodyBones.Head},
	
	{5, HumanBodyBones.LeftUpperArm},
	{6, HumanBodyBones.LeftLowerArm},
	{7, HumanBodyBones.LeftHand},
	//{8, HumanBodyBones.LeftIndexProximal},
	//{9, HumanBodyBones.LeftIndexIntermediate},
	//{10, HumanBodyBones.LeftThumbProximal},
	
	{11, HumanBodyBones.RightUpperArm},
	{12, HumanBodyBones.RightLowerArm},
	{13, HumanBodyBones.RightHand},
	//{14, HumanBodyBones.RightIndexProximal},
	//{15, HumanBodyBones.RightIndexIntermediate},
	//{16, HumanBodyBones.RightThumbProximal},
	
	{17, HumanBodyBones.LeftUpperLeg},
	{18, HumanBodyBones.LeftLowerLeg},
	{19, HumanBodyBones.LeftFoot},
	//{20, HumanBodyBones.LeftToes},
	
	{21, HumanBodyBones.RightUpperLeg},
	{22, HumanBodyBones.RightLowerLeg},
	{23, HumanBodyBones.RightFoot},
	//{24, HumanBodyBones.RightToes},
	
	{25, HumanBodyBones.LeftShoulder},
	{26, HumanBodyBones.RightShoulder},
	{27, HumanBodyBones.LeftIndexProximal},
	{28, HumanBodyBones.RightIndexProximal},
	{29, HumanBodyBones.LeftThumbProximal},
	{30, HumanBodyBones.RightThumbProximal},
};

//kinect手指关节到Mecanim动画系统中的关节映射
protected static readonly Dictionary<int, HumanBodyBones> fingerIndex2MecanimMap = new Dictionary<int, HumanBodyBones>
{
	{0, HumanBodyBones.LeftThumbProximal},
	{1, HumanBodyBones.LeftThumbIntermediate},
	{2, HumanBodyBones.LeftThumbDistal},

	{3, HumanBodyBones.LeftIndexProximal},
	{4, HumanBodyBones.LeftIndexIntermediate},
	{5, HumanBodyBones.LeftIndexDistal},

	{6, HumanBodyBones.LeftMiddleProximal},
	{7, HumanBodyBones.LeftMiddleIntermediate},
	{8, HumanBodyBones.LeftMiddleDistal},

	{9, HumanBodyBones.LeftRingProximal},
	{10, HumanBodyBones.LeftRingIntermediate},
	{11, HumanBodyBones.LeftRingDistal},

	{12, HumanBodyBones.LeftLittleProximal},
	{13, HumanBodyBones.LeftLittleIntermediate},
	{14, HumanBodyBones.LeftLittleDistal},

	{15, HumanBodyBones.RightThumbProximal},
	{16, HumanBodyBones.RightThumbIntermediate},
	{17, HumanBodyBones.RightThumbDistal},

	{18, HumanBodyBones.RightIndexProximal},
	{19, HumanBodyBones.RightIndexIntermediate},
	{20, HumanBodyBones.RightIndexDistal},

	{21, HumanBodyBones.RightMiddleProximal},
	{22, HumanBodyBones.RightMiddleIntermediate},
	{23, HumanBodyBones.RightMiddleDistal},

	{24, HumanBodyBones.RightRingProximal},
	{25, HumanBodyBones.RightRingIntermediate},
	{26, HumanBodyBones.RightRingDistal},

	{27, HumanBodyBones.RightLittleProximal},
	{28, HumanBodyBones.RightLittleIntermediate},
	{29, HumanBodyBones.RightLittleDistal}
};


// get bone transforms from the animator component
//Animator animatorComponent = GetComponent<Animator>();
Transform bone = animatorComponent.GetBoneTransform(boneIndex2MecanimMap[boneIndex]);
Transform fingerBone = animatorComponent.GetBoneTransform(fingerIndex2MecanimMap[boneIndex]);

示例:只跟踪上半身关节


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 只绑定上半身骨骼
/// </summary>
public class UpperAvatarController : AvatarController
{
	// Public variables that will get matched to bones. If empty, the Kinect will simply not track it.
	public Transform HipCenter;
	public Transform Spine;
	public Transform ShoulderCenter;
	public Transform Neck;
	//public Transform Head;

	public Transform ClavicleLeft;
	public Transform ShoulderLeft;
	public Transform ElbowLeft;
	public Transform HandLeft;
	public Transform FingersLeft;
	//private Transform FingerTipsLeft = null;
	public Transform ThumbLeft;

	public Transform ClavicleRight;
	public Transform ShoulderRight;
	public Transform ElbowRight;
	public Transform HandRight;
	public Transform FingersRight;
	//private Transform FingerTipsRight = null;
	public Transform ThumbRight;

	public Transform HipLeft;
	public Transform KneeLeft;
	public Transform FootLeft;
	//private Transform ToesLeft = null;

	public Transform HipRight;
	public Transform KneeRight;
	public Transform FootRight;
	//private Transform ToesRight = null;

	[Tooltip("The body root node (optional).")]
	public Transform BodyRoot;

	// Offset node this transform is relative to, if any (optional)
	//public GameObject OffsetNode;


	// If the bones to be mapped have been declared, map that bone to the model.
	protected override void MapBones()
	{
		bones[0] = HipCenter;
		bones[1] = Spine;
		bones[2] = ShoulderCenter;
		bones[3] = Neck;
		//bones[4] = Head;

		bones[5] = ShoulderLeft;
		bones[6] = ElbowLeft;
		bones[7] = HandLeft;
		//bones[8] = FingersLeft;
		//bones[9] = FingerTipsLeft;
		//bones[10] = ThumbLeft;

		bones[11] = ShoulderRight;
		bones[12] = ElbowRight;
		bones[13] = HandRight;
		//bones[14] = FingersRight;
		//bones[15] = FingerTipsRight;
		//bones[16] = ThumbRight;

		bones[17] = HipLeft;
		bones[18] = KneeLeft;
		bones[19] = FootLeft;
		//bones[20] = ToesLeft;

		bones[21] = HipRight;
		bones[22] = KneeRight;
		bones[23] = FootRight;
		//bones[24] = ToesRight;

		// special bones
		bones[25] = ClavicleLeft;
		bones[26] = ClavicleRight;

		bones[27] = FingersLeft;
		bones[28] = FingersRight;
		bones[29] = ThumbLeft;
		bones[30] = ThumbRight;

		// body root and offset
		bodyRoot = BodyRoot;
	}
}

111.png

»动态加载kinect配置参数
kinect.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
	<!-- 以下参数名与Kinect Manager(Script)属性面板中的参数对应 -->
	<SensorHeight>1</SensorHeight>
	<SensorAngle>0</SensorAngle>
	<!-- DontUse, ShowInfoOnly, AutoUpdate, AutoUpdateAndShowInfo -->
	<AutoHeightAngle>DontUse</AutoHeightAngle>
	<!-- None, RawUserDepth, BodyTexture, UserTexture, CutOutTexture -->
	<ComputeUserMap>RawUserDepth</ComputeUserMap>
	<ComputeColorMap>true</ComputeColorMap>
	<ComputeInfraredMap>false</ComputeInfraredMap>
	<DisplayUserMap>false</DisplayUserMap>
	<DisplayColorMap>false</DisplayColorMap>
	<DisplaySkeletonLines>false</DisplaySkeletonLines>
	<DisplayMapsWidthPercent>20</DisplayMapsWidthPercent>
	<UseMultiSourceReader>true</UseMultiSourceReader>
	<MinUserDistance>0.5</MinUserDistance>
	<MaxUserDistance>0</MaxUserDistance>
	<MaxLeftRightDistance>0</MaxLeftRightDistance>
	<MaxTrackedUsers>6</MaxTrackedUsers>
	<ShowTrackedUsersOnly>true</ShowTrackedUsersOnly>
	<!-- Appearance, Distance, LeftToRight -->
	<UserDetectionOrder>Appearance</UserDetectionOrder>
	<IgnoreInferredJoints>false</IgnoreInferredJoints>
	<IgnoreZCoordinates>false</IgnoreZCoordinates>
	<LateUpdateAvatars>false</LateUpdateAvatars>
	<SkipRemoteAvatars>false</SkipRemoteAvatars>
	<!-- None, Default, Light, Medium, Aggressive -->
	<Smoothing>Default</Smoothing>
	<UseBoneOrientationConstraints>false</UseBoneOrientationConstraints>
	<EstimateJointVelocities>false</EstimateJointVelocities>
	<!-- None, Default, Light, Medium, Aggressive -->
	<VelocitySmoothing>Light</VelocitySmoothing>
	<AllowTurnArounds>false</AllowTurnArounds>
	<!-- None, Default, All -->
	<AllowedHandRotations>Default</AllowedHandRotations>
	<WaitTimeBeforeRemove>1</WaitTimeBeforeRemove>
	<!-- None = 0,RaiseRightHand, RaiseLeftHand, Psi, Tpose,
		 Stop, Wave, Click, SwipeLeft, SwipeRight, SwipeUp,
		 SwipeDown, RightHandCursor, LeftHandCursor, ZoomIn, Wheel,
		 Jump, Squat, Push, Pull, ShoulderLeftFront, ShoulderRightFront,
		 LeanLeft, LeanRight, LeanForward, LeanBack, KickLeft, KickRight, Run,
         RaisedRightHorizontalLeftHand,   // by Andrzej W
         RaisedLeftHorizontalRightHand, 
		 UserGesture1 = 101,
		 UserGesture2 = 102,
		 UserGesture3 = 103,
		 UserGesture4 = 104,
		 UserGesture5 = 105,
		 UserGesture6 = 106,
		 UserGesture7 = 107,
		 UserGesture8 = 108,
		 UserGesture9 = 109,
		 UserGesture10 = 110,
	-->
	<PlayerCalibrationPose>None</PlayerCalibrationPose>
	<MinTimeBetweenGestures>0.7</MinTimeBetweenGestures>
</root>


KinectConfig.cs

// 与kinect.xml对应
public static class KinectConfig
{
    public static float SensorHeight { get; private set; }
    public static float SensorAngle { get; private set; }
    public static bool ComputeColorMap { get; private set; }
    public static bool ComputeInfraredMap { get; private set; }
    public static bool DisplayUserMap { get; private set; }
    public static bool DisplayColorMap { get; private set; }
    public static bool DisplaySkeletonLines { get; private set; }
    public static float DisplayMapsWidthPercent { get; private set; }
    public static bool UseMultiSourceReader { get; private set; }
    public static float MinUserDistance { get; private set; }
    public static float MaxUserDistance { get; private set; }
    public static float MaxLeftRightDistance { get; private set; }
    public static int MaxTrackedUsers { get; private set; }
    public static bool ShowTrackedUsersOnly { get; private set; }
    public static bool IgnoreInferredJoints { get; private set; }
    public static bool IgnoreZCoordinates { get; private set; }
    public static bool LateUpdateAvatars { get; private set; }
    public static bool SkipRemoteAvatars { get; private set; }
    public static bool UseBoneOrientationConstraints { get; private set; }
    public static bool EstimateJointVelocities { get; private set; }
    public static bool AllowTurnArounds { get; private set; }
    public static float WaitTimeBeforeRemove { get; private set; }
    public static float MinTimeBetweenGestures { get; private set; }

    public static KinectManager.AutoHeightAngle AutoHeightAngle = KinectManager.AutoHeightAngle.DontUse;
    public static KinectManager.UserMapType ComputeUserMap = KinectManager.UserMapType.RawUserDepth;
    public static KinectManager.UserDetectionOrder UserDetectionOrder = KinectManager.UserDetectionOrder.Appearance;
    public static KinectManager.Smoothing Smoothing = KinectManager.Smoothing.Default;
    public static KinectManager.Smoothing VelocitySmoothing = KinectManager.Smoothing.Light;
    public static KinectManager.AllowedRotations AllowedHandRotations = KinectManager.AllowedRotations.Default;
    public static KinectGestures.Gestures PlayerCalibrationPose;

    public static bool Done { get; private set; }

    // 加载配置
    public static void Load(string path)
    {
        if (!File.Exists(path))
        {
            Debug.LogErrorFormat("Not found: {0}", path);
            return;
        }
        XmlDocument xml = new XmlDocument();
        xml.Load(path);
        Parse(xml);
    }

    // 读入配置到kinect
    public static void Read(KinectManager kinect)
    {
        if (kinect == null)
        {
            Debug.Log("[KinectConfig] Read(null)");
            return;
        }

        if (!Done)
        {
            Debug.Log("[KinectConfig] Unparse completion");
            return;
        }

        kinect.sensorHeight = SensorHeight;
        kinect.sensorAngle = SensorAngle;
        kinect.computeColorMap = ComputeColorMap;
        kinect.computeInfraredMap = ComputeInfraredMap;
        kinect.displayUserMap = DisplayUserMap;
        kinect.displayColorMap = DisplayColorMap;
        kinect.displaySkeletonLines = DisplaySkeletonLines;
        kinect.DisplayMapsWidthPercent = DisplayMapsWidthPercent;
        kinect.useMultiSourceReader = UseMultiSourceReader;
        kinect.minUserDistance = MinUserDistance;
        kinect.maxUserDistance = MaxUserDistance;
        kinect.maxLeftRightDistance = MaxLeftRightDistance;
        kinect.maxTrackedUsers = MaxTrackedUsers;
        kinect.showTrackedUsersOnly = ShowTrackedUsersOnly;
        kinect.ignoreInferredJoints = IgnoreInferredJoints;
        kinect.ignoreZCoordinates = IgnoreZCoordinates;
        kinect.lateUpdateAvatars = LateUpdateAvatars;
        kinect.skipRemoteAvatars = SkipRemoteAvatars;
        kinect.useBoneOrientationConstraints = UseBoneOrientationConstraints;
        kinect.estimateJointVelocities = EstimateJointVelocities;
        kinect.allowTurnArounds = AllowTurnArounds;
        kinect.waitTimeBeforeRemove = WaitTimeBeforeRemove;
        kinect.minTimeBetweenGestures = MinTimeBetweenGestures;
        kinect.autoHeightAngle = AutoHeightAngle;
        kinect.computeUserMap = ComputeUserMap;
        kinect.userDetectionOrder = UserDetectionOrder;
        kinect.smoothing = Smoothing;
        kinect.velocitySmoothing = VelocitySmoothing;
        kinect.allowedHandRotations = AllowedHandRotations;
        kinect.playerCalibrationPose = PlayerCalibrationPose;
    }

    // 解析配置
    private static void Parse(XmlDocument xml)
    {
        Type type = Type.GetType("KinectConfig");

        XmlNode root = xml.SelectSingleNode("/root");
        XmlNodeList childList = root.ChildNodes;
        for (int i = 0; i < childList.Count; i++)
        {
            XmlNode node = childList.Item(i);
            if (node.NodeType == XmlNodeType.Comment) //跳过XML中的注释
                continue;

            string text = node.InnerText;
            PropertyInfo property = type.GetProperty(node.Name);
            FieldInfo field = type.GetField(node.Name);
            if (property == null && field == null)
            {
                Debug.LogErrorFormat("[KinectConfig] Parse Error, node.Name={0}, MemberType={1}", node.Name, type.MemberType);
                continue;
            }

            if (property != null)
            {
                bool IsInt = property.PropertyType == typeof(int);
                bool IsFloat = property.PropertyType == typeof(float);
                bool IsBool = property.PropertyType == typeof(bool);
                if (IsInt)
                    property.SetValue(null, int.Parse(text));
                else if (IsFloat)
                    property.SetValue(null, float.Parse(text));
                else if (IsBool)
                    property.SetValue(null, bool.Parse(text));
            }

            if (field != null)
            {
                if (field.FieldType.IsEnum)
                {
                    System.Object obj = Enum.Parse(field.FieldType, text);
                    field.SetValue(null, obj);
                }
            }
        }
        Done = true;
        Debug.Log(ToString());
    }

    public static string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("Kinect配置信息\n\n<color=blue>");

        Type type = Type.GetType("KinectConfig");
        PropertyInfo[] propertyInfos = type.GetProperties();
        for (int i=0; i< propertyInfos.Length; i++)
        {
            PropertyInfo p = propertyInfos[i];
            sb.AppendFormat("{0}={1}\n", p.Name, p.GetValue(null));
        }
        FieldInfo[] fieldInfos = type.GetFields();
        for (int i = 0; i < fieldInfos.Length; i++)
        {
            FieldInfo p = fieldInfos[i];
            sb.AppendFormat("{0}={1}\n", p.Name, p.GetValue(null));
        }

        sb.Append("</color>");
        return sb.ToString();
    }
}


»打包发布

注意:需要手动将kinect相关的dll从工程目录拷到发布目录下。

1111.png

22222.png

评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容