はじめてのKinectプログラミング(IVRC ユース部門勉強会資料)


2013/8/21 @明治大学 総合数理学部 先端メディアサイエンス学科 橋本研究室

IVRCユース部門勉強会で使うサンプルコード達デス。

目次

各種リンク

深度画像、デプスマップの取得

Kinectの目玉、深度画像の表示です。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableDepth(); // 深度画像の有効化
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  image(kinect.depthImage(), 0, 0); // 描画
}

赤外線画像の取得

赤外線レーザプロジェクタから投影されている見えないドットパターンが見えます。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableIR(); // 赤外線画像の有効化
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  image(kinect.irImage(), 0, 0); // 描画
}

ユーザマップの取得

ユーザごとに色を割り振って塗りつぶします。


import SimpleOpenNI.*;

SimpleOpenNI kinect;
color[] userColors = { color(255,0,0), color(0,255,0), color(0,0,255),
color(255,255,0), color(255,0,255), color(0,255,255) };

void setup() {
  kinect = new SimpleOpenNI(this);
  kinect.setMirror(false);                          // ミラー反転を無効にする
  kinect.enableDepth();                             // デプス画像を有効にする
  kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL); // ユーザトラッキングを有効にする
  size(kinect.depthWidth(), kinect.depthHeight());
}

void draw() {
  // カメラの更新とデプス画像の描画
  kinect.update();
  image(kinect.depthImage(), 0, 0);

  // ユーザが1人以上いるなら,ユーザマップを取得
  int[] userMap = null;
  int userCount = kinect.getNumberOfUsers();
  if (userCount > 0) {
    userMap = kinect.getUsersPixels(SimpleOpenNI.USERS_ALL);
  }

  // ユーザマップの情報を元に,ユーザがいるピクセルに色を塗る
  loadPixels();
  for (int y=0; y<kinect.depthHeight(); y++) {
    for (int x=0; x<kinect.depthWidth(); x++) {
      int index = x + y * kinect.depthWidth();
      // ユーザマップを読み,値が0より大きければユーザが居るピクセルだと判定
      if ( userMap != null && userMap[index] > 0 ) {
        int colorIndex = userMap[index] % userColors.length;  // ユーザ色を決定
        pixels[index] = userColors[colorIndex];    // ピクセル値をユーザ色にする
      }
    } 
  }
  updatePixels();
}

人物領域の抽出

ユーザマップを使って人物だけを切り出します。ユーザが検知されないとウィンドウは真っ白なままなので注意!


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480);
  kinect = new SimpleOpenNI(this);
  kinect.enableRGB();
  kinect.enableDepth();
  kinect.setMirror(true);
  kinect.alternativeViewPointDepthToImage();
  kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
}

void draw() {
  background(255);
  kinect.update();
  int[] userMap = null;
  int userCount = kinect.getNumberOfUsers();
  if (userCount > 0) {
    userMap = kinect.getUsersPixels(SimpleOpenNI.USERS_ALL);
  }
  loadPixels();
  for (int y=0; y<kinect.rgbHeight(); y++){
    for (int x=0; x<kinect.rgbWidth(); x++){
      int i = x + y * kinect.rgbWidth();
      if (userMap != null && userMap[i] > 0)
      pixels[i]=kinect.rgbImage().pixels[i];
    }
  }
  updatePixels();
}

距離計測

画面中心の座標のKinectとの距離を表示します。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableDepth(); // 深度画像の有効化
  textSize(50); // 文字サイズの設定
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  image(kinect.depthImage(), 0, 0); // 描画
  
  // 中心の距離を表示
  int[] depthMap = kinect.depthMap();

  int x = width/2; // 画像中心
  int y = height/2; // 画像中心
  int index = x + y * width;
  int distance = depthMap[index];
  
  fill(255, 0, 0);
  ellipse(x, y, 10, 10);
  
  if(distance > 0)
    text(distance +" mm", x+10, y-10);
  else
    text("-- mm", x+10, y-10);
}

距離計測(面)1

Kinectから1m未満のエリアを赤くします。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableDepth(); // 深度画像の有効化
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  image(kinect.depthImage(), 0, 0); // 描画
  
  // 1000mm以下を赤くする処理
  int[] depthMap = kinect.depthMap();
  loadPixels();
  for (int y=0; y<kinect.depthHeight(); y++){
    for (int x=0; x<kinect.depthWidth(); x++){
      int d = depthMap[x + y*width];
      if (0 < d && d < 1000 ) {
        pixels[x + y*width] = color(255, 0, 0); // ピクセル値に赤色を設定
      }
    }
  }
  updatePixels();
}

距離計測(面)2

