一覧に戻る

Android ネイティブAPIのインスタントトラッキング

2018.07.08AndroidネイティブAPI

インスタントトラッキング

以下のセクションでは、Wikitude SDKネイティブAPIのインスタントトラッキング機能について詳しく説明します。最小の実装を紹介し、Wikitude SDKネイティブAPIが提供するシンプルさを紹介します。

SMART - シームレスなARトラッキング

SMARTは、ARKit、ARCore、WikitudeのSLAMエンジンをあらゆるデバイスに対応する単一の拡張現実SDK(クロスプラットフォーム)に統合したシームレスなAPIです。 SMARTはiOSデバイスの92.6%、市場で入手可能なAndroidデバイスの約35%をカバーし、多くのデバイスに最適なAR体験を提供します。
SMARTはデフォルトで有効になっていますが、InstantTrackerConfigurationのsetSMARTEnabledで無効にすることができます。この動作は実行時には変更することはできません。

InstantTrackerConfiguration configuration = new InstantTrackerConfiguration();
configuration.setSMARTEnabled(false);
instantTracker = mWikitudeSDK.getTrackerManager().createInstantTracker(this, configuration);

WikitudeでARCoreを有効にするには、Enable ARCoreセクションのARCoreのドキュメントの指示に従います。これはサンプルアプリでも確認することができます。 デバイスがSMART(ARCoreを使用した追跡)を使用してプラットフォームアシスタントトラッキングをサポートしているかどうかを確認するには、TrackerManager.isPlatformAssistedTrackingSupportedを使用します。ARCoreには、必要なARCoreコンパニオンアプリを自動的にダウンロードしてインストールするメカニズムがあります。Wikitude Native SDKを使用してこの手順を開始するには、TrackerManager.isPlatformAssistedTrackingSupportedを呼び出し、必要に応じてTrackerManager.createInstantTrackerを使用してインスタントトラッカーを作成する2段階のプロセスになります。最初の呼び出しでは、現在のデバイスがARCoreをサポートしているかどうかを判断するための連続クエリが開始されます。これは、onAvailabilityChangedコールバック関数を介して現在のステータスを繰り返し報告します。このコールバックは、次の列挙定数の1つを入力パラメータとして提供します。

value action
INDETERMINATE_QUERY_FAILED 何らかの理由でクエリが失敗しました。再試行するか、ARCoreなしで実行するトラッカーを作成してください。コールバックは再び呼び出されません。
CHECKING_QUERY_ONGOING クエリは現在進行中です。何もする必要はありません。コールバックが再度呼び出されます。
UNSUPPORTED デバイスはARCoreをサポートしていません。ARCoreなしで実行するトラッカーを作成します。コールバックは再び呼び出されません。
SUPPORTED_UPDATE_REQUIRED デバイスはARCoreをサポートしていますが、コンパニオンアプリをインストールまたは更新する必要があります。インストールプロセスを開始するトラッカーを作成します。 コールバックが再度呼び出されます。
SUPPORTED デバイスはARCoreをサポートしており、コンパニオンの現在のバージョンはすでにインストールされています。ARCoreで実行するトラッカーを作成します。コールバックは再び呼び出されません。

Javaコードに移された場合、表には次のスニペットが表示されます。

wikitudeSDK.getTrackerManager().isPlatformAssistedTrackingSupported(new IsPlatformAssistedTrackingAvailableCallback() {
    @Override
    public void onAvailabilityChanged(SmartAvailability availability) {
        switch (availability) {
            case INDETERMINATE_QUERY_FAILED:
                /* may try again here */
                instantTracker = wikitudeSDK.getTrackerManager().createInstantTracker(InstantTrackingActivity.this, null);
                break;
            case CHECKING_QUERY_ONGOING:
                /* be patient; do nothing */
                break;
            case UNSUPPORTED:
            case SUPPORTED_UPDATE_REQUIRED:
            case SUPPORTED:
                if (instantTracker == null) {
                    instantTracker = wikitudeSDK.getTrackerManager().createInstantTracker(InstantTrackingActivity.this, null);
                }
                break;
        }
    }
});

