2023.01.17

Ultraleap Hand Tracking SDKを用いたスワイプ操作について

WEBマガジン非接触

  • TOP>
  • 特集>
  • Ultraleap Hand Tracking SDKを用いたスワイプ操作について
コーンテクノロジー
この記事の監修者
コーンズテクノロジー編集部
コーンズテクノロジーでは先進的な製品・技術を日本産業界へ紹介する技術専門商社として、通信計測・自動車・防衛セキュリティ・電子機器装置・航空宇宙・産業機械といった技術分野のお役立ち情報を紹介しています。

1. はじめに
2. 構成
3. スワイプ認識
4. 仮想キーの発行
5. サンプルプログラム修正例
6. PowerPointでの設定
7. 動作確認

 


1. はじめに

当社のWebマガジンにて、「Ultraleap Hand Tracking SDK 取得可能な情報について」としてHand Tracking SDKでのデータ取得方法に関してまとめております。

上記記事ではSDKによる各種データの取得方法に関して説明しただけであり、具体的な利用例を記載していませんでした。

今回、取得した手のデータを基にスワイプ操作を行う方法を具体例としてまとめます。

なお、本記事ではHand Tracking SDKのVersion5.7.2を基に記載しています。Version5.7.2のダウンロード・インストール・build方法などについては、当社Webマガジン「Ultraleap社 Hand Tracking用Windows版SDK Version5.7.2について」に詳細を記載しております。

 

2. 構成

今回のシステム構成図を以下にまとめます。

 

今回のシステムは以下の構成になっております。
(a)Ultraleap社のステレオカメラ(LMC,SIR170,3Di)により手の画像を取得
(b)(a)を基にUltraleap Hand Tracking SDKにより各種手のデータを取得
(c)(b)で取得した手のデータを基に左右のスワイプを検出
(d)(c)でのスワイプ検出結果を基に対応する仮想キーを発行
(e)(d)の仮想キーによりPowerPointのページめくり処理に使用

(c)と(d)の処理はSDK付属のサンプルプログラムを修正して行います。

 

3. スワイプ検出

スワイプ検出に関して、いろいろアルゴルズムが考えられますが、今回は以下の仕様とします。
・最初に手を画面方向に対してまっすぐかざすことにより、スワイプ開始とする(下記写真参照)。

・上記状態から手を右に移動させると右スワイプ、手を左に移動させると左スワイプとする。
・ある一定のフレーム数における手の平のx方向速度を加算し、この値がある閾値を超えた場合にスワイプ検出とする。

上記仕様を基にした処理をフローチャートを用いて説明します。

フローチャート中の赤文字の箇所にて「2. 構成」の(b)で取得した手のデータを使用しています。今回は手の平のx方向のdirectionと手の平のx方向の速度のみ使用しています。

なお、各フロチャート中に「閾値」と記載がありますが、これらはプログラム実行時の引数による設定が可能としています。

まず、スワイプ検出全体のフローチャートです。

チェックフラグおよびキー解除後待ちフラグをfalseに設定し、x速度加算値およびフレームカウンタを0で初期化します。
チェックフラグがfalseであれば、スワイプ開始処理を実施します。
この後、チェックフラグがtrueであれば、スワイプ検出処理を実施します。

次にスワイプ開始処理のフローチャートです。このフローにてスワイプ操作の開始判定をしています。

手の平のz方向のdirection値を取得し、マイナスの閾値よりも小さい値かチェックし、小さければ「手が画面方向を向いている」と判断しスワイプ開始とします。
スワイプ開始と判定後、チェックフラグをtrueにし、開始時刻を取得します。

次にスワイプ検出処理のフローチャートです。このフローにてスワイプ検出後のwait処理、スワイプキャンセル処理、スワイプ判定処理を行っています。

まず左側のフローはスワイプ検出後のwait処理です。スワイプ検出後に手を戻したときに、逆方向のスワイプ操作として誤認識しないようにするため、waitを一定期間挿入する必要があります。
キー解除後待ちフラグがtrue(後述する仮想キー発行処理中にtrueにセットしています)であれば、現在時刻を取得します。
現在時刻と開始時刻の差分が閾値を超えていれば、一定期間のwaitがあったと判断し、x速度加算値およびフレームカウンタを0にセットして初期化し、チェックフラグとキー解除後待ちフラグもfalseにして、次のスワイプ開始に備えます。

