Unity5 利用Kinect Studio 和Gesture Builder建立自定义姿势分类器

Unity升级到5以后,好多以前使用的插件都用不了了,比如,以前利用Kinect来辨别姿势的话,直接可以使用Kinect with MS-SDK (https://www.assetstore.unity3d.com/en/#!/content/7747),就可以利用这个package里面的函数就可以完成一些动作的捕获,是不是,自从使用Unity5之后,这个包导入就会报编译错误了?嗯,既然,别人的库不能用了,那我们就自己建立一个自己的分类器,这样的话,不仅可以完成一些基本的姿势识别,我们还可以定义任意多的姿势!
说到分类器,或许有人会说,那不是机器学习训练样本之后制造的东西嘛?没错,的确是利用机器学习的方式来建立分类器。机器学习的算法太复杂了,我不会怎么办?那么,我介绍的方法刚好适合你!因为微软已经给我们做好了对应的应用程序,我们只要训练样本就好啦!
好的,来介绍一下我们的工具,Kinect Studio 和 Visual Gesture Builder,这两个程序应该在我们下载 Kinect v2 的SDK的时候就已经下载下来了。

一 Kinect Studio 收集视频样本

  1. 首先,打开Kinect Studio,先点击File进入设置界面,这里我们可以设置我们的视频片段存储位置。

  2. 录制视频样本,先转到Record界面,连接Kinect
    这里写图片描述

  3. 成功之后,我们的Kinect的三个红外灯应该是亮了,然后点击那个红色的icon开始录制视频。
    这里写图片描述

  4. 我们这里以挥手这个动作来作为例子,我们站在一个合适的位置,做挥手的动作,一开始慢慢的,多做几组),然后录制完毕,点击停止录制。
    这里写图片描述

  5. 这时候,Kinect Studio 的界面会自动跳转到Play的界面。我们先Disconnect Kinect之后,可以点击play的icon来查看这个视频例子,注意,我用红色方框框起来的部分,这个就是保存的文件名,注意,只要我们点击了录制,Kinect Studio都会给我们自动保存的。保存的位置就是我们刚才设置的文件夹,如果,你没有设置的话,可以去File 的Setting里面看看它的默认文件夹的位置。
    这里写图片描述

  6. 同样的操作,我们在做几遍,当然你的视频样本越多,我们生成的分类器就更准确啊。

