Skip to main content

Custom Pose Trackerの実装

このチュートリアルでは、3.CustomPoseTrackerサンプルプロジェクトを直接実装し、Custom Pose Trackerを直接開発して適用する方法を学びます。Custom Pose Trackerとは、ユーザーが直接開発したPoseTrackerを意味します。

ほとんどの場合、VLSDKが提供する基本Pose Trackerを使用してアプリを開発できます。しかし、ロボット、ARグラスなど、VLSDKが基本的に提供するPoseTrackerを使用するのが難しい環境の場合、PoseTrackerを直接開発して使用する必要があります。

note

このチュートリアルを始める前に、シンプルなアプリの実装を完了する必要があります。

1. CustomPoseTrackerの使用フロー

VLSDK全体の動作プロセスにおいて、PoseTrackerはARFrameを生成し、VLSDKのコアロジックに渡す役割を果たします。詳細はドキュメントを参照してください。

2. CustomPoseTrackerの作成(基本)

2.1 PoseTracker継承スクリプトの作成

  1. 任意の場所にMyPoseTracker.csファイルを作成します。CustomPoseTrackerAdaptorは、任意の場所に作成したPoseTrackerファイルも認識できます。

alt text

  1. 次のようにMyPoseTrackerクラスを実装します。
using UnityEngine;
using ARCeye;

public class MyPoseTracker : PoseTracker // <- PoseTrackerを継承するクラス。
{
// 毎フレーム呼び出されるメソッド。ARFrameを生成して返します。
protected override ARFrame CreateARFrame()
{
// 画像を読み込みます。迅速なテストのため、毎フレームResourcesディレクトリの画像を使用します。
var texture = Resources.Load<Texture2D>("001");

// ARFrameを生成します。
ARFrame frame = new ARFrame();
frame.texture = texture;

return frame;
}
}
  1. テストに使用した画像は、次のようなパスに配置されています。

alt text

3. CustomPoseTrackerの適用

3.1 CustomPoseTrackerAdaptor

VLSDKManagerを選択し、CustomPoseTrackerAdaptorコンポーネントを追加します。Use Custom Editor Pose Tracker項目にチェックを入れると、Editor環境でPlay Mode進入時に使用されるPoseTrackerを選択できます。Use Custom Device Pose Tracker項目にチェックを入れると、実際のデバイス環境で使用されるPoseTrackerを選択できます。ここではUse Custom Editor Pose Trackerにチェックを入れ、先ほど作成したMyPoseTrackerを選択します。

alt text

Play Modeに入ると、MyPoseTrackerを通じてARFrameを生成し、VLリクエストを送信する様子を確認できます。

alt text

4. CustomPoseTrackerの作成(拡張)

次は、CustomPoseTrackerを使用する高度な例です。ユーザーのシステムが独自のループを持っている場合、以下のような形式で実装できます。以下の例はARFoundationをベースにしていますが、様々なシステムに応用可能です。

using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using Unity.Collections.LowLevel.Unsafe;

using ARCeye;

public class MyPoseTracker : PoseTracker
{
private ARCameraManager m_CameraManager;
private ARCameraFrameEventArgs m_LastFrameEventArgs;

private Texture2D m_CameraTexture;
private RawImage m_RequestedTexture;

// MyPoseTracker最初の生成時に呼び出されるイベント。
public override void OnCreate(Config config)
{
config.tracker.useFaceBlurring = false;

// ARFoundationを使用。
m_CameraManager = GameObject.FindObjectOfType<ARCameraManager>();

// デバッグ用のカメラプレビュー出力用画像。
m_RequestedTexture = GameObject.Find("Canvas/RawImage_Test").GetComponent<RawImage>();
}

// ループイベント登録。
public override void RegisterFrameLoop()
{
m_CameraManager.frameReceived += OnCameraFrameReceived;
}

// ループイベント解除。
public override void UnregisterFrameLoop()
{
m_CameraManager.frameReceived -= OnCameraFrameReceived;
}

private void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs)
{
if (!m_IsInitialized)
{
return;
}

m_LastFrameEventArgs = eventArgs;
OnFrameLoop();
}

// ARFrame生成イベント。
protected override ARFrame CreateARFrame()
{
return CreateARFrameFromEventArgs(m_LastFrameEventArgs);
}

private ARFrame CreateARFrameFromEventArgs(ARCameraFrameEventArgs eventArgs)
{
ARFrame frame = new ARFrame();

// Camera texture.
frame.texture = GetCameraTexture();

m_RequestedTexture.texture = frame.texture;

// Camera model matrix.
frame.localPosition = m_ARCamera.transform.localPosition;
frame.localRotation = m_ARCamera.transform.localRotation;

// Intrinsic matrix.
frame.intrinsic = AquireCameraIntrinsic();

// Projection matrix.
frame.projMatrix = eventArgs.projectionMatrix ?? Camera.main.projectionMatrix;

// Display matrix.
frame.displayMatrix = eventArgs.displayMatrix ?? Matrix4x4.identity;

return frame;
}

unsafe private Texture2D GetCameraTexture()
{
if (!m_CameraManager.TryAcquireLatestCpuImage(out XRCpuImage cpuImage))
{
Debug.LogError("Failed to acquire latest CPU image.");
return null;
}

TryUpdateCameraTexture(cpuImage);

var rawTextureData = m_CameraTexture.GetRawTextureData<byte>();
var rawTexturePtr = new IntPtr(rawTextureData.GetUnsafePtr());
var conversionParams = new XRCpuImage.ConversionParams(cpuImage, TextureFormat.RGBA32);

try
{
conversionParams.inputRect = new RectInt(0, 0, cpuImage.width, cpuImage.height);
conversionParams.outputDimensions = cpuImage.dimensions;
cpuImage.Convert(conversionParams, rawTexturePtr, rawTextureData.Length);
}
finally
{
cpuImage.Dispose();
}

m_CameraTexture.Apply();

return m_CameraTexture;
}

private void TryUpdateCameraTexture(XRCpuImage cpuImage)
{
var outputDimensions = cpuImage.dimensions;

if (IsCameraTextureUpdated(outputDimensions))
{
m_CameraTexture = new Texture2D(cpuImage.width, cpuImage.height, TextureFormat.RGBA32, false);
}
}

private bool IsCameraTextureUpdated(Vector2Int outputDimensions)
{
return m_CameraTexture == null || m_CameraTexture.width != outputDimensions.x || m_CameraTexture.height != outputDimensions.y;
}

public ARIntrinsic AquireCameraIntrinsic()
{
float fx, fy, cx, cy;

if (m_CameraManager.TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics))
{
fx = cameraIntrinsics.focalLength.x;
fy = cameraIntrinsics.focalLength.y;
cx = cameraIntrinsics.principalPoint.x;
cy = cameraIntrinsics.principalPoint.y;
}
else
{
fx = 0;
fy = 0;
cx = 0;
cy = 0;
}

return new ARIntrinsic(fx, fy, cx, cy);
}
}

5. まとめ

PoseTrackerを拡張したCustomPoseTrackerを使用して、様々な環境でVLSDKを使用する方法を学びました。これをもとに、ARFoundationをサポートしていない開発環境でも、VLSDKを連動したサービスを開発できます。