見出し画像

Niantic Lightship ARDK の画像検出機能を使用する

Niantic社が提供しているスマホ用のARアプリケーション開発キット、Lightship ARDKを使用し、画像検出を行う方法について説明します。

Niantic Lightship ARDKとは?

『 Ingress 』、『 Pokémon GO 』、『Pikmin Bloom』などでも使用しているNiantic社が提供するARアプリケーション開発キットになります。マルチプレイヤー、深度、物理、オクルージョン、セマンティックセグメンテーションなどの機能が提供されています。

開発環境と動作確認したデバイス

Unity Editor 2019.4 を使用してます。

今回、テストで使用したデバイスはiPhone 12 Proになります。

1. 画像検出用の画像をダウンロード

以下のサイトにある画像をダウンロード。

2. ダウンロードした画像の拡張子を変更します。

スクリーンショット 2022-02-08 18.25.41

Lightship ARDKが提供しているARImageDetectionManagerを使い、検出する画像の登録を行います。ARImageDetectionManagerはJPEG画像をbytesにしたのTextAssetを使用するため、ダウンロードしたJPEG画像ファイルの拡張子を.bytesに変更します。

(参考)拡張子の変更方法

3. Niantic Lightship ARDK ダウンロード

以下から入手してください。今回、使用しているバージョンはv1.1.0になります。入手する際、Niantic Lightshipのアカウント登録が必要です。

4. Niantic Lightship ARDK インポート

UnityEditor のメニューにあるAssets → Import Package → Custom Package...を選び、ダウンロードした ardk-1.1.0.unitypackage をインポートします。

5. 新規でSceneを作成とMain Cameraの削除

新規でSceneを作成後、Main Cameraを削除します。

6. ARSceneManagerをヒエラルキーに追加

画像1

7. TextAsset用の画像ファイル(.bytes)をAssets配下に移動

ダウンロード後に.bytesに拡張子変更したファイルをAssets配下に移動します。(この例ではImage Tracking/imagesのフォルダを作成してます。)

スクリーンショット 2022-02-08 18.33.55

8. Managerの作成

スクリーンショット 2022-02-08 18.39.46

ヒエラルキーにGameObjectを作成。名前をManagerに変更後、ARImageDetectionManagerスクリプトを追加します。今回は1つの画像だけ検出するためImagesのSizeは1とします。Element 0 のImage As Bytesにダウンロード→.拡張を.bytesに変更したファイルをドラッグ&ドロップします。Nameは適当な文字列を入力。Physical Widthは検出する対象の実寸(横幅)を指定する必要があります。ここでは0.25(25cm)としています。

9. 画像検出時に表示するPlaneの作成

GameObjectを生成します。名前をImagePlaneに変更します。

スクリーンショット 2022-02-08 18.48.25

ImagePlaneの中にPlaneを生成します。

スクリーンショット 2022-02-08 18.50.02

Scaleは0.1とします。

スクリーンショット 2022-02-08 18.52.00

Image PlaneをPrefabにします。(ヒエラルキーにあるImagePlaneを削除します。)

10. Exampleの作成

画像検出した場所にImage Planeの生成と表示を行うスクリプトを作成します。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Niantic.ARDK.AR;
using Niantic.ARDK.AR.Configuration;
using Niantic.ARDK.AR.Anchors;
using Niantic.ARDK.AR.ARSessionEventArgs;
using Niantic.ARDK.AR.ReferenceImage;
using Niantic.ARDK.Extensions;
using Niantic.ARDK.Utilities;
using Niantic.ARDK.Utilities.Collections;

public class DetectingImageExample: MonoBehaviour
{
   [SerializeField]
   private GameObject _plane = null;

   private Dictionary<Guid, GameObject> _detectedImages = new Dictionary<Guid, GameObject>();

   // Start is called before the first frame update
   void Start()
   {
       ARSessionFactory.SessionInitialized += SetupSession;
   }

   // Update is called once per frame
   void Update()
   {
       
   }

   private void SetupSession(AnyARSessionInitializedArgs arg)
   {
       var session = arg.Session;
       session.SessionFailed += args => Debug.Log(args.Error);
       session.AnchorsAdded += OnAnchorsAdded;
       session.AnchorsUpdated += OnAnchorsUpdated;
       session.AnchorsRemoved += OnAnchorsRemoved;
   }

   private void OnAnchorsAdded(AnchorsArgs args)
   {
       foreach (var anchor in args.Anchors)
       {
           if (anchor.AnchorType != AnchorType.Image)
           {
               continue;
           }

           var imageAnchor = (IARImageAnchor) anchor;
           var imageName = imageAnchor.ReferenceImage.Name;

           var newPlane = Instantiate(_plane);
           _detectedImages[anchor.Identifier] = newPlane;

           UpdatePlaneTransform(imageAnchor);
       }
   }

   private void OnAnchorsUpdated(AnchorsArgs args)
   {
       foreach (var anchor in args.Anchors)
       {
           if (!_detectedImages.ContainsKey(anchor.Identifier))
           {
               continue;
           }

           var imageAnchor = anchor as IARImageAnchor;
           UpdatePlaneTransform(imageAnchor);
       }
   }

   private void OnAnchorsRemoved(AnchorsArgs args)
   {
       foreach (var anchor in args.Anchors)
       {
           if (!_detectedImages.ContainsKey(anchor.Identifier))
           {
               continue;
           }
           Destroy(_detectedImages[anchor.Identifier]);
           _detectedImages.Remove(anchor.Identifier);
       }
   }

   private void UpdatePlaneTransform(IARImageAnchor imageAnchor)
   {
       var identifier = imageAnchor.Identifier;

       _detectedImages[identifier].transform.position = imageAnchor.Transform.ToPosition();
       _detectedImages[identifier].transform.rotation = imageAnchor.Transform.ToRotation();

       var localScale = _detectedImages[identifier].transform.localScale;
       localScale.x = imageAnchor.ReferenceImage.PhysicalSize.x;
       localScale.z = imageAnchor.ReferenceImage.PhysicalSize.y;
       _detectedImages[identifier].transform.localScale = localScale;
   }
}

SetupSessionでARSessionの初期化を行います。画像が検出されるとOnAnchorsAddedが呼び出されます。OnAnchorsAddedでアンカーのタイプをチェックを行います。画像であれば画像のアンカーを使い、検出処理を行います。

スクリーンショット 2022-02-08 18.59.14

ヒエラルキー上でGameObjectを生成します。名前はExampleとします。このExampleに先程、作成したスクリプトを追加します。PlaneにはPrefabのImagePlaneを設定します。

11. ビルド&実行

実行するとカメラ画像が表示されます。画像に近づけると画像の前にPlaneが表示されます。

スクリーンショット 2022-02-08 19.17.26

スクリーンショット 2022-02-08 19.17.35

まとめ

あくまで画像を検出する機能であるため、スムーズに画像のトラッキングは行われません。ARImageAnchorでなくARAnchorを使用することでスムーズで且つ安定するとドキュメントに記載していましたが、実際はARAnchorもARImageAnchorもあまり変わりなかったです。

スクリーンショット 2022-02-08 19.15.39

参考

最後に

Niantic Lightship ARDK のブログ記事

この記事以外にNiantic Lightship ARDKのブログ記事を投稿しています。

OnePlanet XR

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

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

OnePlanet Tech Magazine

様々な技術記事を定期的に投稿しています。