####二 Visual Gesture Builder制作正负样本

  1. 打开软件,File -> New Solution, 自己随便命个名,我这里叫做DemoWave。

  2. 现在的面板李应该出现了你刚才project,右键这个工程,选择Create New Project With Wizard。
    这里写图片描述

  3. 出现VGB Gesture Wizard 的界面,点击 Next
    这里写图片描述

  4. 给我们的姿势命个名,我这里叫做demowave。
    这里写图片描述

  5. 这里是判断我们的姿势是否包含下半身的骨骼,因为我们只是挥手,腿部做什么动作都可以,所以,选择不包含。
    这里写图片描述

  6. 这里是判断我们的姿势是否需要判断特定的手势。因为我们挥手主要是看胳膊的动作,所以,不关心手部的动作,这里也选择不包含。
    这里写图片描述

  7. 这里是让我们来选择需要判断的骨骼的部分,每个图旁边都有一定的注解说它忽略了什么,我这里就不一一翻译了,因为我们判断的是挥手,所以,左右手都是需要判断的,但是不需要判断下半身的状态,所以,我们选择第一个joint mask就好。
    这里写图片描述

  8. 这里是问我们的动作判断是否是区分左右的?那我们的挥手动作为例,如果我们选择NO,那么Kinect判断挥手的时候,就不区分是左胳膊懂还是右胳膊动;如果我们选择YES的话,那么Visual Gesture Builder就会给我们建立两个Project一个叫做demowave_Right,另外一个叫做demowave_Left,就相当于建立两个分类标准,用于区分左右手。
    这里写图片描述

  9. 这个界面主要是来判断我们的动作是否是连续性的还是离散的。所谓离散性质,就是判断这个我们动作“发生了“还是“没有发生“,就是一个1还是0的判断,如果连续性质的话呢,就是判断Kinect检测到的姿势和我们分类器的姿势的吻合度有多少,它不是一个是否的判断而是一系列的数字来表示当前的姿势和分类器姿势的重合度,而且,这个数字越高,表示当前检测的动作和分类器的标志动作越像。而我们这里指向判断用户是否挥动了左手还是右手,是一个是否的判断,所以我们这里选择NO。
    这里写图片描述

  10. 这里就是说,下面会有这两个gesture会被创建。因为我们在之前选择了左右的判断,所以这里会出现两个getures如果刚才我们没有选择左右的判断,那么这里只会出现一个gesture。
    这里写图片描述

  11. 点击confirm,就会生成对应的工程,现在我们的工程如下图所示。
    这里写图片描述

    这里简单的说一下,一共是两个工程有四个文件生成,所以呢,就是每个gesture右两个文件,这两个文件名字都是一样的,只不过一个是.a结尾的,一个不是,那么.a结尾的文件呢,其实是测试gesture的一个文件,而那个不带.a的才是我们拿来训练的样本。基本上呢,我们是那样本数目的2/3用来训练样本,1/3就是放在那个.a 文件下,用来测试分类器的。

  12. 好的,下面,我们来利用刚才收集到的视频片段来创建正负样本。右键,demowave_Left,选择Add Chip,把我们刚才录的视频片段加入进去。(建议大家录完视频片段之后给它们改个名字,这样便于管理,当然,我说是视频片段,这里的视频片段其实是包含了Kinect的骨骼等数据的)
    这里写图片描述

  13. 我们把视频片段(也就是Chips)加入进去之后,然后告诉训练器哪些帧时正样本,哪些帧时负样本。其中正样本被蓝色的线标志出来了,这里需要手动判别正负样本,当然,对一段视频操作当然是少不了快捷键啊。尴尬的是,默认打开的窗口(也就是你们现在展示的窗口)是看不到快捷键提示的。那么我们只要把下面的Gesture Tags的区域拉大一点,是不是看见下面的快捷键提示啦?(为了避免出现误判的情况,除了收集足够多的正样本之外,我们还需要各种各样的负样本,如图,我们在做一个挥手的样本收集,我在录制视频的时候还跳动几下,用作负样本)
    这里写图片描述

  14. 然后,我们依次把每个Chips的政府样本都设置好之后。我们可以选择总的工程文件,右键Build,当然,你已经迫不及待的想看看当前姿势的训练情况,你也可以选择某一个Gesture文件右键Build。然后就是等待的过程,这个时间取决于样本的多少,因为我这里只有6个视频片段,所以训练时间很短。

  15. 当你发现下面的Output栏中出现build完成的提示,并且没有错误的话,那么恭喜你,你自定义姿势的分类器已经训练完成了。

  16. 还记得那个.a文件嘛?如果你还有没有训练的视频片段的话,可以把那个视频片段加入你的.a文件下,来测试分类器。当然,你也可以右键工程选择Live Preview,这样的话,Visual Gesture Builder就会打开Kinect,就会捕捉你的动作,实时的判断当前的动作是否符合条件。旁边的那个方框里面的数值(类似直方图)越高,说明你的动作越符合分类器。
    这里写图片描述

  17. 训练完成之后,我们会得到一个.gba的文件,这个就是我们训练出来的分类器!

三 Unity5 中调用分类器

  1. 首先创建一个Unity的工程,导入Kinect Unity的开发包。
    什么?不知道怎么使用Unity5 +Kinect v2,好吧,这又一个step by step的教程
    http://www.imaginativeuniversal.com/blog/2015/03/27/unity-5-and-kinect-2-integration/
  2. 在我们的工程文件(Assets)下面创建一个文件夹叫做StreamingAssets,然后把我们刚才生成的分类器文件(.gba文件/.gbd文件)放进去。
  3. 创建一个脚本GestureSourceManger.cs里面的代码如下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Windows.Kinect;
