1. 程式人生 > >kinect獲取深度數據並顯示

kinect獲取深度數據並顯示

超出 index avg upd sensor dex urn .get data

在上述深度幀獲取的基礎上,利用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獲取深度數據並顯示