Kinect ハック on Mac OS X:(3) デモプログラム glview

過去2回、OpenKinectについて書きました(第1回(サンプルの実行)第2回(APIの概要))。今回はデモプログラムであるglviewについて調べてみます。

ポイントは、(1)OpenKinectのAPIをどのように使っているか、(2)OpenGL(GLUT)をどのように使っているか の2点だと思います。今回の目次はこのようになります。
  • main関数の処理内容
  • 描画処理 (gl_threadfunc関数, DrawGLScene関数)の処理内容
  • カメラからの情報取得コールバック関数(depth_cb関数, rgb_cb関数)の処理内容
  • ユーザ入力を受ける関数 (keyPressed関数) の処理内容

main関数の処理内容

main関数の処理の流れは、次のようになっています。(各種APIは第2回(APIの概要)を参照して下さい)
  1. t_gamma配列(※)の事前計算  (※深度カメラからの情報を距離情報に直すときに利用)
  2. コンテキストの作成 (freenect_init関数)
  3. ログレベルの設定 (freenect_set_log_level関数)
  4. 利用するデバイスの決定 (freenect_set_log_level関数):
    プログラム起動時の引数が渡されていれば、その番号のデバイスを利用します。渡されていなければ0番(最初の)デバイスを利用します。もし利用可能なデバイスがなければ、終了します。
  5. デバイスのオープン (freenect_open_device関数)
  6. 種々の設定
    • 傾きの設定 (freenect_set_tilt_degs関数):
      初期設定では0度(==freenect_angle)
    • LEDの設定 (freenect_set_led関数):
      初期設定では赤(==LED_RED)
    • カメラの情報を受け取るコールバック関数の設定(freenect_set_depth_callback関数, freenect_set_rgb_callback関数):
      それぞれdepth_cb関数とrgb_cb関数を設定
    • カメラの情報を受け取る際のフォーマットの指定 (freenect_set_rgb_format関数, freenect_set_depth_format)
    • : それぞれRGB、11bitを指定
  7. 新規スレッドにてgl_threadfunc関数(後に説明)を実行
  8. カメラ情報取得開始 (freenect_start_depth関数, freenect_start_rgb関数)
  9. 正常に処理が行われている間、加速度情報をコンソールに表示 (freenect_get_raw_accel関数, freenect_get_mks_accel関数)
  10. カメラ情報取得終了 (freenect_stop_depth関数, freenect_stop_rgb関数)

描画処理 (gl_threadfunc関数, DrawGLScene関数)の処理内容

描画処理の初期化を行う関数が、gl_threadfunc関数です。main関数から呼ばれ、次の処理を行います。
  1. ウィンドウの作成
  2. 描画関数を設定(DrawGLScene関数)
  3. キー押し下げ時の処理関数の設定(keyPressed関数)
  4. 描画の開始。
gl_threadfunc関数で設定した描画関数(DrawGLScene関数)は、何度も実行され、カメラから取得した情報をOpenGLのウィンドウに表示します。カメラから取得できる深度とRGBはともに、幅640×高さ480の形になっています。その情報は、gl_depth_back配列, gl_rgb_back配列に入っており(カメラからの情報取得用のコールバック(depth_cb関数, rgb_cb関数)で設定されます)、その情報を一旦コピーしてから(gl_depth_front配列, gl_rgb_back配列)、ウィンドウに表示します (※)。深度情報はウィンドウ左側0<x<640の領域に、RGB情報はウィンドウ右側の640<x<1280の領域に描画されます。

※ このようにback(カメラからの取得用)、front(描画用)の2つの分けている理由は、処理速度を上げるため。もし一つの配列を使ってデータをやりとりすると、カメラから情報を取得できても、描画が完全に終わるまでは取得した情報を書き込めず、パフォーマンスが低下します。

カメラからの情報取得コールバック関数(depth_cb関数, rgb_cb関数)の処理内容

カメラからの取得できる情報には、深度情報とRGB情報があります。それらはOpenKinectから定期的に呼ばれるコールバック関数で受け取ることができます。

