本文へスキップ

オブジェクト検出を有効にする

Lightship Object Detectionは、Lightshipのコンテクスト・アウェアネスシステムに200以上のクラスを追加することで、画像内のオブジェクトにセマンティックラベル付きの2Dバウンディングボックスを作成できる機能です。 オブジェクト検出では、バウンディングボックスを生成し、検出されたオブジェクトの信頼度を示すことで、周囲の現実世界を認識し、ARアプリに強力な次元を加えることができます。

この入門ガイドでは、以下の手順でこの機能を使用する方法を説明します。

  • シーンにオブジェクト検出を追加する
  • カメラが認識したオブジェクトをログに記録する
  • 画面上のオブジェクトにリアルタイムにラベル付けする

オブジェクト検出クラスの詳細については、機能ページを参照してください。

Object detection demonstration graphic

前提条件

ARDKがインストールされたUnityプロジェクトと、基本的なARシーンが必要です。 詳しくは、ARDK 3のインストールおよび基本ARシーンの設定を参照してください。

AR Object Detection Managerを追加する

AR Object Detection Manager を追加するには、次の手順を行います。

  1. Lightship のトップメニューを開き、 XR Plug-in Management を選択して、 Niantic Lightship SDK メニューを開きます。 オブジェクト検出が有効になっていることを確認します。

  2. ARシーン階層で、XROriginCamera Offsetを展開し、Main Cameraを選択します。

  3. InspectorAdd Component をクリックし、Main Cameraに AR Object Detection Manager を追加します。

    An AR Object Detection Manager added as a component of the Main Camera

オブジェクト検出の結果を出力する

オブジェクト検出の出力データを確認するには、 ObjectDetectionsUpdated イベントを登録します。 このイベントでは、新しいオブジェクト検出結果がある場合にコールバックが出力されます。 オブジェクト検出によってARカメラの画像でオブジェクトが認識されると、イベントで結果のリストが返ります。このリストの各結果は、カメラ画像の特定の領域に対応しています。 各結果には、オブジェクトに対して複数の分類が予測されることがあるため、複数のオブジェクトカテゴリーが含まれる場合があります。 結果は、信頼度の値でフィルタリングし、ソートすることで、最も可能性の高い分類に焦点を合わせることができます。

オブジェクト検出の出力をモニタリングするには、次の手順を行います。

  1. Hierarchy で右クリックし、 Create Empty を選択して、新しい GameObject をシーンに追加します。 LogResults という名前を付けます。

    LogResults in the scene hierarchy
  2. LogResults を選択した状態で、 InspectorAdd Component をクリックし、 New Script を追加します。 LogResults という名前を付けます。

    LogResults with the new script added
  3. LogResults.cs をダブルクリックして開きます。

  4. AR Object Detection Manager のシリアライズフィールドを追加します。 このマネージャーを使用することで、機能の詳細を処理し、結果に集中することができます。

    using UnityEngine;
using Niantic.Lightship.AR.ObjectDetection;

