2021.09.22

Motion Gestures社 認識したジェスチャーを無償評価用のSDKを用いてSocket通信する方法

WEBマガジン非接触

  • TOP>
  • 特集>
  • Motion Gestures社 認識したジェスチャーを無償評価用のSDKを用いてSocket通信する方法
コーンテクノロジー
この記事の監修者
コーンズテクノロジー編集部
コーンズテクノロジーでは先進的な製品・技術を日本産業界へ紹介する技術専門商社として、通信計測・自動車・防衛セキュリティ・電子機器装置・航空宇宙・産業機械といった技術分野のお役立ち情報を紹介しています。

1. はじめに
2. SDKおよびサンプルプログラムについて
3. Socket通信用のシステム構成
4. 改変版SDKサンプルプログラム
5. クライアント側Socket通信プログラム
6. サーバー側Socket通信プログラム
7. 動作手順
8. おわりに


1. はじめに

Motion Gestures社のAIベース・ハンドジェスチャー認識ソフトウェアプラットフォームですが、昨今のコロナ禍の状況から非接触操作のご要望が増え、お客様からの引き合いが多くなっております。

このハンドジェスチャー認識ソフトウェアの認識精度については、以下のYouTube動画を参照ください。

ハンドジェスチャー認識ソフトウェアは、ソフトウェア使用許諾書による同意が必要ですが、無償評価用のSDKが利用可能です。
今回、この無償評価用のSDKを用いた例として、認識したジェスチャーをSocket通信する方法をご紹介します。

2. SDKおよびサンプルプログラムについて

無償評価用SDKはWindows、Linux、Android、Raspberry Pi4のそれぞれの環境で用意されています。

このSDKにはサンプルプログラムが付属されており、各種APIの使用例が記載されています。

サンプルプログラムではAPIを利用することで、
・左手・右手、裏・表、上・下・左・右など手の各種認識結果
・手の平・Vサイン・拳(Fist)などのジェスチャー認識結果
などが取得・表示できるようになっています。

但し、SDKに認識結果取得用APIと取得した認識結果表示用APIは含まれていますが、それ以外のAPI、例えば認識結果を用いて何らかの制御を行う、などの制御用途としてのAPIが存在しません。
よって無償評価SDKを用いて機器や他のソフトウェアの制御を行うためには、SDKサンプルプログラムを改変し、独自の機能を追加する必要があります。
※機能追加に関して、通常はMotion Gestures社に対して有償で開発依頼を行います。ただし、独自で追加することも可能です。(PoC開発時および商用利用時には別途ライセンス費は必要)

なお、今回はWindows用のSDKを使用した場合を例としてご紹介します。また、Windows用SDKは Visual Studio利用を前提にしているため、Visual Studioでの手順を基に説明しています。

3. Socket通信用のシステム構成

初期時点におけるSocket通信用のシステム構成は、以下の通りです。

このシステムは、改変版SDKサンプルプログラム(SDKサンプルプログラムにクライアント側Socket通信機能を追加したもの)とサーバー側Socket通信プログラムの2つのプログラム(2つのexeファイル)から構成されています。

処理手順は以下の通りです。

(1)改変版SDKサンプルプログラムで、ジェスチャー認識を行う。
(2)改変版SDKサンプルプログラム内で、ジェスチャー認識結果をクライアント側Socket通信部に渡す。
(3)改変版SDKサンプルプログラムのクライアント側Socket通信部からサーバー側Socket通信プログラムへ、Socket通信を介してジェスチャー認識結果を送信する。
(4)サーバー側Socket通信プログラム内で、ジェスチャー認識結果を受け取る。
(5)受け取ったジェスチャー認識結果を基に、サーバー側Socket通信プログラム内で、何らかの処理を行う。

しかしながら、SDKサンプルプログラムにSocket通信機能を追加するために

#include <winsock2.h>

と、Windows用Socket通信headerファイルを組み込んだところ、Visual Studioにて下記Errorが発生するようになりました…。

SDKのAPI内で使用している列挙型のメンバに、「IN」「OUT」を使用しているのが原因だと思われます。無償評価用SDKでのAPI自体の修正は不可能なため、何らかの対策が必要です。

上記問題に対応するため、構想初期時点のシステム構成から、以下の仕様に変更します。
・改変版SDKサンプルプログラム内に、Socket通信機能を入れない。
・改変版SDKサンプルプログラム内にジェスチャー認識結果標準出力を追加。
・改変版SDKサンプルプログラムからクライアント側Socket通信を分離し、単独プログラムとする。
・クライアント側Socket通信プログラムは、ジェスチャー認識結果の標準入力を持つ。