まず、RGB情報について(rgb_cb関数)は、引数として渡された領域(変数rgb)を、gl_rgb_back配列にそのままコピー(memcpy)しています。(前述の通り、書きこまれたgl_rgb_back配列の情報は、描画処理(DrawGLScene関数)内でgl_rgb_front配列にコピーされた上で描画されます)

次に、深度情報について(depth_cb関数)ですが、RGB情報と比べて処理が多少複雑です。というのは、RGBの場合はそのまま表示すれば良かったのですが、深度情報の場合は、(1)深度情報を距離に直す (2)視覚化するために深度情報を色情報に変換(※)する ためです。処理内容は次の通りです。
(※ カメラから近い順に、白→赤→黄→緑→水色→青→黒 と段階的に変化するようにします)
  1. ピクセルの数(FREENECT_FRAME_PIX == 640×480)だけループし、それぞれについて次の処理を行います。
    1. ピクセルの深度の値(depth[i])を距離に補正し、変数pvalに格納します。(main関数であらかじめ計算しておいたt_gamma配列を利用します)
    2. 補正した値pvalを、下位8ビットと上位のビットに分離します。下位の値(0〜255)は変数lbに格納します。
    3. 上位のビットの値に応じて、色分けをします。
      • 上位ビット値=0 (0〜255)の場合:下位ビットの値に応じて白(pval=0)→赤(pval=255)に段階的に変化
      • 上位ビット値=1 (256〜511)の場合:同様に赤(pval=256)→黄(pval=511) に段階的に変化
      • 上位ビット値=2 の場合:黄→緑 に段階的に変化
      • 上位ビット値=3 の場合:緑→水色 に段階的に変化
      • 上位ビット値=4 の場合:水色→青 に段階的に変化
      • 上位ビット値=5 の場合:青→黒 に段階的に変化
      • 上位ビット値>5 の場合:黒(一色)

ユーザ入力を受ける関数 (keyPressed関数) の処理内容

この関数は、OpenGLに登録され、表示しているウィンドウ上でキー入力があった場合に実行されます。
  • 'w', 's', 'x'が押された場合、Kinectの傾きをそれぞれ1度上昇、水平、1度下降とします (関数の最後で、freenect_set_tilt_degs関数を呼び出します)。
  • '0'〜'6'が押された場合、数字の値に応じてLEDを点滅させます。0:無灯、1:緑、2:赤、3:黄(※)、4:黄点滅(※)、5:緑点滅、6:赤と黄(※)を交互に点滅。(※:黄色は、実際のところは橙色のようです)


今回は、前回調べたAPIをもとに、サンプルプログラムの挙動を把握しました。次回は、深度情報と色情報をもとに、データを立体的に表示するプログラムを作ってみたいと思います。

Kinect ハック on Mac OS X:(2) OpenKinect APIの概要

前回(OpenKinect サンプルの実行)に引き続き、今回もOpenKinectについて書いてみます。今回は、現時点で公開されている OpenKinect (正確にはOpenKinectのCモジュール(?)) のAPIについて調べてみました。以下、目次です。
  • モジュール構成
  • core.c (コア部分)
  • camera.c (カメラ情報取得)
  • tilt.c (傾斜制御・LED制御・加速度センサ情報)
なお、ここで書くAPIは、随時変わる可能性があると思います。

モジュール構成

3つのモジュール(コア部分、カメラ情報取得、傾斜制御・LED制御・加速度センサ情報)から構成されています。
  • core.c:OpenKinectのコア部分のモジュール
  • camera.c:Kinectのカメラ情報を取得するモジュール
  • tilt.c:Kinectの傾き制御、KinectのLED制御、Kinectの加速度センサに関するモジュール

core.c (コア部分)

