Object & Scene Recognition


物体と風景認識(Object & Scene Recognition)の紹介

物体認識とトラッキング機能は、ARの実施において、気ままな対象の物体認識とそのトラッキングをすることで、Wikitude SDKの可能性を拡げます。この機能はWikitudeのSLAMエンジンをベースにしています。それは、あらゆるトラッキング環境に対応した機能を持つSDKを通して使用されます。物体認識とトラッキング機能は、あなたが事前に定義した物体(Object) と全体の風景(Scene)を検出します。

認識に適した物体には以下のものが含まれます。

  • おもちゃ
  • 記念碑と像
  • 工業用物体
  • 工具
  • 家庭用品

認識機能は、変化や動的な部分の数が限られている物体に最適です。

風景(Scene)の認識

 

SDK 8では、物体認識エンジンを使用して、テーブルサイズを超える大きな物体を認識することができます。風景(Scene)認識という名前は特にこれを反映しています。新しい画像ベースの変換方式では、もっと大きなサイズのオブジェクトターゲットをうまく認識し、トラッキングをしています。

  • 部屋
  • 建物
  • 広場や中庭

 

物体認識を生成する前に、ターゲットオブジェクトの作成方法chapter on how to create Object Targets に関する章を必ず読んでください。

この事例はどのように物体をトラッキングし、それを遮断する機能を追加し、そしてボタンやアニメーションを追加して自在に動くようにするのか、を示します。

Basic Object Tracking

Basic Object Trackingの事例は、Wikitude SDKによって、どのようにObject Trackingが作用するのか、ラフなアイデアをあなたに与えます。トラッキング情報を扱うwto-fileを使っておもちゃの消防車をトラックしてみましょう。

最初に、新しく作成したARchitect Worldでトラッキング情報をロードして、新規トラッカーに追加します。

 

もしすでにWikitudeSDKを使用していればご存知でしょう。イメージターゲットのトラッキング情報をロードする場合はwtcファイルが使用されますが、物体のトラッキングの場合は、wtoファイルが使用されます。その後、this.targetCollectionResourceが作成されるので、新しいAR.ObjectTrackerを初期化するために使用できます。

this.targetCollectionResource = new AR.TargetCollectionResource("assets/firetruck.wto", {
});

this.tracker = new AR.ObjectTracker(this.targetCollectionResource, {
    onError: function(errorMessage) {
        alert(errorMessage);
    }
});

消防車をトラッキングしたら、表示する3Dモデルを追加します。次に、引きだし可能な部品をAR.ObjectTrackableに追加しなければなりません。そして、それらも含んでいる配列( World.drawables)を作成します。

assetsフォルダーには、ロードコーンの.wt3ファイルが含まれるので、このファイルを使用して3Dモデルの4つの例を作成して、消防車の周りに配置します。getCone関数は、適切なスケール、回転で目的の位置にあるロードコーンのモデルを出します。

getCone: function getConeFn(positionX, positionY, positionZ) {
    var coneScale = 0.05;

    return new AR.Model("assets/traffic_cone.wt3", {
        scale: {
            x: coneScale,
            y: coneScale,
            z: coneScale
        },
        translate: {
            x: positionX,
            y: positionY,
            z: positionZ
        },
        rotate: {   
            x: -90
        }
    });
},

それぞれの4つの位置に対してgetCone関数を呼び出し、取得したコーンを物体がトラッキングされたときに表示できるようにWorld.drawables配列に追加します。

var coneDistance = 1.0;

var frontLeftCone = World.getCone(-coneDistance, 0.0, World.occluderCenterZ + coneDistance);
World.drawables.push(frontLeftCone);

var backLeftCone = World.getCone( coneDistance, 0.0, World.occluderCenterZ + coneDistance);
World.drawables.push(backLeftCone);

var backRightCone = World.getCone( coneDistance, 0.0, World.occluderCenterZ - coneDistance);
World.drawables.push(backRightCone);

var frontRightCone = World.getCone(-coneDistance, 0.0, World.occluderCenterZ - coneDistance);
World.drawables.push(frontRightCone);

