画像の検出

現実世界で画像を探して様々なARエフェクトに利用する。

概要

ARSession を構成し、指定された画像を現実世界から見つけ出してアンカー画像にできます。このアンカーは以下のような様々な効果に使用できます:

  • キャラクターが3Dになって画像から飛び出すように見せる。

  • 現実世界のランドマーク付近の特定地点に宝箱を出現させる(壁画など)。

  • 仮想オーバーレイを作成して静止画を「アニメーション」させる。

ARSession は最初の推定が誤っている場合、あるいは画像が動いた場合、時間が経つにつれてアンカー画像を更新します。ただしこのアルゴリズムはトラッキングではなく検知用にデザインされているため、この更新は必ずしも滑らかなものや一貫したものにはなりません。

この例で示すコードは、ARDK使用例のUnityパッケージのImageDetectionシーンから引用しています。

画像検出を有効にする

画像セットの作成

ARSession には検知する画像を事前に指定しておく必要があります。これを行うには各画像に IARReferenceImage を作成し、 IARSession.Run に渡される ARWorldTrackingConfiguration.DetectionImages にそれらを含めます。

ARReferenceImagesARReferenceImageFactory.Create を呼び出すことで作成され、これには様々なソースからリファレンス画像を作成できるオーバーライドが備わっています。

.jpg.bytesの参照によって作成する

そのためには、.jpg画像にファイル拡張子.bytesを追加します。Macの場合は、右クリック -> Get Info(情報の取得) -> Name & Extension(名前と拡張子) の順に移動して拡張子を追加できます。

using Niantic.ARDK.AR.ReferenceImage;
using Niantic.ARDK.Extensions;

private ARImageDetectionManager _imageDetectionManager;
public TextAsset _imageAsBytes;

// ...

byte[] rawByteBuffer = _imageAsBytes.bytes;
IARReferenceImage _yetiImage =
      ARReferenceImageFactory.Create
      (
        "yeti",
        rawByteBuffer,
        rawByteBuffer.Length,
        _physicalImageWidth
      );

// Add image to the manager.
_imageDetectionManager.AddImage(_yetiImage);

.jpgファイルパスから作成する

デバイスに関係なく、ファイルの永続的なデータパスを指定するには、Unityプロジェクトに Assets/StreamingAssets フォルダを作成します。

string _imagePath = "Yeti.jpg";

// The contents of Assets/StreamingAssets are copied to device when installing an app.
string filePathImageBytes = Path.Combine(Application.streamingAssetsPath, _imagePath);

// Create an ARReferenceImage from the local file path.
IARReferenceImage _yetiImage =
  ARReferenceImageFactory.Create
  (
    "yeti",
    filePathImageBytes,
    _physicalImageWidth
  );

// Add image to the manager.
_imageDetectionManager.AddImage(_yetiImage);

非同期的な画像セットの作成

画像のロードおよび処理には、ARDKがすべてのARReferenceImageFactory機能の非同期バージョンを提供するだけの時間が掛かり、画像がロードされる間も他のUI要素は更新され続けます。コールバックパターンを用いて、 ARReferenceImageFactory.Create のすべてのバージョンに対して非同期メソッドを提供します。

作成後、コールバックを使用して、追跡するIARReferenceImageを追加する方法に関する.jpg.bytesの例をご紹介します。

byte[] rawByteBufferAsync = _imageAsBytes.bytes;
ARReferenceImageFactory.CreateAsync
(
  "yeti",
  rawByteBufferAsync,
  rawByteBufferAsync.Length,
  _physicalImageWidth,
  arReferenceImage =>
  {
    _yetiImage = arReferenceImage;
    _imageDetectionManager.AddImage(_yetiImage);
  }
);

ARImageDetectionManagerの使用

画像検知を可能にし、GameObjectsを作成して現実世界の画像をトラッキングするプロセスを簡略化できるように、シーンに追加できる マネージャー を用意します。ARImageDetectionManagerは、.jpg.bytesファイルへの参照を使用します。

最良の画像の選択

ARSession が最適な速度と精度でアンカー画像を表出させるのを助けるため、リファレンス画像には反復的でない視覚的特徴を多く含むものを使ってください。Androidの場合は厳密な条件があり、画像は300x300ピクセル以上でなければなりません。

アンカー画像の使用

アンカーイベントのサブスクライブ

環境内で画像の検知に成功すると、 ARImageAnchor が作成されます。これはまず ARSession.AnchorsAdded コールバックに現れ、その後 ARSession.AnchorsUpdated コールバックに現れます。

これはゲームオブジェクトを検知された画像にアタッチする例です。ゲームオブジェクトは、OnAnchorsAddedコールバック内で追加され、その位置は、OnAnchorsUpdatedコールバックでアンカーが移動するたびに更新されます。ImageAnchorがセッションから削除されると、アタッチされたゲームオブジェクトは破壊されます。