ARCoreアプリケーションのインストールが必要な場合は、トラッカーを2回作成しないように注意してください。SUPPORTED_UPDATE_REQUIRED定数のあとにSUPPORTED定数が続く場合がありますが、一度だけトラッカーを作成したい場合があります。提示されたスニペットは、nullの参照をチェックすることによってトラッカーを二重に作成することを回避します。

SMARTは、制御を犠牲にして改善されたトラッキング機能を提供します。 そのため、SMARTを有効にしてプラットフォームアシスタントトラッキング機能を使用すると一部のWikitude SDK機能を使用できません。

機能 SMART ON
(プラットフォームアシスタントトラッキングをサポート)
SMART OFF
トラッキングの改善 X
平面の方向 X
カメラの制御 X
インスタントターゲットの保存と読み込み X

 

概要

インスタントトラッキングは、以前にWikitude SDKで導入されたものとは異なり、あらかじめ定義されたターゲットを認識してトラッキングを開始するのではなく、ただちに任意の環境でトラッキングを開始するアルゴリズムです。これにより、非常に具体的なユースケースを実装することができます。

アルゴリズムは2つの異なる状態で動作します。最初のものは初期状態です。この状態では、ユーザーはデバイスに指示を出してインジケータを整列させることで、トラッキングの起点を定義します。ユーザーが満足できる起点が定義できたと判明すると、トラッキング状態への移行が実行されます。この状態では、環境は継続的にトラッキングされているため、シーン内に拡張を配置することができます。

インスタントトラッキングのアルゴリズムは、初期状態で別の入力値を提供することを要求します。具体的には、トラッキングしているデバイスの高さは、シーンの範囲内で正確に増加したスケールを調整するために必要となります。この目的のために、この例ではメートル単位で高さを設定できる範囲入力要素を備えています。

初期化中にインスタントトラッキングの平面を整列するための別のパラメータを設定できます。この平面を初期化インジケータにして表示させ、床の代わりに壁をターゲットとしてトラッキングするために回転させることができます。詳細については、Android ネイティブAPIリファレンスのInstantTrackerConfigurationを参照してください。

基本的なインスタントトラッキング

インスタントトラッキングの例は、インスタントトラッキングアルゴリズムの最小限の実装を提供します。はじめに以下のパッケージをインポートします。

import com.wikitude.tracker.InstantTracker;
import com.wikitude.tracker.InstantTrackerListener;
import com.wikitude.tracker.InstantTarget;
import com.wikitude.tracker.InitializationPose;
import com.wikitude.tracker.InstantTrackingState;

次に、Activityにいくつかの追加が必要になります。インスタントトラッキングを使用するには、ActivityはInstantTrackerListenerの実装が必要になり、InstantTrackerとInstantTrackingStateがメンバーとして必要になります。