Kinectから1m未満のエリアを、RGBカメラで取得したカラー画像にします。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableDepth(); // 深度画像の有効化
  kinect.enableRGB(); // カラー画像の有効化
  kinect.alternativeViewPointDepthToImage(); // 視点の位置合わせ
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  image(kinect.depthImage(), 0, 0); // 描画
  
  // 1000mm未満にカラー画像の色を付ける
  int[] depthMap = kinect.depthMap();
  PImage rgbImage = kinect.rgbImage();
  loadPixels();
  for (int y=0; y<kinect.depthHeight(); y++){
    for (int x=0; x<kinect.depthWidth(); x++){
      int d = depthMap[x + y*width];
      if (0 < d && d < 1000 ) {
        pixels[x + y*width] = rgbImage.pixels[x + y*width]; // ピクセル値をカラー画像に
      }
    }
  }
  updatePixels();
}

距離計測(面)3

距離に応じて色をグラデーションさせます。


import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup() {
  size(640, 480); // 画面サイズの設定
  kinect = new SimpleOpenNI(this); // 初期化
  kinect.enableDepth(); // 深度画像の有効化
  colorMode(HSB, 360, 100, 100); // 色の設定をHSB色空間にする
}

void draw() {  
  background(0); // 背景の初期化
  kinect.update(); // データの更新
  
  // 距離に応じてグラデーション
  int[] depthMap = kinect.depthMap();
  loadPixels();
  for (int y=0; y<kinect.depthHeight(); y++){
    for (int x=0; x<kinect.depthWidth(); x++){
      int d = depthMap[x + y*width];
      pixels[x + y*width] = color(d/3500.0*360, 70, 100);
    }
  }
  updatePixels();
}

骨格の認識とトラッキング

人の骨格をトラッキング!右手を上げると円が表示されます。


import SimpleOpenNI.*;  // simple-openni

SimpleOpenNI kinect;

void setup() {
  kinect = new SimpleOpenNI(this);
  kinect.setMirror(true);                            // ミラー反転を有効化
  kinect.enableDepth();                              // 深度画像を有効化
  kinect.enableRGB();                                // カラー画像を有効化
  kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);  // ユーザトラッキングを有効化
  kinect.alternativeViewPointDepthToImage();         // 視点の位置合わせ
  size(kinect.rgbWidth(), kinect.rgbHeight());       // 画面サイズの設定
}

void draw() {
  kinect.update();                 // Kinectのデータの更新
  image(kinect.rgbImage(), 0, 0);  // カラー画像の描画

  //ユーザごとに骨格のトラッキングができていたら骨格を描画
  for (int userId = 1; userId <= kinect.getNumberOfUsers(); userId++) {
    if( kinect.isTrackingSkeleton(userId) ) {
      strokeWeight(10);       // 線の太さの設定
      stroke(255,0,0);        // 線の色の設定
      drawSkeleton(userId);   // 骨格の描画
      detectGesture(userId);  // ジェスチャ認識
    }
  }
}

// 新しいユーザを見つけた場合の処理
void onNewUser(int userId) {
  kinect.requestCalibrationSkeleton(userId, true);  // 骨格キャリブレーションのリクエスト
}

// 骨格キャリブレーション終了時の処理
void onEndCalibration(int userId, boolean successfull) {
  if (successfull) { 
    kinect.startTrackingSkeleton(userId);  // 骨格トラッキングの開始
  }
}

// 骨格の描画
void drawSkeleton(int userId) {
  // 関節間を結ぶ直線を描画
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_HEAD,           SimpleOpenNI.SKEL_NECK);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_NECK,           SimpleOpenNI.SKEL_LEFT_SHOULDER);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER,  SimpleOpenNI.SKEL_LEFT_ELBOW); 
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_ELBOW,     SimpleOpenNI.SKEL_LEFT_HAND);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_NECK,           SimpleOpenNI.SKEL_RIGHT_SHOULDER);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_RIGHT_ELBOW);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW,    SimpleOpenNI.SKEL_RIGHT_HAND);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER,  SimpleOpenNI.SKEL_TORSO);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_TORSO);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_TORSO,          SimpleOpenNI.SKEL_LEFT_HIP);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_HIP,       SimpleOpenNI.SKEL_LEFT_KNEE);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_KNEE,      SimpleOpenNI.SKEL_LEFT_FOOT);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_TORSO,          SimpleOpenNI.SKEL_RIGHT_HIP);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_HIP,      SimpleOpenNI.SKEL_RIGHT_KNEE);
  kinect.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_KNEE,     SimpleOpenNI.SKEL_RIGHT_FOOT);  
}

// ジェスチャ認識
void detectGesture(int userId) {
  // 右手の3次元位置を取得する
  PVector hand3d = new PVector(); // 3次元位置
  kinect.getJointPositionSkeleton(userId, SimpleOpenNI.SKEL_RIGHT_HAND, hand3d);

  // 右手の2次元位置を取得する
  PVector hand2d = new PVector(); // 2次元位置
  kinect.convertRealWorldToProjective(hand3d, hand2d);

  // 右肩の3次元位置を取得する
  PVector shoulder3d = new PVector();
  kinect.getJointPositionSkeleton(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, shoulder3d);

  // 右手が右肩よりも高い位置にあるとき,手の2次元位置に円を表示する
  if ( hand3d.y - shoulder3d.y > 0 ) {
    ellipse( hand2d.x, hand2d.y, 50, 50 );
  }
}