中央のフローはスワイプキャンセル処理です。スワイプ検出時に手が動いていないようであれば、キャンセルします。
手の平のx方向の速度(の絶対値)が閾値未満であれば、手が動いていないと判定します。
この後で、x速度加算値およびフレームカウンタを0にセットして初期化し、チェックフラグをfalseにすることでスワイプ検出処理をキャンセルさせ、次のスワイプ開始に備えます。

右側のフローはスワイプ判定処理です。
手の平のx方向速度が閾値以上なのでこれをx方向速度加算値に加え、フレームカウンタをインクリメントします。
フレームカウンタが閾値以上であれば、スワイプ判定も含めた仮想キー発行処理を行います。
なお、「キー解除後待ちフラグ=true」の処理が以上のフローチャート上では発生していませんが、仮想キーの発行の箇所で行っています(詳細は後述)。
仮想キー発行処理にて、スワイプと判定されずに仮想キーの発行がなかった場合、キー解除待ちフラグがfalseのままになります。
この場合は、一度スワイプ判定を解除する必要があり、キャンセル時と同じくx速度加算値およびフレームカウンタを0にセットして初期化し、チェックフラグをfalseにし、次のスワイプ開始に備えます。

以上のフローチャートの処理をSDK付属のサンプルプログラムに「2. 構成」の(c)の箇所として追加します。サンプルプログラムの修正例については後述します。

 

4. 仮想キーの発行

仮想キーの発行について、今回は以下の仕様で行います。
・右スワイプであれば、仮想キーコード「VK_RIGHT」(0x27、右方向キー)を使用
・左スワイプであれば、仮想キーコード「VK_LEFT」(0x25、左方向キー)を使用
・keybd_event関数で仮想キーコードを使用し、キーダウン->キーアップを発行する。

上記仕様を基にした処理をフローチャートを用いて説明します。

まず仮想キー発行処理全体のフローチャートです。

screnntopモードであればscreentopモード時の仮想キー処理、desktopモードであればdesktopモード時の仮想キー処理を行います。
なお、screnntopモード・desktopモードはプログラム実行時の引数による設定が可能としています(defaultはdesktopモード)。
また、Ultraleap社のステレオカメラのトラッキングモード設定は、上記で選択するモードに合わせておく必要があります。
※Windowsのタスクトレイ中の「∧」をクリック->Ultraleap Cantrol Panelのアイコンを右クリック->Open Control Panelをクリック->右上のTracking Modeの箇所で変更可能。

screentopモード時の仮想キー処理のフローチャートを以下に示します。

Screentop(画面上部から下に向けて設置)なので、x方向の速度がマイナスであれば右に、プラスであれば左にスワイプしたと判定できます。
x方向の速度加算値がマイナスの閾値未満であれば、文字列1に”DETECT_SWIPE_RIGHT”、文字列2に”Screentop mode”、仮想キーコードとしてVK_RIGHTを設定し、仮想キー処理関数に引数として渡します。
x方向の速度加算値がプラスの閾値超であれば、文字列1に”DETECT_SWIPE_LEFT”、文字列2に”Screentop mode”、仮想キーコードとしてVK_LEFTを設定し、仮想キー処理関数に引数として渡します。

desktopモード時の仮想キー処理のフローチャートを以下に示します。

Desktop(画面下部から上に向けて設置)なので、x方向の速度がマイナスであれば左に、プラスであれば右にスワイプしたと判定できます。
x方向の速度加算値がマイナスの閾値未満であれば、文字列1に”DETECT_SWIPE_LEFT”、文字列2に”Desktop mode”、仮想キーコードとしてVK_LEFTを設定し、仮想キー処理関数に引数として渡します。
x方向の速度加算値がプラスの閾値超であれば、文字列1に”DETECT_SWIPE_RIGHT”、文字列2に”Screentop mode”、仮想キーコードとしてVK_RIGHTを設定し、仮想キー処理関数に引数として渡します。

最後に仮想キー処理関数のフローチャートを以下に示します。

