Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
書籍転載:Intel RealSense SDKセンサープログラミング(8)

書籍転載:Intel RealSense SDKセンサープログラミング(8)

openFrameworksによるメディアアートとRealSenseを組み合わせよう!
― Chapter 10 openFrameworksで作る「メディアアート」アプリ ―

2015年9月15日

RealSenseとopenFrameworksを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

谷口 直嗣
  • このエントリーをはてなブックマークに追加

 前回は、openFrameworksを使ったサンプルアプリケーションを実際に作成しました。今回はその続きです。

書籍転載について

 本コーナーは、翔泳社発行の書籍『Intel RealSense SDKセンサープログラミング』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。

 

 『Intel RealSense SDKセンサープログラミング』の詳細や購入は翔泳社のサイト目次ページをご覧ください。プログラムのダウンロードも、翔泳社のサイトから行えます。

ご注意

本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。

10-3-4 インテル RealSense SDKを使うための設定

 いよいよ、インテル RealSense SDKをopenFrameworksのプログラムに仕込んでいきます。

>インクルードパスの設定

 Visual StudioのmySketch2プロジェクトのプロパティから[C/C++]→[全般]を選択し、[追加のインクルードディレクトリ]にて、C++ のインクルードパスに$(RSSDK_DIR)/includeを追加します(図10.13)。

図10.13 インクルードパスを設定する
図10.13 インクルードパスを設定する
図10.13 インクルードパスを設定する
>ライブラリパスの設定

 同様に、mySketch2プロジェクトのプロパティから[リンカー]→[全般]を選択し、[追加のライブラリディレクトリ]にて、C++ のライブラリパスに$(RSSDK_DIR)/lib/$(PlatformName)を追加します(図10.14)。

図10.14 ライブラリパスを設定する
図10.14 ライブラリパスを設定する
図10.14 ライブラリパスを設定する
>ライブラリの設定

 さらに、mySketch2プロジェクトのプロパティから、[リンカー]→[入力]を選択し、[追加の依存ファイル]にて、デバッグビルドの時はlibpxc_d.libを追加、リリースビルドの時はlibpxc.libを追加します(図10.15)。

図10.15 ライブラリを設定する
図10.15 ライブラリを設定する
図10.15 ライブラリを設定する

10-3-5 インテル RealSense SDKのコードを追加する

 準備ができたので、いよいよインテル RealSense SDKを使うコードをアプリに追加していきます。今回使うコードは「Chapter 5 手指の検出」(転載対象外)でサンプルとして使われているコードとほとんど同じです。

>インクルードファイルの追加

 インテル RealSense SDKを使うためのインクルードファイルをofApp.cppに追加します。

C++
#include "pxccapture.h"
#include "pxchandmodule.h"
#include "pxchandconfiguration.h"
#include "pxchanddata.h"
#include "pxcsensemanager.h"
リスト10.8 インテル RealSense SDK用のインクルードファイルを追加する(ofApp.cpp)
>手の解析に使うクラスのインスタンスの追加

 続いて、手の解析に使うクラスのインスタンスをofApp.cppに追加します。

C++
PXCSenseManager* senseManager = 0;
PXCHandModule* handAnalyzer = 0;
PXCHandData* handData = 0;
リスト10.9 インテル RealSense SDKで手の解析に使うクラスのインスタンスを追加(ofApp.cpp)
>変数の追加

 さらに、Depthストリームの定義用変数、手の情報を保存する変数、openFrameworkで描画する為の変数を、ofApp.cppに追加します。

C++
const int DEPTH_WIDTH = 640;
const int DEPTH_HEIGHT = 480;
const int DEPTH_FPS = 30;
リスト10.10 Depthストリームの定義用変数を追加する(ofApp.cpp)
C++
int sides[2];
float openesses[2];
リスト10.11 手の情報を保存する変数を追加する(ofApp.cpp)
C++
ofPoint centers[2];
ofColor circleColor;
ofColor clearWhiteColor;
リスト10.12 openFrameworkで描画するための変数を追加する(ofApp.cpp)

 以上で、インクルードファイル、グローバル変数の追加は終了です。まとめると、追加したコードは、リスト10.13のようになっています。

