2016年5月19日木曜日

RUST でファイル入力処理

迷路のレイアウトをコードにベタ書きで埋め込むのは骨が折れるので、下のようなテキストで作成した迷路を読み込めるようにした。

だいぶ長くなってきたので、アルゴリズム以外を別ファイルに移した上で、ファイルから迷路を構築する処理を追加。
メインからはモジュールとして読み込んで利用する。
ファイル入力自体は比較的簡単だったが、エラー処理にだいぶ嵌められた。
エラー処理の仕様をきちんと理解しないままサンプルを見よう見まねでコピペしても全然コンパイルできなかった。
その分ちゃんと理解すれば柔軟なエラー処理ができるんだが、正直かなりとっかかりにくいと思う。

2016年5月14日土曜日

RUST 勉強中

環境構築

インストール

Ubuntu16.04 だと apt-get から入手できるようになった。

エディタ設定

Emacs 使いなので、melpa から lang-rust を導入

お勉強

何は無くとも RUST のドキュメントを熟読。
どうやらメモリ参照の透過性の高いプログラミング言語らしい。
なので、マルチスレッドとかに向いているそうだ。
一応オブジェクトっぽいこともできそうだが、オブジェクト指向言語ではないようだ。

実践

試しに単純な右手の法則で迷路を解くプログラムを書いてみた。

どうしても関数の引数が borrow ばっかりになってしまうが、これでいいんだろうか。

オブジェクト指向のようにポリモーフィズムばりばりで抽象化すると言うよりも、テンプレートで抽象化するが、テンプレートだとなんでもありになってしまうので、Trait で期待するテンプレートを指定するという感じか。
なんとなく、Cのマクロでこうできれば便利なんだけどなー、ってもどかしかったのができるようになっている感じで使っていて気持ちいい。

2016年1月18日月曜日

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

運用の自動化

前回からだいぶ間が開いてしまったが、再起動のたびに手動でプログラムを起動するのはアホらしいので、Kinect プログラムを自動で起動するようにしていく。

自動起動の設定

まずは、camtest を適当にそれっぽいディレクトリにコピーしてついでにファイル名も kinect に変えておく。

> sudo install camtest /usr/local/sbin/kinect

で、/etc/init.d の下に起動/終了用スクリプトを作成
------------------------------
#! /bin/sh
### BEGIN INIT INFO
# Provides:             kinect
# Required-Start:       $remote_fs $syslog
# Required-Stop:        $remote_fs $syslog
# Default-Start:        2 3 4 5
# Default-Stop:        
# Short-Description:    Kinect remote webcam system
### END INIT INFO
set -e
test -x /usr/local/sbin/kinect || exit 0
. /lib/lsb/init-functions
case "$1" in
  start)
        log_daemon_msg "Starting Kinect webcam system" "kinect" || true
        if start-stop-daemon --background --start --quiet --oknodo --pidfile /var/run/kinect.pid --make-pidfile --exec /usr/local/sbin/kinect; then
            log_end_msg 0 || true
        else
            log_end_msg 1 || true
        fi
        ;;
  stop)
        log_daemon_msg "Stopping Kinect webcam system" "kinect" || true
        if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/kinect.pid; then
            log_end_msg 0 || true
        else
            log_end_msg 1 || true
        fi
        ;;
  *)
        log_action_msg "Usage: /etc/init.d/kinect {start|stop}" || true
        exit 1
esac
exit 0
------------------------------
sshd のスクリプトをベースに見よう見まねで改造しただけ。
本当は restart や status も作ったが、スペースの都合で割愛した。

出来上がったら、
> /etc/init.d/kinect start

などして動くのを確認したら
> sudo init q

でスクリプトのリストを更新(Ubuntu だと要らなかったのだが、よくわからん)

最後に
> update-rc.d kinect defaults

で、自動起動に登録。

ラズパイを一度再起動して自動で立ち上がるか確認しておく。

自動終了

Kinect の深度センサーは赤外線レーザーを使っている。
人体に影響があるとは思えないが、一晩中我が子に照射し続けるのも気が引けるため、深夜になったら自動で切れるようにする。

