前回で
- Kinectの準備 (OpenNI編) で、OpenNIの設定
- Kinect + XNAの準備 で、XNAでKinectを使う設定
を、終えました。
今回からは、実際にKinectを用いて骨格情報を取得してみたいと思います。
●ソースプログラム
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using Microsoft.Xna.Framework;
5: using Microsoft.Xna.Framework.Audio;
6: using Microsoft.Xna.Framework.Content;
7: using Microsoft.Xna.Framework.GamerServices;
8: using Microsoft.Xna.Framework.Graphics;
9: using Microsoft.Xna.Framework.Input;
10: using Microsoft.Xna.Framework.Media;
11: using xn; //OpenNIの読み込み
12:
13: namespace FirstKinect{
14: public class Game1 : Microsoft.Xna.Framework.Game{
15: GraphicsDeviceManager graphics;
16: SpriteBatch spriteBatch;
17:
18: #region Kinect必要変数宣言
19: private Context context;
20: private DepthGenerator depthGenerator; // 深度 深さ Generator
21: private UserGenerator userGenerator; // ユーザ Generator
22: private PoseDetectionCapability poseDetectionCapability; // キャリブレーションの認識
23: private SkeletonCapability skeletonCapability; // 骨格
24: private string calibPose; // // キャリブレーションポーズの情報
25: private Dictionary<uint, Dictionary<SkeletonJoint, SkeletonJointPosition>> joints; // ジョイント情報
26: private SkeletonJointPosition[] SkeletonJointData = new SkeletonJointPosition[16]; //骨格情報
27: #endregion
28:
29: #region Game1
30: public Game1(){
31: graphics = new GraphicsDeviceManager(this);
32: Content.RootDirectory = "Content";
33:
34: #region 初期化と設定
35: context = new Context(@"C:\Program Files (x86)\OpenNI\Data\SamplesConfig.xml");
36: depthGenerator = context.FindExistingNode(NodeType.Depth) as DepthGenerator;
37:
38:
39: //UserGenerator設定
40: userGenerator = new UserGenerator(context);
41: userGenerator.NewUser +=
42: new UserGenerator.NewUserHandler(userGenerator_NewUser);
43: userGenerator.LostUser +=
44: new UserGenerator.LostUserHandler(userGenerator_LostUser);
45:
46: //PoseDetectionCapability設定
47: poseDetectionCapability = new PoseDetectionCapability(userGenerator);
48: poseDetectionCapability.PoseDetected +=
49: new PoseDetectionCapability.PoseDetectedHandler
50: (poseDetectionCapability_PoseDetected);
51:
52: //SkeletonCapability設定
53: skeletonCapability = new SkeletonCapability(userGenerator);
54: skeletonCapability.CalibrationEnd +=
55: new SkeletonCapability.CalibrationEndHandler(skeletonCapbility_CalibrationEnd);
56: skeletonCapability.SetSkeletonProfile(SkeletonProfile.All);
57:
58: calibPose = skeletonCapability.GetCalibrationPose();
59:
60: //ジョイント設定
61: joints = new Dictionary<uint, Dictionary<SkeletonJoint, SkeletonJointPosition>>();
62:
63: //UserGeneratorの開始
64: userGenerator.StartGenerating();
65: #endregion
66: }
67: #endregion
68:
69: #region 初期化
70: protected override void Initialize(){
71: base.Initialize();
72: }
73: #endregion
74:
75: #region 読み込み処理
76: protected override void LoadContent(){
77: spriteBatch = new SpriteBatch(GraphicsDevice);
78: }
79: #endregion
80:
81: #region 破棄処理
82: protected override void UnloadContent(){
83: }
84: #endregion
85:
86: #region 描画以外の処理 毎フレーム実行
87: protected override void Update(GameTime gameTime)
88: {
89:
90: #region ゲームパットでの終了処理
91: if(GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed){
92: this.Exit();
93: }
94: #endregion
95:
96: #region Kinectの値を取得
97:
98: try{
99: context.WaitOneUpdateAll(depthGenerator); // 深度の更新を待つ
100: }catch (Exception){
101: }
102:
103: uint[] users = userGenerator.GetUsers(); // 検出ユーザ数の取得
104: Console.WriteLine("検出人数:"+users.Length);
105:
106: foreach (uint user in users){
107: if (skeletonCapability.IsCalibrated(user)){ //トラッキング出来るかどうか
108: GetJoints(user); //ジョイント情報を取得
109: Console.WriteLine("頭:"+SkeletonJointData[0].position.X + ","+
110: SkeletonJointData[0].position.Y + ","+
111: SkeletonJointData[0].position.Z );
112: }
113: }
114: #endregion
115:
116: base.Update(gameTime);
117: }
118: #endregion
119:
120: #region 描写処理 毎フレーム実行
121: protected override void Draw(GameTime gameTime){
122: GraphicsDevice.Clear(Color.CornflowerBlue);
123: base.Draw(gameTime);
124: }
125: #endregion
126:
127: #region Kinect処理関係
128: // キャリブレーションが完了しトラッキングするかどうか
129: #region skeletonCapbility_CalibrationEnd
130: void skeletonCapbility_CalibrationEnd(ProductionNode node, uint id, bool success){
131: if (success){
132: skeletonCapability.StartTracking(id);
133: joints.Add(id, new Dictionary<SkeletonJoint, SkeletonJointPosition>());
134: }else{
135: this.poseDetectionCapability.StartPoseDetection(calibPose, id);
136: }
137: }
138: #endregion
139:
140: // キャリブレーションの認識
141: #region poseDetectionCapability_PoseDetected
142: void poseDetectionCapability_PoseDetected(ProductionNode node, string pose, uint id){
143: poseDetectionCapability.StopPoseDetection(id);
144: skeletonCapability.RequestCalibration(id, true);
145: }
146: #endregion
147:
148: // 新しいユーザの検出
149: #region userGenerator_NewUser
150: void userGenerator_NewUser(ProductionNode node, uint id){
151: poseDetectionCapability.StartPoseDetection(this.calibPose, id);
152: }
153: #endregion
154:
155: // ユーザの消滅(ロスト)
156: #region userGenerator_LostUser
157: void userGenerator_LostUser(ProductionNode node, uint id){
158: joints.Remove(id);
159: }
160: #endregion
161:
162: #region GetJonts 骨格情報の取得
163: private void GetJoints(uint user){
164: GetJoint(user, SkeletonJoint.Head, 0); //【頭】
165: GetJoint(user, SkeletonJoint.Neck, 1); //【首】
166: GetJoint(user, SkeletonJoint.LeftShoulder, 2); //【左肩】
167: GetJoint(user, SkeletonJoint.LeftElbow, 3); //【左ひじ】
168: GetJoint(user, SkeletonJoint.LeftHand, 4); //【左手】
169: GetJoint(user, SkeletonJoint.RightShoulder, 5); //【右肩】
170: GetJoint(user, SkeletonJoint.RightElbow, 6); //【右ひじ】
171: GetJoint(user, SkeletonJoint.RightHand, 7); //【右手】
172: GetJoint(user, SkeletonJoint.Torso, 8); //【胴】
173: GetJoint(user, SkeletonJoint.LeftHip, 9); //【左尻】
174: GetJoint(user, SkeletonJoint.LeftKnee, 10); //【左膝】
175: GetJoint(user, SkeletonJoint.LeftFoot, 11); //【左足首】
176: GetJoint(user, SkeletonJoint.RightHip, 12); //【右尻】
177: GetJoint(user, SkeletonJoint.RightKnee, 13); //【右膝】
178: GetJoint(user, SkeletonJoint.RightFoot, 14); //【右足首】
179:
180: if (SkeletonJointData[9].fConfidence != 0 && SkeletonJointData[12].fConfidence != 0){ //【下腹部】
181: #region 下腹部を計算により求める。
182: SkeletonJointData[15] = SkeletonJointData[9];
183: SkeletonJointData[15].position.X = 0.5f * (SkeletonJointData[9].position.X + SkeletonJointData[12].position.X);
184: SkeletonJointData[15].position.Y = 0.5f * (SkeletonJointData[9].position.Y + SkeletonJointData[12].position.Y);
185: SkeletonJointData[15].position.Z = 0.5f * (SkeletonJointData[9].position.Z + SkeletonJointData[12].position.Z);
186: #endregion
187: }
188: }
189: #endregion
190:
191: #region GetJont 各ジョイントの取得
192: private void GetJoint(uint user, SkeletonJoint joint, int k){
193: SkeletonJointPosition pos = new SkeletonJointPosition();
194: this.skeletonCapability.GetSkeletonJointPosition(user, joint, ref pos);
195: if (pos.position.Z == 0){
196: pos.fConfidence = 0;
197: }else{
198: pos.position = this.depthGenerator.ConvertRealWorldToProjective(pos.position);
199: }
200: this.joints[user][joint] = pos;
201: SkeletonJointData[k] = pos; //pos.fConfidenceも含めて保存される
202: }
203: #endregion
204:
205: #endregion
206:
207: }
208: }
次のプロプラムは、骨格情報を検出できると、検出できた人数と、頭の座標(X,Y,Z)座標をコンソールに返します。
●実行結果

骨格情報を取得検出できると、頭の座標データが送られてきます。
画面にはカメラの映像も何も表示されないので、OpenNIのサンプルを動かし、骨格情報が検出できるカメラの位置、人の立ち位置を確認した状態でプログラムを動かすようにしてください。
●解説
解説するほどではありませんが、骨格情報が取得できるとSkeletonJointPosition配列に三次元の座標が格納されます。SkeletonJointPositionの配列番号と関節の関係を次の図に示します。

