オブジェクト検出を有効にする
Lightship Object Detectionは、Lightshipのコンテクスト・アウェアネスシステムに200以上のクラスを追加することで、画像内のオブジェクトにセマンティックラベル付きの2Dバウンディングボックスを作成できる機能です。 オブジェクト検出では、バウンディングボックスを生成し、検出されたオブジェクトの信頼度を示すことで、周囲の現実世界を認識し、ARアプリに強力な次元を加えることができます。
この入門ガイドでは、以下の手順でこの機能を使用する方法を説明します。
- シーンにオブジェクト検出を追加する
- カメラが認識したオブジェクトをログに記録する
- 画面上のオブジェクトにリアルタイムにラベル付けする
オブジェクト検出クラスの詳細については、機能ページを参照してください。
前提条件
ARDKがインストールされたUnityプロジェクトと、基本的なARシーンが必要です。 詳しくは、ARDK 3をインストールするおよびARシーンを設定するを参照してください。
AR Object Detection Managerを追加する
AR Object Detection Manager を追加するには、次の手順を行います。
Lightship のトップメニューを開き、 XR Plug-in Management を選択して、 Niantic Lightship SDK メニューを開きます。 オブジェクト検出が有効になっていることを確認します。
ARシーンの階層で、
XROrigin
とCamera Offsetを展開し、Main Cameraを選択します。Inspector で Add Component をクリックし、Main Cameraに
AR Object Detection Manager
を追加します。
オブジェクト検出の結果を出力する
オブジェクト検出の出力データを確認するには、 ObjectDetectionsUpdated
イベントを登録します。 このイベントでは、新しいオブジェクト検出結果がある場合にコールバックが出力されます。 オブジェクト検出によってARカメラの画像でオブジェクトが認識されると、イベントで結果のリストが返ります。このリストの各結果は、カメラ画像の特定の領域に対応しています。 各結果には、オブジェクトに対して複数の分類が予測されることがあるため、複数のオブジェクトカテゴリーが含まれる場合があります。 結果は、信頼度の値でフィルタリングし、ソートすることで、最も可能性の高い分類に焦点を合わせることができます。
オブジェクト検出の出力をモニタリングするには、次の手順を行います。
Hierarchy で右クリックし、 Create Empty を選択して、新しい
GameObject
をシーンに追加します。LogResults
という名前を付けます。LogResults
を選択した状態で、 Inspector で Add Component をクリックし、 New Script を追加します。LogResults
という名前を付けます。LogResults.cs
をダブルクリックして開きます。AR Object Detection Manager
のシリアライズフィールドを追加します。 このマネージャーを使用することで、機能の詳細を処理し、結果に集中することができます。
using UnityEngine;
using Niantic.Lightship.AR.ObjectDetection;
public class LogResults : MonoBehaviour
{
[SerializeField]
private ARObjectDetectionManager _objectDetectionManager;
Start()
メソッド内でマネージャーを有効にし、OnMetadataInitialized
イベントを登録します。 このイベントは、オブジェクト検出の処理が開始されたタイミングで呼び出されます。
private void Start()
{
_objectDetectionManager.enabled = true;
_objectDetectionManager.MetadataInitialized += OnMetadataInitialized;
}
- 機能が準備できたら、
ObjectDetectionsUpdated
イベントに登録して、結果を自動的に受け取ります。
private void OnMetadataInitialized(ARObjectDetectionModelEventArgs args)
{
_objectDetectionManager.ObjectDetectionsUpdated += 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;
}
確率の閾値を指定しない場合、デフォルトでは信頼度スコアが 0.4 の結果のみがフィルタリングされます。
- 各結果には複数のオブジェクト・カテゴリーが含まれることがあるため、信頼度の高い順に一覧表示します。
// 信頼度が高い順にカテゴリーを並べ替える
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;
}
クリックして `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;
}
}
完成したスクリプトをプロジェクトに追加する
Hierarchy で
LogResults
を選択し、 Inspector でLogResults
コンポーネントの Object Detection Manager フィールドに Main Camera を割り当てます。Unityエディターでプレイバックデータセットを使用してシーンを実行するか、モバイルデバイスでログコンソールを接続して実行してみましょう。 オブジェクト検出機能によって、カメラフィード内で検出されたクラスと信頼度の値が表示されます。
バウンディングボックスを設定する
オーバーレイを作成してバウンディングボックスを表示することで、オブジェクト検出の結果を画面上にリアルタイムに表示することができます。 それぞれのバウンディングボックスは、左上隅を指す座標と、オブジェクト検出コードから得られるサイズで定義されます。 このオーバーレイをARカメラの背景に配置し、複数のバウンディングボックスを同時に表示できる再利用可能なプレハブを作成します。
Hierarchyでメインシーンを右クリックし、UIにマウスオーバーしてCanvasを選択します。
Canvasを選択した状態で、 Inspector で Canvas Scalar コンポーネントを見つけます。 UI Scale ModeをSelect Scale With Screen Sizeに設定し、Matchを0.5に設定します。
Hierarchy で、 Canvas を右クリックして Create Empty を選択します。 新しいオブジェクトに
BoundingBoxOverlay
という名前を付けます。BoundingBoxOverlay
を選択した状態で、 Inspector でRect Transform
の Anchor Presets を両軸で Stretch に設定し、 Shift キーと Alt キーを押しながらピボットと位置も設定します。- Anchor Presets を Stretch に設定した後、
Rect Transform
のLeft
、Right
、Top
、Bottom
の位置フィールドが変更された場合は、これらを再度ゼロにリセットします。
- Anchor Presets を Stretch に設定した後、
以下の手順で、バウンディングボックスプレハブを作成します。
Hierarchy で、
BoundingBoxOverlay
を右クリックして Create Empty を選択します。 新しいオブジェクトにRectObject
という名前を付けます。RectObject
を選択した状態で、 Inspector でRect Transform
の Anchor Presets を Bottom-Left に設定します。Rect Transformの Pivot をX:0, Y:1 に設定します。
位置の値を次のように設定します。
- Pos X: 0
- Pos Y: 1920
- Pos Z: 0
- Width: 1080
- Height: 1920
Add Component をクリックし、
RectObject
に Image コンポーネントを追加します。Source Image フィールドで、検索ボックスを開き、アセットを検索します。
Background
アセットを見つけて選択します(Resources/unity_builtin_extra/
にあるビルトインアセット)。Fill Center ボックスのチェックをオフにします。
Add Component をクリックし、
RectObject
に New script を追加します。 スクリプトに UIRectObject という名前を付けます。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;
}
}
Hierarchy で、
RectObject
を右クリックして Create Empty を選択します。 新しいオブジェクトにCategory Text
という名前を付けます。Inspector で、Shift キーと Alt キーを押しながら、Rect Transformの Anchor Presets を Stretchに設定して、ピボットと位置も設定します。
Height を 400 に設定します。
Add Component をクリックして、
Category Text
に Text コンポーネントを追加します。Text フィールドに、
Label: Prob
などのダミーテキストを追加します。Alignment を水平方向および垂直方向で中央揃えに設定します。 Best Fit にチェックを入れ、 Max Size を 100 に設定します。 Color を緑色に設定します。
Project タブ で、 Assets ディレクトリを右クリックし、 Create メニューを開いて Folder を選択します。
Prefabs
という名前を付けます。RectObject
ゲームオブジェクトを Inspector から Project タブの Prefab ディレクトリにドラッグして、バウンディングボックスのプレハブを作成します。
Hierarchy で
BoundingBoxOverlay
を選択し、Inspector で Add Component をクリックして New Script を選択します。 スクリプトにDrawRect
という名前を付けます。DrawRect
スクリプトをダブルクリックして開きます。検出された各オブジェクトに対してインスタンス化するプレハブの型に
SerializedField
を追加します。 これをプールにキャッシュしてパフォーマンスを向上させます。
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);
}
}
クリックして `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);
}
}
}
Hierarchy で
RectObject
を削除します。 これでBoundingBoxOverlay
に子オブジェクトがなくなります。RectObject
は次のセクションでスクリプトによって動的に配置されます。Inspector で RectObject プレハブを
DrawRect
コンポーネントの Rectangle Prefab フィールドに割り当てます。
バウンディングボックス検出スクリプトを追加する
最後のタスクは、Lightship Object検出機能からの結果をバウンディングボックスオーバーレイに渡すスクリプトを書きます。 このスクリプトは先に書いた LogResults
クラスと似ているが、表示スペースを節約するために、このクラスは各結果に対して最も確信の持てる分類だけをラベル付けします。
Hierarchyで右クリックし、Create Emptyを選択すると、シーンに新しいゲームオブジェクトが作成されます。
Sample
という名前を付けます。Sample
を選択した状態で、Inspector で Add Component をクリックし、New Script を追加します。ObjectDetectionSample
という名前を付けます。ObjectDetectionSample
スクリプトをダブルクリックして開きます。検出閾値として、
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;
- 初期化と初期化解除のライフサイクルに関数を追加します。
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;
}.
- 次に、オブジェクトの検出結果とユーザーが画面に表示するものを結びつけるために、
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;
- オブジェクトの最も確実な予測ができたら、
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);
}
}
}
セットアップを完了するには、
ObjectDetectionSample
コンポーネントにオブジェクトを割り当てます:Object Detection Managerフィールドに
Main Camera
を割り当てます。BoundingBoxOverlay
を Draw Rect フィールドに割り当てます。
結果例
これで、AR Playbackまたはモバイルデバイスを使ってオブジェクト検出をテストし、適切なクラスのオブジェクトにオーバーレイされたバウンディングボックスを確認できるはずです。
デフォルトの確率閾値は0.5で、これはオブジェクト検出アルゴリズムが少なくとも50%の一致の確信がある場合にバウンディングボックスを作成することを意味します。 Object Detection SampleコンポーネントのProbability Thresholdを増やしてみて、結果がどう変わるか見てみましょう。