変更後のシステム構成を以下に示します。

変更後のシステムは、改変版SDKサンプルプログラム、クライアント側Socket通信プログラム、サーバー側Socket通信プログラムの3つのプログラム(3つのexeファイル)から構成されています。

標準出力/標準入力を使用した場合の処理手順は以下の通りです。
(1)改変版SDKサンプルプログラムで、ジェスチャー認識を行う。
(2)改変版SDKサンプルプログラムから、ジェスチャー認識結果を標準出力で出力する。
(3)クライアント側Socket通信プログラムで、ジェスチャー認識結果を標準入力として取り込む。
(4)クライアント側Socket通信プログラムからサーバー側Socket通信プログラムへSocket通信を介して、ジェスチャー認識結果を送信する。
(5)サーバー側Socket通信プログラム内で、ジェスチャー認識結果をサーバー側Socket通信部から受け取る。
(6)受け取ったジェスチャー認識結果を基に、サーバー側Socket通信プログラム側で、何らかの処理を行う。

以上で、SDKサンプルプログラムを流用したSocket通信用のシステム構成を説明しました。
次からは、各プログラムの中身に関して説明します。

4. 改変版SDKサンプルプログラム

まず、SDKサンプルプログラムですが、Motion Gestures社のSDKに関するソフトウェア使用許諾書による同意を頂いてからの開示となるため、Webマガジン上では公開不可となっております。ご了承下さい。
よって改変版SDKサンプルプログラムについては、修正内容の概要説明のみとなります。

改変版SDKサンプルプログラムにて、ジェスチャー認識後に以下の標準出力記述を追加しています。

std::string str = ジェスチャー認識結果を取得するAPI;
str.erase(std::remove(str.begin(), str.end(), ‘ ‘), str.end()); //スペースを削除
std::cout << “SOCKT_COMM:” << str<< std::cout;

ジェスチャー認識結果は、SDKに取得用APIがあるので、それを使用しています。

str.eraseでスペースを削除しているのは、一部のジャスチャー認識結果(ジェスチャー名)にスペースが含まれているためです。スペースが入ったジャスチャー認識結果をそのまま標準出力した場合、クライアント側Socket通信プログラムの標準入力時に2つのジェスチャー認識結果として取り込んでしまうため、これを回避するためです。

また、ジェスチャー認識結果を標準出力する際に「SOCKET_COMM:」を付けています。後段のクライアント側Socket通信プログラムにて、標準入力で文字列を受け取った後、「SOCKET_COMM:」がついているものだけをSocket通信で送信する仕様にしているためです。

なお、実際は改変版SDKサンプルプログラム内で以下の修正も加えておりますが、ソースコードを表示して説明する必要があるため、今回の説明では省略しています。
・一定の間隔(例えば0.5秒間隔)で、ジェスチャーを認識する。
・ジェスチャー認識結果を標準出力後、これを前回のジェスチャー認識結果として保持する。
・ジェスチャー認識結果が、前回と同じ場合は標準出力しない。
・手が消えた場合は、前回のジェスチャー認識結果をクリアする。
・クライアント側Socket通信プログラムの終了用として、改変版SDKサンプルプログラム終了時に「MG_DEMOSW_END」を標準出力する。

5. クライアント側Socket通信プログラム

クライアント側Socket通信プログラムはVisual Studioでコンソールアプリケーションとして作成します。コンソールアプリの作成については「Visual Studio C++ コンソールアプリケーション 作成」などで検索し、新規プロジェクトとして作成する方法を調べてください。またbuild方法(exeファイル生成)に関しても、「Visual Studio C++ コンソールアプリケーション build」などで検索し、調べてください。

本プログラムのソースコードを以下に示します。コンソールアプリケーションのソースコードとして使用可能です。
なお、クライアント側・サーバー側の両Socket通信に関しては、「技術的特異点」ブログの『Winsock2を使ってC++でネットワークプログラミング』のエントリーでのプログラムを基に改変しております。
当社で追記したコメントには「//★」と星印をつけています。

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>

