12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

3D SensorAdvent Calendar 2019

Day 10

初心者が Unity で始める AzureKinect

Last updated at Posted at 2019-12-09

3D Sensor Advent Calendar 2019 の 10 日目の記事です。

概要

3D センサー歴 3 か月程度の初心者ですが、もう一度、 AzureKinect で頑張ってみます。
前回は、AzureKinect をVisual Studio で動かしてみました。今回の目標としては、初心者レベルでUnity の使用と点群表示、画面を移動するところまで解説してみたいと思います。

環境

  • windows 10 pro
  • Unity 2019.2.12
  • Microsoft .NET Framework Version 4.8.03752
  • Azure Kinect Sensor SDK 1.3.0

環境としては、Unityと前回の Visual Studio、それと Azure Kinect Sensor SDK 3.0 がインストールされている状態で開始します。

環境について

今回もできるだけ初心者(私)向けに最小限でやりたいのですが、Unity だと、以下のような手順を踏みます

  1. Azure Kinect のソースからコンパイル、C/C++ SDK 作成
  2. Azure Kinect の SDK -> SDK を呼び出す C# wrapper 作成
  3. Unity から呼び出し

となります。準備するだけでも手間がかかる・・・でも、前回の知識で省略できるところがあります。

1.Azure Kinect のソースからコンパイル

最終的にはSensor SDK の k4a.dll / depthengine_2_0.dll というコンパイル済のモジュールが Unity では必要になります。しかし、これは、Azure Kinect Sensor SDK をサイトからダウンロードしていれば以下の場所にありますので、これを使うことにします。
これを Unity でネイティブプラグイン、今回なら、AzureKinect Sensor SDKや、C# Wrapper といった、別言語でコンパイルし多様なモジュールを読み込むための特殊フォルダ「Plugins」に格納すれば、読み込めるようになります。SDKはWindowsの64bit版だと
以下のデフォルトの場所に配置されます。

C:\Program Files\Azure Kinect SDK v1.3.0\sdk\windows-desktop\amd64\release\bin

コメント 2019-12-06 080310.png

2.Azure Kinect の SDK -> SDK を呼び出す C# wrapper 作成

これは、本来なら C# wrapper をコンパイルして Microsoft.Azure.Kinect.Sensor モジュールを配置する必要があります。
実はこれもまた、 Sensor SDK 内にあるのですが・・・

C:\Program Files\Azure Kinect SDK v1.3.0\sdk\netstandard2.0\release

コメント 2019-12-06 080545.png

最小限のサンプルという意味では、AzureKinect の Azure Kinect SensorSDK と、SDK C#Wrapper、これだけあれば済むかと思いきや、とりあえずやってみると・・・

コメント 2019-12-06 074457.png

おや・・・?

コメント 2019-12-06 074655.png

これは、System.Memoryというモジュールが足りない、と言われているようですね。それがないのでC# wrapper 本体がloadできないようです。(他のエラーは別原因でした)

The type 'Memory<>' is defined in an assembly that is not referenced. You must add a reference to assembly 
'System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.

どうやら、C# SDK 内のモジュールをPlugins に配置するだけでは動かないようです。これは 3DSensor Advent Calendar 二日目に書いた、「NuGet」を使用したいと思います(visual studioと同じ資源が使用できるのは非常に便利ですね。作成者に感謝です)。
それでは、以下に手順を書きます

1.先に「NuGetForUnity」をWebからダウンロードします
https://github.com/GlitchEnzo/NuGetForUnity/releases
NuGetForUnity.2.0.0.unitypackage をダウンロード

2.Unityを起動します。

3.NuGetForUnity.2.0.0.unitypackage をUnity のAssetsフォルダにドラッグしインストールします。

コメント 2019-12-06 084908.png

4.Nugetを選択します

コメント 2019-12-06 085744.png

  1. Azure Kinect で検索し、Azure Kinect Sensor SDK 1.3.0 をインストールします。(これで、SensorSDK以外に必要なモジュールも自動で入る)

コメント 2019-12-06 091943.png

6.C:\Program Files\Azure Kinect SDK v1.3.0\sdk\windows-desktop\amd64\release\bin
から「k4a.dll」「depthengine_2_0.dll」をPluginsフォルダに配置します。

7.これで環境構築は終了です。

Azure Kinect での point cloud 表示

では、本題の point cloud つまり点群を表示してみようと思います。

調べていると点群を Unity で表示する方法は二つあって、
1.mesh を使う方法
2.particle を使う方法

があります。どちらもスクリプトの用意さえできれば難しくないのですが、より簡単だと思った particle を使う方法を紹介します。

点群表示までの手順

  1. ヒエラルキーを右クリックし、Effects>Particleで Particle を生成します

コメント 2019-12-08 111402.png

2.Particle System にカーソルを合わせ、右側のInspectorをプロパティのデフォルトから一部変更します。まず、Transform のRotation を X=0,Y=0,Z=-180に変更します。その次に、Play on Awake と Auto Random Seed のチェックボックスを外してください。

コメント 2019-12-08 111643.png

3.Assetフォルダを右クリックして Create>C#script を指定します。名前はTestPointCloudにしますが任意の名前で大丈夫です。

コメント 2019-12-08 112155.png

4.TestPointCloud.cs の中身を以下で書き換えてください。

TestPointcloud.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.Azure.Kinect.Sensor;
using System;


