オブジェクト検出を有効にする方法
Lightship Object Detectionは、Lightshipのコンテキスト認識システムに200以上のクラスを追加し、画像内のオブ ジェクトの周囲にセマンティックにラベル付けされた2Dバウンディングボックスを可能にします。 バウンディングボックスと検出信頼度を提供することで、オブジェクト検出は、あなたの周りの物質世界をインテリジェントに認識し、ARアプリに強力な次元を追加します。
このハウツーは、あなたがこの機能を使い始めるためのものです:
- シーンにオブジェクト検出を追加する
- カメラに映った被写体のログ
- 画面上のオブジェクトにリアルタイムでラベリング
オブジェクト検出クラスの詳細については、機能ページを参照してください。
前提条件
ARDKをインストールしたUnityプロジェクトと、基本的なARシーンが必要です。 詳しくは、ARDK 3のインストールおよびARシーンの設定を参照してください。
ARオブジェクト検出マネージャの追加
AR Object Detection Manager**を追加する:
-
Lightship**トップメニューを開き、XR Plug-in Managementを選択し、Niantic Lightship SDKメニューを開きます。 オブジェクト検出が有効になっていることを確認する。
-
ARシーンの階層で、
XROrigin
とCamera Offsetを展開し、Main Cameraを選択します。 -
Inspector**で、Add Componentをクリックし、メインカメラに「AR Object Detection Manager」を追加します。
オブジェクト検出結果の印刷
Object Detection が提供するデータを見るには、ObjectDetectionsUpdated
イベントを登録する。 これは、新しいオブジェクト検出結果があったときにコールバックを出力する。 オブジェクト検出がARカメラ画像内の何かを認識した場合、イベントは結果のリストを返し、各結果はカメラ画像の領域に対応する。 オブジェクト検出がオブジェクトの複数の可能な分類を予測した場合、各結果は複数のオブジェクトカテゴリを含む可能性がある。 最も可能性の高い分類に焦点を当てるために、結果を信頼値でフィルタリングして並べ替えることができます。
物体検出出力をモニターする:
-
Hierarchy** で右クリックし、Create Emptyを選択すると、新しい
GameObject
がシーンに追加されます。 名前をLogResults
とする。 -
LogResults
を選択した状態で、**Inspector** で **Add Component** をクリックし、**New Script** を追加します。 名前を
LogResults` とする。 -
LogResults.cs`をダブルクリックして開く。
-
ARオブジェクト検出マネージャ`のシリアライズフィールドを追加する。 マネージャーは、私たちが結果に集中できるように、機能実行に関する詳細を世話する。
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} 信頼性 \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;
} // 結果文字列をリセットする。
// 結果をリセットする string
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 += "検出された " + $"{categoryToDisplay.CategoryName}: " + "with " + $".{categoryToDisplay.Confidence} 信頼性 \n";
}.
}
// 全ての結果をログ
Debug.Log(resultString);
}
private void OnDestroy()
{
_objectDetectionManager.MetadataInitialized -= OnMetadataInitialized;
_objectDetectionManager.ObjectDetectionsUpdated -= ObjectDetectionsUpdated;
}.
}
完成したスクリプトをプロジェクトに追加する
-
Hierarchyで
LogResults
を選択し、InspectorでLogResults
ComponentのObject Detection ManagerフィールドにMain Camera**を割り当てます。 -
Unityエディタでplayback datasetを使って、またはモバイルデバイスでロギングコン ソールを接続してシーンを実行してみてください。 オブジェクト検出では、カメラフィードで検出されたクラスと信頼値が表示されます。
バウンディング・ボックスの設定
バウンディングボックスを表示するオーバーレイを作成することで、オブジェクト検出結果を画面上にライブ表示することができる。 それぞれのバウンディングボックスは、左上隅を指す座標と、オブジェクト検出コードから得られるサイズで定義されます。 このオーバーレイをARカメラ背景の上に配置し、複数のバウンディングボックスを一度に表示するために再利用できるプレハブを作成します。
-
Hierarchyでメインシーンを右クリックし、UIにマウスオーバーしてCanvasを選択します。
-
Canvas を選択した状態で、Inspector で、Canvas Scalar Component を見つけます。 UI Scale ModeをSelect Scale With Screen Sizeに設定し、Matchを0.5に設定します。
-
階層**で、キャンバスを右クリックし、空の作成を選択します。 新しいオブジェクトの名前を
BoundingBoxOverlay
とする。 -
BoundingBoxOverlay
を選択した状態で、**Inspector**で、**Shift**と**Alt**を押しながら、両軸の
Rect Transform` Anchor PresetsをStretchに設定し、ピボットと位置も設定します。- 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に設定します。
-
ポジションの値は以下のように設定する:
- Xポジション:0
- ポスY:1920
- ポジション Z: 0
- 幅1080
- 身長1920
-
Add Componentをクリックし、
RectObject
にImage**コンポーネントを追加します。 -
ソース画像**フィールドで、アセットを検索するために検索ボックスを開きます。 Background
アセットを探して選択します(これは
Resources/unity_builtin_extra/` にある組み込みアセットです)。 -
センターを埋める**のチェックを外す。
-
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で、矩形変形Anchor PresetsをStretchに設定し、ShiftとAlt**を押しながらピボットと位置も設定します。
-
高さを400**に設定する。
-
Add Componentをクリックして、
Category Text
にText**コンポーネントを追加します。 -
Text**フィールドに、
Label
のようなフィラー・テキストを追加する:Prob`. -
Alignmentを水平方向と垂直方向の中央に設定する。 Best Fitをチェックし、Max Sizeを100に設定します。 色**を緑に設定する。
-
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);
}.
}
}
-
階層**で、
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); { 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 フィールドに割り当てます。
- Object Detection Managerフィールド に
結果例
これで、AR Playbackまたはモバイルデバイスを使ってオブジェクト検出をテストし、適切なクラスのオブジェクトにオーバーレイされたバウンディングボックスを確認できるはずです。
デフォルトの確率閾値は0.5で、これはオブジェクト検出アルゴリズムが少なくとも50%の一致の確信がある場合にバウンディングボックスを作成することを意味します。 Object Detection SampleコンポーネントのProbability Thresholdを増やしてみて、結果がどう変わるか見てみましょう。