public class InstantTrackingActivity extends Activity implements InstantTrackerListener, ExternalRendering {
private InstantTracker instantTracker;

private InstantTrackingState currentTrackingState = InstantTrackingState.Initializing;

InstantTrackerの初期化はかなり簡単です。Activityの初期化フェーズでこの行を実行するだけです。

instantTracker = wikitudeSDK.getTrackerManager().createInstantTracker(this, null);

初期状態とトラッキング状態の両方でレンダリングされる描画可能なものを供給することは、実用的な使用の場合には推奨されますが、InstantTrackerは上記の行だけでインスタンス化をできます。そのため、CustomSurfaceViewインスタンスは生成され、提供されます。

@Override
public void onRenderExtensionCreated(final RenderExtension renderExtension_) {
    glRenderer = new GLRenderer(renderExtension_);
    surfaceView = new CustomSurfaceView(getApplicationContext(), glRenderer);
    driver = new Driver(surfaceView, 30);
    setContentView(surfaceView);

    FrameLayout viewHolder = new FrameLayout(getApplicationContext());
    setContentView(viewHolder);

    viewHolder.addView(surfaceView);

InstantTrackerの初期化にCustomSurfaceViewを使用するには、InstantTrackerListenerのonInitializationPoseChangedコールバック関数を実装します。

@Override
public void onInitializationPoseChanged(InstantTracker tracker, InitializationPose pose) {
    StrokedRectangle strokedRectangle = getRectangle();
    strokedRectangle.projectionMatrix = pose.getProjectionMatrix();
    strokedRectangle.viewMatrix = pose.getViewMatrix();
}

このコールバック関数はInstantTrackerと現在のInitializationPoseを提供し、後者は拡張表示のポーズを設定するために使用できます(この例ではStrokedRectangleです。)
SMARTを使用して、ARCoreがサポートされている場合、ARCoreが平面を検出したあとにトラッキングを開始することができます。トラッキングを開始するときは、ユーザーに通知することを推奨します。この例では、トラッキングできない場合は赤色、トラッキングできる場合は緑色に拡張表示の枠線の色を変更することで通知しています。トラッキングが可能かどうかを確認するには、InstantTracker.canStartTrackingを使用します。

@Override
public void onInitializationPoseChanged(InstantTracker tracker, InitializationPose pose) {
    [...]
    if (instantTracker.canStartTracking()) {
        strokedRectangle.setColor(0.0f, 1.0f, 0.0f);
    } else {
        strokedRectangle.setColor(1.0f, 0.0f, 0.0f);
    }
}

インスタントトラッキングの現在の状況を常に把握する必要があるので、onStateChangedを呼び出すたびにcurrentTrackingStateを更新します。

@Override
public void onStateChanged(InstantTracker tracker, InstantTrackingState state) {
    currentTrackingState = state;

    runOnUiThread(() -> {
        if (state == InstantTrackingState.Tracking) {
            changeStateButton.setText(R.string.instant_tracking_button_start_initialization); 
            getRectangle().setColor(1.0f, 0.58f, 0.16f);
        } else {
            changeStateButton.setText(R.string.instant_tracking_button_start_tracking);
        }
    });
}

次に、ある状態から別の状態に移行する手段が必要です。このタスクでは、現在のトラッキング状態をトグルするボタンを用意しています。

final Button changeStateButton = (Button) findViewById(R.id.on_change_tracker_state);
changeStateButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
        if (currentTrackingState == InstantTrackingState.Initializing) {
            instantTracker.setState(InstantTrackingState.Tracking);
        } else {
            instantTracker.setState(InstantTrackingState.Initializing);
        }
    }
});

これで、2つの状態を切り替えることができます。トラッキング状態がアクティブになったら、矩形の位置を更新する必要があります。これはonTrackedコールバック関数で行います。

@Override
public void onTracked(InstantTracker tracker, InstantTarget target) {
    StrokedRectangle strokedRectangle = getRectangle();
    strokedRectangle.projectionMatrix = target.getProjectionMatrix();
    strokedRectangle.viewMatrix = target.getViewMatrix();
}

最後に、InstantTrackerのdeviceHeightプロパティを設定するSeekBarを用意しています。この変更は厳密に言えば必須ではありませんが、この方法で正確にデバイスの高さを指定することを推奨します。

final SeekBar heightSlider = (SeekBar) findViewById(R.id.heightSeekBar);
heightSlider.setMax(190);
heightSlider.setProgress(90);
heightSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        float height = (progress + 10) / 100.f;
        instantTracker.setDeviceHeightAboveGround(height);
        heightBox.setText(String.format( "%.2f", height ));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
});

このセクションで説明した例では、トラッキング状態のときに初期状態で割り当てられた位置にとどまるオレンジ色の矩形をレンダリングしています。この例はとても単純なものですが、インスタントトラッキングの基本概念を理解するのに適しています。

インスタントシーンのピッキング

インスタントトラッキング機能は、さらに、3Dポイントを基礎としたポイントクラウド構造から照会されることが可能になります。このセクションは、サンプルアプリケーションの対応するサンプルに基づいてこの機能を紹介します。
この機能を利用するには、画面上の2D入力位置が必要です。GLSurfaceViewのOnTouchListenerは、その目的のために設定されます。

this.surfaceView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        [...]
    }
});

onTouch関数は、タッチ位置を照会し、これらの座標をconvertScreenCoordinateToPointCloudCoordinate関数に渡すために実装されます。これらの入力座標は、画面の左上隅に原点を持ち、[0、screen_width_in_pixels]と[0、screen_height_in_pixels]の間隔内にあります。完了ハンドラ関数内では、ブール値と3Dポイントを受信します。前者は、操作が正常に完了したかどうかを通知します。後者には成功の場合の結果が含まれ、失敗の場合はnullが含まれます。特定の間隔内の入力座標に対してポイントクラウドの位置が見つからないときはクエリが失敗することに注意してください。成功したクエリでは、StrokedCubeインスタンスが生成され、その結果の位置に拡張として表示されます。