C++
#include "pxccapture.h"
#include "pxchandmodule.h"
#include "pxchandconfiguration.h"
#include "pxchanddata.h"
#include "pxcsensemanager.h"

PXCSenseManager* senseManager = 0;
PXCHandModule* handAnalyzer = 0;
PXCHandData* handData = 0;

const int DEPTH_WIDTH = 640;
const int DEPTH_HEIGHT = 480;
const int DEPTH_FPS = 30;

int sides[2];
float openesses[2];
ofPoint centers[2];
ofColor circleColor;
ofColor clearWhiteColor;
リスト10.13 インクルードファイルと変数を追加するコード(ofApp.cpp)
>セットアップのコードを追加

 続いて、インテル RealSense SDKを使って手の解析をするためのセットアップ、解析した手のデータを表示するためのセットアップのコードを、setup()に追加していきます。以降に挙げるコードを10-3-3項でやったGPUパーティクルのセットアップのコード(リスト10.4)の後に、それぞれ追加します。

 解析した手のデータを表示するためのセットアップ

 今回のデモは手の重心を赤い円で表示するので、openFrameworksで赤い色を使えるように赤色を定義しておきます。

C++
circleColor.r = 255;
circleColor.g = 0;
circleColor.b = 0;
circleColor.a = 128;
リスト10.14 赤色を使えるように定義する(ofApp.cpp)
 インテル RealSense SDKのセットアップ

 インテル RealSense SDKのセットアップをするためのコードを追加します。

C++
senseManager = PXCSenseManager::CreateInstance();
if (senseManager == 0) {
  throw std::runtime_error("SenseManagerの生成に失敗しました");
}

// Depthストリームを有効にする
auto sts = senseManager->EnableStream(PXCCapture::StreamType::STREAM_TYPE_DEPTH, DEPTH_WIDTH, DEPTH_HEIGHT, DEPTH_FPS);
if (sts < PXC_STATUS_NO_ERROR) {
  throw std::runtime_error("Depthストリームの有効化に失敗しました");
}
// 手の検出を有効にする
sts = senseManager->EnableHand();
if (sts < PXC_STATUS_NO_ERROR) {
  throw std::runtime_error("手の検出の有効化に失敗しました");
}
// パイプラインを初期化する
sts = senseManager->Init();
if (sts < PXC_STATUS_NO_ERROR) {
  throw std::runtime_error("パイプラインの初期化に失敗しました");
}

initializeHandTracking();
リスト10.15 インテル RealSense SDKのセットアップのためのコードを追加する(ofApp.cpp)
 手の認識のセットアップ

 インテル RealSense SDKの手の認識セットアップのコードとして、initializeHandTracking()を追加します。

C++
void ofApp::initializeHandTracking()
{
  // 手の検出器を取得する
  handAnalyzer = senseManager->QueryHand();
  if (handAnalyzer == 0) {
    throw std::runtime_error("手の検出器の取得に失敗しました");
  }
  // 手のデータを作成する
  handData = handAnalyzer->CreateOutput();
  if (handData == 0) {
    throw std::runtime_error("手の検出器の作成に失敗しました");
  }
  PXCCapture::Device *device = senseManager->QueryCaptureManager()->QueryDevice();
  PXCCapture::DeviceInfo dinfo;
  device->QueryDeviceInfo(&dinfo);
  if (dinfo.model == PXCCapture::DEVICE_MODEL_IVCAM) {
    device->SetDepthConfidenceThreshold(1);
    //device->SetMirrorMode( PXCCapture::Device::MIRROR_MODE_DISABLED );
    device->SetIVCAMFilterOption(6);
  }
  // 手の検出の設定
  PXCHandConfiguration* config = handAnalyzer->CreateActiveConfiguration();
  config->EnableSegmentationImage(true);
  config->ApplyChanges();
  config->Update();
}
リスト10.16 手を認識するためのセットアップのコードを追加する(ofApp.cpp)

 ここまでできると、setup()関数はリスト10.17のようになっています。

