TECHブログ


Azure Kinectで非接触型のインタラクティブコンテンツを考える

はじめに

コロナ禍において、デジタルサイネージ等のインタラクティブコンテンツ制作は、タッチパネル操作などの接触型ではなく、非接触での操作を考えていく必要がありそうです。そこで、Azure Kinectを使って身体の動きによって画面操作する方法を紹介します。

  • Azure Kinectとは
    洗練されたコンピュータービジョンと音声モデル、高度な AI センサー、Azure Cognitive Servicesに接続できるさまざまな強力なSDKを備えた、最先端の空間コンピューティング開発者キットです。

動作デモ

Azure Kinectを利用して、以下のような非接触型の操作を考えました。

Handの動きとUIカーソルの連動

マウス操作の代わりを、手の動きで行えるようにします。

  • カーソル操作 -> Handの動きに追従
  • ボタンをクリックして決定 -> ボタンに一定時間カーソルを合わせたら決定

<デモ動画>

Hand Upジェスチャー

表示画像を切り替えるを、手を挙げる操作で行えるようにします。

  • 右手を挙げると次の画像
  • 左手を挙げると前の画像

<デモ動画>

Swipeジェスチャー

表示画像を切り替えるを、手を横に振る(スワイプする)動作で行えるようにします。

  • 右手を振ると次の画像
  • 左手を振ると前の画像

<デモ動画>

Unityでの実装

動作デモで利用しているAzure KinectのBody TrackingのUnityを使用した実装について説明します。

前提

動作環境

システム要件

  • CPU:第7世代 Core i5 クアッドコア 2.4GHz 以上
  • メモリ:4GB 以上
  • GPU:NVIDIA GeForce GTX 1070 以上推奨
  • USB接続:USB 3.x (2.0は不可)
開発環境
  • Unity2019.4.x
  • Visual Studio 2019
  • MicrosoftのGitHub Azure-Kinect-Samples内のUnityプロジェクト sample_unity_bodytracking を利用
UnityでBody Trackingを使用可能にする

sample_unity_bodytrackingプロジェクト直下のREADME.mdの内容を参考に設定を行います。

  1. get the latest nuget packages of libraries
    UnityプロジェクトをVisual Studioで開いて、Microsoft.Azure.Kinect.BodyTrackingのNuGetパッケージをインストールする。
  2. add these libraries to the Assets/Plugins folder
    Packegsフォルダ内の各ファイルをPluginsフォルダにコピーする。
  3. add these libraries to the sample_unity_bodytracking project root directory that contains the Assets folder
    Packegsフォルダ内の各ファイルをプロジェクト直下にコピーする。

2と3は、プロジェクト直下のMoveLibraryFiles.batを実行すれば自動で処理できます。

ビルド時にdllファイルをコピーする

以下のファイルをビルドしたフォルダ直下(exeファイルのあるフォルダ)にコピーすることで、exeファイル実行時にAzure Kinectを使用できるようになります。

  • cublas64_100.dll
  • cudart64_100.dll
  • cudnn64_7.dll
  • dnn_model.onnx
  • onnxruntime.dll

※Body Tracking使用のための設定をした際に、プロジェクト直下にコピーしたものと同じです。

Skeleton情報を取得する

Skeleton(骨格)情報には、階層構造を持つ32の関節(Azure Kinect ボディ トラッキングの関節)の情報が含まれています。

Skeleton情報をC#から取得します。

using Microsoft.Azure.Kinect.Sensor;
using Microsoft.Azure.Kinect.BodyTracking;
using System.Threading.Tasks;
// Kinectの情報を扱う変数
private Device device;
private DeviceConfiguration deviceConfig;

private Tracker tracker;
private TrackerConfiguration trackerConfig;
private Calibration calibration;

private Skeleton kinectSkeleton;

void Start()
{
    bool kinectCheck = InitDevice();

    if (kinectCheck)
    {
        Task trackerTask = GetTrackerLoop();
    }
}

// Kinectの初期化を行う
private bool InitDevice()
{
    bool devideCheck = false;

    // 接続されているKinectの数をチェック
    int count = Device.GetInstalledCount();

    if (count > 0)
    {
        // 1台目のKinectに接続
        try
        {
            device = Device.Open(0);
        }
        catch (AzureKinectOpenDeviceException ex)
        {
            return devideCheck;
        }

        // 起動
        try
        {
            device.StartCameras(deviceConfig = new DeviceConfiguration
            {
                ColorFormat = ImageFormat.ColorBGRA32,
                ColorResolution = ColorResolution.R720p,
                DepthMode = DepthMode.NFOV_Unbinned,
                SynchronizedImagesOnly = true,
                CameraFPS = FPS.FPS30
            });
        }
        catch (AzureKinectStartCamerasException ex)
        {
            return devideCheck;
        }

        // トラッカーを作成する
        calibration = device.GetCalibration(deviceConfig.DepthMode, deviceConfig.ColorResolution);

        trackerConfig = new TrackerConfiguration();
        trackerConfig.ProcessingMode = TrackerProcessingMode.Gpu;
        trackerConfig.SensorOrientation = SensorOrientation.Default;

        skeletonPoints = new GameObject[(int)JointId.Count - 1];

        devideCheck = true;
    }
    return devideCheck;
}

// 別スレッドでSkeletonを取得
private async Task GetTrackerLoop()
{
    using (tracker = Tracker.Create(calibration, trackerConfig))
    {
        while (true)
        {
            // Kinectから送られてくるフレームを取得
            using (Capture capture = await Task.Run(() =>
                    device.GetCapture()).ConfigureAwait(true))
            {
                tracker.EnqueueCapture(capture);
            }

            using (Frame frame = await Task.Run(() =>
                    tracker.PopResult(TimeSpan.Zero, throwOnTimeout: false)).ConfigureAwait(true))
            {
                if (frame != null)
                {
                    if (frame.NumberOfBodies > 0)
                    {
                        // 最初にKinectが認識したSkeletonを取得
                        kinectSkeleton = frame.GetBodySkeleton(0);
                    }
                }
            }
        }
    }
}

取得したSkeleton情報から、今回はHANDのPositionを操作に利用します。

  • HAND_LEFT
  • HAND_RIGHT

HANDの位置を画面操作に利用する

3次元座標のHANDの位置情報を使って、どうやって2次元座標のスクリーン上のカーソルを動かせばいいのか悩みます。そこで、Azure Kinectで認識している体験者の前に追従する見えないスクリーンがあると考え、仮想スクリーン上のローカル座標のPositionを、UIのスクリーン上のローカル座標のPositionに代入します。

<Unity Editor上の動作>

Skeletonが取得できていれば、モニターの真正面に体験者が立っている必要はなく、多少ズレた位置に立っていてもカーソルの操作ができます。また、ジェスチャー操作は、手を頭より高い位置まで挙げた場合などの条件を設定することで実現できます。

まとめ

Azure Kinectを使用することで簡単に非接触の画面操作が実現できました。今回は簡単な例を試しましたが、Body Trackingによる位置情報やジェスチャーによる操作方法は色々と考えられ、新様式のコンテンツ制作にも合わせることができそうです。