Kinect结合Unity基础使用(一)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kinect结合Unity基础使用(一)相关的知识,希望对你有一定的参考价值。
参考技术A SDK 安装kinect 是输入。类似鼠标 即时动态捕捉、影像辨识、麦克风输入、语音辨识
Kinect 是一组传感器的组合,包含一个彩色摄像头,一对深度摄像头(一个用以发射红
外,一个用以接收) ,一组麦克风阵列和底座马达。
彩色摄像头想必大家都已经很熟悉了。深度摄像头是由红外线提供的,返回画面上每个
点的深度值(离 Kinect 的距离,单位是 mm,标称精度约 5mm) 。麦克风阵列其实是数个水平放置的麦克风,因为彼此间有距离,所以可以识别说话人的方向,借此粗略估计是哪个玩家在说话,便于想象的话可以理解为类似声纳图(声音我们不涉及)。
读取彩色数据流,可以获得一张1080p的图片(1920*1080);
犹如拍照应用
看SDK自带示例
Color Depth D2D 彩色数据流
和许多输入设备不一样,Kinect 能够产生三维数据,它有红外发射器和摄像头。 Kinect SDK 从红外摄像头获取的红外数据后,对其进行计算处理,然后产生景深影像数据。
深度帧数据中,每个像素占 16 位,即每一个像素占 2 个字节。每一个像素的深度值只占用了 16 个位中的 13 个位。
获取每一个像素的距离很容易,但是要直接使用还需要做一些位操作。如上图所示,深度值存储在第 3 至 15 位中,要获取能够直接使用的深度数据需要向右移位,将游戏者索引(Player Index)位移除
看SDK自带示例
Depth Basics-D2D 深度数据流
0.5-4.5米
25个关节点 每个关节点的 3D空间坐标和转向
看SDK自带示例
Body Basics D2D 骨骼数据流
示例:
Color Depth D2D 彩色数据流
Depth Basics-D2D 深度数据流
Coordinate Mapping Basics 扣出人物,置换背景
Body Basics D2D 骨骼数据流
kinect获取深度数据并显示
在上述深度帧获取的基础上,利用unity的Mesh组件,将深度帧显示出来。
工具为Unity5.6、Kinect开发包KinectForWindows_UnityPro_2.0.1410
首先讲一个Mesh的应用
Mesh有多种方式实现,这里只用最简单的,通过设定顶点组成三角形集合的方式,主要工作是设定三个属性:
① vertices,顶点集合,Vector3类型,一般为所要显示的像素坐标集合,这里为深度帧每个像素的坐标值,其中z为深度值。
注意:Unity中顶点数量不能超过65000个。
②uv ,UV集合,Vector2类型,
③triangles,三角形所含顶点索引集合,int类型。在由三个顶点组合时,索引的顺序必须按照顺时针存储。
假设现有4个顶点 上左(1)上右(2)下左(3)下右(4),括号内位索引,则三角形1的存放顺序为1、2、3,三角形2的存放顺序为3、1、4。
基本实现思路是:
①根据深度帧的大小和采样频率,创建一个Mesh,为什么需要采样,因为顶点数不能太多,这时Mesh中的属性除了顶点的z坐标未确定外,其余都已经OK。
②获取深度帧,找到Mesh中每个顶点所对应的像素点,就找到了该顶点的深度值,这时z坐标也确定了,刷新,完成。
下面开始上代码:
1 定义变量
private KinectSensor _Sensor; //Kincet传感器 private DepthFrameReader _depthReader; //深度帧读取器 private ushort[] _depthData; //因为深度数据为2字节16bit,所以定为ushort存放数据 private Mesh _Mesh; //顶点集合 private Vector3[] _Vertices; //顶点的UV坐标集合 private Vector2[] _UV; //三角形的顶点集合,_Vertices中的 private int[] _Triangles; //only works at 4 right now //降频采样大小 private const int _DownsampleSize = 4; //实际距离与Unity内模型距离的缩放系数,若实际距离为4m,则在Unity中显示为0.04m private const float _DepthScale = 0.01f;
2.初始化
void Start () { _Sensor = KinectSensor.GetDefault (); if (_Sensor != null) { _depthReader = _Sensor.DepthFrameSource.OpenReader (); var frameDescript = _Sensor.DepthFrameSource.FrameDescription; //数组大小为整个像素的长度 _depthData = new ushort[frameDescript.LengthInPixels]; //创建Mesh网格 还差顶点的z值 即深度值,为了减少顶点数量 所以进行采样。 CreateMesh (frameDescript.Width / _DownsampleSize, frameDescript.Height / _DownsampleSize); } if (!_Sensor.IsOpen) _Sensor.Open (); }
3.创建Mesh网格,网格中顶点的数量为像素点的数量,width*height
void CreateMesh(int width,int height) { _Mesh = new Mesh (); GetComponent<MeshFilter> ().mesh = _Mesh; _Vertices = new Vector3[width * height]; _UV = new Vector2[width * height]; //三角形的数量等于横竖像素点减1“-1”后相乘 再乘以6 如下: _Triangles = new int[(width - 1) * (height - 1) * 6]; int triangleIndex = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { //顶点的索引就是像素按照一行一行的记录,坐标值x,y为像素在平面的位置,z为深度值 int index = y * width + x; _Vertices [index] = new Vector3 (x, -y, 0); _UV [index] = new Vector2((float)x /width, (float)y /height); //确定三角形的顶点索引,每四个顶点确定两个三角形,每个三角形的顶点索引顺序为顺时针,否则不予显示 //忽略最后一行和列, if (x != (width - 1) && y != (height - 1)) { int top_left = index; int top_right = top_left + 1; int bottom_left = top_left + width; int bottom_right = bottom_left + 1; //第一个三角形 _Triangles [triangleIndex++] = top_left; _Triangles [triangleIndex++] = top_right; _Triangles [triangleIndex++] = bottom_left; //第二个三角形 _Triangles [triangleIndex++] = bottom_left; _Triangles [triangleIndex++] = top_right; _Triangles [triangleIndex++] = bottom_right; } } } _Mesh.vertices = _Vertices; _Mesh.uv = _UV; _Mesh.triangles = _Triangles; _Mesh.RecalculateNormals (); }
4获取深度值后,通过将深度数据赋予对应的顶点,显示深度图
//将深度数据赋予对应的顶点,显示深度帧数据, void ShowDepthView() { var frameDescript = _Sensor.DepthFrameSource.FrameDescription; //这里的x,y是没有采样的像素点,所以需要加采样频率才能和采样过的顶点集合想对应,x,y主要用来计算采样点和周围点的平均深度值 for (int y = 0; y < frameDescript.Height; y += _DownsampleSize) { for (int x = 0; x < frameDescript.Width; x += _DownsampleSize) { int indexX = x / _DownsampleSize; int indexY = y / _DownsampleSize; int smallIndex = (indexY*(frameDescript.Width/_DownsampleSize)) + indexX; //计算平均深度值 float avg = GetAvgDepth (x, y, frameDescript.Width, frameDescript.Height); _Vertices [smallIndex].z = avg * _DepthScale; } } _Mesh.vertices = _Vertices; _Mesh.uv = _UV; _Mesh.triangles = _Triangles; _Mesh.RecalculateNormals (); } float GetAvgDepth(int x,int y,int width,int height) {//计算平均深度值 double sum = 0; for (int y1 = y; y1 < y + _DownsampleSize; y1++) { for (int x1 = x; x1 < x + _DownsampleSize; x1++) { int index = y1 * width + x1; if (_depthData [index] == 0) sum += 4500;//表示超出范围 else sum += _depthData [index]; } } int count = _DownsampleSize * _DownsampleSize; return (float)(sum/count); }
5 获取深度数据,并显示
void Update () { if (_depthReader == null) return; var frame = _depthReader.AcquireLatestFrame (); if (frame != null) { frame.CopyFrameDataToArray (_depthData); ShowDepthView (); frame.Dispose (); frame = null; } }
6 释放
void OnApplicationQuit() { if (_depthReader != null) { _depthReader.Dispose (); _depthReader = null; } if (_Sensor != null) { if (_Sensor.IsOpen) { _Sensor.Close (); } _Sensor = null; } }
7效果,当取样频率为4时
以上是关于Kinect结合Unity基础使用(一)的主要内容,如果未能解决你的问题,请参考以下文章