Comments
Description
Transcript
2 - Cyber Interface Lab
1 平成 24 年度 機械情報工学科演習 メディアインタフェース(2) インタフェースデバイスと NUI の基礎 担当:谷川智洋,鳴海拓志,中垣好之 TA: 泉雅彦,鈴木瑛二 2013 年 11 月 7 日 1 演習の目的 本日の演習では,「コンピュータグラフィックス」で学習した OpenGL の基礎,画像の表示,テクスチャ マッピングを元に,三次元コンピュータグラフィックスとのインタラクションについて学ぶ.一つは,インタ フェースデバイスの一種である Wii リモコンを用いて,三次元グラフィックスとのインタラクション手法を 学習する.Wii リモコンは,リモコンを操作する人間の手の動きやボタン入力などを取り込むことが可能で あり,計算機の世界と実世界をつなぐインタフェースになる.本演習ではボタンを押すことや腕ふりなどの 動作と OpenGL 内での動作を連携させるインタラクション手法を学習する.もう一つは,別の種類のインタ フェースデバイスである Kinect を用いて,奥行きを表現するデプス画像や,それに基づいて人間の位置や姿 勢を判別し,それらの情報を元に三次元グラフィックスとインタラクションする手法について学習する. 1.1 資料など 本日の演習の資料などは, http://www.cyber.t.u-tokyo.ac.jp/~tani/class/mech_enshu/ においてある. 本日使用するソースファイルも同じ場所からダウンロードすること.「イメージプロセッシング」「コン ピュータグラフィックス」や前回の「メディアインタフェース」の資料を参照したい場合も同じ場所を参照の こと.参考となる URL もリンクが貼ってあるため,参照のこと.ネットワークにつながらない者は,教員に USB メモリを借りてデータをコピーすること. 2 貸し出し物および動作確認 2 1.2 出席・課題の確認について 出席の確認は,課題の確認およびレポートの提出によって行う.課題が終了したら,教員・TA を呼び,指 示に従って実行して説明せよ. 課題が全て終了したらメール課題に取り組み, 「メール課題」を [email protected] 宛てにメー ルせよ.件名は「メディアインタフェース演習 20131108 の課題メール:abcdefgh」とせよ.ただし abcdefgh は学籍番号(半角の数字列)に置き換えよ.なお,本文中には氏名と学籍番号を含めること.適切な件名でな いものは未提出扱いになる可能性があるので要注意.メール提出後は退室してよい.演習時間終了後(16 時 15 分以降)は,課題が全て終了せずとも退室は自由である.メール課題は来週 (11/13) までに提出のこと. 1.3 貸し出し物の返却について 課題が全て終了後,帰宅前に,貸し出している Wii リモコン,電池,Bluetooth アダプタ,Kinect を TA まで返却せよ.USB カメラについては各自保管し,自主プロ終了時に返却のこと.不具合があったりした場 合は,来年も使うために,不具合の内容を返却時に報告すること. 2 貸し出し物および動作確認 2.1 Bluetooth の USB アダプタ Princeton PTM-UBT3S を 1 人 1 台貸し出す.小さくなくしやすいため,USB アダプタをさしたままにす るなど,なくさないように(個数に余裕がないため).テプラなどは外さないように注意. 2.2 Bluetooth の USB アダプタの動作確認 USB にさして青色のランプが点滅するか確認する. 2.3 Wii リモコンと電池 Wii リモコンは 1 人に 1 台,電池は 1 人に 2 本を貸し出す.なくさないように.来年度以降も貸し出しを するので大切に取り扱いをする.不具合があったりした場合は,交換を行うが,来年も使うため不具合の内容 を報告すること. 2.4 Wii リモコンの動作確認 本 演 習 で は ,Linux 用 に C で 書 か れ た Wii リ モ コ ン(Wiimote) 用 の ラ イ ブ ラ リ で あ る CWiid (http://abstrakraft.org/cwiid/)を使用する.まず,CWiid の動作確認として,下記を実行する.下記の 00:00:00:00:00:00 には,Wii リモコンにかかれている Bluetooth のアドレスを入力する. $ wmgui 00:00:00:00:00:00 2.4 Wii リモコンの動作確認 3 wmgui の画面が立ち上がったら,「File」→「Connect」する (図 1).図 2 のようなダイアログが出てくる ので,Wii リモコンの 1 ボタンと 2 ボタンを同時に押して LED が点滅するのを確認し,OK ボタンを押す. 図 1 Connect 画面 図 2 Connect のダイアログウィンドウ Connect したら,「Settings」→「Acc Data」にチェックをする.Wii リモコンを動かしてデータが動いて いるのを見ることができたら,動作確認終了. 図3 設定画面 2 貸し出し物および動作確認 4 2.5 CWiid のインストール 今回の演習で使用する Wii リモコン (Wiimote) の開発用ライブラリのインストールを行う (図 4).下記の 手順でインストールすること. 1. 左のパネルから,Ubuntu ソフトウェアセンターを起動する. 2. ソフトウェア検索ボックスを使って,”libcwiid” を検索する. 3. インストールボタンを押し,libcwiid-dev をインストールする. 図 4 Ubuntu ソフトウェアセンターによる CWiid のインストール パスワードを聞かれるので,適宜自分の設定したパスワードを入力する.インストール終了後,上図のよ うにアイコンに緑のチェックが付いていることを確認する.環境によっては libbluetooth-dev を別途インス トール必要があるが,その場合は上記と同様にしてインストールすること. それでもエラーが出てインストールできない場合は,下記のコマンドを打ち込んで試してみるとよい. $ sudo apt-get install libbluetooth-dev $ sudo apt-get install libcwiid-dev 5 3 三次元コンピュータグラフィックスとインタラクション 三次元コンピュータグラフィックスを操作するには,キーボード,マウスその他のインタフェースを用い る.本演習では,身体の動きも取得することができる Wii リモコンを用いたインタラクション手法の基礎につ いて学習する.任天堂 Wii は家庭用ゲーム機であり,従来型のボタンや矢印キーに加え,体を動かしたり,腕 を振ったり,という身体的動作まで含めたインタフェースを提供している.また,特徴としては,Bluetooth を用いた無線での操作が可能であり,Wii リモコンにケーブルがついておらず,空中での自由な操作が可能と なっている点も特徴である. 3.1 Wii リモコン Wii のインタフェースとしては,従来型のクラシックコントローラ,Wii リモコン,Wii リモコンに接続す るヌンチャク,Wii Fit などが発売されている.その中でも Wii リモコンは,簡単な操作,直感的な操作を目 指して開発されており,誰でも容易に使えるインタフェースの一つである. Wii リモコンには,3 軸加速度センサ,スピーカ,スピーカ用アンプ,電源コントロール IC,サウンドデ コーダ,Bluetooth 用 IC,振動モータ,ポインタ用 CMOS カメラ,似顔絵データ用メモリが搭載されてい る.特に,Bluetooth が搭載されていることにより無線での利用が可能であり,3 軸リニア加速度センサを搭 載していることによって,腕を振る動作や傾ける動作などを検出することが可能である. 図 5 Wii リモコン(http://www.nintendo.co.jp/wii/features/wii remote.html) 3.2 CWiid CWiid(http://abstrakraft.org/cwiid/)は,Linux 用に C で書かれた Wii リモコン(Wiimote) 用のツー ルであり,イベントの API などが含まれている.Wii リモコンの API や Wiimote ライブラリ,GTK をベー スにした GUI インタフェースなどが含まれている.なお,CWiid では対応しているが,センサーバーなどが ないため,今回の演習ではポインタ用 CMOS カメラやヌンチャクなどは用いずに行う.また,スピーカ部分 も用いない.ボタンや加速度,振動を用いることとする. 3 三次元コンピュータグラフィックスとインタラクション 6 Wii リモコンの接続と切断 Wii リモコンを接続するには,cwiid open() 関数を用いる. bdaddr には,bluetooth のアドレスが入る.BDADDR ANY を指定した場合には,利用可能な任意の (1 台)Wii リモコンと接続される.Wii リモコンに接続が成功した場合,返り値として cwiid wiimote handle が 返る. cwiid_wiimote_t *cwiid_open(bdaddr_t *bdaddr, int flags); また,接続した Wii リモコンとコールバック関数との対応付けは,cwiid set mesg callback() 関数を用 いる. typedef void cwiid_mesg_callback_t(cwiid_wiimote_t *, int, union cwiid_mesg []); int cwiid_set_mesg_callback(cwiid_wiimote_t *wiimote, cwiid_mesg_callback_t *callback); よって,Wii リモコンの接続とコールバック関数の対応付けを行った例は下記のようになる. if ((wiimote = cwiid_open(&bdaddr, CWIID_FLAG_MESG_IFC)) == NULL) { printf("Unable to connect\n"); } else { if (cwiid_set_mesg_callback(wiimote, &wiimote_callback)) { printf("Unable to set message callback\n"); } .... } なお,切断するときは,cwiid close() 関数を用いる.接続のときに得られた cwiid wiimote handle を引数 に取る. int cwiid_close(cwiid_wiimote_t *wiimote); if(cwiid_close(wiimote)){ fprintf(stderr,"Error on wiimote disconnect\n"); exit(-1); } 3.2 CWiid 7 Wii リモコンからのコールバック Wii リ モ コ ン か ら の コ ー ル バ ッ ク と し て は ,union cwiid mesg mesg array[] に 入 っ て い る . mesg array.type を 用 い て 加 速 度 と ボ タ ン の 情 報 を 識 別 す る .acc zero, acc one は zero point と 1G point の初期値である. void wiimote_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg_array[], struct timespec *timestamp) { int i; for (i=0; i < mesg_count; i++) { switch (mesg_array[i].type) { case CWIID_MESG_ACC: wiimote_acc(&mesg_array[i].acc_mesg); break; case CWIID_MESG_BTN: wiimote_btn(&mesg_array[i].btn_mesg); break; default: break; } } } コマンドの送信 Wii リモコンへのコマンドを送信するには,cwiid command() 関数を用いる. state が 1 で ON,0 で OFF になる. command では, CWIID CMD RUMBLE が振動,CWIID CMD LED が LED である. cwiid_command(wiimote, command, int state); 下記のようにして,任意の LED の消灯や振動の ON/OFF が可能になる. 3 三次元コンピュータグラフィックスとインタラクション 8 //vibration ON cwiid_command(wiimote, CWIID_CMD_RUMBLE, 1); //vibration OFF cwiid_command(wiimote, CWIID_CMD_RUMBLE, 0); //LED 1 ON cwiid_command(wiimote, CWIID_CMD_LED, 1); //LED 1 OFF cwiid_command(wiimote, CWIID_CMD_LED, 0); どのように Wii リモコンからデータを返してもらうか設定するには,下記の様に書く. void set_report_mode(void) { uint8_t rpt_mode; rpt_mode = CWIID_RPT_STATUS | CWIID_RPT_BTN; rpt_mode |= CWIID_RPT_ACC; cwiid_command(wiimote, CWIID_CMD_RPT_MODE, rpt_mode); } 加速度とボタン Wii リ モ コ ン か ら の 傾 き な ど の 値 と し て は ,mesg-¿acc[CWIID X], mesg-¿acc[CWIID X], mesg¿acc[CWIID X] に格納されているが,初期値との差をとることによって,加速度を計算することがで きる. void wiimote_acc(struct cwiid_acc_mesg *mesg) { printf("x: %d, y: %d, z: %d\t", mesg->x, mesg->y, mesg->z); double a_x, a_y, a_z; a_x = ((double)mesg->acc[CWIID_X] - acc_zero.x) / (acc_one.x - acc_zero.x); a_y = ((double)mesg->acc[CWIID_Y] - acc_zero.y) / (acc_one.y - acc_zero.y); a_z = ((double)mesg->acc[CWIID_Z] - acc_zero.z) / (acc_one.z - acc_zero.z); } 3.2 CWiid 9 Wii リモコンからのボタンの値は,下記の手法で取得できる. void wiimote_btn(struct cwiid_btn_mesg *mesg){ if(mesg->buttons & CWIID_BTN_UP){ ... } ボタンは,下記の引数で取得できる. CWIID BTN UP 十字キーの上ボタン CWIID BTN DOWN 十字キーの下ボタン CWIID BTN RIGHT 十字キーの右ボタン CWIID BTN LEFT 十字キーの左ボタン CWIID BTN PLUS +ボタン CWIID BTN MINUS −ボタン CWIID BTN A A ボタン CWIID BTN B B ボタン CWIID BTN HOME HOME ボタン CWIID BTN 1 1 ボタン CWIID BTN 2 2 ボタン Wii リモコンの軸 Wii リモコンの軸は下記の通りである. 図 6 Wii リモコンの軸(http://wiibrew.org/images/9/9e/Wiimote axis2.png) 3 三次元コンピュータグラフィックスとインタラクション 10 Yaw, Pitch, Roll 参考までに,Yaw, Pitch, Roll について図 7 に示す.クォータニオン(四元数)を用いることで簡単に計算 できるようになるが,今回は省略する. 図 7 Yaw,Pitch,Roll(http://sorceryforce.com/programing/mdx/direct3d/stepup/quaternion.html) CWiid を用いたサンプルプログラム CWiid ライブラリを使用する Makefile の例は下記の通りである. CC = g++ COMMON RM = . = rm -f TARGET OBJS = sample = sample.o CFLAGS = -Wall -fpermissive LDFLAGS = LDLIBS = -lglut -lGL -lGLU -lcwiid -lrt -lbluetooth -lpthread .c.o: ${CC} -c ${CFLAGS} $< ${TARGET}: ${OBJS} ${CC} -o ${TARGET} ${OBJS} ${LDLIBS} ${LDFLAGS} clean: ${RM} ${TARGET} *.o *~ 3.2 CWiid 11 ◇ 課題 1 ◇ 1. kadai1-1 のフォルダに入っている sample.c をコンパイルし,実行せよ.実行方法は Bluetooth のアドレス 00:00:00:00:00:00 を引数にとり,実行する.Bluetooth のアドレスは貸し 出した Wii リモコンに書かれている. 例: ./sample 00:00:00:00:00:00 (a)Wii リモコンを傾けると,赤いボールが移動することを確認せよ.また,十字キー,+ ボタン,−ボタンで赤いボールが動くことを確認せよ.また,A ボタン,B ボタンなど でボールの色が変化することを確認せよ. (b)キーボードの「1」と「!」 , 「2」と「”」を押して,Wii リモコンの LED の 1 番,2 番が光 る,消えることを確認せよ.また「r」と「R] を押して,Wii リモコンの振動が始まる・ 止まることを確認せよ. (c)ボタンを押すことによって,表示されている WireCube の回転がスタート,ストップで きるようにせよ. 2. kadai1-2 のフォルダに入っている sample.c をコンパイルし,Wii リモコンを傾けることで, それに応じて Teapot が傾くことを確認せよ. (a)十字キーを用いて大きさを変更できるようにせよ. (b)B ボタンを押すと,つかめて動かせるようにせよ. (c)移動平均などをとることにより,動きを安定化させよ(オプション). 3. kadai1-3 のフォルダに入っている sample.c をコンパイルし,Wii リモコンを振ることによっ て球の動く方向が変化することを確認せよ. disp flag によって,左向き,右向き,静止の状 態が変化する.ホームボタンを押すと位置がリセットされる. (a)加速度のパラメータを変化させ,手の動きに敏感あるいは鈍感に変更させてみよ. (b)壁面を書いて,ぶつかったら反対向きに動くように変更せよ.壁をある位置に設定し, そこに来たら反対方向に動くようにさせる.これができると,壁にあたって帰ってきた 球を,Wii リモコンを振ることによって打ち返せる感じになる. (c)ぶつかった際に振動が起きるようにせよ(オプション). (d)余裕があったら,壁あてゲームのように,球の速さが変わりながら,壁に当たって,戻っ てきたら打ち返すようにせよ.(オプション). 3 三次元コンピュータグラフィックスとインタラクション 12 図8 球が Wii リモコンの傾きにより動く例 図 9 Teapot が Wii リモコンの向きにより動く例 図 10 Wii リモコンを大きく振るとボールの動く向きが変更になる例 13 4 インタフェースとしてのカメラ・Wii リモコン 前回の演習で学習したように,カメラの三次元位置をチェスボードとの相対関係で知ることができる.応用 方法としての一つは上述したカメラの三次元位置を計算機内に描画することである.もう一つの方法として は,実世界でチェスボードの周囲でカメラを動かし,そのカメラの動きに合わせて,計算機の世界を変化させ る.つまり,あたかもチェスボードに物が乗っているかのように動かすことで,視点を動かし,見回す映像を 作ることができる.カメラを固定して,チェスボードを動かすことによっても同じような映像を作ることがで きる.今回は,OpenGL の世界の中の映像を見回すために,カメラをインタフェースとして利用する.また, 同時に,Wii リモコンを利用し,Wii リモコンの動きに合わせて描画オブジェクトを変化させる. 図 12 カメラの角度により,表示角度が変わる 図 11 カメラからキャプチャした画像 Teapot ◇ 課題 2 ◇ 1. kadai2 のフォルダに入っている cvglview wii.c をコンパイルし,実行せよ.実行には, Bluetooth のアドレス 00:00:00:00:00:00 を引数にとり,実行する.Bluetooth のアドレスは 貸し出した Wii リモコンの裏側に書かれている. ./cvglview_wii 00:00:00:00:00:00 2. Wii リモコン部分のソースが追加されているが,プログラム内でどう利用されているのか, ざっと読んで把握せよ.Wii リモコンのイベントのコールバックを実装せよ.その後,カメ ラ位置に合わせて映像が動くように gluLookAt を修正せよ.結果のイメージは,カメラ映像 が図 11 であり,生成されている CG が図 12 である.カメラの動きに応じて,Teapot の向 きも変わることを確認せよ. 3. Wii リモコンのボタンや傾きによって,Teapot の移動や色の変化などインタラクションを加 えよ.この変更結果はスクリーンキャプチャせよ. 4 インタフェースとしてのカメラ・Wii リモコン 14 座標マッピングの方法をうまく考えることによって,視点位置や描画画面の全体の変化をカメラで操作し, 描画されているオブジェクトの個別の変化を Wii リモコンで操作することなど,直観的な操作が可能となる. インタフェースの特性を考慮に入れた設計などをすることが重要である. 課題 2 において,カメラの三次元位置をチェスボードとの相対関係を知り,操作可能になった.そこで,カ メラからの映像をコンピュータグラフィックスの中に取り込み,その中にコンピュータグラフィックスを重ね 合わせることにより,あたかもコンピュータグラフィックスが現実世界の上に乗っているように見せることが 可能となる.カメラを動かすことにより位置を動かすことができ,Wii リモコンを動かすことにより他のパラ メータをいじることができる.これらを発展させた技術が拡張現実感(AR)である. ◇ 課題 3(オプション) ◇ 1. kadai3 のフォルダに入っている cvglarview.c をコンパイルし,実行せよ.実行には Bluetooth のアドレス 00:00:00:00:00:00 を引数にとり,実行する.Bluetooth のアドレスは貸し 出した Wii リモコンに書かれている. ./cvglarview 00:00:00:00:00:00 2. Wii リモコン部分,キャプチャ部分,表示部分など,プログラムの全体像を把握せよ.カメ ラ位置に合わせて映像が動くように gluLookAt を修正せよ.結果のイメージは,カメラ映像 が図 13 であり,生成されているコンピュータグラフィックスが図 14 である. 3. 表示させる物体を teapot 以外の形に変化させよ.独自性が高いものが望ましい,その結果, 現実世界の上に重なっているように見えるか試みてみよ.この変更結果はスクリーンキャプ チャせよ. 4. チェスボードの上で球が転がるようにし,それを Wii リモコンで操作できるようにせよ.そ の結果をスクリーンキャプチャせよ. 図 14 カメラからの現実世界の上に Teapot が乗っ 図 13 カメラからキャプチャした現実世界 ている状態 15 5 三次元コンピュータグラフィックスと Natural User Interface 人間とコンピュータがインタラクションを行う手法として,NUI(Natural User Interface) が注目を集めて いる.NUI とは,「触れる」「動く」「話す」などの人間の自然な動作を利用してコンピューターを操作するイ ンタフェースを指す.人間とコンピュータがインタラクションを行う手法としては,コマンドを文字で打ち込 んで対話する CUI(Character User Interface),PC のデスクトップ画面のようなコンピュータグラフィック スとポインティングデバイスを利用して対話する GUI(Graphical User Interface) などが用いられてきたが, これらはある一定の「お約束」を学習 (例えば特定のコマンドや,特定の場所をクリックすることなどを覚え る必要がある) してから利用する必要があり,誰もがすぐに使いこなせるわけではない.一方,NUI を上手に 設計できた場合には,直観的で学習コストが少なく,すぐに誰でも使いこなせるインタフェースを実現するこ とも可能となる. 5.1 Kinect 前章では人間とコンピュータがインタラクションを行う際に,人間の身体動作を取り入れるためのデバイス として Wii リモコンを紹介した.ここでは同様のインタフェースとして昨今注目を集めている Kinect を紹介 する.Kinect は,2010 年に Microsoft 社から発売された,Xbox 360 向けのゲームデバイスである.Kinect では画像ベースで人間のジェスチャを検出することができるため,Wii リモコンのようにユーザ側にデバイス を身に着けさせる必要が無いというメリットがある.Kinect には RGB カメラ・深度センサ・マイクロフォ ンアレイが付いており,ジェスチャ認識機能の他にも,深度画像取得や音声認識などの機能がある.これらの 機能は,Kinect を PC から扱うためのライブラリである OpenNI や Kinect Windows SDK を介して利用す ることができる.そのため,Kinect はゲームのインタフェースとしてだけでなく,趣味の工作から研究まで 幅広く利用されている.また,Kinect と同等に扱えるデバイスとして,Xtion PRO・Xtion PRO LIVE と いった類似デバイスも登場している. 5.2 OpenNI の概要 OpenNI (Open Natural Interaction) は,Kinect のセンサ部を開発した PrimeSence 社が中心となって開 発したオープンソースのライブラリである.非公式のドライバを用いることで,OpenNI から Kinect の機能 にアクセスできるようになる.OpenNI の公式ページ http://openni.org/Downloads/OpenNIModules.aspx から入手することができる. OpenNI は RGB 画像取得,深度データ取得,ユーザトラッキング(人間の識別と追跡),スケルトントラッ キング(人間の関節位置などを推定し,ポーズを識別する)などの諸機能を有している. Kinect からの画像 をミラー反転させたり,RGB 画像と深度画像のビューポイントを一致させる関数や,Kinect から取得した データをファイル(.oni)に書き出すなど,便利な機能が用意されている. また,GNU Lesser General Public License (LGPL) ライセンス下でオープンソースとして公開されてお り,商業利用時にも利用しやすいという利点がある. 本資料ではインストール方法の紹介は割愛する.利用したい場合には Kinect wiki ( http://www.kinect- wiki.info/ ) の情報が詳しいので参考にされたい.Kinect wiki には Windows,Mac OS X の場合のインストー ル方法が紹介されている.Linux (Ubuntu) での利用方法については「 Ubuntu で OpenNI と Kinect ドライバ 5 三次元コンピュータグラフィックスと Natural User Interface 16 図 15 手をつかったジェスチャの認識 図 16 スケルトンの認識 と NITE のダウンロードとビルドとインストール」 ( http://www.kkaneko.com/rinkou/linux/openni.html ) 等に解説がある. 5.3 OpenNI を利用した深度と画像情報の取得 17 5.3 OpenNI を利用した深度と画像情報の取得 本演習では,ROS 用の OpenNI から Kinect を利用する.現在の演習環境で ROS の OpenNI を起動するに は,まず ROS のバージョンを Hydro に指定した上で,下記のように roslaunch コマンドから openni launch を呼び出す. init_hydro roslaunch openni_launch openni.launch 起動するとエラーが表示されることがあるが,これは利用には問題ないので無視して良い.roslaunch を実 行した状態で,別の端末からプログラムを呼び出すと kinect が利用できる. 01-depth-test のフォルダに入っている main.cpp は,Kinect の赤外線カメラを利用して距離を測るプログ ラムである.Kinect では 500mm の位置からミリメートル単位で,全てのピクセルの距離を計測できる.な お,500mm 以下の位置では正しく距離が計測できない.距離の測定には DepthGenerator を使用する. //DepthGenerator の生成 DepthGenerator depth; nRetVal = context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth); //デプスメタデータの取得 DepthMetaData depthMD; depth.GetMetaData(depthMD); //カメラ中心のピクセルに写った物体までの距離を計測して表示 printf("Frame %d Middle point is: %u. FPS: %f\n", depthMD.FrameID(), depthMD(depthMD.XRes() / 2, depthMD.YRes() / 2), xnFPSCalc(&xnFPS)); DepthGenerator を生成し,depth.GetMetaData(depthMD) とすることで depthMD にデプスメタデータ が格納される.この状態で depthMD(x, y) とするとカメラ画像内の座標(x, y)のピクセルの距離が求めら れる.なお,depthMD.XRes(),depthMD.YRes() はデプス画像の幅,高さを返す. 02-simple-image のフォルダに入っている simple-image.cpp は,Kinect の通常のカメラ画像を取得して表 示するプログラムである.なお,通常の OpenNI から呼び出す場合と,ROS から OpenNI を呼び出す場合 では画像データに格納されるデータの順番が異なる.simple-image.cpp では ROS MODE を 1 にすることで ROS の格納順に対応し,ROS MODE を 0 にすることで通常の OpenNI に対応できるようにしてある. 5 三次元コンピュータグラフィックスと Natural User Interface 18 //ImageGenerator の生成 rc = g_context.FindExistingNode(XN_NODE_TYPE_IMAGE, g_image); //フレームの更新 rc = g_context.WaitAnyUpdateAll(); //イメージメタデータの取得 g_image.GetMetaData(g_imageMD); //イメージデータのバッファの先頭アドレスの取得 const XnUInt8* pImageRow = g_imageMD.Data(); ImageGenerator を生成し,image.GetMetaData(imageMD) とすることで imageMD にデプスメタデー タが格納される.この状態で imageMD.Data() によりイメージデータのバッファの先頭アドレスを取得でき る.その後,適切な色データの配列に変換し,表示する. 03-simple-depth のフォルダに入っている simple-depth.cpp は,Kinect からデプス画像を取得して表示す るプログラムである.デプス画像とは,Kinect の赤外線カメラを利用して計測した,各ピクセルに写った対 象までの距離を,一枚の濃淡画像として表わしたものである.全てのピクセルの距離データを使って奥行きを 可視化するために,元の画像中の距離の分布を平坦化して補正し,正しく描画可能なヒストグラムを求める. 得られたヒストグラムを元のデプス画像に上書きして表示することで,見やすいデプス画像を表示している. 図 17 simple-depth.cpp を実行しデプス画像が表示されている様子 5.3 OpenNI を利用した深度と画像情報の取得 19 // デプスのヒストグラムを計算し,デプス画像を生成する unsigned int nNumberOfPoints = 0; for (XnUInt y = 0; y < g_depthMD.YRes(); ++y) { for (XnUInt x = 0; x < g_depthMD.XRes(); ++x, ++pDepth) { if (*pDepth != 0) { g_pDepthHist[*pDepth]++; nNumberOfPoints++; } } } for (int nIndex=1; nIndex<g_nZRes; ++nIndex) { g_pDepthHist[nIndex] += g_pDepthHist[nIndex - 1]; } if (nNumberOfPoints) { for (int nIndex=1; nIndex<g_nZRes; ++nIndex) { g_pDepthHist[nIndex] = (unsigned int)(256 * (1.0f - (g_pDepthHist[nIndex] / nNumberOfPoints))); } } ◇ 課題 4 ◇ 1. 01-depth-test のフォルダに入っている main.cpp を実行し,動作を確認せよ. 2. 02-simple-image のフォルダに入っている simple-image.cpp を実行し,動作を確認せよ. 3. 03-simple-depth のフォルダに入っている simple-depth.cpp を実行し,動作を確認せよ. 4.(オプション)main.cpp と simple-image.cpp を組み合わせ,カメラ画像を確認しながら,そ の中央に写った物体までの距離を出力するプログラムを作成せよ.画面中央には赤い円等を 表示し,画像を見ながら直観的にある物体までの距離を計測できるようにするとなお良い. 5 三次元コンピュータグラフィックスと Natural User Interface 20 5.4 OpenNI による人物の検出・トラッキングと姿勢推定 ここまで Kinect の基本機能であるカラー画像,デプス画像の取得を行ってきた.それだけでなく,NUI と しての Kinect を特徴付けるものとして,デプス画像を基に人物を検出し,トラッキング,ジェスチャ認識, 姿勢推定など高度なユーザ認識機能がある. 人間の三次元的な姿勢の判別はアルゴリズムのみでは不可能である.そこで Kinect では,大勢の人間にさ まざまな姿勢をとってもらい,計測したデプス画像中での姿勢のパターンを機械学習させている.これによ り,デプス画像中の特定のパターンから人間と思われる箇所を即座に抽出し,その三次元的な姿勢を推定する ことを可能にしている.本演習では,人物の検出・トラッキングとユーザの姿勢推定について取り上げる. 04-user のフォルダに入っている user.cpp は,デプス画像中の人物領域を検出し,色づけして表示するプロ グラムである.このプログラムでは,まずユーザジェネレータを作成し,ユーザが検出された時,消失した時 (検出できなくなった時)に呼び出されるコールバック関数を登録しておく. //ユーザジェネレータの作成 nRetVal = g_Context.FindExistingNode(XN_NODE_TYPE_USER, g_UserGenerator); // ユーザ検出時のコールバック関数 void XN_CALLBACK_TYPE User_NewUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie) { printf("New user: %d\n", nId); } // ユーザ消失時のコールバック関数 void XN_CALLBACK_TYPE User_LostUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie) { printf("Lost user: %d\n", nId); } //ユーザコールバックの登録 XnCallbackHandle hUserCallbacks; nRetVal = g_UserGenerator.RegisterUserCallbacks(User_NewUser, User_LostUser, NULL, hUserCallbacks); 描画ループでは UserGenerator.GetUserPixels(0, sceneMD) によりユーザデータを取得する.このデー タ を 基 に ユ ー ザ デ ー タ が 振 ら れ て い る 領 域 を 判 定 し ,色 分 け す る .認 識 中 の ユ ー ザ 数 は UserGenera- tor.GetUsers(aUsers, nUsers) を利用して取得することができる.i 番目に認識されているユーザの重心 5.4 OpenNI による人物の検出・トラッキングと姿勢推定 21 位置は UserGenerator.GetCoM(aUsers[i], com) を利用して求めることができる. //ユーザデータの取得 g_UserGenerator.GetUserPixels(0, sceneMD); //pLabels が 0 でない領域(ユーザが検出されている領域)に色を塗る if (g_bDrawBackground || *pLabels != 0) { nValue = *pDepth; XnLabel label = *pLabels; XnUInt32 nColorID = label % nColors; if (label == 0) { nColorID = nColors; } if (nValue != 0) { nHistValue = pDepthHist[nValue]; pDestImage[0] = nHistValue * Colors[nColorID][0]; pDestImage[1] = nHistValue * Colors[nColorID][1]; pDestImage[2] = nHistValue * Colors[nColorID][2]; } } //認識中の人数の取得 g_UserGenerator.GetUsers(aUsers, nUsers); //カメラを原点としたユーザの重心位置の取得 XnPoint3D com; g_UserGenerator.GetCoM(aUsers[i], com); //重心位置にラベルを描画 glRasterPos2i(com.X, com.Y); glPrintString(GLUT_BITMAP_HELVETICA_18, strLabel); ここまででユーザを検出することができた.続いてユーザの姿勢を推定する.05-skelton のフォルダに入っ ている main.cpp および SceneDrawer.cpp は,デプス画像中の人物領域を検出し,色づけして表示するだけ でなく,人物の姿勢を推定し,スケルトン(姿勢の骨格)も表示するプログラムである.このプログラムで は,先ほどまでのプログラムに加えて,ポーズが検出された時,キャリブレーションが開始された時,キャリ 22 5 三次元コンピュータグラフィックスと Natural User Interface 図 18 user.cpp を実行しデプス画像中の人物が検出されている様子 ブレーションが終了した時に呼び出されるコールバック関数も登録されている. // ポーズ認識時のコールバック関数 void XN_CALLBACK_TYPE UserPose_PoseDetected (xn::PoseDetectionCapability& /*capability*/, const XnChar* strPose, XnUserID nId, void* /*pCookie*/) // キャリブレーション開始時のコールバック関数 void XN_CALLBACK_TYPE UserCalibration_CalibrationStart (xn::SkeletonCapability& /*capability*/, XnUserID nId, void* /*pCookie*/) // キャリブレーション終了時のコールバック関数 void XN_CALLBACK_TYPE UserCalibration_CalibrationComplete (xn::SkeletonCapability& /*capability*/, XnUserID nId, XnCalibrationStatus eStatus, void* /*pCookie*/) スケルトンがトラッキング中かどうかは UserGenerator.GetSkeletonCap().IsTracking(aUsers[i]) で判別 できる.これを利用し,スケルトンが確からしくトラッキングされている時だけスケルトンを描画する.スケ ルトンから関節位置を取得するには,UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint, joint) によって推定された関節位置を取得した後,その関節位置の推定の確からしさを判別し,ある程 度確からしければその位置を関節位置として描画する.eJoint には関節ごとに割り当てられた番号(たとえば 頭:XN SKEL HEAD = 1 など)が入る.同様の方法で関節位置間のボーン(骨)についても描画している. 5.4 OpenNI による人物の検出・トラッキングと姿勢推定 //スケルトンのトラッキング中かどうかの判定 if (g_bDrawSkeleton && g_UserGenerator.GetSkeletonCap().IsTracking(aUsers[i])) { //player の eJoint の情報を joint に入れる XnSkeletonJointPosition joint; g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition (player, eJoint, joint); //joint が確からしく検出されているかを確認 if (joint.fConfidence < 0.5) { return; } //joint の位置情報を得て関節位置に球を描画 XnPoint3D pt; pt = joint.position; g_DepthGenerator.ConvertRealWorldToProjective(1, &pt, &pt); drawCircle(pt.X, pt.Y, 2); 図 19 05-skelton のプログラムを実行し,人物のスケルトンが推定されている様子 23 5 三次元コンピュータグラフィックスと Natural User Interface 24 ◇ 課題 5 ◇ 1. 04-user のフォルダに入っている user.cpp を実行し,動作を確認せよ. 2. 05-skelton のフォルダに入っているプログラムを実行し,動作を確認せよ.glutKeyboard を参照し,実行中にキーボードのボタンを押すことでどのように表示が変わるかを確認せよ. 3. 06-head-teapot のフォルダに入っているプログラムを完成させ,ユーザを認識するとユーザ の頭の位置に teapot を描画するようにせよ. 4.(オプション)06-head-teapot のフォルダに入っているプログラムの背景をデプス画像では なく実写画像に変更し,実際のユーザの顔を teapot で置き換えるプログラムに変更せよ. ◇ 課題 6(オプション) ◇ 1. 本日の演習で習得した Wii リモコン,Kinect の使用方法と,イメージプロセッシング,コン ピュータグラフィックス,リアルワールド認識等の演習で学んだ手法を組み合わせたアプリ ケーションを作成してみよ.例えば Kinect と画像処理・CG の組み合わせだけでも,人物領 域にだけモザイクをかける,人物だけを抽出して背景画像と置き換えることで光学迷彩(物 体を透明にする技術)を実現する,人物領域のみを切り出して CG の背景と合成する,ポー ズをとることで必殺技が出せる,等が考えられる. 図 20 06-head-teapot のプログラムを実行し,人物の頭部に teapot が表示されている様子の例 25 ◇ 課題メール ◇ 1. 自分の名前と学籍番号を書く. 2. 課題 2-3 の実行結果をスクリーンキャプチャしてメールに貼付せよ.メールにファイル名も 記述せよ.ファイル名を kadai2.png あるいは kadai2.jpg などのようにせよ.どのように変 更を加えたのか,簡便に記述せよ. 3. 課題 5-3 の実行結果をスクリーンキャプチャしてメールに貼付せよ.スケルトンをトラッ キングしつつキャプチャを行うのは一人では難しいため,友人らと協力することを薦める. メールにファイル名も記述せよ.ファイル名を kadai5.png あるいは kadai5.jpg のようにせ よ.どのように変更を加えたのか,簡便に記述せよ. 4. オプション課題を実行していた場合,それらについても上記と同様に実行結果のスクリーン キャプチャを添付し,工夫点を記述せよ. 5.「メディアインタフェース」の授業の感想と意見があれば意見をメールにて送付せよ. 6 参考文献 伊藤邦朗,福田隆宏,”Wii リモコン”,日本機械学会誌,2007.12, Vol.110, No.1069, pp.6-7,2007. 26 メモ 6 参考文献