Sadao Tokuyama
Magic Leap 1 QRコードトラッキングについて
見出し画像

Magic Leap 1 QRコードトラッキングについて

Sadao Tokuyama

概要

Lumin SDK 0.25からバーコードトラッキング APIが搭載されました。この記事ではMagic Leap 1でバーコードトラッキング機能を実装する方法と利用する際に気をつけるべき点を説明します。

現時点において、バーコードトラッキングはQRコードのみサポートしているため、QRコードによるトラッキングについての説明となります。

QRコードトラッキング機能について

Magic Leap 1でQRコードのポジション、エンコードされたデータ(URLやテキスト情報)を取得することができます。QRコードはモデル1とモデル2のみサポートしています。(モデル1とモデル2については以下のリンク先ページにてご確認ください。)

同時追跡可能なQRコードの数

16個のQRコードを同時に追跡することができます。(16個以上のQRコードがある場合、一番小さいQRコードまたは一番遠いQRコードが除外されます。)

開発環境

Lumin SDK 0.25 の場合 Unity Editor 2020.2
Lumin SDK 0.26 の場合 Unity Editor 2020.3  

以下はLumin SDK のバージョンに対し、サポートしているUniry Editorのバージョンが全て網羅したページになります。

https://developer.magicleap.com/en-us/learn/guides/unity-doc-archive

※ Unreal Engine、Lumin Runtimeでは当機能を利用することができません。

構築 / 実装

ここではUnity Editorを使用し、Magic Leap 1上で動作するQRコードトラッキングのサンプルアプリケーションを構築します。

Privileges(特権)

Camera Captureの設定が必要です。(この設定を行うと、Magic Leap 1のカメラ撮影機能とビデオ撮影機能は利用できません、)

API Level

Level 9 以上の設定が必要です。

1. シーンの作成

Magic Leap の Exampleシーン(Hello Cubeなど)を複製。ヒエラルキー上にあるMain CameraとDirectional Light 以外のGame Objectを全て削除します。

画像1

2. Privilege Requesterの作成

画像2

Privilege Requester というGameObjectを作成します。

画像3

Privilege RequesterのGame Objectに対し、Add Componentを行い、MLPrivilegeRequesterBehaviorBehaviorクラスを追加します。

画像4

MLPrivilegeRequesterBehavior クラスの Privilege Camera Capture を追加します。

3. MLQRCodeVisual の作成

画像6

QRコードトラッキングのポジションに配置するCubeを作成。

名称をMLQRCodeVisualに変更。

Scaleは x = 0.06, y = 0.06, y = 0.06 に変更。

画像7

MLQRCodeVisualの配下にTextMeshを追加。生成したTextMeshの名称をinfoに変更。

画像13

infoのPosition、Scale、Text、Text Meshのカラーなどの設定値は、上記の設定を参考にしてください。

次に MLQRCodeVisual.cs というC#スクリプトを新規作成します。その後、以下のコードを反映します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.MagicLeap;

public class MLQRCodeVisual : MonoBehaviour
{
   [SerializeField]
   private TextMesh dataText;
#if UNITY_LUMIN
   private Timer disableTimer;
#endif

   void Awake()
   {
#if UNITY_LUMIN
       disableTimer = new Timer(3f);
#endif
   }

   void Update()
   {
#if UNITY_LUMIN
       if (gameObject.activeSelf && disableTimer.LimitPassed)
       {
           gameObject.SetActive(false);
       }
#endif
   }

   public void Set(MLBarcodeScanner.BarcodeData data)
   {
#if UNITY_LUMIN
       disableTimer?.Reset();
#endif

       transform.position = data.Pose.position;
       transform.rotation = data.Pose.rotation;
       dataText.text = data.ToString();
       gameObject.SetActive(true);
   }
}

画像8

MLQRCodeVisual.cs MLQRCodeVisual Game Object にAdd Componentで追加。その後、MLQRCodeVisual.cs Data Text MLQRCodeVisual の 子コンポーネント、info を設定します。

画像10

MLQRCodeVisualをPrefab化。ヒエラルキーにあるMLQRCodeVisualは削除します。

4. MLQRCodeSampleの作成

画像10

MLQRCodeSample というGameObjectを作成します。

次にMLQRCodeSample.csというC#スクリプトを新規作成後、以下のコードを反映します。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.MagicLeap;
using UnityEngine.XR.Management;
using static UnityEngine.XR.MagicLeap.MLBarcodeScanner;
using BarcodeSettings = UnityEngine.XR.MagicLeap.MLBarcodeScanner.Settings;

public class MLQRCodeSample : MonoBehaviour
{
    [SerializeField, Tooltip("QRコードの大きさをメートル単位で表したもの")]
    private float QRCodeSize = 0.1f;
    [SerializeField, Tooltip("QRコードのデータを可視化するPrefab")]
    private MLQRCodeVisual qrCodeVisualPrefab;

    // トラッキング済みのQRコード
    private Dictionary<string, MLQRCodeVisual> qrCodeVisualByBarcodeData = new Dictionary<string, MLQRCodeVisual>();