受け取った文字列のコンソール表示->キーダウン発行->wait処理->キーアップ発行を行ったのち、キー解除後待ちフラグをtrueにセットします。

以上のフローチャートの処理をSDK付属のサンプルプログラムに「2. 構成」の(d)の箇所として追加します。ソースコード例については後述します。

 

5. サンプルプログラム修正例

「3. スワイプ認識」および「4. 仮想キー発行」で記載したフローチャートの処理をHand Tracking SDK付属のサンプルプログラムの一つ「CallbackSample.c」に追加した場合の修正例を以下に示します。

なお、「//★」の印があるコメントは、説明用に追記したものです。

サンプルプログラムは、defult設定でSDKをインストールした場合は、以下のフォルダ上にあります。
C:\Program Files\Ultraleap\LeapSDK\samples

◆Headerファイルの追加
以下のHeaderファイルを追加します。
既存のHeaderファイルのinclude文のすぐ後に追加して下さい。

#include <windows.h> //★仮想キー用に追加
#include <time.h> //★開始時刻および現時刻取得用に追加
#include <sys/timeb.h> //★開始時刻および現時刻取得用に追加

 

◆グローバル変数の追加
以下のグローバル変数を追加します。
既存のグローバル変数の宣言のすぐ後に追加して下さい。

static double th_direction = 0.8; //★direction閾値用
static double th_min_swipe_verocity = 100.0; //★x方向の最小速度閾値用
static double th_sum_swipe_verocity = 3000.0; //★x方向の速度換算閾値用
static int max_cnt_frame = 5; //※フレームカウンタ閾値用
static int after_vk_keydown_wait = 10; //★キーダウン後のwait時間用
static int after_vk_keyup_wait = 300; //★キーアップ後のwait時間(=次のスワイプ認識までのwait時間)用//★モードを列挙型を用いて定義
typedef enum
{
E_DESKTOP = 0, //★desctopモード
E_SCREENTOP, //★screnntopモード
} E_MODE;static E_MODE mode = E_DESKTOP; //★モード設定用static bool swipe_check = false; //★チェックフラグ用
static double sum_x_verocity = 0.0; //★x方向の速度加算値用
static int cnt_frame = 0; //★フレームカウンタ用
static bool after_vk_keyup_wait_check = false; //★キー解除後待ちフラグ用
static unsigned int start_time = 0; //★スワイプ開始時刻用

 

◆仮想キー処理関数の追加
仮想キー処理用関数を追加します。
OnFrame関数よりも前に追記して下さい。

//★仮想キー処理関数
static void vkey(char* str_det, char* str_mode, int vkey_code){
  printf(“%s @ %s\n”, str_det, str_mode); //★文字列1と文字列2をコンソールに表示
  keybd_event(vkey_code,0,0,0); //★仮想キーコードのキーダウンを発行
  Sleep(after_vk_keydown_wait); //★一定期間 wait
  keybd_event(vkey_code,0,KEYEVENTF_KEYUP,0); //★仮想キーコードのキーアップ発行
  after_vk_keyup_wait_check = true; //★キー解除後待ちフラグをtrueにセット
}

 

◆OnFrame関数の差し替え
既存のOnFrame関数を以下の内容に書き換えてください。