C++
void ofApp::setup(){
  ofBackground(0);
  ofSetFrameRate(60);

  // 1,000,000 particles
  unsigned w = 1000;
  unsigned h = 1000;

  particles.init(w, h);

  // initial positions
  // use new to allocate 4,000,000 floats on the heap rather than
  // the stack
  float* particlePosns = new float[w * h * 4];
  for (unsigned y = 0; y < h; ++y)
  {
    for (unsigned x = 0; x < w; ++x)
    {
      unsigned idx = y * w + x;
      particlePosns[idx * 4] = 400.f * x / (float)w - 200.f; // particle x
      particlePosns[idx * 4 + 1] = 400.f * y / (float)h - 200.f; // particle y
      particlePosns[idx * 4 + 2] = 0.f; // particle z
      particlePosns[idx * 4 + 3] = 0.f; // dummy
    }
  }
  particles.loadDataTexture(ofxGpuParticles::POSITION, particlePosns);
  delete[] particlePosns;

  // initial velocities
  particles.zeroDataTexture(ofxGpuParticles::VELOCITY);

  // listen for update event to set additonal update uniforms
  ofAddListener(particles.updateEvent, this, &ofApp::onParticlesUpdate);

  // ▼▼ここから追加▼▼
  circleColor.r = 255;
  circleColor.g = 0;
  circleColor.b = 0;
  circleColor.a = 128;

  senseManager = PXCSenseManager::CreateInstance();
  if (senseManager == 0) {
    throw  std::runtime_error("SenseManagerの生成に失敗しました");
  }

  // Depthストリームを有効にする
  auto sts = senseManager->EnableStream(
    PXCCapture::StreamType::STREAM_TYPE_DEPTH,
    DEPTH_WIDTH, DEPTH_HEIGHT, DEPTH_FPS);

  if (sts < PXC_STATUS_NO_ERROR) {
    throw  std::runtime_error("Depthストリームの有効化に失敗しました");
  }
  // 手の検出を有効にする
  sts = senseManager->EnableHand();
  if (sts < PXC_STATUS_NO_ERROR) {
    throw  std::runtime_error("手の検出の有効化に失敗しました");
  }
  // パイプラインを初期化する
  sts = senseManager->Init();
  if (sts < PXC_STATUS_NO_ERROR) {
    throw  std::runtime_error("パイプラインの初期化に失敗しました");
  }

  initializeHandTracking();
  // ▲▲ここまで追加▲▲

}
リスト10.17 セットアップを追加したofApp.cpp
>フレームごとのトラッキング処理を追加

 フレームごとに手をトラッキングするための処理をofApp::update()に追加します。

C++
void ofApp::update(){

  // ▼▼ここから追加▼▼
  pxcStatus sts = senseManager->AcquireFrame(false);
  if (sts < PXC_STATUS_NO_ERROR) {
    return;
  }
  // 手のデータを更新する
  updateHandFrame();
  // フレームを解放する
  senseManager->ReleaseFrame();
  // ▲▲ここまで追加▲▲

  ofSetWindowTitle(ofToString(ofGetFrameRate(), 2));
  particles.update();
}
リスト10.18 フレームごとに手をトラッキングするための処理を追加する(ofApp.cpp)
>手のデータの更新処理を追加

 今回は手の重心と、手の開閉度を取得します。そのためのコードを更新処理updateHandFrame()として追加します。