public class LogResults : MonoBehaviour
{
[SerializeField]
private ARObjectDetectionManager _objectDetectionManager;
  1. Start() メソッド内でマネージャーを有効にし、 OnMetadataInitialized イベントを登録します。 このイベントは、オブジェクト検出の処理が開始されたタイミングで呼び出されます。
private void Start()
{
_objectDetectionManager.enabled = true;
_objectDetectionManager.MetadataInitialized += OnMetadataInitialized;
}
  1. 機能が準備できたら、 ObjectDetectionsUpdated イベントに登録して、結果を自動的に受け取ります。
private void OnMetadataInitialized(ARObjectDetectionModelEventArgs args)
{
_objectDetectionManager.ObjectDetectionsUpdated += ObjectDetectionsUpdated;
}
  1. ObjectDetectionsUpdated() 関数を作成し、結果を文字列として収集し、コンソールにログ出力します。
    private void ObjectDetectionsUpdated(ARObjectDetectionsUpdatedEventArgs args)
{
// 出力用の文字列を初期化
string resultString = "";
var result = args.Results;

if (result == null)
{
return;
}

// 結果文字列をリセット
resultString = "";
  1. 結果をループ処理します。
    // 結果をループ処理します。各結果には複数のカテゴリーが含まれる場合があります。
for (int i = 0; i < result.Count; i++)
{
var detection = result[i];
var categorizations = detection.GetConfidentCategorizations();
if (categorizations.Count <= 0)
{
break;
}
信頼度でフィルタリングする

確率の閾値を指定しない場合、デフォルトでは信頼度スコアが 0.4 の結果のみがフィルタリングされます。

  1. 各結果には複数のオブジェクト・カテゴリーが含まれることがあるため、信頼度の高い順に一覧表示します。
    // 信頼度が高い順にカテゴリーを並べ替える
categorizations.Sort((a, b) => b.Confidence.CompareTo(a.Confidence));

// このオブジェクトが含まれている可能性がある各カテゴリーの一覧を作成
for (int j = 0; j < categorizations.Count; j++)
{
var categoryToDisplay = categorizations[j];

resultString += "Detected " + $"{categoryToDisplay.CategoryName}: " + "with " + $"{categoryToDisplay.Confidence} Confidence \n";
}
}
  1. 最後に、すべての結果とカテゴリーをログに出力します。
        // すべての結果をログに出力
Debug.Log(resultString);
}
  1. 終了時にクリーンアップを忘れずに行いましょう。
    private void OnDestroy()
{
_objectDetectionManager.MetadataInitialized -= OnMetadataInitialized;
_objectDetectionManager.ObjectDetectionsUpdated -= ObjectDetectionsUpdated;
}
クリックして LogResults スクリプト全体を表示
using UnityEngine;
using Niantic.Lightship.AR.ObjectDetection;

public class LogResults : MonoBehaviour
{
[SerializeField]
private ARObjectDetectionManager _objectDetectionManager;

private void Start()
{
_objectDetectionManager.enabled = true;
_objectDetectionManager.MetadataInitialized += OnMetadataInitialized;
}

private void OnMetadataInitialized(ARObjectDetectionModelEventArgs args)
{
_objectDetectionManager.ObjectDetectionsUpdated += ObjectDetectionsUpdated;
}

private void ObjectDetectionsUpdated(ARObjectDetectionsUpdatedEventArgs args)
{
// 出力用の文字列を初期化
string resultString = "";
var result = args.Results;

if (result == null)
{
return;
}

// 結果文字列をリセット
resultString = "";

// 結果をループ処理します。各結果には複数のカテゴリーが含まれる場合があります。
for (int i = 0; i < result.Count; i++)
{
var detection = result[i];
var categorizations = detection.GetConfidentCategorizations();
if (categorizations.Count <= 0)
{
break;
}

// 信頼度が高い順にカテゴリーを並べ替える
categorizations.Sort((a, b) => b.Confidence.CompareTo(a.Confidence));

// このオブジェクトが含まれている可能性がある各カテゴリーの一覧を作成
for (int j = 0; j < categorizations.Count; j++)
{
var categoryToDisplay = categorizations[j];

resultString += "Detected " + $"{categoryToDisplay.CategoryName}: " + "with " + $"{categoryToDisplay.Confidence} Confidence \n";
}
}

// すべての結果をログに出力
Debug.Log(resultString);
}


private void OnDestroy()
{
_objectDetectionManager.MetadataInitialized -= OnMetadataInitialized;
_objectDetectionManager.ObjectDetectionsUpdated -= ObjectDetectionsUpdated;
}
}

完成したスクリプトをプロジェクトに追加する

  1. HierarchyLogResults を選択し、 InspectorLogResults コンポーネントの Object Detection Manager フィールドに Main Camera を割り当てます。