int main(int argc, char *argv[]) {
    char *server_ip_addr;
    int port_number;
    if (argc < 3) { //★argcが3未満であればUsage表示
      std::cout << “Usage: ” << argv[0] << ” IP_address Port_number” << std::endl;
      return -1;
    }
    else {
        server_ip_addr = argv[1]; //★サーバーIPアドレス
        port_number = atoi(argv[2]); //★ポート番号
    }

    // Windows Sockets仕様に関する情報を格納する構造体
    WSADATA wsa_data;

    // WinSockの初期化処理(Version 2.0)
    if (WSAStartup(MAKEWORD(2, 0), &wsa_data) != 0) {
        std::cerr << “Winsockの初期化失敗(WSAStartup)” << std::endl;
    }

    // sockaddr_in構造体の作成とポート番号、IPタイプの入力
    struct sockaddr_in dst_addr;
    memset(&dst_addr, 0, sizeof(dst_addr));
    dst_addr.sin_port = htons(port_number);  // ポート番号
    dst_addr.sin_family = AF_INET; // AF_INETはipv4を示す

    // 引数は (1) Type(ipv4 or v6) (2) IPアドレスのテキスト形式 (3) IPアドレスのバイナリ形式【(2)→(3)に変換】
    inet_pton(dst_addr.sin_family, server_ip_addr, &dst_addr.sin_addr.s_addr);

    // AF_INETはipv4のIPプロトコル & SOCK_STREAMはTCPプロトコル
    int dst_socket = socket(AF_INET, SOCK_STREAM, 0);

    // 接続処理
    if (connect(dst_socket, (struct sockaddr*)&dst_addr, sizeof(dst_addr))) {
        std::cerr << “接続失敗(サーバIPアドレス” << server_ip_addr << “/接続先ポート番号” << port_number << std::endl;
        exit(0);
    }
    std::cout << “接続完了(サーバIPアドレス” << server_ip_addr << “/接続先ポート番号” << port_number << std::endl << std::endl;

    char send_buf[256]; //★送信用バッファー
    char recv_buf[256]; //★受信用バッファー
    while (1) {
        std::cout << “Gesture Waitting…” << std::endl;
        //★SDKサンプルプログラムの標準出力を取得
        scanf_s(“%s”, send_buf, 256); //★標準入力をsend_bufへ格納
        std::string temp_str(send_buf, 256); //★char[]→stringへ変換
        //★改変版SDKサンプルプログラム終了時の処理
        //★標準入力した文字列に”MG_DEMOSW_END”が含まれていれば終了
        if (temp_str.find(“MG_DEMOSW_END”) == 0) {
            std::cout << “Gesture送信終了” << std::endl;
            break;
        }

        //★Socket送信処理
        //★標準入力した文字列に”SOCKET_COMM:”が含まれていいればSocket送信実施
        //★(含まれていなければ、Socket送信せずに標準出力のみ)
        if (temp_str.find(“SOCKET_COMM:”) == 0) {
            // Packetの送信(SOCKET, Buffer, Datasize, 送信方法)
            send(dst_socket, send_buf, 256, 0); //★Socket送信
            std::cout << “Sent Gesture:” << send_buf << std::endl; //★送信内容表示
            // Packetの受信
            recv(dst_socket, recv_buf, 256, 0); //★サーバー側の受信確認として
        }
        else {
            std::cout << send_buf << std::endl; //★単に標準出力するだけ
        }
    }

    // 解放処理
    closesocket(dst_socket);

    // WinSockの終了処理
    WSACleanup();
    return 0;
}

なお、Visual Studioにて、x86ではなくx64でbuildする場合、リンカツールエラー(LNK2019)が発生します。

その場合は、winsock2.hのinclude後に、以下の記述を追加してbuildしてください。

    #pragma comment(lib, “ws2_32.lib”)

6. サーバー側Socket通信プログラム

サーバー側Socket通信プログラムのソースコードを、以下に示します。
注意事項については、クライアント側Socket通信プログラムと同じです。

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
int main() {
    // ポート番号
    int port_number = 12345;

    // Windows Sockets仕様に関する情報を格納する構造体
    WSADATA wsa_data;

    // WinSockの初期化処理(Version 2.0)
    if (WSAStartup(MAKEWORD(2, 0), &wsa_data) != 0) {
        std::cerr << “Winsockの初期化失敗(WSAStartup)” << std::endl;
    }

    // サーバ側ソケット作成
    int src_socket;

    // sockaddr_in構造体の作成とポート番号、IPタイプの入力
    struct sockaddr_in src_addr;
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.sin_port = htons(port_number);
    src_addr.sin_family = AF_INET;
    src_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    // AF_INETはipv4のIPプロトコル & SOCK_STREAMはTCPプロトコル
    src_socket = socket(AF_INET, SOCK_STREAM, 0);

    // クライアント側のソケット設定
    int dst_socket;
    struct sockaddr_in dst_addr;
    int dst_addr_size = sizeof(dst_addr);

    // 接続の待受を開始する
    listen(src_socket, 1);

    // 送受信に使用するバッファ
    char recv_buf[256] = { ‘\0’ }; //★受信用バッファー
    char send_buf[256] = { ‘\0’ }; //★送信用バッファー

    // クライアントからの接続待ちループ関数
    while (1) {
        std::cout << “クライアントからの接続待ち” << std::endl;

       // クライアントからの接続を受信する
        dst_socket = accept(src_socket, (struct sockaddr *) &dst_addr, &dst_addr_size);
        std::cout << “クライアントからの接続有り” << std::endl;

        // 接続後の処理
        while (1) {
            int status;

            //パケットの受信(recvは成功すると受信したデータのバイト数を返却。切断で0、失敗で-1が返却される
            int recv_result = recv(dst_socket, recv_buf, sizeof(char) * 256, 0);
            if (recv_result == 0 || recv_result == -1) {
                status = closesocket(dst_socket);
                break;
            }

            //★受信したジェスチャー名を表示
            std::cout << “Received Gesture : ” << recv_buf << std::endl;

            //★受信した文字列をrecv_bufに格納
            snprintf(send_buf, 256, “%s”, recv_buf);

            //★受信後の文字列を使って何か処理をする場合はここに追加
            //★★★★★★★★★★★★★★★★

            // 結果を格納したパケットの送信
            send(dst_socket, send_buf, sizeof(char) * 256, 0);
            memset(recv_buf, ‘\0’, sizeof(recv_buf));
            memset(send_buf, ‘\0’, sizeof(send_buf));
        }
    }
    // WinSockの終了処理
    WSACleanup();
    return 0;
}

7. 動作手順

改変版SDKサンプルプログラム、クライアント側Socket通信プログラム、サーバー側Socket通信プログラムのbuildを行い、exeファイルを生成します。exeファイル生成後の動作手順を、以下に示します。
なお、以下の操作はWindowsのコマンドプロンプトまたはPowerShellにて実施します。

・事前準備

(1)Windowsのエクスプローラーなどを使用し、クライアント側Socket通信プログラムのexeファイルを、改変版SDKサンプルプログラムのexeファイルがあるフォルダにコピーしておきます。
(2)サーバー側Socket通信プログラムを実行するWindows PCのIPアドレスを調べておきます。

・サーバー側Socket通信プログラム実行手順 ※こちらを先に起動する必要があります。

(1)コマンドプロンプロ、またはPowerShellを起動します。
(2)サーバー側Socket通信プログラムのexeファイルがあるフォルダに移動します。
(3)サーバー側Socket通信プログラムのexeファイルを起動します。
※サーバー側Socket通信プログラムのexeファイル名がtest_server.exeであれば、.\test_server.exeで起動します。

・改変版SDKサンプルプログラム+クライアント側Socket通信プログラム実行手順

(1)コマンドプロンプト、またはPowerShellを起動します。
(2)改変版SDKサンプルプログラムの、exeファイルがあるフォルダに移動します。
(3)以下のコマンドで、改変版SDKサンプルプログラムとクライアント側Socket通信プログラムを起動します。
.\改変版SDKサンプルプログラムexeファイル名 各種コマンドオプション | .\クライアント側Socket通信プログラムexeフィル名 サーバーIPアドレス 12345
※クライアント側Socket通信プログラムexeファイル名がtest_cliant.exe、サーバー側Socket通信プログラムを実行するWindows PCのIPアドレスが192.168.0.10とした場合、パイプ( | )以降のコマンドを以下に示します。
.\test_cliant.exe 192.168.0.10 12345

上記手順で実行した際の表示結果を、以下に示します。この写真ではサーバー側Socket通信プログラムも同一Windows PC上で動作させていますが、別のWindows PCでも問題なく動作します。

 

8. 終わりに

今回はMotion Gestures社ハンドジェスチャー認識ソフトウェアの無償評価用SDKのサンプルプログラムを用いて、認識したジェスチャーをSocket通信する方法をご紹介しました。

ちなみに、このシステム構成であれば、改変版SDKサンプルプログラムを他の標準出力を持つプログラムに差し替えて、Socket通信用のプログラムとして流用することが可能となっています。

Motion Gestures社の無償評価用SDKに関して、Windows用SDKインストールマニュアル・SDKサンプルプログラムの使用方法・SDKのAPI使用方法など、各種日本語マニュアルを用意しております。
本コラムに関するご質問やご要望等ございましたら、ページ下部のWEBマガジンお問い合わせフォームより、お気軽にお問い合わせください。