step4
ハンドポーズを認識してシャッターを切ろう!

step4-1プレビュー表示欄を作成

シャッターを切った時に撮影イメージを確認するプレビュー表示欄を作成します。

(1) Imageオブジェクトを作成して設定します。

  • メニュー: GameObject / UI / Image
  • 名前: OverlayImage
  • Inspectorの設定:
    Rect Transform / Anchor Presets: stretch - stretch
    Rect Transform / Left: 0
    Rect Transform / Top: 0
    Rect Transform / Right: 0
    Rect Transform / Bottom: 0
    Image / Color: 0, 0, 0, 224 ( R, G, B, A )
step4-1-1

(2) 上記で作成したOverlayImageの中にImageオブジェクトを作成します。

  • メニュー: (Hierarchy) / シーン / Canvas / OverlayImage を右クリック / UI / Image
  • 名前: PreviewImage

(3) Gameウィンドウで確認しながら PreviewImage のサイズを調整します。

  • Inspectorの設定:
    Rect Transform / Anchors / Min / X: 0.15
    Rect Transform / Anchors / Min / Y: 0.15
    Rect Transform / Anchors / Max / X: 0.85
    Rect Transform / Anchors / Max / Y: 0.85
    Rect Transform / Left: 0
    Rect Transform / Top: 0
    Rect Transform / Right: 0
    Rect Transform / Bottom: 0
step4-1-2

step4-2スクリプトを作成

フレーム毎に手のポーズを確認して、決まったポーズであれば撮影を行うスクリプトを作成します。撮影は、カメラ映像を取得してImageオブジェクトに表示します。
※ UnityでiOS端末のアルバム保存を行うにはプラグインが必要となるため、今回は表示のみとしています。

(1) Scriptsフォルダを作成します。

  • メニュー: (Project) / Assets を右クリック / Create / Folder
  • 名前: Scripts

(2) スクリプトファイルを作成します。

  • メニュー: (Project) / Assets / Scripts を右クリック / Create / C# Script
  • 名前: PoseShutter
step4-2

(3) 作成したスクリプトファイルをダブルクリックしてスクリプトを記述します。

  • スクリプト: (Project) / Assets / Scripts / PoseShutter
  • スクリプト内容:

using System.Collections;
using TofAr.V0.Hand;
using UnityEngine;
using UnityEngine.UI;

public class PoseShutter : MonoBehaviour
{
    // 撮影時にキャプチャーするカメラ
    public Camera captureCamera;

    // プレビューをオーバーレイ表示にするImageオブジェクト
    public Image overlayImage;

    // プレビューを表示するImageオブジェクト
    public Image previewImage;

    // シャッターポーズ
    private const PoseIndex SHUTTER_POSE = PoseIndex.Peace;

    // Start is called before the first frame update
    void Start()
    {
        // プレビュー表示を閉じる
        this.overlayImage.gameObject.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        // プレビューの表示状態で処理を切り替え
        if (this.overlayImage.gameObject.activeSelf == true)
        {
            // 表示時は、タップ操作でプレビュー表示を閉じる
            if (Input.GetMouseButtonDown(0))
            {
                this.overlayImage.gameObject.SetActive(false);
            }
        }
        else
        {
            // 非表示時は、Handデータを取得してポーズを判定
            StartCoroutine("CheckHandPose");
        }
    }

    // Handデータを取得してポーズを判定
    private IEnumerator CheckHandPose()
    {
        // 1回目で撮影するとポーズの途中でも認識されて撮影されるため、
        // 時間をあけて2回判定する
        var loopCount = 2;
        for (var cnt = 0; cnt < loopCount; cnt++)
        {
            // Handデータを取得
            var handData = TofArHandManager.Instance.HandData;
            if (handData == null)
            {
                yield break;
            }

            // 認識したポーズを取得
            handData.GetPoseIndex(out var leftPose, out var rightPose);

            // シャッターポーズ以外の場合、判定は終了
            if (leftPose != SHUTTER_POSE && rightPose != SHUTTER_POSE)
            {
                yield break;
            }

            // 最終ループ以外は1秒待機
            if (cnt < loopCount - 1)
            {
                yield return new WaitForSeconds(1);
            }
            
        }

        // 判定を通過してプレビューが表示されていない場合、プレビューを表示
        if (this.overlayImage.gameObject.activeSelf == false)
        {
            this.ShowPreview();
        }
    }

    // カメラ映像を取得して、プレビューを表示する
    private void ShowPreview()
    {
        // カメラと映像サイズを取得
        var camera = this.captureCamera;
        var cameraW = camera.pixelWidth;
        var cameraH = camera.pixelHeight;

        // 既存の設定をバックアップ
        var backupActiveRenderTexture = RenderTexture.active;
        var backupCameraTargetTexture = camera.targetTexture;

        // レンダーテクスチャを一時的に変更して、カメラ映像を取得
        var rect = new Rect(0, 0, cameraW, cameraH);
        var renderTexture = new RenderTexture(cameraW, cameraH, 24);
        try
        {
            var texture2D = new Texture2D(cameraW, cameraH, TextureFormat.RGB24, false);
            RenderTexture.active = renderTexture;
            camera.targetTexture = renderTexture;
            camera.Render();
            texture2D.ReadPixels(rect, 0, 0);
            texture2D.Apply();

            // カメラ映像からSpriteを作成し、プレビューイメージとして設定
            this.previewImage.sprite = Sprite.Create(texture2D, rect, new Vector2(0.5f, 0.5f));
            this.previewImage.preserveAspect = true;
        }
        finally
        {
            RenderTexture.active = backupActiveRenderTexture;
            camera.targetTexture = backupCameraTargetTexture;
            Destroy(renderTexture);
        }

        // プレビューを表示
        this.overlayImage.gameObject.SetActive(true);
    }
}

step4-3スクリプトをアタッチ

スクリプトを設定して、Unityから実行されるようにします。

(1) スクリプトをアタッチするために「空のオブジェクト」を作成して設定します。

  • メニュー: GameObject / Create Empty
  • 名前: PoseShutter
  • Inspectorの設定:
    Add Component: Pose Shutter ※ 上記で作成したスクリプト

(2) 上記で追加したコンポーネント(Pose Shutter)を設定します。

  • Inspectorの設定:
    Capture Camera: Hierarchyウィンドウの Main Camera を設定
    Overlay Image: Hierarchyウィンドウの OverlayImage を設定
    Preview Image: Hierarchyウィンドウの PreviewImage を設定
step4-3

これでハンドポーズを認識してシャッターが切れるようになりました。フロントカメラで自分を映して、ピースサインをしてみましょう!

ToF ARでは、他にも色々なポーズを認識します。 使用できるポーズを確認して、シャッターポーズを変えてみましょう。
API references - TofAr.V0.Hand.PoseIndex

また、ポーズ以外に ToF AR はジェスチャーを認識する事もできます。
API references - TofAr.V0.Hand.GestureIndex

但し、ポーズと違って、ジェスチャーの認識結果は static のイベント形式になっています。リファレンスの TofArHandManager からジェスチャーが推定された時に発生するイベントとジェスチャー推定を開始するメソッドを調べて、ジェスチャーでシャッターを切れるようにしてみましょう!
API references - TofAr.V0.Hand.TofArHandManager