Kinectのベースとなる関数が含まれます。実際の処理の流れは次のようになると思います:コンテキストの作成(freenect_init)→デバイスのオープン(freenect_open_device)→処理→デバイスのクローズ(freenect_close_device)→コンテキストの破棄(freenect_shutdown)。また、ログ関連の関数や、ユーザ情報を構造体に設定/取得する関数等も含まれます。
  • コンテキストの作成 (コンテキストを作成し、指定されたアドレスへポインタを設定する):
    int freenect_init(freenect_context **ctx, freenect_usb_context *usb_ctx);
  • コンテキストの破棄 (指定されたコンテキストを破棄する):
    int freenect_shutdown(freenect_context *ctx);
  • ログレベルの設定 (指定されたコンテキストのログレベルを設定する。※後注1):
    void freenect_set_log_level(freenect_context *ctx, freenect_loglevel level);
  • ログ出力関数の設定 (指定されたコンテキストにログ出力を行う関数を設定する。※後注2):
    void freenect_set_log_callback(freenect_context *ctx, freenect_log_cb cb);
  • イベント処理の実行 (指定されたコンテキストに溜まっているイベントを処理する):
    int freenect_process_events(freenect_context *ctx);
  • コンテキストに含まれるデバイス数の取得
    int freenect_num_devices(freenect_context *ctx);
  • デバイスのオープン
    int freenect_open_device(freenect_context *ctx, freenect_device **dev, int index);
  • デバイスのクローズ
    int freenect_close_device(freenect_device *dev);
  • ユーザーデータのポインタの設定と取得
    void freenect_set_user(freenect_device *dev, void *user);
    void *freenect_get_user(freenect_device *dev);
  • ログの出力
    void fn_log(freenect_context *ctx, freenect_loglevel level, const char *fmt, ...)

camera.c (カメラ情報取得)

Kinectから取得できるカメラ情報には、RGB情報と深度情報があります。それぞれの情報は、あらかじめ指定したコールバック関数に、定期的に渡されます。
  • コールバック関数の登録 (カメラからの情報を処理するコールバック関数の登録):
    void freenect_set_{depth,rgb}_callback(freenect_device *dev, freenect_{depth,rgb}_cb cb);
  • 取得する情報のフォーマットの設定(後注3):
    int freenect_set_{depth,rgb}_format(freenect_device *dev, freenect_{depth,rgb}_format fmt);
  • 情報取得の開始
    int freenect_start_{depth,rgb}(freenect_device *dev);
  • 情報取得の停止
    int freenect_stop_{depth,rgb}(freenect_device *dev);

tilt.c (傾斜制御・LED制御・加速度センサ情報)

Kinectは、傾斜を変えたり(±15度)、LEDを点灯(後注4)させることができます。また加速度センサがあります。角度を指定する時には、目的の角度を2倍して(+31〜-31度)指定します。
  • 傾斜 (指定された角度にKinectを傾けます):
    int freenect_set_tilt_degs(freenect_device *dev, double angle);
  • LEDの点灯 (指定された方法(後注4)でLEDを光らせます):
    int freenect_set_led(freenect_device *dev, freenect_led_options option);
  • 加速度情報の取得 (Kinect内蔵の素子の加速度情報を取得します):
    int freenect_get_raw_accel(freenect_device *dev, int16_t* x, int16_t* y, int16_t* z);
  • 加速度情報(補正)の取得 (加速度情報をMKS単位系に単位換算して取得します):
    int freenect_get_mks_accel(freenect_device *dev, double* x, double* y, double* z);

補足

  1. ログレベルはlibfreenect.h参照
  2. ログコールバックを設定しない場合は、ログは標準エラー出力に出力される。
  3. RGB情報のフォーマットは、RGBとベイヤ型配列の2種類。深度情報のフォーマットは、11bit, 10bit, packed-11bit, packed-10bit の4種類。(libfreenect.h参照)
  4. LEDの光らせ方は、LED_GREEN, LED_RED, LED_YELLOW, LED_BLINK_YELLOW, LED_BLINK_GREEN, LED_BLINK_RED_YELLOW の6種類。(libfreenect.h参照)


次回は、デモプログラム(glview.c)とOpenGLについて調べてみようと思います。

Kinect ハック on Mac OS X:(1) OpenKinect サンプルの実行