入手したラズパイはタイムゾーンが違っていたので、まずはそれを合わせておく。
> cp /etc/localtime /etc/localtime.org #念のためバックアップ
> cp  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

色々と試行錯誤したのだが、Kinect の深度センサーの照射を止めるには USB から切り離す方法でしか止められなかった。
ちと乱暴だが、以下のコマンドで大本のポートから止めることで深度センサーの照射を止めることが出来るようだった。
> echo -n '1-1' > /sys/bus/usb/drivers/usb/unbind
(止まっているかどうかは、肉眼で深度センサーからうっすら出ている赤い光をみて確認した)

しかしながら、これをやると無線 LAN まで止まってしまうので、子供を寝かせる時間になったら自動で再起動するようにしたい。

というわけで、cron を使って以下のように自動で Kinect センサーの停止とラズパイの再起動を行うように設定した。

> sudo crontab -u root -e
※エディタが起動するので以下の行を追加
0 23 * * * /etc/init.d/kinect stop
5 23 * * * echo -n '1-1' > /sys/bus/usb/drivers/usb/unbind
0 18 * * * /sbin/reboot

これで、毎晩 23 時に Kinect プログラムが終了。その 5 分後にセンサーを停止し、翌日の 18 時になったら自動で再起動するようになる。

2015年12月27日日曜日

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

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

html の作成

とりあえず、以前作成した自動画像リロードのページをベースに制御コマンドを送信するためのフォームとボタンを追加する。

<body onload="loadimage()">
<form id="cntlform" action="/cgi-bin/control.pl" method="post"">
<div class="tcontrol">
<div class="tilt_up">
<input type="submit" class="tilt" name="cntl" value="UP">
<input type="submit" class="tilt" name="cntl" value="up">
</div>
<div class="tilt_down">
<input type="submit" class="tilt" name="cntl" value="dn">
<input type="submit" class="tilt" name="cntl" value="DN">
</div>
</div>
<div style="float:left;">
<canvas id="cv" width="640" height="480"></canvas>
<div class="vcontrol">
<input type="submit" class="video" name="cntl" value="RGB">
<input type="submit" class="video" name="cntl" value="IR">
<input type="submit" class="video" name="cntl" value="DEPTH">
</div>
</div>
</form>
</body>

正直 html はいまいち不慣れなので、divの構造などは適当。

POST と GET はどちらが良いのか毎度悩むが、POST は純粋にサーバーにデータを送信する場合(サーバーの状態を変更する)、GET はサーバーから情報を取得する時のパラメータとして送る情報(サーバーの状態は変わらない)と覚えている。
なぜそうなるかは、submit の挙動ではなく、submit 後に遷移したページをリロードしたり URL をコピペしたときにどうなる考えると想像が付くと思う。
当然今回は POST を使っている。

css をよしなに設定してあげればこんな感じで画面が表示される。

ボタンを押すたびにリロードされるのが嫌なら iframe を使うなどすれば回避できるそうだが、そんなに頻繁に押さなければ気にならないので放置。

CGI の作成

CGI自体は POST で送られてきたデータを解釈して FIFO に送るだけの非常にシンプルなもの。
一瞬 bash でも十分かと思ったが、そういえばシェルショックなる話があったなーと思い出して念のため perl で作成した。

#!/usr/bin/perl
use Fcntl;
print "Location: /kinect\n\n";
sysopen(FIFO, "/tmp/kinect", O_NONBLOCK | O_WRONLY) or die "Failef to open FIFO: $!\n";
read(STDIN, $data, $ENV{'CONTENT_LENGTH'});
foreach $item (split(/&/, $data)) {
        ($key, $value) = split(/=/,$item);
        if($key eq "cntl") {
                print $value;
                if($value eq "UP") {
                        $cmd = "P";
                } elsif($value eq "up") {
                        $cmd = "p";
                } elsif($value eq "dn") {
                        $cmd = "m";
                } elsif($value eq "DN") {
                        $cmd = "M";
                } elsif($value eq "RGB") {
                        $cmd = "R";
                } elsif($value eq "IR") {
                        $cmd = "I";
                } elsif($value eq "DEPTH") {
                        $cmd = "D";
                }
               
                $r = syswrite(FIFO, $cmd);
                if(!defined($r)) {
                        die "Write failed\n";
                }
        }
}
close(FIFO);