C++
void ofApp::updateHandFrame(){

  handData->Update();
  // 検出した手の数を取得する
  auto numOfHands = handData->QueryNumberOfHands();
  for (int i = 0; i < numOfHands; i++) {
    // 手を取得する
    pxcUID handID;
    PXCHandData::IHand* hand;
    auto sts = handData->QueryHandData( 
      PXCHandData::AccessOrderType::ACCESS_ORDER_BY_ID, i, hand);
    if (sts < PXC_STATUS_NO_ERROR) {
      continue;
    }

    auto side = hand->QueryBodySide();
    auto openness = hand->QueryOpenness();
    auto center = hand->QueryMassCenterImage();

    if (i < 2){
      sides[i] = side;
      openesses[i] = openness;
      centers[i].x = center.x;
      centers[i].y = center.y;
    }
  }
}
リスト10.19 手のデータの更新処理を追加する(ofApp.cpp)

 これで、openFrameworksのアプリケーションにおいて、RealSenseによる手のトラッキングができるようになりました。

10-3-6 トラッキングしている手の情報の表示

 続いて、インテル RealSenseカメラでトラッキングした手の情報をopenFrameworksのアプリ上で表示してみましょう。今回はofEasyCamという簡単に3D表示ができるクラスを使っています。デフォルトでは画面中央が(0,0)となっています。

 また、RealSenseの解析で定義している範囲が、X方向にDEPTH_WIDTH、Y方向にDEPTH_HEIGHTなので、手の重心の値はその範囲で返ってくるのを、openFrameworksアプリの画面のサイズに合わせています。さらに、X方向の動きに-1をかけることでミラーリングしています。

 手の開閉度は、円の大きさで表しています。

 リスト10.20のようにofApp::draw()にコードを追加します。

C++
void ofApp::draw(){
  cam.begin();
  ofEnableBlendMode(OF_BLENDMODE_ADD);
  particles.draw();
  ofDisableBlendMode();

  // ▼▼ここから追加▼▼
  // 円の位置の計算
  ofVec3f handPoint(
    -1.0f * (centers[0].x * ofGetWidth() / DEPTH_WIDTH - .5f * ofGetWidth()),
    .5f * ofGetHeight() - (centers[0].y * ofGetHeight() / DEPTH_HEIGHT), 0.f);
  // 円の色の指定
  ofSetColor(circleColor);
  // アウトラインのみ描画
  ofNoFill();
  // 円の描画
  ofCircle(handPoint.x, handPoint.y, 2.0f * openesses[0]);
  // ▲▲ここまで追加▲▲

  cam.end();
}
リスト10.20 トラッキングしている手の情報をopenFrameworksアプリ上に表示する(ofApp.cpp)

10-3-7 手でパーティクルを動かそう!

 さて、いよいよサンプルも最終段階です。RealSenseでトラッキングした手でGPUパーティクルを動かすようにします。ofApp::onParticlesUpdate関数を少し修正して、手の位置、開閉度をシェーダーに送ります。

 下記の行を書き換えます。

C++
ofVec3f mouse(ofGetMouseX() - .5f * ofGetWidth(), .5f * ofGetHeight() - ofGetMouseY(), 0.f);

 リスト10.21のように書き換えます。座標の変換は10-3-6項で手の位置の表示に使ったロジック(リスト10.20)と全く同じです。

C++
ofVec3f mouse(
  -1.0f * ( centers[0].x * ofGetWidth() / DEPTH_WIDTH - .5f * ofGetWidth()),
  .5f * ofGetHeight() - (centers[0].y * ofGetHeight() / DEPTH_HEIGHT ), 0.f);
リスト10.21 手の位置をシェーダーの座標系に合わせて計算

 これで、パーティクルを手のトラッキングで動かすことができるようになりました。次に、手の開閉値でパーティクルの集まり具合を変化させます。手の開閉値の最大値は

 100です。手をすぼめたらパーティクルが集まり、手を開いたらパーティクルが広がるようするので、手の開閉値を100で割った値を1.0から引くことで、手が広がるとパーティクルの集まる力が弱くなり、手を閉じるとパーティクルの集まる力が強くなるようにします。
