ProcessingでKinect - スケルトンをトラッキングしてジョイント位置を描画する
ソースコード
ちょっと汚いコードだけど…
import processing.core.*; import SimpleOpenNI.*; import java.util.ArrayList; @SuppressWarnings("serial") public class SkeltonTracker extends PApplet { private class Joint { int type; String name; Joint(int type, String name) { this.type = type; this.name = name; } } private ArrayList<Joint> joints; private SimpleOpenNI context; public void setup() { // initialize openni properties context = new SimpleOpenNI(this); context.setMirror(false); if (context.enableRGB() == false) { println("cannot enable rgb map!!"); exit(); } if (context.enableDepth() == false) { println("cannot enable depth map!!"); exit(); } if (context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL) == false) { println("cannot enable user skelton!!"); exit(); } // align depth data to image data context.alternativeViewPointDepthToImage(); size(context.depthWidth() + 10 + context.rgbWidth(), context.depthHeight()); background(200, 0, 0); // initialize joint list initJointList(); } public void draw() { // カメラ情報を更新 context.update(); // Depth映像を描画 image(context.depthImage(), 0, 0); // Depth映像にジョイント位置を重畳 int[] users = context.getUsers(); for (int user: users) { if (context.isTrackingSkeleton(user)) { for (Joint joint: joints) { drawJointPoint(user, joint.type, joint.name); } } } // Camera映像を描画 image(context.rgbImage(),context.depthWidth() + 10,0); } // ジョイント位置を描画 private void drawJointPoint(int userId, int jointType, String name) { PVector worldPosition = new PVector(); PVector projPosition = new PVector(); context.getJointPositionSkeleton(userId, jointType, worldPosition); context.convertRealWorldToProjective(worldPosition, projPosition); if (userId == 1) fill(255, 0, 0); else if (userId == 2) fill(0, 255, 0); else fill(0, 0, 255); ellipse(projPosition.x, projPosition.y, 10, 10); text(name, projPosition.x+10, projPosition.y+10); } // ジョイントリストを初期化 private void initJointList() { joints = new ArrayList<Joint>(); joints.add(new Joint(SimpleOpenNI.SKEL_HEAD, "HEAD")); joints.add(new Joint(SimpleOpenNI.SKEL_NECK, "NECK")); joints.add(new Joint(SimpleOpenNI.SKEL_TORSO, "TORSO")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_ELBOW, "LEFT ELBOW")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_ELBOW, "RIGHT ELBOW")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_HIP, "LEFT HIP")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_HIP, "RIGHT HIP")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_KNEE, "LEFT KNEE")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_KNEE, "RIGHT KNEE")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_HAND, "LEFT HAND")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_HAND, "RIGHT HAND")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_SHOULDER, "LEFT SHOULDER")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_SHOULDER, "RIGHT SHOULDER")); joints.add(new Joint(SimpleOpenNI.SKEL_LEFT_FOOT, "LEFT FOOT")); joints.add(new Joint(SimpleOpenNI.SKEL_RIGHT_FOOT, "RIGHT FOOT")); } // 新たにユーザーを検出した時のコールバック public void onNewUser(int userId) { println("onNewUser - userId:" + userId); // キャリブレーションを開始する // (NITE 1.5からキャリブレーションポーズは不要) context.requestCalibrationSkeleton(userId, true); //context.startPoseDetection("Psi", userId); } // ユーザーの消失を検出した時のコールバック public void onLostUser(int userId) { println("onLostUser - userId:" + userId); } // キャリブレーションを開始した時のコールバック public void onStartCalibration(int userId) { println("onStartCalibration - userId:" + userId); } // キャリブレーションが終了した時のコールバック public void onEndCalibration(int userId, boolean bSuccess) { println("onEndCalibration - userId:" + userId); if (bSuccess) { // トラッキングを開始する println(" => success to calibrate"); context.startTrackingSkeleton(userId); } else { // 失敗。ポーズ検出からやり直す println(" => faied to calibrate"); context.startPoseDetection("Psi", userId); } } /* // ポーズを検出した時のコールバック public void onStartPose(String pose, int userId) { println("onStartPose - userId:" + userId); // ポーズ検出を中止してキャリブレーションを開始 context.stopPoseDetection(userId); context.requestCalibrationSkeleton(userId, true); } // ポーズを消失した時のコールバック public void onEndPose(String pose, int userId) { println("onEndPose - userId:" + userId); } */ }
実行結果
ハマりポイント
- context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL)を呼んだ状態でcontext.rgbImage()を取得するには、context.enableRGB()だけでなくcontext.enableDepth()も必要?
- よくわからないけど、そうしないと動かなかった
- onNewUser()からcontext.startPoseDetection()しなくても、context.requestCalibrationSkeleton(userId, true)を呼べばよい
- NITE 1.5以降ではキャリブレーションポーズは不要。そのためonStartPause()コールバックは呼ばれない