久々に perl 書いたけど、そういえば標準だと switch 文使えなかったのね。
正直ノンブロックにする必要はないのだが、後からFIFOが開いた時に溜まっていたコマンドが送られるケースがあり得るのがなんとなく嫌だったのでノンブロックにした。

CGIのテストと有効化

職業柄プログラムを作ったら単体で動かして見ないと落ち着かないので、一旦単体で動かして見る。
とは言え、POST に相当するものをブラウザ以外から与えるのはなかなか面倒なようで、以下のように長ったらしい呪文を追加してあげる必要がある。

echo cntl=p | env REQUEST_METHOD='POST' CONTENT_LENGTH=6 HTTP_REFERER='http://localhost/index.html' perl -d control.cgi

無事に動くのが確認できたら、cgi-bin に配置する。
ラズパイの場合は /usr/lib/cgi-bin になるが、一つトラップがあって以下のように apache の設定を変えてあげないとたとえ cgi-bin に配置しても動いてくれなかった。(apache のドキュメントを読んでもそんなことどこにも書いていないので、正直かなり苦戦した・・・)

> sudo ln -s /etc/apache2/mods-available/cgi.load /etc/apache2/mods-enabled/
> sudo service apache2 restart

これで無事にブラウザから Kinect の制御が可能になった。

どうしてもブログだとソースを全てかけないので細切れになってしまうが、需要があればそのうち GitHub なり何なりに上げようと思う。

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 を作成する。

2015年12月5日土曜日

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

Depth と IR 画像の取得

Depth は depth_cb の data 引数から取得できる。
ただし、データは 11bit 形式なので、8bit のグレイスケールに変換する必要がある。
最初は律儀に RGB 各色に同じ値を入れていたが、JPEG のオプションを調べたら 1byte のグレイスケールでも出力できるようだった。

    JSAMPLE jpg_line[640];
    ・・・
    jpg_cinfo.input_components = 1;
    jpg_cinfo.in_color_space = JCS_GRAYSCALE;
    ・・・
    for(i = 0; i < 480; i++) {
        int j;
        for(j = 0; j < 640; j++) {
                unsigned short tmp;
                tmp = ((unsigned short)buf[i * 640 * 2 + j * 2 + 1] << 8)
                          + ((unsigned short)buf[i * 640 * 2 + j * 2] & 0xff);
                jpg_line[j] = (tmp >> 3) & 0xff;
        }
        jpg_row[0] = jpg_line;
        jpeg_write_scanlines(&jpg_cinfo, jpg_row, 1);
    }

IR の方は、最初に freenect_set_video_mode で FREENECT_VIDEO_RGB の代わりに FREENECT_VIDEO_IR_8BIT を指定する必要がある。
10BIT精度にも出来るが、どうせグレイスケールに変換するのでここでは 8bit で十分。こうすれば切り出したバッファをそのまま JPEG のライブラリに食わせられる。
このあたりからあまりまとまった解説が見当たらくなってきたので、わからなかったらlibfreenect のヘッダのコメントを見て解決している。

    freenect_set_video_mode(fn_dev,  freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_IR_8BIT));
    ・・・
   
    ・・・
    for(i = 0; i < 480; i++) {
        jpg_row[0] = (JSAMPROW)&buf[i * 640];
        jpeg_write_scanlines(&jpg_cinfo, jpg_row, 1);
    }

動作中の RGB と IR の切り替え方法については、正式なドキュメントは見当たらなかったが、とりあえず以下のように一旦止めてから、モードを変更して再度スタートしたら無事に動いたので、とりあえずはこのやり方で行くことにした。

    freenect_stop_video(fn_dev);
    freenect_set_video_mode(fn_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_IR_8BIT));
    freenect_start_video(fn_dev);