    // QRコードトラッキングの設定
    private BarcodeSettings _barcodeSettings;

    // QRコードスキャンの重複起動を防ぐためのフラグ
    private bool _isScanning;

    // Control入力がサブスクライブされているかどうかを確認するためのフラグ
    private bool _didInit;

    void Start()
    {
        MLBarcodeScanner.BarcodeType type = MLBarcodeScanner.BarcodeType.All;

        _barcodeSettings = MLBarcodeScanner.Settings.Create(false, type, QRCodeSize);
        SetSettingsAsync(_barcodeSettings);
    }

    private void MLInputOnOnTriggerDown(byte controllerid, float triggervalue)
    {
#if PLATFORM_LUMIN
       // Controlのトリガーが押した状態で且つ、QRコードのスキャンを実施していない場合
       if (triggervalue > .5f && !_isScanning)
       {
           // QRコードのスキャンを開始
           StartScanningAsync();

           // QRコードのスキャン中を判定するフラグを立てる
           _isScanning = true;
           
           // Controlを振動させる 
           MLInput.Controller targetController = MLInput.GetController(controllerid);

           targetController.StartFeedbackPatternVibe(MLInput.Controller.FeedbackPatternVibe.ForceDown, MLInput.Controller.FeedbackIntensity.Medium);
       }
#endif
    }

    private void MLInputOnOnTriggerUp(byte controllerid, float triggervalue)
    {
#if PLATFORM_LUMIN
       // Controlのトリガーが離した状態で且つ、QRコードのスキャンを実施している場合
       if (triggervalue < .5f && _isScanning)
       {
           // QRコードのスキャンを停止
           StopScanningAsync();

           // QRコードのスキャン中を判定するフラグをOFFにする
           _isScanning = false;

           // Controlを振動させる 
           MLInput.Controller targetController = MLInput.GetController(controllerid);

           targetController.StartFeedbackPatternVibe(MLInput.Controller.FeedbackPatternVibe.ForceDown, MLInput.Controller.FeedbackIntensity.Medium);
       }
#endif
    }

    private void OnDestroy()
    {

    }

    private void OnEnable()
    {
#if PLATFORM_LUMIN
       // XR Managerが動作中かを確認します。1つでも実行されていれば、Magic Leap 1であるとみなし、
       // ControlのTriggerのイベントハンドラーを登録する。
       if (XRGeneralSettings.Instance.Manager.isInitializationComplete)
       {
           _didInit = true;
           MLBarcodeScanner.OnMLBarcodeScannerResultsFound += OnMLBarcodeScannerResultsFound;
           MLInput.OnTriggerDown += MLInputOnOnTriggerDown;
           MLInput.OnTriggerUp += MLInputOnOnTriggerUp;
       }
#endif
    }

    private void OnDisable()
    {
       if (_didInit)
       {
           MLBarcodeScanner.OnMLBarcodeScannerResultsFound -= OnMLBarcodeScannerResultsFound;
           MLInput.OnTriggerDown -= MLInputOnOnTriggerDown;
           MLInput.OnTriggerUp -= MLInputOnOnTriggerUp;
       }
    }

    #region Magic Leap Barcode Scanner API
    private void SetSettingsAsync(BarcodeSettings value)
    {
       _ = MLBarcodeScanner.SetSettingsAsync(value);
    }

    private void StartScanningAsync(BarcodeSettings? settings = null)
    {
       _ = MLBarcodeScanner.StartScanningAsync(settings);
    }

    private void StopScanningAsync()
    {
       _ = MLBarcodeScanner.StopScanningAsync();
    }

    private void OnMLBarcodeScannerResultsFound(BarcodeData data)
    {
           Debug.Log(data.StringData);

       if (data.Type != MLBarcodeScanner.BarcodeType.None)
       {
               ExtractBarcodeScannerData(data);
       }
    }


   /// <summary>
   /// QRコードのPrefabを作成または更新します。
   /// </summary>
   /// <param name="data"></param>
   private void ExtractBarcodeScannerData(BarcodeData data)
    {
       Debug.Log(data.StringData);
       if (qrCodeVisualByBarcodeData.TryGetValue(data.StringData, out MLQRCodeVisual visual))
       {
               visual.Set(data);
       }
       else
       {
           MLQRCodeVisual qrCode = Instantiate(qrCodeVisualPrefab);
           qrCodeVisualByBarcodeData.Add(data.StringData, qrCode);
           qrCode.Set(data);
       }
    }
    #endregion
}

画像11

MLQRCodeSample.cs MLQRCodeSample Game Object にAdd Componentで追加。

画像12

MLQRCodeSample.cs の qrCodeVisualPrefabにプレハブ化したMLQRCodeVisual を設定します。

画像14

QR Code Sizeのデフォルト値を0.1としています。この値はメートル単位となるため10cmのQRコードを対象とする設定になります。(QR Code Sizeで設定した大きさのQRコードを用意してください。サイズが異なるとポジションがズレます。)

