物体のとシーンの認識

物体とシーンの認識の紹介

以下のセクションでは、Wikitude JavaScript SDKの物体のトラッキング機能について紹介し、拡張現実体験のために任意の物体を認識してトラッキングする方法を示します。この機能は、あらゆる種類の環境をトラッキングするためにSDK全体で使用されるWikitude独自のSLAMエンジンに基づいた機能です。物体の認識とトラッキングにより、あらかじめ定義された物体やシーン全体を検出できます。認識に適した物体には以下のものが含まれます。

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

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

シーンの認識

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

  • 部屋
  • 建物
  • 広場や中庭
物体認識を使用する前に、ターゲットオブジェクトの作成方法に関する章を必ず読んでください。

物体は多くの動的な部分で構成されていなければ正常に認識できます。このサンプルは、物体をトラッキングし、物体にオクルージョンを追加し、ボタンやアニメーションを追加して対話型物体にする方法を示します。

基本的な物体のトラッキング

このサンプルは、物体のトラッキングのアルゴリズムの最小限の実装を示します。さらに、トラッキング情報を含むwtoファイルを使用しておもちゃの消防車をトラッキングします。

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

ターゲット画像のトラッキング情報をロードする場合は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に追加するDrawableの要素を含む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つのロードコーンが表示されます。しかし、これらのロードコーンが消防車に隠されていても表示されます。この動作を避けるためにオクルージョンが使用されます。

オクルージョンは、レンダリングされない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);

プログラムを実行して、消防車を認識すると、消防車の背後にあるロードコーンが消えます。

基本的な物体のトラッキング

画像と音声の拡張

このセクションでは、以前に実装されたサンプルアプリケーションを修正して、消防車にボタン、非常灯、サイレンを追加する方法を示します。

まず、サンプルに非常灯を追加します。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つの非常灯を作成するために、getLight関数を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関数は、1つの非常灯をパラメータとして対応します。これは、フェードイン/フェードアウト効果を適用するために非常灯の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);
},

両方の非常灯に対してaddLightAnimation関数を呼び出して、アニメーションを作成します。非常灯を作成したときに無効(enabled: false)にしたので、現時点では表示されません。

次に、assetsフォルダーにあるsiren.wav音声ファイルとAR.Sound型を使用してサイレンを追加します。以下のようにAR.Soundを初期化して、ロードします。これにより、play()を最初に呼び出すときにAR.Soundを再ロードする必要がありません。

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

なお、アニメーションと音声ファイルを実行するためにアニメーションボタンを追加します。このボタンはAR.Modelであり、onClickコールバックメソッドがWorld.setLightsEnabled(true)に設定されます。これは、非常灯を有効にし、ボタンを無効にして、サイレンを鳴らします。

このサイレンを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インスタンスで一緒にグループ化された後、すべてのアニメーショングループを一度に開始すると、無期限に実行します。

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拡張

アニメーション化された3D拡張

このセクションは、2D画像と音声の拡張サンプルアプリケーションに基づいて消防車の車輪を分解するねじ回しのアニメーションを追加する方法を示します。

まず、ねじ回しとねじを作成します。これらの座標は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拡張