    LogResults with the Object Detection Manager field assigned
  2. Unityエディターでプレイバックデータセットを使用してシーンを実行するか、モバイルデバイスでログコンソールを接続して実行してみましょう。 オブジェクト検出機能によって、カメラフィード内で検出されたクラスと信頼度の値が表示されます。

    The console output after running object detection on a dataset in the Editor

バウンディングボックスを設定する

オーバーレイを作成してバウンディングボックスを表示することで、オブジェクト検出の結果を画面上にリアルタイムに表示することができます。 それぞれのバウンディングボックスは、左上隅を指す座標と、オブジェクト検出コードから得られるサイズで定義されます。 このオーバーレイをARカメラの背景に配置し、複数のバウンディングボックスを同時に表示できる再利用可能なプレハブを作成します。

  1. Hierarchyでメインシーンを右クリックし、UIにマウスオーバーしてCanvasを選択します。

  2. Canvasを選択した状態で、 InspectorCanvas Scalar コンポーネントを見つけます。 UI Scale ModeSelect Scale With Screen Sizeに設定し、Match0.5に設定します。

    Canvas with fields configured
  3. Hierarchy で、 Canvas を右クリックして Create Empty を選択します。 新しいオブジェクトに BoundingBoxOverlay という名前を付けます。

    BoundingBoxOverlay in the hierarchy
  4. BoundingBoxOverlay を選択した状態で、 InspectorRect TransformAnchor Presets を両軸で Stretch に設定し、 Shift キーと Alt キーを押しながらピボットと位置も設定します。

    Setting the Anchor Presets of BoundingBoxOverlay to stretch for X and Y
    1. Anchor PresetsStretch に設定した後、 Rect TransformLeftRightTopBottom の位置フィールドが変更された場合は、これらを再度ゼロにリセットします。
  5. 以下の手順で、バウンディングボックスプレハブを作成します。

    1. Hierarchy で、 BoundingBoxOverlay を右クリックして Create Empty を選択します。 新しいオブジェクトに RectObject という名前を付けます。

      RectObject in the hierarchy
    2. RectObject を選択した状態で、 InspectorRect TransformAnchor PresetsBottom-Left に設定します。

    3. Rect Transformの Pivot をX:0, Y:1 に設定します。

    4. 位置の値を次のように設定します。

      • Pos X: 0
      • Pos Y: 1920
      • Pos Z: 0
      • Width: 1080
      • Height: 1920
    5. Add Component をクリックし、 RectObjectImage コンポーネントを追加します。

    6. Source Image フィールドで、検索ボックスを開き、アセットを検索します。 Background アセットを見つけて選択します(Resources/unity_builtin_extra/ にあるビルトインアセット)。

    7. Fill Center ボックスのチェックをオフにします。

    8. Add Component をクリックし、RectObjectNew script を追加します。 スクリプトに UIRectObject という名前を付けます。

      RectObject with fields configured
    9. UIRectObject スクリプトをダブルクリックして開き、 内容を次のコードに置き換えて、バウンディングボックスがオブジェクトを正確に囲むようにします。

クリックしてバウンディングボックスのコードを表示
    using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(RectTransform), typeof(Image))]