5. アプリケーションのビルド

QRコードトラッキングのサンプルアプリケーションの構築は、これで完了しました。最後はMagic Leap 1にビルドする設定~ビルドまでを行います。

画像13

Player Settings の Magic Leap Manifest Settings にある Controller Pose、Camera Capture にチェックを入れます。以上でQRコードのトラッキングの実装と設定は全て完了しました。Magic Leap 1をPCに接続して、Build And Run を実行します。

6. 動作確認

Control の Triggerを押しつづけるとQRコードのトラッキング処理が開始され、対象のQRコードが見つかれば、MLQRCodeVisual GameObjectがQRコードの上に配置されます。(Triggerを離すとQRコードのトラッキング処理は停止します。)

注意点

1. トラッキングの遅延について

検出されたQRコードの位置と実際の位置の間に遅延が発生します。

遅延が発生する原因は2つ。

・MLBarcodeScannerが非同期で動作している。
・カメラ画像は最大で8fpsで更新している。

上記により、リアルタイムで且つスムーズなトラッキングを行うことができません。

対応案

1. QRコードトラッキングするオブジェクトが静止状態の場合

検出したタイミングでQRコードトラッキングのスキャンを無効にし、PCFを使ってオブジェクトの位置を保存します。(複数のセッションでコンテンツを持続させたい場合や、ヘッドトラッキングが失われても、QRコードを再スキャンすることなくオブジェクトの位置を復元させたい場合など、)

2. QRコードトラッキングに連動してオブジェクトの位置をリアルタイムにトラッキングを行う場合

ポーズを補間・予測してトラッキングの品質を向上させます。
ネットワークライブラリは、動的オブジェクトに対してこれを行います。 Photon SmoothSync または MirrorNetworkingのNetworkTransformBase.cs が参考になります。

2. QRコードの推奨サイズ

ユーザーがQRコードとどれくらい離れているかによって異なります。一般的な照明の明るさの場合、A4サイズの紙に印刷されたQRコードであれば、約1メートルの距離からトラッキングされます。(トラッキングされた後、よりある程度の距離が離れてもトラッキングは維持されます。)

QRコード自体が小さい場合と大きい場合、距離によって小さくなったり大きくなったりします。それに応じて、検出範囲と追跡範囲が短くなったり長くなったりします。

QRコードは少なくとも4cm(明るい部屋の場合)以上の大きさで、QRコードの周りに白い枠があること。カメラからはっきり見えるような大きさにする必要があります。

16個のQRコードを同時に追跡することができますが、16個のQRコードをひとつのカメラに収めようとした場合、必然的にQRコードのサイズが小さくなるため、認識率は悪くなります。(16個のQRコードを同時追跡するのは現実的でなく、数個程度に留めるべきです。)

まとめ

・QRコードのトラッキングはLumin SDK 0.25(Lumin OS 0.98.20)から利用可。
・QRコードはモデル1とモデル2のみサポート。
・QRコードのポジションとデータ(URLなどのテキスト情報)が取得可。
・QRコードは最大16個まで認識可。
・トラッキングはリアルタイムでない。
(トラッキングの機能が非同期であり、カメラのFPSが最大8fpsで更新しているため。)
・QRコードは4cm以上の大きさであること。
・QRコードのサイズをMLBarcodeScanner.Settingsに設定すること。
(異なっていると正しい位置に配置してくれないため。)
・カメラにはっきり見える大きさであること。
・トラッキングの遅延対策としてPCFやポーズを補間・予測処理を行う方法がある。

今後、Magic Leapは、QRコードトラッキングを効果的に使用するための情報をオンラインのガイドで提供する予定です。(注意点でまとめた内容をより深掘りしたガイドになると思います。)

参考

QRコードのExample PackageはMagic Leap Developer Relations の方が作成しております。パッケージは以下から入手することができます。(今回の記事のサンプルもこちらのExampleを参考に構築しました。)

Magic Leapは、QRコードトラッキングを効果的に使用するための情報をオンラインのガイドで提供する予定です。

最後に

弊社では、Magic Leap 1を活用したアプリケーションをMagic Leap Worldに2つリリースしています!

OnePlanet XR

OnePlanet XR」はAR/MR技術に専門特化したコンサルティングサービスです。豊富な実績を元に、AR/MR技術を活用した新たな事業の立ち上げ支援や、社内業務のデジタル化/DX推進など、貴社の必要とするイノベーションを実現いたします。

MRグラスを活用した3Dモデル設置シミュレーション

ご相談から受け付けております。ご興味ございましたら弊社までお問い合わせください。

お問い合わせ先: https://1planet.co.jp/xrconsulting.html

OnePlanet Tech Magazine

技術ブログマガジンも連載中です!


Sadao Tokuyama
Magic Leap 公式のアンバサダー。ARスタートアップ会社OnePlanet所属。SIerとして16年間、システム開発の従事。2020年8月に株式会社OnePlanetに入社。OnePlanetではARアプリのR&D開発を担当。