最後に、AR.ObjectTrackableを作成して、World.drawables配列で初期化します。さらに、AR.ObjectTrackableの2つのコールバック機能を実装して、物体が認識されたかどうかを確認します。

this.objectTrackable = new AR.ObjectTrackable(this.tracker, "*", {
    drawables: {
        cam: World.drawables
    },
    onObjectRecognized: this.objectRecognized,
    onObjectLost: this.objectLost,
    onError: function(errorMessage) {
        alert(errorMessage);
    }
});

なお、アプリケーションを実行して消防車を見ると、周りに4つのロードコーンが表示されます。しかし、これらのロードコーンが消防車に隠されていても表示されます。この動作を避けるために遮断機能occluder )が使用されます。

遮断は、描写されない3Dモデルであり、描写機能を無効にして背後に置きます。assetsフォルダーには、消防車の形をした遮断された3Dモデルが配置されています。このモデルは簡単にARchitect Worldに追加できます。

AR.ObjectTrackerを作成する前に、firetruck_occluder.wt3を使用して適切なスケールと回転でAR.Occluderを作成し、World.drawables配列に追加します。

var occluderScale = 0.0057;

this.firetruckOccluder = new AR.Occluder("assets/firetruck_occluder.wt3", {
    onLoaded: this.loadingStep,
    scale: {
        x: occluderScale,
        y: occluderScale,
        z: occluderScale
    },
    translate: {
        x: -0.25,
        z: -0.3
    },
    rotate: {
        x: 180
    }
});
World.drawables.push(this.firetruckOccluder);

プログラムを走らせてみましょう。消防車が認識し、コーンが正確にその陰に隠れて見えません。

Basic Object Tracking

画像と音声AR(Image and Sound AR)

この例では、最初の物体トラッキングのサンプルとして消防車に緊急ライトとサイレンのボタンを加えてみましょう。

最初に、ライトを付け加えます。getLight機能がAR.ImageDrawableから引き出され、青い緊急ライトを正しい位置に表示します。

getLight: function getLightFn(positionX, positionY, positionZ) {
    var lightScale = 0.3;
    var lightResource = new AR.ImageResource("assets/emergency_light.png");

    return new AR.ImageDrawable(lightResource, lightScale, {
        translate: {
            x: positionX,
            y: positionY,
            z: positionZ
        },
        rotate: {
            x: 90
        },
        enabled: false
    });
},

2つの緊急ライトをキャビンの上につけるために、この操作を2度行います。

var leftLight = World.getLight(-0.6, 0.9, World.occluderCenterZ + 0.2);
World.drawables.push(leftLight);

var rightLight = World.getLight(-0.6, 0.9, World.occluderCenterZ - 0.2);
World.drawables.push(rightLight);

ライトが ARchitect worldに追加された後、それをアニメーションしてみます。両方のライトがフラッシュするようにしたいので、ライトが不透明になるようにアニメーションして、それが付いたり消えたりするようにしてみます。

 

addLightAnimationの機能は一つのライトをパラメーターにします。それがライトの2つのアニメーションを生成し、着いたり消えたりするようにします。このために、AR.PropertyAnimationを生成し、そして、以下の5つのパラメーターを入れます。

  • アニメーション化されるオブジェクト
  • アニメーション化されるパラメータ名
  • アニメーションの開始値
  • アニメーションの最終値
  • アニメーションの持続時間

2つの値の間にスムーズな移行を行うために、AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINEに設定します。

両方のアニメーションを変数に保存し、AR.AnimationGroupAR.CONST.ANIMATION_GROUP_TYPE.SEQUENTIALでグループ化します。これにより、グループ内のアニメーションは並行しないで続々と再生されます。最後に、AR.AnimationGroup-1を指定し、無限ループを作成します。