public class UIRectObject : MonoBehaviour
{
private RectTransform _rectangleRectTransform;
private Image _rectangleImage;
private Text _text;

public void Awake()
{
_rectangleRectTransform = GetComponent<RectTransform>();
_rectangleImage = GetComponent<Image>();
_text = GetComponentInChildren<Text>();
}

public void SetRectTransform(Rect rect)
{
_rectangleRectTransform.anchoredPosition = new Vector2(rect.x, rect.y);
_rectangleRectTransform.sizeDelta = new Vector2(rect.width, rect.height);
}

public void SetColor(Color color)
{
_rectangleImage.color = color;
}

public void SetText(string text)
{
_text.text = text;
}

public RectTransform getRectTransform(){
return _rectangleRectTransform;
}
}
  1. Hierarchy で、RectObject を右クリックして Create Empty を選択します。 新しいオブジェクトに Category Text という名前を付けます。

    Category Text in the hierarchy
    1. Inspector で、Shift キーと Alt キーを押しながら、Rect Transformの Anchor PresetsStretchに設定して、ピボットと位置も設定します。

    2. Height400 に設定します。

    3. Add Component をクリックして、 Category TextText コンポーネントを追加します。

    4. Text フィールドに、 Label: Prob などのダミーテキストを追加します。

    5. Alignment を水平方向および垂直方向で中央揃えに設定します。 Best Fit にチェックを入れ、 Max Size100 に設定します。 Color を緑色に設定します。

      Category Text with fields configured
    6. Project タブ で、 Assets ディレクトリを右クリックし、 Create メニューを開いて Folder を選択します。 Prefabs という名前を付けます。

    7. RectObject ゲームオブジェクトを Inspector から Project タブの Prefab ディレクトリにドラッグして、バウンディングボックスのプレハブを作成します。

      The final RectObject prefab in the Project tab
  2. HierarchyBoundingBoxOverlay を選択し、InspectorAdd Component をクリックして New Script を選択します。 スクリプトに DrawRect という名前を付けます。

  3. DrawRect スクリプトをダブルクリックして開きます。

  4. 検出された各オブジェクトに対してインスタンス化するプレハブの型に SerializedField を追加します。 これをプールにキャッシュしてパフォーマンスを向上させます。

    public class DrawRect : MonoBehaviour
{
[SerializeField]
private GameObject _rectanglePrefab;

private List<UIRectObject> _rectangleObjects = new List<UIRectObject>();
private List<int> _openIndices = new List<int>();
  1. 新しいバウンディングボックスを作成して、プールに追加する関数を追加します。 プレハブのプールを維持し、繰り返しの割り当てを避けるために再利用します。
    public void CreateRect(Rect rect, Color color, string text)
{
if (_openIndices.Count == 0)
{
var newRect = Instantiate(_rectanglePrefab, parent: this.transform).GetComponent<UIRectObject>();

_rectangleObjects.Add(newRect);
_openIndices.Add(_rectangleObjects.Count - 1);
}

// 最初のインデックスをキューとして扱う
int index = _openIndices[0];
_openIndices.RemoveAt(0);

UIRectObject rectangle = _rectangleObjects[index];
rectangle.SetRectTransform(rect);
rectangle.SetColor(color);
rectangle.SetText(text);
rectangle.gameObject.SetActive(true);
}
  1. すべてのバウンディングボックスを非表示にし、次の予測結果のためにプールに戻す関数を追加します。
    public void ClearRects()
{
for (var i = 0; i < _rectangleObjects.Count; i++)
{
_rectangleObjects[i].gameObject.SetActive(false);
_openIndices.Add(i);
}
}
クリックして DrawRect スクリプト全体を表示
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DrawRect : MonoBehaviour
{
[SerializeField]
private GameObject _rectanglePrefab;

private List<UIRectObject> _rectangleObjects = new List<UIRectObject>();
private List<int> _openIndices = new List<int>();

public void CreateRect(Rect rect, Color color, string text)
{
if (_openIndices.Count == 0)
{
var newRect = Instantiate(_rectanglePrefab, parent: this.transform).GetComponent<UIRectObject>();

_rectangleObjects.Add(newRect);
_openIndices.Add(_rectangleObjects.Count - 1);
}

// 最初のインデックスをキューとして扱う
int index = _openIndices[0];
_openIndices.RemoveAt(0);

UIRectObject rectangle = _rectangleObjects[index];
rectangle.SetRectTransform(rect);
rectangle.SetColor(color);
rectangle.SetText(text);
rectangle.gameObject.SetActive(true);
}

public void ClearRects()
{
for (var i = 0; i < _rectangleObjects.Count; i++)
{
_rectangleObjects[i].gameObject.SetActive(false);
_openIndices.Add(i);
}
}
}
  1. HierarchyRectObject を削除します。 これで BoundingBoxOverlay に子オブジェクトがなくなります。 RectObject は次のセクションでスクリプトによって動的に配置されます。