Kinectとは、11月に発売された、MicrosoftのXbox 360用のデバイス。カメラの他に深度センサーやマイクが付いており、プレイヤーはコントローラを持たずにゲームを操作できます。今までのゲームコントローラとは異なる、最先端のデバイスです。
KinectはXbox用のコントローラなのですが、既にいくつかの機能がハックされ、Windows, Linux, Mac OS X用のドライバが出されています(OpenKinect Project)。こんなデバイスが簡単に使えるようになっているのであれば、色々と試してみる価値はありそうです(※ちなみに価格も11,693円(Amazon)と安価)。
そこで、このOpenKinectを使って、Kinectを使ったプログラムを作ってみようと思います。このブログでは覚え書きをまとめておきます。

目次

  • 環境
  • 事前準備
  • OpenKinectのビルド
  • サンプルプログラムの実行


環境

  • iMac (Early 2006)
  • Mac OS X 10.6.5
  • Xcode 3.2.4

事前準備

  • Xcode(開発環境):
    インストールされていなければ、 Mac OS Xのインストールディスクを挿入し、Optional Install→XcodeToolsと辿り、XcodeTools.mpkgをダブルクリックします。
  • gitとcmake:
    インストールされていなければ、下記のように、MacPortをインストールした後で、git-coreとcmakeをインストールします。
    • http://www.macports.org/install.php からインストーラをダウンロードし、実行
    • ターミナルから、sudo port install git-core を実行
    • ターミナルから、sudo port install cmake を実行

OpenKinectのビルド

基本的にhttp://openkinect.org/wiki/Getting_Started#OS_Xに従って行います。
  1. OpenKinectとlibusbをダウンロード:
    • git clone https://github.com/OpenKinect/libfreenect.git
      git clone git://git.libusb.org/libusb.gi
  2. libusbにパッチを当てた上でインストール:
    • cd libusb
      ./autogen.sh
      patch -p1 < ../libfreenect/platform/osx/libusb-osx-kinect.diff
      ./configure
      make
      sudo make install
  3. OpenKinectをconfigure:
    • まずはccmake (※Xcodeを使いたいため最後のccmakeには "-G Xcode"を渡す):
      • cd ../libfreenect/
        cd c
        mkdir build
        cd build
        ccmake -G Xcode ..
    • 画面に "Required library USB NOT FOUND."と表示されます。キーボードで'e'を押して、編集モードに入ります。
    • カーソルを下に動かし、"USB_INCLUDE_DIR-NOTFOUND" の行に合わせます。
    • Enterを押し、"/usr/local/include/libusb-1.0/"を入力し、再度Enterを押します。
    • キーボードから、'c'を2回押してconfigureを2度実行し、その後'g'を押して設定ファイルを生成します。
    • 最後に cmake:
      • cmake -G Xcode ..
    • "Build files have been written to: (略)"と表示されていれば成功
  4. Xcodeから、buildディレクトリにあるlibfreenect.xcodeprojを開く。
  5. "Build and Run" アイコンをクリックする。 ("Build Succeeded" と表示されていれば成功。逆にErrorと表示されていれば失敗(後注))

サンプルプログラムの実行

  1. ターミナル上からglviewを実行 (もしくはXcode上でglviewをダブルクリック):
    • cd examples/Debug/
      ./glview
  2. 次のように表示されていれば成功:
    • Kinect camera test
      Number of devices found: 0
実はまだKinectが届いていないのですが、プログラム上では問題なさそうなので、後はKinectが届いてからUSBに接続すれば動くはず...です。
次はサンプルプログラムのソースコードを読んで、Kinectからのデータ取得方法を調べてみます。

注: 自分の環境では、最初に実行したときに、エラーが出てビルドできませんでした。そこで以下の方法で暫定的に回避しました。(ビルド時のライブラリへのパスを指定する方法が分からなかったので...汚い方法です)
  • エラーの内容:
    ld: library not found for -lJPEG
    collect2: ld returned 1 exit status
    Command /Developer/usr/bin/gcc-4.2 failed with exit code 1
  • 回避方法:
    • MacPortでjpegライブラリをインストール:
      • sudo port install jpeg +universal
    • /opt/local/lib/libjpeg.* を libfreenect/c/build/lib にコピー。