addLightAnimation: function addLightAnimationFn(light) {
    var animationDuration = 500;
    var lowerOpacity = 0.5;
    var upperOpacity = 1.0;

    var lightAnimationForward = new AR.PropertyAnimation(light, "opacity", lowerOpacity, upperOpacity, animationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });

    var lightAnimationBack = new AR.PropertyAnimation(light, "opacity", upperOpacity, lowerOpacity, animationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });

    var lightAnimation = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.SEQUENTIAL, [lightAnimationForward, lightAnimationBack]);
    lightAnimation.start(-1);
},

この関数を生成すると2つのライトのアニメーションができます。ライトを作成した時にライトを着かないようにしたので(enabled: false)、この時点では消えています。しかし、しばらくするとつきます。

 

さて、サイレンを追加しましょう。 siren.wav の音声ファイルから タイプ AR.Sound を使います。それはassetsフォルダーで見つけます。下記のように、あなたのAR.Soundを初期化しそれを読込みますが、これにより、play()を最初に呼び出すときにAR.Soundを再ロードする必要がありません。

this.sirenSound = new AR.Sound("assets/siren.wav", {
    onError : function(){
        alert(errorMessage);
    }
});
this.sirenSound.load();

すべての部品は準備完了しました。しかし、どれも表示されても動いてもいません。アニメーションを動かし、サイレンを鳴らすアニメーションボタンを追加します。このボタンはAR.Model、それはWorld.setLightsEnabled(true)に設定したonClick コールバック方法です。それがライトを点灯し、ボタンを消し、サイレンを鳴らします。

 

このサイレンをassetsフォルダに配置されるmarker.wt3モデルで初期化し、運転席の上に位置付けます。変化するアニメーションを追加するために、addButtonAnimation関数を呼び出します。

this.lightsButton = new AR.Model("assets/marker.wt3", {
    translate: {
        x: -0.6,
        y: 0.9,
        z: World.occluderCenterZ
    },
    rotate: {
        x: -90
    },
    onClick: function() {
        World.setLightsEnabled(true);
    }
});
World.addButtonAnimation(this.lightsButton);
World.drawables.push(this.lightsButton);

addButtonAnimationでは、AR.PropertyAnimationを使用してボタンのサイズを大きくしたり小さくしたりします。それぞれの比率で2つのアニメーションを設定します。一つは大きく、一つは小さくします。両方のアニメーションはアニメーションの持続時間の半分をとり、両方ともスムーズに相互変換するためにEASE_IN_OUT_SINEを使用します。6つのすべてのアニメーションが作成され、別個のAR.AnimationGroupの例が一緒に統合された後、すべてのアニメーショングループを一度に開始すると、無期限に実行しますindefinitely (-1)

