hagino3000's blog

平成アーカイブス (更新停止)

OpenNIをopenFrameworksから使う Advanced編

OpenNI Advent Calendar20日目の記事です。

24日目の記事でもopenFrameworksについて書かれているので、内容がかぶらない様にしています。

何故24日目の記事の方が先に公開されているかは気にしないでください。oFについて基本的な事は上記 @mmlemon_twitter さんの記事に書かれているので割愛します。あと、oFはMacユーザーが多いのでMacOSX + Xcode4の環境を前提に書きます。

ofxOpenNIについて

oFのaddonの一つ。OpenNIをoFっぽいインタフェースで扱える様にした物。誰のブランチを使っていいのかわからないけど、gameoverhack氏のブランチが一番forkされているので自分が使う時はそれを使っている。

DepthGenerator, ImageGenerator, UserGenetator, HandGenerator, IRGenerator, GestureGeneratorの機能はカバーしていて、 exampleを見れば大体の使い方は掴めるはず。ofxOpenNIのジェネレータから得られるデータは、既に世界座標から表示座標系に変換されているため、そのまま表示に使う事ができる。

チルトモーターの首ふりも実装されているが、内部でlibusbを使っているためかMacOSでしか動作しない。Windowsでも首ふりしたかったらOpenNIのXnUSBを使う実装に書きかえれば良い。

リポジトリにある物はKinectで使う事が想定されているらしく、Xtion Proではそのまま使えない。SensorKinectの代りにSensorモジュールを自分でビルドして上書きすればいけそう。あとOpenNIのバージョンも古いので注意。今から使う場合は自分がメンテナになる勢いで使っていかないと厳しいだろう。

アドオンを使わない方法

さて、ofxOpenNIを紹介したが、現在自分の作業マシンにはKinectでは無くXtion Pro liveが接続されているのでofxOpenNIは動かない。なのでoFのプロジェクトにOpenNIを普通に追加して使う時に気にする事を書きます。

config.xmlがアプリケーションの内部に配置される様にする

Build Phasesで「Add Build Phase」してOpenNIの設定ファイルをビルドした.appファイルの中にコピーする。しておかないとHoge.app単体を配布して動作させた時にconfig.xmlが見つからない、とエラーになってしまう。

OpenNIの初期化コードは次の通りになる。

xn::EnumerationErrors ctxErrors;
static string configFilePath = ofFilePath::getCurrentWorkingDirectory() + "../Resources/config.xml";
XnStatus rc = openNIContext.InitFromXmlFile(configFilePath.c_str(), &ctxErrors);
if (rc != XN_STATUS_OK) {
  XnChar strError[1024];
  ctxErrors.ToString(strError, 1024);
  throw std::runtime_error(strError);
}
updateメソッドでfpsを落さないようにする

updateメソッドで注意しないといけないのは、WaitAndUpdata系のメソッドを毎回使うと、ofSetFrameRateでフレームレートを指定していてもブロックがかかりfps30程度に落ちてしまう。fpsに影響を与えたく無い場合はジェネレーターのWaitAndUpdateDataを呼ぶ前にIsNewDataAvailableで新しいデータが来ているかチェックする。ofxOpenNIではupdateメソッド内でWaitAndUpdateDataを呼んでいるため、フレームレートが30fpsに落ちてしまう。

コードは次の通り。

// 各種ジェネレータとOpenNIコンテキストはヘッダファイルでメンバとして定義してある。
void testApp::update(){
 
  if (depthGen.IsNewDataAvailable()) {
    // 深度データの更新
    depthGen.WaitAndUpdateData();
   
    xn::DepthMetaData depthMetaData;
    depthGen.GetMetaData(depthMetaData);
   
    XnMapOutputMode mapMode;
    depthGen.GetMapOutputMode(mapMode);
   
    int width = mapMode.nXRes;
    int height = mapMode.nYRes;
    int numPixels = width * height;
   
    float* depthHist = createHist(&depthMetaData, numPixels);
   
    const XnDepthPixel* depthPixels = depthMetaData.Data();
    for (XnUInt i = 0; i < numPixels; i++, ++depthPixels) {
      grayPixels[i] = depthHist[*depthPixels];
    }
   
    grayImage.setFromPixels(grayPixels, width, height);   
  }
 
  if (imageGen.IsNewDataAvailable()) {
    // ビデオカメラ画像の更新
    imageGen.WaitAndUpdateData();
   
    xn::ImageMetaData imageMetaData;
    imageGen.GetMetaData(imageMetaData);
   
    colorImage.setFromPixels((unsigned char*)imageMetaData.RGB24Data(), colorImage.width, colorImage.height);
   
  }
 
  if (audioGen.IsNewDataAvailable()) {
    // オーディオの更新
    audioGen.WaitAndUpdateData();
    ofLog(OF_LOG_NOTICE, "Audio data size:" + ofToString(audioGen.GetDataSize()));
   
    // doSomething
  }
}

void testApp::draw(){
    ofScale(0.8, 0.8);
    // 深度画像の描画
    grayImage.draw(0, 0);
    // ビデオカメラ画像の描画
    colorImage.draw(640, 0);
}

どうしてもupdateメソッド内がごちゃごちゃしてくるので、OpenNIの処理を分離するためのクラスを一つ作るのが良いだろう。Audioジェネレーターはこの記事のために初めて使ってみたが、安定して動作しなかったので特に何も処理は実装しなかった。*1

コード一式はgistにアップしてあります。

openFrameworks + Kinectな作品

Vimeoで検索するといろいろ見つかる。

自分のおきにいりはWebcam Piano 2.0 Teaserという作品 (Kinectは使っていないけど)

まとめ

android対応も進んでおり、openFrameworksも発展も楽しみなプラットフォームです。OpenNIと一緒に使えるようになっておくと良いかもしれません。openFrameworks + Kinectについての解説はキネクハッカーズマニュアルにもページを裂いて書いたので、興味のある方はチェックしてみてください。

書名
キネクトハッカーズマニュアル
著者
@hagino3000 + 小野 憲史
発売日
2011-08-26
価格
2,604 JPY (256ページ)


*1:Windowsでは動作実績があるみたいだが、自分の環境ではすぐにデータが取得できなくなり、Xtion pro liveを抜いて刺し直すというのが必要だった。