//★OnFrame関数をスワイプ検出用に修正
/** Callback for when a frame of tracking data is available. */
static void OnFrame(const LEAP_TRACKING_EVENT *frame){  unsigned int sec; //★sec用
  unsigned int msec; //★msec用
  unsigned int current_time; //★現在時刻用
  struct timeb timebuffer; //★現在時刻を求める際に必要  char str_1st[64]; //★文字列1用
  char str_2nd[64]; //★文字列2用  for(uint32_t h = 0; h < frame->nHands; h++){ //★認識した手の数だけ繰り返し
    LEAP_HAND* hand = &frame->pHands[h]; //★手の情報をhandにセット    if (swipe_check == false) { //★チェックフラグがfalseであれば
      //★以下、スワイプ開始処理
      if (hand->palm.direction.z < -th_direction){ //★手の平のz方向のdirection値 < -閾値であれば
        //★手の平がモニター方向を向いているとしてスワイプ開始
        swipe_check = true; //★チェックフラグをtrueにセット
        ftime( &timebuffer );
        sec = (unsigned int)(timebuffer.time);
        msec = (unsigned int)(timebuffer.millitm);
        start_time = (unsigned int)(sec*1000 + msec); //★開始時刻取得
      }
    }    if(swipe_check == true) { //★チェックフラグがtrueであれば
      //★以下、スワイプ検出処理
      if(after_vk_keyup_wait_check == true){ //★キー解除後待ちフラグがtrueであれば
        //★スワイプ検出後のwait処理
        ftime( &timebuffer );
        sec = (unsigned int)(timebuffer.time);
        msec = (unsigned int)(timebuffer.millitm);
        current_time = (unsigned int)(sec*1000 + msec); //★現時刻取得
        if ((current_time – start_time) > after_vk_keyup_wait) { //★現時刻-開始時刻 > 閾値であれば
          sum_x_verocity = 0; //★x方向速度加算値を0クリア
          cnt_frame = 0; //★フレームカウンタを0クリア
          swipe_check = false; //★チェックフラグをfalseにセット
          after_vk_keyup_wait_check = false; //★キー解除後待ちフラグをfalseにセット
        }
      }
      else if((hand->palm.velocity.x < th_min_swipe_verocity) && (hand->palm.velocity.x > -th_min_swipe_verocity)){ //★|x方向速度| < 閾値であれば
        //★スワイプキャンセル処理
        sum_x_verocity = 0; //★x方向速度加算値を0クリア
        cnt_frame = 0; //★フレームカウンタを0クリア
        swipe_check = false; //★チェックフラグをfalseにセット
      }
      else {
        //★スワイプ判定処理
        sum_x_verocity += hand->palm.velocity.x; //★x方向速度加算値に現x方向速度を加算
        cnt_frame++; //★フレームカウンタをインクリメント
        if(cnt_frame > max_cnt_frame){ //★フレームカウンタ > 閾値であれば
          //★以下、仮想キー発行処理
          if(mode == E_SCREENTOP) { //★screentopモードであれば
            //★以下、screnntopモード時の仮想キー処理
            if(sum_x_verocity < -th_sum_swipe_verocity){ //★x方向速度加算値 < -閾値であれば(右スワイプ)
              strcpy(str_1st, “DETECT_SWIPE_RIGHT”); //★文字列1設定
              strcpy(str_2nd, “Screentop Mode”); //★文字列2設定
              vkey(str_1st, str_2nd, VK_RIGHT); //★VK_RIGHTとして仮想キー処理
            }
            else if (sum_x_verocity > th_sum_swipe_verocity) { //★x方向速度加算値 > 閾値であれば(左スワイプ)
              strcpy(str_1st, “DETECT_SWIPE_LEFT”); //★文字列1設定
              strcpy(str_2nd, “Screentop Mode”); //★文字列2設定
              vkey(str_1st, str_2nd, VK_LEFT); //★VK_LEFTとして仮想キー処理
            }
          }
          else if(mode == E_DESKTOP) { //★desktopモードであれば
            //★以下、desktopモード時の仮想キー処理
            if(sum_x_verocity < -th_sum_swipe_verocity){ //★x方向速度加算値 < -閾値であれば(左スワイプ)
              strcpy(str_1st, “DETECT_SWIPE_LEFT”); //★文字列1設定
              strcpy(str_2nd, “Desktop Mode”); //★文字列2設定
              vkey(str_1st, str_2nd, VK_LEFT); //★VK_LEFTとして仮想キー処理
            }
            else if (sum_x_verocity > th_sum_swipe_verocity) { //★x方向速度加算値 > 閾値であれば(右スワイプ)
              strcpy(str_1st, “DETECT_SWIPE_RIGHT”); //★文字列1設定
              strcpy(str_2nd, “Desktop Mode”); //★文字列2設定
              vkey(str_1st, str_2nd, VK_RIGHT); //★VK_RIGHTとして仮想キー処理
            }
          }          if(after_vk_keyup_wait_check == false){ //★キー解除後待ちフラグがfalseであれば (スワイプではないと判定した場合)
            sum_x_verocity = 0; //★x方向速度加算値を0クリア
            cnt_frame = 0; //★フレームカウンタを0クリア
            swipe_check = false; //★チェックフラグをfalseにセット
          }
        }
      }
    }
  }
}

 