addButtonAnimation: function addButtonAnimationFn(button) {
    var smallerScale = 0.03;
    var biggerScale = 0.04;
    var scaleAnimationDuration = 2000;

    // x
    var buttonScaleAnimationXOut = new AR.PropertyAnimation(button, "scale.x", smallerScale, biggerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationXIn = new AR.PropertyAnimation(button, "scale.x", biggerScale, smallerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationX = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.SEQUENTIAL, [buttonScaleAnimationXOut, buttonScaleAnimationXIn]);

    // y
    var buttonScaleAnimationYOut = new AR.PropertyAnimation(button, "scale.y", smallerScale, biggerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationYIn = new AR.PropertyAnimation(button, "scale.y", biggerScale, smallerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationY = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.SEQUENTIAL, [buttonScaleAnimationYOut, buttonScaleAnimationYIn]);

    // z
    var buttonScaleAnimationZOut = new AR.PropertyAnimation(button, "scale.z", smallerScale, biggerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationZIn = new AR.PropertyAnimation(button, "scale.z", biggerScale, smallerScale, scaleAnimationDuration/2, {
        type: AR.CONST.EASING_CURVE_TYPE.EASE_IN_OUT_SINE
    });
    var buttonScaleAnimationZ = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.SEQUENTIAL, [buttonScaleAnimationZOut, buttonScaleAnimationZIn]);

    // start all animation groups
    buttonScaleAnimationX.start(-1);
    buttonScaleAnimationY.start(-1);
    buttonScaleAnimationZ.start(-1);
},

2D AR

Animated 3D AR

この事例の中では、消防車の車輪を分解するねじ回しのアニメーションを追加する方法を示します。それは2Dイメージ(画像)と音声ARがベースになっています。

まず、何もしていないアニメーションの上で動く、ねじ回しとねじを作成します。これらは座標z方向にのみ動くので、部品はx座標とy座標を使用します。また、ねじのサイズをねじ回しのサイズに合わせて簡単に変更できるようにします。両方のオブジェクトをWorld.drawables配列に追加します。

var screwdriverScale = 0.04;
var screwdriverPositionX = -0.52;
var screwdriverPositionY = 0.24;

this.screwdriver = new AR.Model("assets/screwdriver.wt3", {
    scale: {
        x: screwdriverScale,
        y: screwdriverScale,
        z: screwdriverScale
    },
    translate: {
        x: screwdriverPositionX,
        y: screwdriverPositionY
    },
    rotate: {
        y: 180
    },
    enabled: false
});
World.drawables.push(this.screwdriver);

var screwScale = screwdriverScale * 0.6;
this.screw = new AR.Model("assets/screw.wt3", {
    scale: {
        x: screwScale,
        y: screwScale,
        z: screwScale
    },
    translate: {
        x: screwdriverPositionX,
        y: screwdriverPositionY
    },
    enabled: false
});
World.drawables.push(this.screw);

次は、アニメーション中にねじとねじ回しの動きをより分かりやすく指し示す、回す矢印が必要なので、そのモデルです。

var turningArrowScale = screwdriverScale * 0.2;
this.turningArrow = new AR.Model("assets/arrow.wt3", {
    scale: {
        x: turningArrowScale,
        y: turningArrowScale,
        z: turningArrowScale
    },
    translate: {
        x: screwdriverPositionX,
        y: screwdriverPositionY,
        z: World.occluderCenterZ + 0.7
    },
    rotate: {
        y: -90
    },
    enabled: false
});
World.drawables.push(this.turningArrow);

前の例のように、アニメーション中に、これらすべての部品を全て見えなくして、アニメーションを稼働させるためのボタンを付け加えます。

this.tireButton = new AR.Model("assets/marker.wt3", {
    translate: {
        x: -0.55,
        y: 0.25,
        z: World.occluderCenterZ + 0.4
    },
    onClick: function() {
        World.runScrewdriverAnimation();
    }
});
World.addButtonAnimation(this.tireButton);
World.drawables.push(this.tireButton);

今するのはrunScrewdriverAnimation( )機能を実装することです。まず、すべての必要とされている部品を有効状態にして、アニメーションの持続時間を決めるanimationDurationとアニメーション中のねじと、ねじ回しの移動距離を表すtranslateDistanceを指定します。次に、ねじ回しとねじの平行移動アニメーションを作成します。また、アニメーションが完了した後にすべての部品が見えなくなるように、最初のアニメーション上にonFinishコールバックを実装します。ねじ回しの回転方向を示すために、ねじを1回転させるように回転する矢印のアニメーションを追加します。すべてのアニメーションが作成されたら、AR.AnimationGroupで一つにまとめて、AR.CONST.ANIMATION_GROUP_TYPE.PARALLELパラメータを使用して一緒に実行します。

runScrewdriverAnimation: function runScrewdriverAnimationFn() {
    World.setScrewdriverEnabled(true);

    var animationDuration = 2000;

    var translateDistance = 0.2;
    var screwdriverZOffset = World.occluderCenterZ + 1.0;

    var screwdriverTranslateAnimation = new AR.PropertyAnimation(World.screwdriver, "translate.z", screwdriverZOffset, screwdriverZOffset + translateDistance, animationDuration, {}, {
        onFinish: function() {
            World.setScrewdriverEnabled(false);
        }
    });

    var screwZOffset = screwdriverZOffset - 0.65;
    var screwTranslateAnimation = new AR.PropertyAnimation(World.screw, "translate.z", screwZOffset, screwZOffset + translateDistance, animationDuration);

    var arrowRotationAnimation = new AR.PropertyAnimation(World.turningArrow, "rotate.z", 0, 360, animationDuration);

    var animationGroup = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.PARALLEL, [screwdriverTranslateAnimation, screwTranslateAnimation, arrowRotationAnimation]);
    animationGroup.start();
},

3D AR

拡張Object Tracking

拡張トラッキング機能はそれぞれのターゲットに個別につけられるオプションです。このモードでは、WikitudeSDKはユーザーがオリジナルのターゲットをどこにも見つけられなくても、置かれた環境をスキャンし続けます。そのため、オリジナルターゲットのObjectの範囲を超えてトラッキングを拡大します。この機能の性能は、デバイスのコンピュータパワー、手触りの感覚、Objectなど、多くのファクターに依存しています。

 

拡張Object Trackingを可能にするには、単純に、AR.ObjectTrackable での生成をenableExtendedTracking,と extendedTarget  onExtendedTrackingQualityChangedのパラメーターを含んだ生成に変えます。私達がtrue設定したenableExtendedTracking のパラメーターは、拡張Object Tracking起動します。 デフォルトバリューは失敗false.にセットしてあります。これは、Objectが生成されている間に終了します。後にプロパティをセットするのはエラーになります。拡張Object Tracking をすべてのObjectsに適用するためにextendedTargetのパラメーターに’*’をセットします。他に有効なのはは’?’とWTOファイルにある個々のターゲットの識別子です。これらのプロパティはObjectの生成が終わると変更されます。

しかしながら、効果取得を変化させるためにトラッキングは断続的に消滅させることが必要です。これは、stopExtendedTracking機能を呼ぶことで達成されます。最後に、私達は onExtendedTrackingQualityChangedパラメーターをセットします。これは短く会話をする特注機能です。この機能は拡張Trackingの品質が変化した時はいつでも呼び出せます。

this.objectTrackable = new AR.ObjectTrackable(this.tracker, "*", {
    [...]
    enableExtendedTracking: true,
    extendedTarget: "*",
    onExtendedTrackingQualityChanged: World.extendedTrackingQualityChanged,
});

これらの改良が出たことで、トラッキングは、Objectがカメラの視界フィールドから外れてしまった後でも、代わりに周りの環境をトラッキングすることで有効に残っています。この作業の信頼性のおかげで、トラックされる環境は機能を無効にしません。現在の環境のトラッキング品質は、onExtendedTrackingQualityChangedの以下の特注機能が張り付けてあるイベントを通して情報交換されています。それは、3つの別個の品質基準のうちのどれに当たるか、1つを報告するために、わかりやすくHTMLの部分の背景の色を変えてあります。よりよいトラッキング品質のために、アルゴリズムはよりよい能動的なトラッキングと小刻みに動く動きに耐えることを維持できるようになっています。

逆に、品質の悪いトラッキングの反対語は正確さです。デバイスをもっとゆっくり動かし、より豊富な分野をカバーする機能は、トラッキングの品質の現在の状況に良い影響を与えると思えます。

extendedTrackingQualityChanged: function(targetName, oldTrackingQuality, newTrackingQuality) {
    console.log('extendedTrackingQualityChanged ' + oldTrackingQuality + ' ' + newTrackingQuality)
    var newBackgroundClass;

    switch (newTrackingQuality) {
    case -1:
        newBackgroundClass = 'trackingBad';
        break;
    case 0:
        newBackgroundClass = 'trackingMedium';
        break;
    default:
        newBackgroundClass = 'trackingGood';
        break;
    }

    World.removeTrackingIndicator();

    var trackingIndicatorDiv = document.getElementById('trackingIndicator');
    World.trackingIndicatorBackgroundClass = newBackgroundClass;

    trackingIndicatorDiv.classList.add(World.trackingIndicatorBackgroundClass);
},

トラッキングを成功させるために、どこの地点にどのようにユーザーのデバイスを向ければよいか、ユーザーをガイドする、このような品質レポートの手順を持つことを推奨します。