2023.01.17
Ultraleap Hand Tracking SDKを用いたスワイプ操作について
WEBマガジン非接触
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マガジンお問い合わせフォームより、お気軽にお問い合わせください。