this.surfaceView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {

            Point2D<Float> screenCoordinates = new Point2D<>();
            screenCoordinates.setX(event.getX());
            screenCoordinates.setY(event.getY());

            instantTracker.convertScreenCoordinateToPointCloudCoordinate(screenCoordinates, new InstantTrackerScenePickingCallback() {
                @Override
                public void onCompletion(boolean success, Point3D result) {
                    if (success) {
                        StrokedCube strokedCube = new StrokedCube();
                        strokedCube.setXScale(0.05f);
                        strokedCube.setYScale(0.05f);
                        strokedCube.setZScale(0.05f);
                        strokedCube.setXTranslate(result.getX());
                        strokedCube.setYTranslate(result.getY());
                        strokedCube.setZTranslate(result.getZ());
                        glRenderer.setRenderablesForKey("" + cubeID++, strokedCube, null);
                    }
                }
            });
        }

        return true;
    }
});

最後に、サンプルを実行することで、トラッキング状態のときにキューブ拡張をスクリーンタッチに置くことができます。

持続的なインスタントターゲット

インスタントターゲットの保存および読み込みの機能により、AR体験は複数のユーザーがデバイスやオペレーティングシステムを横切って持続的にアクセスできるようになります。 さらにインスタントターゲットを拡大することができます。このセクションでは、サンプルアプリケーションに基づいてこの機能を紹介します。 この機能は、プラットフォームによるトラッキングが有効な場合は使用できません。

インスタントターゲットの保存

インスタントターゲットを保存するには、アクティブなInstantTrackerがトラッキング状態でなければならず、指定されたパスのディレクトリが存在する必要があります。提供されたパスへの書き込みに必要な権限はすべて付与する必要があります。特定のスレッドでコールバックを受け取るハンドラを提供することはオプションになります。

instantTracker.saveCurrentInstantTarget(
        new File(getExternalFilesDir(null), "savedTarget.wto").getAbsolutePath(),
        new CompletionCallback() {
            @Override
            public void onCompletion() {
                // ...
            }
        },
        new ErrorCallback() {
            @Override
            public void onError(@NonNull final WikitudeError error) {
                // ...
            }
        },
        new Handler(getMainLooper()) // Receive callbacks on main thread.
);

インスタントターゲットの読み込み

インスタントターゲットを読み込むには、アクティブトラッカーと以前に保存したインスタントターゲットがなければなりません。
InstantTargetRestorationConfigurationは、読み込まれたインスタントターゲットの動作を定義します。この例では、ポリシーはALLOW_EXPANSIONに設定されます。これは、Wikitude SDKがトラッキング可能な新しいポイントを見つけようとすることを意味します。

InstantTargetRestorationConfiguration configuration = new InstantTargetRestorationConfiguration();
configuration.setInstantTargetExpansionPolicy(InstantTargetRestorationConfiguration.InstantTargetExpansionPolicy.ALLOW_EXPANSION);

以前に保存したインスタントターゲットを読み込むには、saveCurrentInstantTargetによって保存されたファイルからTargetCollectionResourceを作成する必要があります。

String savedTargetPath = "file://" + new File(getExternalFilesDir(null), "savedTarget.wto").getAbsolutePath();
TargetCollectionResource savedTarget = wikitudeSDK.getTrackerManager().createTargetCollectionResource(savedTargetPath);

インスタントターゲットが読み込まれた後、トラッカーは即座にターゲットを探してトラッキングしようとします。特定のスレッドでコールバックを受け取るハンドラを提供することはオプションになります。

instantTracker.loadExistingInstantTarget(
        savedTarget,
        new CompletionCallback() {
            @Override
            public void onCompletion() {
                // ...
            }
        },
        new ErrorCallback() {
            @Override
            public void onError(@NonNull final WikitudeError error) {
                // ...
            }
        },
        configuration,
        new Handler(getMainLooper()) // Receive callbacks on main thread.
);