下記の行を書き換えます。

C++
shader.setUniform1f("radiusSquared", 200.f * 200.f);

 リスト10.22のように書き換えます。

C++
float openForRad;
if (openesses[0] < 10.0f){
  openForRad = 10.0f;
}
else if (openesses[0] > 100.0f){
  openForRad = 100.0f;
}
else{
  openForRad = openesses[0];
}
openForRad = 1.0f - ( openForRad / 100.0f ) + 0.01f ;

shader.setUniform1f("radiusSquared", 200.f * 200.f * 10.0f * openForRad );
リスト10.22 手の開閉値でパーティクルの集まり具合を変化させる

 すべての修正が終わるとofApp::onParticlesUpdate()関数は、リスト10.23のようになります。

C++
void ofApp::onParticlesUpdate(ofShader& shader)
{
  // 手の位置をシェーダーの座標系に合わせて計算
  ofVec3f mouse(
    -1.0f * (centers[0].x * ofGetWidth() / DEPTH_WIDTH - .5f * ofGetWidth()),
    .5f * ofGetHeight() - (centers[0].y * ofGetHeight() / DEPTH_HEIGHT), 0.f);

  // 計算した手の位置をシェーダーにセット
  shader.setUniform3fv("mouse", mouse.getPtr());
  shader.setUniform1f("elapsed", ofGetLastFrameTime());

  // 手の開閉値でパーティクルの集まり具合を変化させる
  float openForRad;
  if (openesses[0] < 10.0f){
    openForRad = 10.0f;
  }
  else if (openesses[0] > 100.0f){
    openForRad = 100.0f;
  }
  else{
    openForRad = openesses[0];
  }
  openForRad = 1.0f - (openForRad / 100.0f) + 0.01f;

  shader.setUniform1f("radiusSquared", 200.f * 200.f * 10.0f * openForRad);
}
リスト10.23 手の開閉度合でパーティクルを操作できるコード(ofApp.cpp)

 ビルドすると、手の動きでパーティクルがコントロールできるようになりました! 手を閉じるとパーティクルが集まり、開くとパーティクルが拡散します。

図10.16 手の開閉でパーティクルを操作できる
図10.16 手の開閉でパーティクルを操作できる
図10.16 手の開閉でパーティクルを操作できる
>Special Thanks!

 今回はNeil Mendozaさんの作ったofxGpuParticlesを使わせていただきました。

 以上で、書籍『Intel RealSense SDKセンサープログラミング』からの全8本の記事転載は終了です。

※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

書籍転載:Intel RealSense SDKセンサープログラミング(8)
4. Unity+RealSenseで作るノンゲームアプリ「スマイルトレーニング」

インテル RealSense SDKとUnityを使ったサンプルアプリケーションを実際に作成してみる。

書籍転載:Intel RealSense SDKセンサープログラミング(8)
5. WPF(Visual Studio)+RealSenseで作る表情感知アプリ

インテル RealSenseを活用したサンプルアプリケーション開発の第2弾として、Visual StudioとRealSenseを組み合わせる方法を紹介する。

書籍転載:Intel RealSense SDKセンサープログラミング(8)
6. Visual Studioで活用するRealSenseセンシング機能

RealSenseとVisual Studioを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

書籍転載:Intel RealSense SDKセンサープログラミング(8)
7. openFrameworksで作成するメディアアート入門

インテル RealSenseを活用したサンプルアプリケーション開発の第3弾として、openFrameworksとRealSenseを組み合わせる方法を紹介する。

書籍転載:Intel RealSense SDKセンサープログラミング(8)
8. 【現在、表示中】≫ openFrameworksによるメディアアートとRealSenseを組み合わせよう!

RealSenseとopenFrameworksを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

サイトからのお知らせ

Twitterでつぶやこう!