もしかしたら止めなくても良いのかもしれないが、なんとなく気分的にやってしまう。

Tilt(傾き) の操作

Tilt の操作は freenect_set_tilt_degs を呼べば良いはずなのだが、ベースとした camtest.c にこれを組み込んでもさっぱり反応してくれない。
コードを読み返したら一カ所怪しいところがあったので、ヘッダのコメントをみたら案の定これが悪さをしていた。というわけで、以下の一行をコメントアウト。

    //freenect_select_subdevices(fn_ctx, FREENECT_DEVICE_CAMERA);

これは、アプリで使用するキネクトのデバイスを指定するオプション。キネクトは、カメラ、モーター、LEDの3つのデバイスから構成されており、この関数を利用することで個別に指定して開くことが出来る。
例えば、アプリ A でカメラを操作し、アプリ B でチルトの制御を行うときなどはこれを使うと便利だが、今回はアプリを分けるつもりは無い。指定なしの場合は全部のデバイスをオープンするそうなので、コメントアウトしてしまうのが手っ取り早い。

傾きの角度は浮動小数点で自由に指定できるが、サンプルを見た限りだと ±30度 の範囲がデバイスとしての限界っぽい。(API 側で制限かけていないのは、将来的に新型 Kinect が出たときに動作範囲が変わることを想定してるんだろうな)
角度の指定は動いているときでも出来るようだが、気分的に以下のように止まっていることを確認してから行うようにする。

    freenect_raw_tilt_state* p_raw_tilt_state;
    freenect_tilt_status_code tilt_status;
    double tilt_deg = 10.0;

    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, tilt_deg);
    }

なお、±30度を超える角度を指定した場合は状態は TILT_STATUS_LIMIT になる。なのであえて TILT_STATUS_STOPPED ではなく !TILT_STATUS_MOVING で動作中かどうかを判定している。

これで必要な機能の使い方は一通りわかったので、後はこれをどうやってブラウザから制御するかってことになる。

2015年12月1日火曜日

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

Kinect のカメラ画像を Jpeg ファイルに出力してブラウザで見る

freenect の example には camtest.c が入っている。
ソースを読んだ感じでは、RGB/Depth カメラのデータを繰り返し取得しているようなのだが、そのままではメッセージを出力するだけで画像が取得できないのでこれを改造して画像を出力するようにする。

カメラのデータは video_cb という関数の第二引数であるdata に格納されていると思われるが、どのようなフォーマットで格納されているかいまいちドキュメントが見当たらなかった。仕方なしに先頭数バイトを HEX でダンプして心の目で見た感じだとどうやらRGBの生データぽかったので、これを上手いこと整形して JPEG のライブラリに食わせれば画像ファイルに変換できると考えた。

まずは Jpeg ライブラリのインストール
> sudo apt-get install libjpeg-dev

で、video_cb の中身を以下のように変更

#include <jpeglib.h>

char filename[] = "/var/www/html/kinect/output.jpg";

void video_cb(freenect_device* dev, void* data, uint32_t timestamp)
{
        JSAMPROW jpg_row[1];
        struct jpeg_compress_struct jpg_cinfo;
        struct jpeg_error_mgr jerr;
        FILE *fp;
        int i;
        char* buf = data;

        jpg_cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_compress(&jpg_cinfo);
        fp = fopen(filename, "wb");
        jpeg_stdio_dest(&jpg_cinfo, fp);
        jpg_cinfo.image_width = 640;
        jpg_cinfo.image_height  = 480;
        jpg_cinfo.input_components = 3;
        jpg_cinfo.in_color_space = JCS_RGB;
        jpeg_set_defaults(&jpg_cinfo);
        jpeg_set_quality(&jpg_cinfo, 75, TRUE);
        jpeg_start_compress(&jpg_cinfo, TRUE);
        for(i = 0; i < 480; i++) {
                jpg_row[0] = (JSAMPROW)&buf[i * 640 * 3];
                jpeg_write_scanlines(&jpg_cinfo, jpg_row, 1);
        }
        jpeg_finish_compress(&jpg_cinfo);
        jpeg_destroy_compress(&jpg_cinfo);
        fclose(fp);
}

