一覧に戻る

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

2017.09.22AndroidネイティブAPI

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

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

概要

インスタントトラッキングは、以前に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の2つのインスタンスがメンバーとして必要になります。

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

private InstantTrackingState mCurrentTrackingState = InstantTrackingState.Initializing;
private InstantTrackingState mRequestedTrackingState = InstantTrackingState.Initializing;

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

mInstantTracker = mWikitudeSDK.getTrackerManager().createInstantTracker(this, null);

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

@Override
public void onRenderExtensionCreated(final RenderExtension renderExtension_) {
    mGLRenderer = new GLRenderer(renderExtension_);
    mSurfaceView = new CustomSurfaceView(getApplicationContext(), mGLRenderer);
    mDriver = new Driver(mSurfaceView, 30);
    setContentView(mSurfaceView);

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

    viewHolder.addView(mSurfaceView);

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

@Override
public void onInitializationPoseChanged(InstantTracker tracker, InitializationPose pose) {
    mGLRenderer.setCurrentlyRecognizedTarget(pose);
}

このコールバック関数は、InstantTrackerと現在のInitializationPoseを提供します。InitializationPoseはレンダラーを現在認識しているターゲットに設定して、CustomSurfaceViewが初期状態で正しく表示されるようにします。
インスタントトラッキングの現在の状況を常に把握する必要があるので、onStateChangedを呼び出すたびにmCurrentTrackingStateを更新します。

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

次に、ある状態から別の状態に移行する手段が必要です。このタスクでは、現在のトラッキング状態をトグルするボタンを用意しています。ここでは、mRequestedTrackingStateを使用して、2つの状態間の切り替えが正しく機能するようにします。

final Button changeStateButton = (Button) findViewById(R.id.on_change_tracker_state);
changeStateButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
        if (mRequestedTrackingState == InstantTrackingState.Initializing) {
            if (mCurrentTrackingState == InstantTrackingState.Initializing) {
                mRequestedTrackingState = InstantTrackingState.Tracking;
                mInstantTracker.setState(mRequestedTrackingState);
                changeStateButton.setText(R.string.instant_tracking_button_start_initialization);
            }
        } else {
            if (mCurrentTrackingState == InstantTrackingState.Tracking) {
                mRequestedTrackingState = InstantTrackingState.Initializing;
                mInstantTracker.setState(mRequestedTrackingState);
                changeStateButton.setText(R.string.instant_tracking_button_start_tracking);
            }
        }
    }
});

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

@Override
public void onTracked(InstantTracker tracker, InstantTarget target) {
    mGLRenderer.setCurrentlyRecognizedTarget(target);
}

最後に、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;
        mInstantTracker.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.mSurfaceView.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.mSurfaceView.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());

            mInstantTracker.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());
                        mGLRenderer.setRenderablesForKey("" + cubeID++, strokedCube, null);
                    }
                }
            });
        }

        return true;
    }
});

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