2015年12月14日月曜日

Kinect + Raspberry Pi 2 で監視カメラを作る(6)

ブラウザから Kinect を操作する(前編)

前回で libfreenect を利用した Kinect の操作方法は大体わかったので、今度はWebカメラらしくブラウザから仰角や表示モードを操作できるようにする。

やり方は色々あるが今回は古典的だが一番簡単そうなネームドパイプ(FIFO)を使って cgi から camtest にコマンドを送る。

概念的にはこんな感じになる。

  camtest ← FIFO ← CGI ← ブラウザ

双方向の情報伝達も出来なくはないが、ちゃんとやろうとすると非常に面倒なので今回は一方通行にする。

まずは、camtest.c に以下の行を追加してFIFOの生成とオープンを行う。
マルチスレッドにするならブロックモードでも良いが、それはそれでめんどいありがたいことにイベントループ方式で Kinect から画像データを取得してくれているので、それに乗っかって今回はノンブロックモードを利用してシングルスレッドで運用する。

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
...
char fifoname[] = "/tmp/kinect";
...

int main(int argc, char** argv)
{
        ...
        int fifo;
        if(mkfifo(fifoname, 0666) == 0) { // FIFOの作成
                chmod(fifoname, 0777);
        } // すでにFIFOがある場合は失敗するだけなので気にしない
        if((fifo = open(fifoname, O_RDONLY | O_NONBLOCK)) < 0) { // オープン
                printf("Failed to open fifo\n");
                return 1;
        }

ルート権限で実行すると mkfifo で0777を指定しても umask の影響で一般ユーザーの書き込み権限がなくなってしまうので、わざわざ作った後に chmod で権限を変更している。
この辺もう少し賢く出来ないもんか・・・

んでもってあとはイベントループの中でFIFOからコマンドをリードしてそれに応じて仰角とカメラモードを変更するようにすればよい。

        enum video_mode { KINECT_RGB, KINECT_IR, KINECT_DEPTH };

        // とりあえず最初はRGBモードでカメラだけ起動しておく
        current_mode = KINECT_RGB;        
        ret = freenect_set_video_mode(fn_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB));
        ret = freenect_start_video(fn_dev);

        // 起動時の仰角を取得
        freenect_update_tilt_state(fn_dev);
        p_raw_tilt_state = freenect_get_tilt_state(fn_dev);
        current_tilt = freenect_get_tilt_degs(p_raw_tilt_state);
        next_tilt = current_tilt;

        while (running && freenect_process_events(fn_ctx) >= 0)
        {
                char cmd;
                enum video_mode next_mode = current_mode;
                ...
                while(read(fifo, &cmd, sizeof(cmd)) == sizeof(cmd)) {
                        switch(cmd) {
                        case 'p':
                                next_tilt += 1.0;
                                if(next_tilt > 30.0)
                                        next_tilt = 30.0;
                                break;
                        ...
                        case 'D':
                                next_mode = KINECT_DEPTH;
                                break;
                        }
                }
                if(current_tilt != next_tilt) {
                        freenect_update_tilt_state(fn_dev);
                        p_raw_tilt_state = freenect_get_tilt_state(fn_dev);
                        tilt_status = freenect_get_tilt_status(p_raw_tilt_state);
                        if(tilt_status != TILT_STATUS_MOVING) {
                                freenect_set_tilt_degs(fn_dev, next_tilt);
                                current_tilt = next_tilt;
                        }      
                }
                if(current_mode != next_mode) {
                        set_mode(fn_dev, next_mode);
                }
        }

コマンドは複数文字にすると送信オーバーフロー時のバッファの処理や終端コードなど色々考える必要が出てくるので、単純化のため今回は全て1文字で p が仰角+1度、P が+5度、m が-1度、M が-5度、R がRGBモード、I が赤外線モード、D が深度モードとした。

set_mode 関数では以下のようにビデオモードの切り替えを行う。
貧乏性なので律儀に使わないモードを止めているが、どれだけ性能に影響するかは不明。

void set_mode(freenect_device* fn_dev, enum video_mode next_mode) {
        switch(current_mode) {
        case KINECT_RGB:
        case KINECT_IR:
                freenect_stop_video(fn_dev);
                break;
        case KINECT_DEPTH:
                freenect_stop_depth(fn_dev);
                break;
        }
        switch(next_mode) {
        case KINECT_IR:
                freenect_set_video_mode(fn_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_IR_10BIT));
                freenect_start_video(fn_dev);
                current_mode = KINECT_IR;
                break;
        case KINECT_DEPTH:
                freenect_start_depth(fn_dev);
                current_mode = KINECT_DEPTH;
                break;
        case KINECT_RGB:
                freenect_set_video_mode(fn_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB));
                freenect_start_video(fn_dev);
                current_mode = KINECT_RGB;
                break;
        }
}

無事に出来たところで動作確認を行う。
camtest を実行すると、"/tmp/kinect”というファイルが生成されるので、試しにリダイレクトなどでコマンドを書き込んで動作確認を行う。

# echo p > /tmp/kinect

camtest の方はこれで出来たので、あとは CGI と html を作成する。

0 件のコメント:

コメントを投稿