コンパイルは -lfreenect に加えて -ljpeg を追加してコンパイルする。
無事にコンパイルできたら、/var/www/html/kinect ディレクトリを作成してから実行すればここに Kinect カメラで撮影した画像が保存される。
実のところ Jpeg のライブラリを使うのは初めてだったが、思ってた以上にすんなり出来た。なので、細かいパラメータはどっかで拾ったサンプルそのまま。

画像を見るにはPCのブラウザで "http://ラズパイのIP/Kinect/output.jpg" と入れれば撮影した画像が閲覧できる。
ブラウザのリロードボタンをひたすら連打すればアニメーションぽいのも見れるはずである・・・が、
ここで問題が発生した。
一つはそのままだと画像がキャッシュされてしまい更新されない。
これは以下の.httaccesを作成して対処した。

<Files ~ "\.jpg$">
Header set Cache-Control "no-cache"
Header set Pragma "no-cache"
</Files>

もう一つは読み込むタイミングによってはライブラリが書き込んでる途中のデータが読み込まれてしまい、画像の一部が切れてしまうことがあった。

苦肉の策として、一旦テンポラリに吐き出した後で、mv コマンドで移動するようにした。
互換性がだいぶ犠牲になるが、どうせラズパイ専用なので気にしない。

char filename[] = "tmp.jpg";
char postcmd[] = "mv tmp.jpg /var/www/html/kinect/output.jpg";

void video_cb(freenect_device* dev, void* data, uint32_t timestamp)
{
        JSAMPROW jpg_row[1];

   ・・・

        fclose(fp);
        system(postcmd);
}

蛇足だが、Linux/Unix 系のファイルシステムでは読み込み途中のファイルが mv コマンドで変更されても、読み込んでファイルがクローズされるまでは読み込み側ではもとのファイルを参照し続けるので、このように mv で上書きしてしまってもブラウザで表示されるデータが混ざったり崩れたりする心配は無い。
FATやNTFSの場合は知らんが。

手動でリロードするのも何なので、JavaScriptで自動でリロードするようにしてあげれば、Kinectのカメラで撮った画像をぱらぱら漫画風の動画として見ることが出来る。

/var/www/html/kinect/index.html

<html>
<head>
<script language="javascript"><!--
function autoreload() {
    location.reload();
}
//--></script>
</head>
<body onload="setInterval('autoreload()',100)">
<img src="output.jpg">
</body>

このままでも良いのだが、リロードの時のちらつきが気になる。
見栄えの問題なので後回しにしようと思ったが、canvas を使えば意外と簡単に解決できそうなのでやってみた。

<html>
<head>
<script language="javascript"><!--
function loadimage() {
        var canvas = document.getElementById("cv");
        if( ! canvas || ! canvas.getContext ) { return false; }
        var ctx = canvas.getContext('2d');
        var img = new Image();
        img.src = "output.jpg?" + new Date().getTime();
        img.onload = function() {
                ctx.drawImage(img, 0, 0);
                setTimeout(loadimage, 100);
        }
}
//--></script>
</head>
<body onload="loadimage()">
<canvas id="cv" width="640" height="480"></canvas>
</body>

同じページでのリロードの場合は画像のキャッシュを無効にしても意味が無いようなので、結局ファイル名の後に"?"と日時を追加してブラウザに違うファイルとして認識させるようにする必要がある。

実態はパラパラ漫画なのだが、この程度でもLAN 環境なら動画と遜色ない感じで表示できる。
setTimeout はもっと短くても良いかもしれない。

とりあえずはこれでスタート地点と同じ RGB の動画(っぽいの)を表示するところまではたどり着いた。
せっかく頑張って libfreenect を導入したのだから、IR/Depth 画像の表示やチルト操作もできるように今後拡張していく。