◆OnImage関数のコメントアウト
OnImage関数のコンソール出力が不要なので、コメントアウトします。

static void OnImage(const LEAP_IMAGE_EVENT *image){
  //★表示不要なのでコメントアウト
  //printf(“Image %lli => Left: %d x %d (bpp=%d), Right: %d x %d (bpp=%d)\n”,
  // (long long int)image->info.frame_id,
  // image->image[0].properties.width,image->image[0].properties.height,image->image[0].properties.bpp*8,
  // image->image[1].properties.width,image->image[1].properties.height,image->image[1].properties.bpp*8);
}

 

◆コマンドライン引数処理追加
main関数内にコマンドライン引数処理の記述を追加します。
以下の記述をmain関数の先頭に追加して下さい。

    //★以下、本ファイル実行時のコマンドライン引数用に追加
  for(int i =1; i < argc; i++){
    //★モード設定用
    if(strcmp(argv[i], “–mode”) == 0){
      printf(“mode = %s\n”,argv[i+1]);
      if(strcmp(argv[i+1], “desktop”) == 0){
        mode = E_DESKTOP;
      }
      else if(strcmp(argv[i+1], “screentop”) == 0){
        mode = E_SCREENTOP;
      }
      else {
        printf(“Error:Use one of the following for mode setting.\n”);
        printf(” desktop, screentop\n”);
        return (-1);
      }
      i++;
    }
    //★z方向のdirection閾値設定用
    else if(strcmp(argv[i], “–direction”) == 0){
      printf(“th_direction = %s\n”,argv[i+1]);
      th_direction = atof(argv[i+1]);
      i++;
    }
    //★x方向の最小速度閾値設定用
    else if(strcmp(argv[i], “–min_swipe_v”) == 0){
      printf(“th_min_swipe_verocity = %s\n”,argv[i+1]);
      th_min_swipe_verocity = atof(argv[i+1]);
      i++;
    }
    //★x方向の速度加算値閾値設定用
    else if(strcmp(argv[i], “–sum_swipe_v”) == 0){
      printf(“th_sum_swipe_verocity = %s\n”,argv[i+1]);
      th_sum_swipe_verocity = atof(argv[i+1]);
      i++;
    }
    //★速度加算時の最大フレーム数設定用
    else if(strcmp(argv[i], “–max_cnt_frame”) == 0){
      printf(“max_cnt_frame = %s\n”,argv[i+1]);
      max_cnt_frame = atoi(argv[i+1]);
      i++;
    }
    //★仮想キーダウン後のwait時間設定用
    else if(strcmp(argv[i], “–after_vk_keydown_wait”) == 0){
      printf(“after_vk_keydown_wait = %s\n”,argv[i+1]);
      after_vk_keydown_wait = atoi(argv[i+1]);
      i++;
    }
    //★仮想キーアップ後のwait時間(=次のスワイプ認識までのwait時間)設定用
    else if(strcmp(argv[i], “–after_vk_keyup_wait”) == 0){
      printf(“after_vk_keyup_wait = %s\n”,argv[i+1]);
      after_vk_keyup_wait = atoi(argv[i+1]);
      i++;
    }
    //★Usage表示用
    else if(strcmp(argv[i], “–help”) == 0){
      printf(“Usage: %s [optins]\n”, argv[0]);
      printf(“Options: –help Print usage\n”);
      printf(” –mode [desktop|screentop] Select Camera Mode (default:desktop)\n”);
      printf(” –direction value Threshold setting for direction (default:%.1f)\n”, th_direction);
      printf(” –min_swipe_v value Threshold setting for Min. swipe verocity (default:%.1f)\n”, th_min_swipe_verocity);
      printf(” –sum_swipe_v value Threshold setting for sum of velocities (default:%.1f)\n”, th_sum_swipe_verocity);
      printf(” –max_cnt_frame value Number of frame counts when summing velocities (default:%d)\n”, max_cnt_frame);
      printf(” –after_vk_keydown_wait value Wait period after key down of virtual key [msec] (default:%d)\n”, after_vk_keydown_wait);
      printf(” –after_vk_keyup_wait value Wait period after key up of virtual key [msec] (default:%d)\n”, after_vk_keyup_wait);
      return (0);
    }
  }

 