public class TestPointCloud : MonoBehaviour
{
    public GameObject particleSystemGameObject;
    private ParticleSystem pointCloudParticleSystem;
    private ParticleSystem.Particle[] particles;

    private Device device = null;
    private Capture capture = null;
    private Transformation transformation = null;

    private Color pointColor = Color.white;
    private float size = 0.02f;
    private float scale = 10.0f;

    void Start()
    {
        particleSystemGameObject = GameObject.Find("Particle System");


        device = Device.Open(0);
        device.StartCameras(new DeviceConfiguration
        {
            ColorFormat = ImageFormat.ColorBGRA32,
            ColorResolution = ColorResolution.R720p,
            DepthMode = DepthMode.NFOV_2x2Binned,
            SynchronizedImagesOnly = true,
            CameraFPS = FPS.FPS30
        });
        transformation = device.GetCalibration().CreateTransformation();
    }


    void Update()
    {
        capture = device.GetCapture();

        Image colorImage = capture.Color;
        Image depthImage = capture.Depth;

        int colorImageWidth = colorImage.WidthPixels;
        int colorImageHeight = colorImage.HeightPixels;

        List<Vector3> vertices = new List<Vector3>(colorImageWidth * colorImageHeight);
        List<Color32> colors = new List<Color32>(colorImageWidth * colorImageHeight);

        convertRGBDepthToPointXYZRGB(capture, ref vertices, ref colors);

        particles = new ParticleSystem.Particle[colorImageWidth * colorImageHeight];
        for (int i = 0; i < colorImageWidth * colorImageHeight; ++i)
        {
            particles[i].position = vertices[i];
            particles[i].startColor = colors[i];
            particles[i].startSize = size;
            if (vertices[i].z == 0.0f)
            {
                particles[i].startSize = 0;
            }
        }

        pointCloudParticleSystem = particleSystemGameObject.GetComponent<ParticleSystem>();
        pointCloudParticleSystem.Clear();
        pointCloudParticleSystem.SetParticles(particles, particles.Length);
    }
    public struct Point : IEquatable<Point>
    {
        private short x;
        private short y;
        private short z;

        public Point(short x, short y, short z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public short X { get => x; set => x = value; }
        public short Y { get => y; set => y = value; }
        public short Z { get => z; set => z = value; }

        public bool Equals(Point other)
        {
            return (this.x == other.x) && (this.y == other.y) && (this.z == other.z);
        }
    }
    public void convertRGBDepthToPointXYZRGB(Capture capture, ref List<Vector3> vertices, ref List<Color32> colors)
    {
        Image colorImage = capture.Color.Reference();
        int colorImageWidth = colorImage.WidthPixels;
        int colorImageHeight = colorImage.HeightPixels;

        Image transformedDepthImage = new Image(ImageFormat.Depth16, colorImageWidth, colorImageHeight, colorImageWidth * sizeof(short));
        Image pointCloudImage = new Image(ImageFormat.Custom, colorImageWidth, colorImageHeight, colorImageWidth * 3 * sizeof(short));

        transformation.DepthImageToColorCamera(capture, transformedDepthImage);
        transformation.DepthImageToPointCloud(transformedDepthImage, pointCloudImage, CalibrationDeviceType.Color);

        for (int h = 0; h < colorImageHeight; ++h)
        {
            for (int w = 0; w < colorImageWidth; ++w)
            {
                short x = pointCloudImage.GetPixel<Point>(h, w).X;
                short y = pointCloudImage.GetPixel<Point>(h, w).Y;
                short z = pointCloudImage.GetPixel<Point>(h, w).Z;

                Vector3 xyz = new Vector3(
                    (float)x / 1000.0f,
                    (float)y / 1000.0f,
                    (float)z / 1000.0f
                    );

                Color32 rgb = new Color32(
                    colorImage.GetPixel<BGRA>(h, w).R,
                    colorImage.GetPixel<BGRA>(h, w).G,
                    colorImage.GetPixel<BGRA>(h, w).B,
                    Byte.MaxValue);

                vertices.Add(xyz);
                colors.Add(rgb);
            }
        }
    }

    private void OnDestroy()
    {
        device.StopCameras();
    }

}

4.Particle System にカーソルを合わせ、右側のInspectorの一番下のAddComponentで今作成したTestPointCloud.csを指定します。

コメント 2019-12-08 113432.png
コメント 2019-12-08 113817.png

5.指定したTestPointCloud.cs にはParticle System という部分が GameObject(None) 、つまり何も設定されていない状態になっていますので、ここに、左側のヒエラルキーから「Particle System」をドラッグして設定しましょう。

コメント 2019-12-08 114027.png

最後に上のPlayボタンで、実行します。うまくいったらカメラから点群が見えるようになります。

コメント 2019-12-08 114637.png

・・・この処理では、カメラの移動などの機能はないのですが、Unity では Scene から視点を変えられるので、立体的に表示されているのも分かると思います。

振り返り

  • 今回は、 C# から Unity まで手を伸ばしました。
  • 実は、AzureKinectの Unity 初心者向けは既に吉永崇さんが、ハンズオンセミナーや、記事にまとめています。(私もこれで学びました)
  • まだあまり中身が説明できていない or 自分でも分かっていないので、これは別に書いてみようかなと思います。

3D Sensor は奥が深く、多様な使い方ができるし、もっと役に立つ使い方があると思うので、色々と考えたいなと・・・

12
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?