using Microsoft.Kinect.VisualGestureBuilder;
public class GestureSourceManager : MonoBehaviour {
public BodySourceManager _BodySource;
public string databasePath;
private KinectSensor _Sensor;
private VisualGestureBuilderFrameSource _Source;
private VisualGestureBuilderFrameReader _Reader;
private VisualGestureBuilderDatabase _Database;
// Gesture Detection Events
public event GestureEvent OnGesture;
private static GestureSourceManager instance = null;

public static GestureSourceManager Instance{
	get{
		return instance;
	}
}

// Use this for initialization
void Start() {
_Sensor = KinectSensor.GetDefault();
if (_Sensor != null) {
if (!_Sensor.IsOpen) {
_Sensor.Open();
}
// Set up Gesture Source
_Source = VisualGestureBuilderFrameSource.Create(_Sensor, 0);
// open the reader for the vgb frames
_Reader = _Source.OpenReader();
if (_Reader != null) {
_Reader.IsPaused = true;
_Reader.FrameArrived += GestureFrameArrived;
}
// load the ‘Seated’ gesture from the gesture database
string path = System.IO.Path.Combine(Application.streamingAssetsPath, databasePath);
Debug.Log ("database path is "+path);
_Database = VisualGestureBuilderDatabase.Create(path);
// Load all gestures
IList gesturesList = _Database.AvailableGestures;
for (int g = 0; g < gesturesList.Count; g++) {
Gesture gesture = gesturesList[g];
Debug.Log ("database name is "+ gesture.Name);
_Source.AddGesture(gesture);
}
}
instance = this;
}
// Public setter for Body ID to track
public void SetBody(ulong id) {
if (id > 0) {
_Source.TrackingId = id;
_Reader.IsPaused = false;
Debug.Log ("id is "+id);
} else {
_Source.TrackingId = 0;
_Reader.IsPaused = true;
}
gameObject.GetComponent ().AddGesture ();
}
// Update Loop, set body if we need one
void Update() {
if (!_Source.IsTrackingIdValid) {
FindValidBody();
}
}
// Check Body Manager, grab first valid body
void FindValidBody() {
if (_BodySource != null) {
Body[] bodies = _BodySource.GetData();
if (bodies != null) {
foreach (Body body in bodies) {
if (body.IsTracked) {
SetBody(body.TrackingId);
break;
}
}
}
}
}
/// Handles gesture detection results arriving from the sensor for the associated body tracking Id
private void GestureFrameArrived(object sender, VisualGestureBuilderFrameArrivedEventArgs e) {
VisualGestureBuilderFrameReference frameReference = e.FrameReference;
using (VisualGestureBuilderFrame frame = frameReference.AcquireFrame()) {
if (frame != null) {
// get the discrete gesture results which arrived with the latest frame
IDictionary<Gesture, DiscreteGestureResult> discreteResults = frame.DiscreteGestureResults;
if (discreteResults != null) {
foreach (Gesture gesture in _Source.Gestures) {
if (gesture.GestureType == GestureType.Discrete) {
DiscreteGestureResult result = null;
discreteResults.TryGetValue(gesture, out result);

						if (result.Confidence > 0.05) { 
						// Fire Event 
						if(OnGesture!=null){
							Debug.Log("Detected Gesture " + gesture.Name + " with Confidence " + result.Confidence);
							OnGesture(this,new KinectGestureEvent(gesture.Name, result.Confidence));	
						} 
					}
				} 
			} 
		} 
	} 
}

}
}
```
4. 然后在外部调用这个代码,注意:这个脚本是必须传入一个BodyManager对象的,还要写一下我们的分类器的名称(当然,这个你在脚本里面hardcode也可以)。
这里写图片描述

  1. 现在,点击运行,挥一下手,你就会发现,控制栏有输出啦!

我这里有一个KinectMLTest的工程,感兴趣的朋友,可以下载看看~
传送门:https://github.com/ArlexDu/UnityKinectMLTest

参考链接:
[1]https://channel9.msdn.com/Blogs/k4wdev/Custom-Gestures-End-to-End-with-Kinect-and-Visual-Gesture-Builder
[2]https://channel9.msdn.com/Blogs/k4wdev/Custom-Gestures-End-to-End-with-Kinect-and-Visual-Gesture-Builder-part-2-
[3]https://blog.hynra.com/post/kinect-2-0-unity-5-membuat-gesture-discrete-mode-dengan-visual-gesture-builder/
[4]https://social.msdn.microsoft.com/Forums/en-US/42a4059a-e8b4-4ffd-87e7-757e19dcd7ca/how-to-load-a-vgb-database-with-unity-plugin?forum=kinectv2sdk#4c895477-b120-4806-9f3c-5930b07ac8a1

2020.6.27更新
距离当初做这个小探索已经过去三年多了,好多细节也比较生疏… 可能无法及时回复有问题的小伙伴,其实我这个博文的主要参考来源就是我提供的这4篇参考文献,如果有问题的话也可以去看一下那几个链接…我这次也做了部分更新,在原有的仓库中增加了unity 2018的分支,希望可以在大家电脑上正常运行:
https://github.com/ArlexDu/UnityKinectMLTest/tree/unity_2018

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页