以上の記述を「CallbackSample.c」に反映することによって、スワイプ操作用のプログラムとなります。この内容を反映させたプログラムをbuildし、実行ファイル(exe)ファイルを生成することができます。

build手順については当社Webマガジン「Ultraleap社 Hand Tracking用Windows版SDK Version5.7.2について」の「5. SDKサンプルプログラムのbuild」を参照ください。

Ultraleap社のステレオカメラをUltraleap Hand Tracking SDKをインストール済みのWindows PCに接続したのち、生成した実行ファイルをコマンドプロンプトから実行して下さい。

左右のスワイプ操作を行うと、以下のメッセージが表示されます。

 

6. PowerPointでの設定

今回のシステムでは、スワイプ操作によりPowerPointのページめくりを行います。

以下にスワイプ操作用のPowerPointの設定手順を説明します。

(1)PowerPointのファイルを新規作成します。

(2)表示するページ分、作成します。下記例では動作確認用として、A~Dまでを1ページづつ配置し、計4ページ作成しています。

※以下、(3)~(5)は手に合わせてページ切り替えをするための設定であり、任意です(右スワイプ時にはページが左から右に切り替わり、左スワイプ時にはページが右から左に切り替わる)。

(3)1ページ目を選択後、「画面切り替え」→「カバー」をクリックします。

(4)「効果のオプション」→「左から」をクリックします。

(5)1ページ目を選択した状態で、「すべてに適用」をクリックします。

(6)「スライドショー」→「スライドショーの設定」をクリックします。

(7)「Escキーが押されるまで繰り返す」にチェックをつけ、「クリック時」が選択されていることを確認し、「OK」をクリックします。

(8)Ctrl+Sで設定した内容を上書き保存します。

(9)「ファイル」をクリックします。

(10)「名前をつけて保存」をクリックします。

(11)ファイルタイプのリストから「PowerPoint スライドショー (*.ppsx)」を選択し、「保存」をクリックします。

以上の手順により、スワイプ操作可能なPowerPointのファイルが作成できます。

 

7. 動作確認

「5. ソースコード例」でbuildしたの実行ファイルと「6. PowerPointでの設定」で作成したPowerPointスライドショーファイルを用いて動作確認を行います。

動作確認の前に、下記内容のbatファイルを作成し、任意のフォルダに保存します。

@echo off
pushd buildした実行ファイルがあるフォルダ
start /MIN ウィンドウのタイトル名 実行ファイル名
pushd ppsxファイルのあるフォルダ
ppsxファイル名

実際の記述例を以下に示します。
なお、各フォルダおよびファイルは以下の名前としています。
 ・buildした実行ファイルがあるフォルダ:C:\build\Release\LeapSDK\leapc_example\Release
 ・ウィンドウのタイトル名:Ultraleap Swipe SW for Web Magazine
・実行ファイル名:
CallbackSample.exe
 ・ppsxファイルがあるフォルダ:C:\swipe_check
 ・ppsxファイル名:swipe_check.ppsx

@echo off
pushd “C:\build\Release\LeapSDK\leapc_example\Release”
start /MIN “Ultraleap Swipe SW for Web Magazine” “CallbackSample.exe”
pushd “C:\swipe_check”
“swipe_check.ppsx”

上記で作成したbatファイルをWindowsのエクスプローラからダブルクリップして起動します。

Windows上にppsxファイルの内容が表示され、スワイプ操作でページめくりができます。
※右スワイプで次のページへ、左スワイプで前のページに切り替わる。

スワイプ操作例を以下に示します。

必要であれば、batファイルをコピーし(またはショートカットを作成し)、それを下記フォルダに保存することで、Window PCの立ち上げ時に本batファイルを自動起動することができます。
C:\Users\ログインアカウント名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

 

今回はUltraleap Hand Tracking SDKを用いたスワイプ操作についてご紹介しました。

本コラムに関するご質問やご要望等ございましたら、ページ下部のWEBマガジンお問い合わせフォームより、お気軽にお問い合わせください。