在上述深度幀獲取的基礎上,利用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時