    The Hierarchy after deleting the original RectObject
  2. InspectorRectObject プレハブを DrawRect コンポーネントの Rectangle Prefab フィールドに割り当てます。

    The final BoundingBoxOverlay

バウンディングボックス検出スクリプトを追加する

最後のタスクは、Lightship Object検出機能からの結果をバウンディングボックスオーバーレイに渡すスクリプトを書きます。 このスクリプトは先に書いた LogResults クラスと似ているが、表示スペースを節約するために、このクラスは各結果に対して最も確信の持てる分類だけをラベル付けします。

  1. Hierarchyで右クリックし、Create Emptyを選択すると、シーンに新しいゲームオブジェクトが作成されます。 Sample という名前を付けます。

    Sample in the scene hierarchy
  2. Sample を選択した状態で、InspectorAdd Component をクリックし、New Script を追加します。 ObjectDetectionSample という名前を付けます。

  3. ObjectDetectionSampleスクリプトをダブルクリックして開きます。

  4. 検出閾値として、AR Object Detection Manager、バウンディングボックスを画面に表示するクラスのフィールドを追加します:

    public class ObjectDetectionSample:MonoBehaviour
{
[SerializeField]
private float _probabilityThreshold = 0.5f;

[SerializeField]
private ARObjectDetectionManager _objectDetectionManager;

private Color[] _colors = new Color[]
{
Color.red,
Color.blue,
Color.green,
Color.yellow,
Color.magenta,
Color.cyan,
Color.white,
Color.black
};

[SerializeField]
private DrawRect _drawRect;

private Canvas _canvas;
  1. 初期化と初期化解除のライフサイクルに関数を追加します。 AR Object Detection Manager に新しいオブジェクト検出結果があると、新しい ObjectDetectionsUpdated 関数が最新の結果とともに呼び出されます。
    private void Awake()
{
_canvas = FindObjectOfType<Canvas>();
}

public void Start()
{
_objectDetectionManager.enabled = true;
_objectDetectionManager.MetadataInitialized += OnMetadataInitialized;
}

private void OnDestroy()
{
_objectDetectionManager.MetadataInitialized -= OnMetadataInitialized;
_objectDetectionManager.ObjectDetectionsUpdated -= ObjectDetectionsUpdated;
}

private void OnMetadataInitialized(ARObjectDetectionModelEventArgs args)
{
_objectDetectionManager.ObjectDetectionsUpdated += ObjectDetectionsUpdated;
}.
  1. 次に、オブジェクトの検出結果とユーザーが画面に表示するものを結びつけるために、ObjectDetectionsUpdatedを追加します。 この関数は、以前のバウンディングボックスの結果のフレームをクリアすることから始まります。
    private void ObjectDetectionsUpdated(ARObjectDetectionsUpdatedEventArgs args)
{
string resultString = "";
float _confidence = 0;
string _name = "";
var result = args.Results;
if (result == null)
{
return;
}

_drawRect.ClearRects();
  1. 結果をループ処理します。 結果には、異なる信頼度の複数の分類が含まれることがあります。 ここでは、この関数を使用して、信頼度が最も高い分類を表示します。
    for (int i = 0; i < result.Count; i++)
{
var detection = result[i];
var categorizations = detection.GetConfidentCategorizations(_probabilityThreshold);
if (categorizations.Count <= 0)
{
break;
}

categorizations.Sort((a, b) => b.Confidence.CompareTo(a.Confidence));
var categoryToDisplay = categorizations[0];
_confidence = categoryToDisplay.Confidence;
_name = categoryToDisplay.CategoryName;
  1. オブジェクトの最も確実な予測ができたら、CalculateRectを使ってそのバウンディングボックス領域をビューポート空間に変換します。 この例では、ARカメラの背景画像に合わせたキャンバスに結果が表示されます。 先ほどの DrawRect クラスは、新しい位置とラベルを持つ RectObject バウンディングボックスをアクティブにします。
        int h = Mathf.FloorToInt(_canvas.GetComponent<RectTransform>().rect.height);
int w = Mathf.FloorToInt(_canvas.GetComponent<RectTransform>().rect.width);

// 検出されたオブジェクトの周りの矩形を取得
var _rect = result[i].CalculateRect(w,h,Screen.orientation);

resultString = $"{_name}: {_confidence}\n";
// 矩形を描画
_drawRect.CreateRect(_rect, _colors[i % _colors.Length], resultString);
}
クリックして、 ObjectDetectionSample スクリプト全体を表示
    using Niantic.Lightship.AR.ObjectDetection;
using UnityEngine;

public class ObjectDetectionSample: MonoBehaviour
{
[SerializeField]
private float _probabilityThreshold = 0.5f;

[SerializeField]
private ARObjectDetectionManager _objectDetectionManager;

private Color[] _colors = new Color[]
{
Color.red,
Color.blue,
Color.green,
Color.yellow,
Color.magenta,
Color.cyan,
Color.white,
Color.black
};

[SerializeField]
private DrawRect _drawRect;

private Canvas _canvas;

private void Awake()
{
_canvas = FindObjectOfType<Canvas>();
}

public void Start()
{
_objectDetectionManager.enabled = true;
_objectDetectionManager.MetadataInitialized += OnMetadataInitialized;
}

private void OnDestroy()
{
_objectDetectionManager.MetadataInitialized -= OnMetadataInitialized;
_objectDetectionManager.ObjectDetectionsUpdated -= ObjectDetectionsUpdated;
}

private void OnMetadataInitialized(ARObjectDetectionModelEventArgs args)
{
_objectDetectionManager.ObjectDetectionsUpdated += ObjectDetectionsUpdated;
}

private void ObjectDetectionsUpdated(ARObjectDetectionsUpdatedEventArgs args)
{
string resultString = "";
float _confidence = 0;
string _name = "";
var result = args.Results;
if (result == null)
{
return;
}

_drawRect.ClearRects();

for (int i = 0; i < result.Count; i++)
{
var detection = result[i];
var categorizations = detection.GetConfidentCategorizations(_probabilityThreshold);
if (categorizations.Count <= 0)
{
break;
}

categorizations.Sort((a, b) => b.Confidence.CompareTo(a.Confidence));
var categoryToDisplay = categorizations[0];
_confidence = categoryToDisplay.Confidence;
_name = categoryToDisplay.CategoryName;

int h = Mathf.FloorToInt(_canvas.GetComponent<RectTransform>().rect.height);
int w = Mathf.FloorToInt(_canvas.GetComponent<RectTransform>().rect.width);

// 検出されたオブジェクトの周囲の矩形を取得
var _rect = result[i].CalculateRect(w,h,Screen.orientation);

resultString = $"{_name}: {_confidence}\n";
// 矩形を描画
_drawRect.CreateRect(_rect, _colors[i % _colors.Length], resultString);
}
}
}
  1. セットアップを完了するには、ObjectDetectionSampleコンポーネントにオブジェクトを割り当てます:

    1. Object Detection ManagerフィールドにMain Cameraを割り当てます。
    2. BoundingBoxOverlayDraw Rect フィールドに割り当てます。
    The Sample object with fields configured

結果例

これで、AR Playbackまたはモバイルデバイスを使ってオブジェクト検出をテストし、適切なクラスのオブジェクトにオーバーレイされたバウンディングボックスを確認できるはずです。

確率の閾値

デフォルトの確率閾値は0.5で、これはオブジェクト検出アルゴリズムが少なくとも50%の一致の確信がある場合にバウンディングボックスを作成することを意味します。 Object Detection SampleコンポーネントのProbability Thresholdを増やしてみて、結果がどう変わるか見てみましょう。

An example of object detection in action