using Niantic.ARDK.AR.Anchors;
using Niantic.ARDK.Extensions;

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

private void Start()
{
    ARSessionFactory.SessionInitialized += SetupSession;
}

private void SetupSession(AnyARSessionInitializedArgs arg)
{
    // Add listeners to all relevant ARSession events.
    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;

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

        GameObject gameObjectOnImage = Instantiate(_prefab);
        gameObjectOnImage.name = "Image-" + imageName;
        _detectedImages[anchor.Identifier] = gameObjectOnImage;

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

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

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

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

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

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);
    }
}

ARセッションの実行オプション

IARReferenceImageがImageDetectionManagerに追加されると、ARセッションは更新後の設定値で再実行されます。IARReferenceImageをImageDetectionManagerから削除すると、ARセッションも再実行され、ImageAnchorのトラッキングとこの画像のOnAnchorsUpdatedコールバックのトリガーが停止されます。

ARセッションについて選択した 実行オプション に応じて、更新後の設定でセッションを再実行する際の動作が異なります。「Remove Existing Anchors(既存のアンカーを削除する)」を選択した場合は、ImageAnchorsを含むすべてのアンカーがセッションから削除されるため、画像を再度検出してからアンカーを再度追加する必要があります。

注釈

AndroidやARCoreでは、セッションの再実行間にアンカーを保持することができません。Androidでは、実行オプションで「None」を選択した場合でも、既存のアンカーは削除されます。

モック画像のアンカー

Unityエディターで画像検出の体験を繰り返し行う場合は、 モックモード ページのモック画像のアンカーを追加する手順を参照してください。

ベストプラクティス

使用する画像

画像トラッキングの質はトラッキング対象の画像に大きく左右されます。トラッキングを容易にする要素は直感的に理解しやすいものではありません。推奨されるのは次のような画像です:

  • 多くの特徴を備えた画像。視覚的コントラストが大きな領域を持つ。ソリッドカラーまたはグラデーションを持つものには必要な特徴が含まれません。

  • 反復するパターンや特徴を持たない。たとえばチェス盤には正方形が交わるシャープな視覚的特徴が存在しますが、この視覚的特徴が繰り返されるため画像の配置にはあまり適しません。

ARCoreとARKitには、与えられた画像の品質を判断するツールが用意されています。ある画像がトラッキングに適しているかどうかを確かめたい時は、このツールを活用しましょう。

仮想コンテンツを配置する

画像検知機能は動く画像を正確にトラッキングできないため、アンカーの位置が突然移動する場合があります。以前の位置から新しい位置への連続的な更新ではなく断続した移動として更新が行われ、画像平面の角度がプレーンアンカーよりも大きく変化することがあり、特に向きが安定しません。この問題は画像が動くと顕著になりますが、画像検知のエラーが修正されるなかで静止している画像にも問題を引き起こすことがあります。配置する仮想コンテンツのタイプによって、この問題には回避方法があります。

  • 取り外し可能な3Dコンテンツの場合(絵から飛び出して宙を漂う宇宙飛行士など)、アンカー画像の位置で新しい ARAnchor を作成し、仮想オブジェクトをそのアンカーにアタッチするのが最善策です。そのアンカーを用いることで、アンカー画像を用いるよりもポーズの更新がスムーズかつ一貫して行われます。

  • 同様に、画像の近くではないコンテンツに新しい ARAnchor を追加し、それをランドマークとして利用しましょう。たとえば ARSession が映画ポスターを検知したら、アンカー画像の1メートル前に ARAnchor を配置し、それに仮想コンテンツをアタッチします。

  • 画像と直接結びついているフラットな2Dコンテンツ(仮想ピクチャーフレームなど)については、アンカー画像を直接使いましょう。この場合、画像の最新検知地点の近くにいることのほうが、前のフレームの仮想オブジェクトの位置との一貫性よりも重要です。画像が動くとトラッキングの精度が下がって見えますが、画像の動きが止まると修正されるはずです。

  • 画像と直接結びついた3Dコンテンツ(画像に直接繋がれたまま手を伸ばす仮想キャラクターなど)は、画像検知の弱点が最も顕著に表れます。ですがクリエイティブなテクニックを用いて問題の軽減が可能です。

    • アンカー画像の向きに少しでも変化があると、そこから手を伸ばしているキャラクターが大きく動いてしまいます。これは、画像の中心から遠いものが長いレバーの先端にある状態となるため、小さな向きの変化が大きな変化となって表れるためです。そのため、画像が壁にあるとわかっている場合、仮想オブジェクトはアンカー画像の位置にアタッチして方向は無視してください。

    • 仮想オブジェクトと画像がシームレスに噛み合うことが重要な場合、現実世界の画像の仮想コピーを用いて全体を覆う方法を検討してみてください。