Bakulog

獏の夢日記的な何か。

Azure Kinect DK+Unityで点群を取ろうとして失敗したときに読む記事

Azure Kinect DK+Unityで点群を取ろうとして失敗したときに読む記事です。

概要

最終的にこういう点群が出ます。

リファレンスにする記事

こちらの2記事を読んでUnityへの導入を試します。ゴールは後者の記事に書かれた点群表示の再現です。

tks-yoshinaga.hatenablog.com

tks-yoshinaga.hatenablog.com

しかしコレで実行すると何も表示されません。

UnityかAzure Kinect SDKのバージョン差が原因で動かなくなったようです。

エラーの原因追跡

ちょっと調べると、Kinectの画像を取得するTask内で例外スローされている事が確認できます。

AzureKinectException: result = K4A_RESULT_FAILED
Microsoft.Azure.Kinect.Sensor.AzureKinectException.ThrowIfNotSuccess[T] (System.Func`1[TResult] function) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
Microsoft.Azure.Kinect.Sensor.Image..ctor (Microsoft.Azure.Kinect.Sensor.ImageFormat format, System.Int32 widthPixels, System.Int32 heightPixels) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
Microsoft.Azure.Kinect.Sensor.Transformation.ColorImageToDepthCamera (Microsoft.Azure.Kinect.Sensor.Image depth, Microsoft.Azure.Kinect.Sensor.Image color) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
Microsoft.Azure.Kinect.Sensor.Transformation.ColorImageToDepthCamera (Microsoft.Azure.Kinect.Sensor.Capture capture) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
KinectScript+<KinectLoop>d__11.MoveNext () (at Assets/xxx/Scripts/KinectScript.cs:133)
UnityEngine.Debug:LogException(Exception)
KinectScript:OnDestroy() (at Assets/xxx/Scripts/KinectScript.cs:143)

サンプルコード中のここがエラーになります。

Image colorImage = transformation.ColorImageToDepthCamera(capture);

修正方針はいくつか考えられますが、その前にカラー画像用のImageStart時点で作るように書き換えます。

これはメモリアロケーションを少し削りたいな~という程度のモチベーションです。

まずprivateフィールドに_depthColorを追加。

    //...

    //vertices中の何番目の点を描画するかのリスト(全部描画するけど手続き上必要)
    int[] indices;
    //座標変換(Color⇔Depth対応やDepth→xyzなど)をするためのクラス
    Transformation transformation;
    
    //追加: Depth用に変換された画像を保持するクラス
    Image _depthColor;

   //...

次に、InitKinectの末尾に_depthColorの初期化を追加。

//Kinectの初期化
private void InitKinect()
{
    //0番目のKinectと接続
    kinect = Device.Open(0);     
    //Kinectの各種モードを設定して動作開始
    kinect.StartCameras(new DeviceConfiguration
    {
        ColorFormat = ImageFormat.ColorBGRA32,
        ColorResolution = ColorResolution.R720p,
        DepthMode = DepthMode.NFOV_2x2Binned,
        SynchronizedImagesOnly = true,
        CameraFPS = FPS.FPS30
    });        
    //座標変換(Color⇔Depth対応やDepth→xyz)のための情報を生成
    transformation = kinect.GetCalibration().CreateTransformation();
    
    //追加: depthColorの初期化。Depthの解像度ベースになることに注意
    _depthColor = new Image(
        ImageFormat.ColorBGRA32,
        kinect.GetCalibration().DepthCameraCalibration.ResolutionWidth,
        kinect.GetCalibration().DepthCameraCalibration.ResolutionHeight
        );
}

初期化した_depthColorはタスク内のループで使います。

    //...

    //GetCaptureでKinectのデータを取得
    using (Capture capture = await Task.Run(() => kinect.GetCapture()).ConfigureAwait(true))
    {
        //Depth画像との位置・サイズ合わせ済みの画像を取得
        //Image colorImage = transformation.ColorImageToDepthCamera(capture);
        transformation.ColorImageToDepthCamera(capture, _depthColor);
        
        //色情報のみの配列を_depthColorから取得
        BGRA[] colorArray = _depthColor.GetPixels<BGRA>().ToArray();

        //capture.DepthでDepth画像を取得

        //...

これで再度実行するとうまくいき…ません。

今度は_depthColorの初期化時点で例外スローされます。

AzureKinectException: result = K4A_RESULT_FAILED
Microsoft.Azure.Kinect.Sensor.AzureKinectException.ThrowIfNotSuccess[T] (System.Func`1[TResult] function) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
Microsoft.Azure.Kinect.Sensor.Image..ctor (Microsoft.Azure.Kinect.Sensor.ImageFormat format, System.Int32 widthPixels, System.Int32 heightPixels) (at <1f5823aa499a4d77b8e0e7cd2a5615aa>:0)
KinectScript.InitKinect () (at Assets/xxx/Scripts/KinectScript.cs:57)
KinectScript.Start () (at Assets/xxx/Scripts/KinectScript.cs:32)

根本的なスロー箇所は最初と同じで、Imageの生成処理がエラーになっています。

ここでちょっと想像を働かせて、「Imageの別のコンストラクタを使えばいいのでは?」と考えてみます。

_depthColorの初期化をこう書き換えます。

_depthColor = new Image(
    ImageFormat.ColorBGRA32,
    kinect.GetCalibration().DepthCameraCalibration.ResolutionWidth,
    kinect.GetCalibration().DepthCameraCalibration.ResolutionHeight,
    kinect.GetCalibration().DepthCameraCalibration.ResolutionWidth * 4
    );

4つ目の引数はstrideで、画像1行ぶんのバイト数を指定します。

今回は画像フォーマットがBGRA=32bit=4byteなため、画像幅に4を掛けるだけです。

これで再度実行すると正常に動き、冒頭の画像のような点群を得られます。

最後に

タスク周りとか細かいとこを整形したコードを貼っておきます。

https://gist.github.com/malaybaku/59c69906e11bb9a0d008783ea4db5d08

リファレンスの記事通りにNuGetパッケージの取得とかDLLの配置を行ったうえで使うと動くはずです。最適化の観点で見るとまだまだ怪しいコードなので、実用時はご注意下さい。

Azure Kinect自体の所感としては、点群はけっこうキレイに出るので点群ビジュアライズで遊ぶのが良いのかな~と思いました。