|
|
|
||
【Kinect Sensorを利用して拡張現実センサを作る試み】 |
||
|
【リンクフリー】 私設研究所ネオテックラボ Neo-Tech-Lab.co.uk 【記載者】 東京工業大学 ソリューション研究機構 (特任教授) 上田智章 |
作成日 2011/01/08 更新 2011/04/03 最終更新 2011/05/06 |
ここにチェックボックス型外部コンテンツ・メニューが入ります。 | ||
|
|
|
||
![]() 【図1】試作中の高周波磁界分布可視化拡張現実センサ 【写真2】試作中のワイヤレス高周波磁界・超音波センサ ![]() 【図】JavaScript音声合成エンジン『μ-iVoice』の表示例 |
【目次】 ⇒ 【English Version】【Kinect for Windows SDK Version1導入編】●2012年2月1日、遂にMicrosoft社が正式にKinect for Windows SDKのVersion1の配布を開始しました。どうやら、PC接続専用センサのKinect for Windows向けらしいのですが、筆者が確認したところ、XBOX360用センサをPCで動作させることができました。 Colorイメージ、Depth情報、Skelton(骨格)情報の取得だけでなく、音源方向の検出、音声の.wavファイル形式での録音、音声認識エンジンSpeechの実装、センサの上下角の遠隔操作などもXBOX360用センサで問題なく行うことができました。 英語ですが、各機能の使い方の解説ドキュメントが付いています。目的別に非常に丁寧な文章で解説されています。 ●骨格抽出の簡単なサンプルプログラムを公開しています。/2012/02/16/ Visual C#版 KINECT骨格抽出サンプルプログラム(KINECT for Windows SDK Version1対応) 【OpenNI編】●【Windows7でKinectを使えるようにするには?】●【手順1:ダウンロード】 ●【手順2:デバイスドライバのインストール】 ●【手順3:OpenNIのインストール】 ●【手順4:NITEのインストール】 ●【手順5:Sensorのインストール】 ●【手順6:環境変数の修正】 ●【手順7:OpenNI用xmlファイルの置き換え】 ●【手順8:NITE用xmlファイルの置き換え】 ●【動作確認】 ●【Kinect Sensorの使い方】 ● 【以下の項目書きかけです。】 ●【Kinect Sensorについて】 ●イスラエルのPrime Sense社のミドルウェアNITEとオープンソースOpenNI 【ARを支える他の技術】■『Kinectで拡張現実センサを構成する試み』【C#】■『Google翻訳の音声合成(Text-To-Speech: TTS)API』【JavaScript】 ■『Webで使える音声合成API μ-iVoice』【JavaScript】 変形DDS(Direct Digital Synthesizer)アルゴリズムを用いた音声合成/シンセサイザAPI(Version0.07)です。Google TTSと違ってブラウザ上で音声合成を行いますので文字数の制約はありません。簡易なスクリプトで音声に抑揚(音階、幅、強弱)をつけることができます。iframeでブログなどに埋め込めます。現状ひらがなだけですが、Version0.08で漢字への対応、自動抑揚付与などの機能追加、音質向上等の改良を予定。加えてローカルからオリジナル音声データの組み込みを可能にします。 現状、声域の狭い初音ミク風味(あまり似ていない)の音声データのみですが、今後、オリジナル公開音声データベースを構築する予定です。 |
【WebGLのページ】JavaScriptでリアルタイム3次元グラフィックスが楽しめる時代の到来までもうすぐです。現在、Google Chrome12、Mozilla FireFox5、及び開発版のSafari、Operaで使うことができます。 WebGLは、JavaScriptエンジンv8とDirectXを利用して高速化したOpenGLを融合して構成されたものです。 2011年5月に指摘されたセキュリティー問題はありますが、処理速度の高速性から利便性も高いので、将来的には普及するのではないでしょうか? ●WebGLとは? ●事前準備:利用できるウェブブラウザは? ●事前準備:FireFox5でWebGLが動作しない場合には? ●WebGLのサンプル ●WebGLのデモ ●クロスドメインテクスチャーを将来も利用できる方法 |
【Microsoft Kinect for Windows SDK Version1導入編】 |
|
■■■記載日2012年02月09日■■■21:28頃記載【Microsoft社からKinect for Windows SDK Version1が正式に発表されています】2012年02月01日(米国時間)にMicrosoft社がKinect for Windows SDK Version1の配布を開始しました。リンク先のページに中央下にある『GET STARTED>』というリンクからダウンロードページに遷移します。 現在の所、このSDKはWindows7専用です。Version1はβ版と全く別物になっていますが、わかりやすい構造で、ドキュメントも付いています。 Microsoft社のページによれば、このSDK Version1は、予約受付中のハードウェア『Kinect for Windows』というPC接続専用のKinectセンサ用と記載されていますが、 筆者が試してみたところ、XBOX360用のKinectセンサでもちゃんと使うことができました。 インストールは超簡単です。許諾条件に同意してクリックするだけです。 5分もかからずインストールを完了させることができます。 C#, Visual Basic等の.net系に対応しています。 【センサの上下角を遠隔操作できる電動チルト】C#のデモプログラムで驚いたのは、センサ本体がなんと電動チルトするじゃあありませんか。(笑)XBOX360を持っている人には当たり前なのかもしれませんが、OpenNIやKinect for Windows SDKβでは現れない機能だったので、びっくりしました。 そうかこんな機能が付いていたのかと。 KinectSensorのpropertyに読出し専用のMaxElevationAngle、MinElevationAngleがあり、その範囲の値をElevationAngleにセットするだけで動くようです。 【音声認識】Speech言語は英語に限定されるようですが、Speechという音声認識の機能も使えるようになっています。サンプルプログラムでは、red(赤), green(緑), blue(青)の3色を認識するように記述してありましたが、 Choiceを勝手に追加してみたところ、white, black, pink等の色だけでなく、Japanとかの単語もちゃんと認識させることができました。 これで、start, stop等の音声でセンサを制御することが可能になりそうですね。この機能は期待できます。 いちいち離れたパソコンまでクリックしに戻らなくてもいいですから。これは使える機能ですね。 でも、正しい英語の発音でないと動作しませんけどね。 【音源方向検出機能】内蔵マイクアレイで、音源の方向を検出する機能があり動作します。【感想】XBOX360用Kinectセンサで問題なく、動作するようです。PC専用のKinect for Windowsは予定価格24,800円。対してXBOX360用は15,000円弱。 1万円の差額には謎があります。 Colorイメージ、Depthイメージ、骨格情報抽出だけでなく、電動チルト、音声認識(Speech)、音源方向検出、音声記録などはXBOX360用でも動作しました。 まだ試していないのは、デプスの測定レンジ切り替え機能くらいではないでしょうか? 従来は800mm~4000mmだった測定レンジ(default)に、nearモードが追加されているようです。 範囲は400mm~3000mmだそうです。XBOX360用でこのモードが使えるかどうかはわかりません。 それともPC向けは画面分解能がもっといろいろあるのだろうか? ううん。謎だ。これだけの機能が動作する現状ではPC用を購入するメリットを感じられない。 サンプルプログラムをDirectX向けに書きなおす予定です。少々お待ち下さい。 ![]() |
【Frameworks Ver.4.0でManaged DirectXを使う方法】KINECT for WindowsはVer.4.0以降のランタイムでしか動作しないようで、今まで適当にごまかしていた、Ver.2.0で動かす方法が使えなくなった。 だからと言ってXNAじゃ困るし、使いたくない。 ちょっと苦労したのでメモ。 [1] Visual C#でFormタイプの新規プロジェクトを作成。 [2] プロジェクト(P)⇒新しい項目の追加(W)⇒『アプリケーション構成ファイル』を追加する。 これは、app.configというファイルを追加する手順だが、このファイルを以下のように書き換える。 キーワードがわからず、検索しまくったら海外の掲示板でこの方法を見つけた。 コンパイルの度に3行程『スキームが見つかりません』というようなメッセージが出るようになるが 気にする必要はない。取り敢えず、機嫌よく実行するようになった。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
</startup>
</configuration>
[3] プロジェクト(P)⇒参照の追加(F)で参照タブからC:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0フォルダの Microsoft.DirectX.dll, Microsoft.DirectX.Direct3D.dll, Microsoft.DirectX.Direct3DX.dllを参照。 [4] プロジェクト(P)⇒参照の追加(F)で.NETタブから Microsoft.Kinectを参照。 [5] Unmanagedなこともするので、プロジェクト(P)⇒*******のプロパティーに入って ビルド・オプションのアンセーフコードの許可(F)にチェックを入れる。 [6] DirectXの表示はTopLevel.Contorolでは困るので、
D3Ddevice = PrepareD3dDevice(pictureBox1); //【DirectX初期化】
とか記述して下のようにPictureBoxに描画する。
private Device PrepareD3dDevice(Control i_window)
{
try {
return new Device(0,
DeviceType.Hardware,
i_window.Handle,
fl_base | CreateFlags.HardwareVertexProcessing,
pp);
}
}
![]() " |
【コーディング予定】今後、下記、6種類を作成の予定です。●骨格抽出 :Skelton Tracking ●Cyber Eye :デプス・イメージを音響オブジェクトに変換する代替視覚システム ●Cyber Board :GoogleTTS(音声合成)とKINECTのコラボ ●Cyber Sign :Gesture検出のまねごと ●拡張現実センサ サンプルプログラム ●KinectのColor Imageを使ったマーカー式拡張現実サンプルプログラム ARToolKitの移植ではなく、オリジナル理論で開発中。 既存Hough変換を使わない私オリジナルのBase Transition Ruleを使った方法です。 ![]() 【Kinect for Windows SDK Version1でのC#使い方概要】ドキュメントに英語で記載されていますが、ちょっとだけ、使い方を記載します。英語が読める方は、How TosのKinect Explorer C#のColor Viewer C# How To, Depth Viewer C# How To, Skelton C# How Toに書かれています。 また、Record Audio C# How To, Audio Demo C# How To, Speech C# How Toに使い方が書かれているので有用な情報です。 一読をお勧めいたします。 ■利用可能なKINECTセンサを取得する
//【利用可能なKINECTセンサを取得する】
kinectSensor = (from sensorToCheck in KinectSensor.KinectSensors where sensorToCheck.Status == KinectStatus.Connected select sensorToCheck).FirstOrDefault();
if (kinectSensor == null)
{
MessageBox.Show(
"接続可能なKINECTセンサが見当たりません。",
"KINECT Viewer Error Message",
MessageBoxButtons.OK
);
return;
}
■Colorイメージの取得方法 1)Color Data Streamを許可する Enableで設定しているのが、RGB、640×480画素、毎秒30フレームでカラーイメージを取得する設定です。 RGB、1280×960画素、毎秒12フレームのモードもありますが、関西(60Hz圏)では照明のせいでモアレが発生します。 KinectSensor kinectSensor; kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30); kinectSensor.Start(); kinectSensor.ColorFrameReady += ColorImageReady; //【準備完了でColorImageReadyがcallされる】2)取得するColorデータ byte型でバッファを定義します。 private byte[] pixelData; this.pixelData = new byte[imageFrame.PixelDataLength];キャプチャー準備完了毎に1)で指定したColorImageReadyがcallされるので、上記バッファにデータをコピーします。
private void ColorImageReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame imageFrame = e.OpenColorImageFrame())
{
if (imageFrame != null)
{
imageFrame.CopyPixelDataTo(this.pixelData);
}
else
{
// imageFrame is null because the request did not arrive in time }
}
}
3)Colorデータを描画(render)するWindows Presentation Foundation (WPF)の構造であるWriteableBitmap形式を定義します。 this.outputImage = new WriteableBitmap(imageFrame.Width, imageFrame.Height, 96, 96, PixelFormats.Bgr32, null); this.kinectColorImage.Source = this.outputImage;以下のように指定すれば、ColorImageFrameをWriteableBitmapにコピーして表示を更新することができます。
this.outputImage.WritePixels(new Int32Rect(0, 0, imageFrame.Width, imageFrame.Height),
this.pixelData, imageFrame.Width * Bgr32BytesPerPixel, 0);
■Depthイメージの取得方法 1)Depth Data Streamを許可する 利用可能な分解能設定は次の3つがあります。 Resolution320x240Fps30 Resolution640x480Fps30 Resolution80x60Fps30
KinectSensor kinectSensor;
kinectSensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
kinectSensor.Start();
kinectSensor.DepthFrameReady += new EventHandler
2)取得するDepthデータ深さ(距離)情報を取得する目的のshort型バッファとスクリーン表示用のbyte型バッファを定義します。
private short[] pixelData;
private byte[] depthFrame32;
pixelData = new short[imageFrame.PixelDataLength];
depthFrame32 = new byte[imageFrame.Width * imageFrame.Height * 4];
取得可能状態になると、1)で指定したDepthImageReadyがcallされます。
private void DepthImageReady(object sender, DepthImageFrameReadyEventArgs e)
{
using (DepthImageFrame imageFrame = e.OpenDepthImageFrame())
{
if (imageFrame != null)
{
imageFrame.CopyPixelDataTo(this.pixelData);
}
else
{
// imageFrame is null because the request did not arrive in time
}
}
}
3)Depth(距離)データの形式をスクリーン表示用データに変換する
この変換で、16bitの距離情報に、プレーヤー毎に異なる色を与え、表示用の変換をワンタッチで変換してしまいます。byte[] convertedDepthBits = this.ConvertDepthFrame(this.pixelData, ((KinectSensor)sender).DepthStream);4)Depthデータの描画 WPFのWriteableBitmap形式への変換フォーマットを指定します。
KinectDepthViewer kinectDepthImage;
WriteableBitmap outputImage;
this.outputImage = new WriteableBitmap(imageFrame.Width, imageFrame.Height, 96, 96,
PixelFormats.Bgr32, null);
this.kinectDepthImage.Source = this.outputImage;
次の行の実行でイメージが更新されます。this.outputImage.WritePixels(new Int32Rect(0, 0, imageFrame.Width, imageFrame.Height), this.pixelData, imageFrame.Width * Bgr32BytesPerPixel, 0);■Skeltonデータの取得方法 ColorイメージやDepth情報取得と類似のコードの記述方法が、Skelton C# How Toに記載されています。 ■Audioの記録方法 .wavファイル形式ファイルへの記録方法が、Record Audio C# How Toに記載されています。 ■音声認識エンジンの使い方 Audio Demo C# How ToやSpeech C# How Toにコードの記述方法が書かれています。 |
【Kinect for Windows SDKで骨格抽出を行うサンプルプログラム】Kinect for Windows SDK Version1を使って骨格抽出を行う場合のサンプル・プログラムを例示します。●骨格抽出サンプル(Kinect for Windows SDK Version1版) Formの部分だけを以下に示します。 一からFormコードを記述する場合には、まずMicrosoft.Kinectを参照追加する必要があります。 プロジェクト(P)⇒参照の追加(F)⇒.NETタブ⇒Microsoft.Kinectを選択 で参照を追加することができます。 短時間のやっつけで作成したので細かなところは気にしないでください。 特に、Skeltonのところは動かしただけのめちゃくちゃです。 |
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Kinect; //【参照設定】プロジェクト(P)⇒参照の追加(F)⇒.NETタブ⇒Microsoft.Kinectを選択
using Microsoft.DirectX.Direct3D; //【参照設定】プロジェクト(P)⇒参照の追加(F)⇒参照タブ
//【ディレクトリ】C:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\
//【選択dll】Microsoft.DirectX.dll, Microsoft.DirectX.Direct3D.dll, Microsoft.DirectX.Direct3DX.dll
namespace KinectTest
{
public partial class Form1 : Form
{
private KinectSensor kinectSensor;
private ColorImageFrame imageFrame; //【KINECTのColorImage】
private DepthImageFrame depthFrame; //【KINECTのDepthImage】
private SkeletonFrame skeletonFrame; //【KINECTのSkeletonFrame】
private byte[] pixelData; //【データ受取り用】32bit
private short[] depthData; //【データ受取り用】16bit
private byte[] depthFrame32; //【カラー表示用】
private Skeleton[] skeletonData; //【データ受取り用】
private Bitmap bmp1; //【ColorImage表示用Bitmapオブジェクト】
private Bitmap bmp2; //【DepthImage表示用Bitmapオブジェクト】
private BitmapData data1; //【ColorImage用BitmapData】
private BitmapData data2; //【DepthImage用BitmapData】
private Rectangle rect1; //【ColorImage用枠】
private Rectangle rect2; //【DepthImage用枠】
private Thread processThread; //【MainLoop用Thread】
private bool processRun; //Thread実行フラグ
private int bmsk = DepthImageFrame.PlayerIndexBitmask;
private int bwd = DepthImageFrame.PlayerIndexBitmaskWidth;
private int Lmax; //【測定可能最大距離】
private int Lmin; //【測定可能最短距離】
private Joint[] pos = new Joint[20];
private ColorImagePoint[] pix = new ColorImagePoint[20];
private int flagFlip=1; //【reverse】左右反転フラグ
private Color[] colors = { Color.Red, Color.Blue, Color.ForestGreen, Color.Yellow, Color.Orange, Color.Purple, Color.White };
private Color[] anticolors = { Color.Green, Color.Orange, Color.Red, Color.Purple, Color.Blue, Color.Yellow, Color.Black };
private int ncolors = 6;
//【カラーインデックスデータ】A/Dコンバータの計測値に対応する球体表示色を与える配列
public static Int16 nColors = 256;
public static int[] ColorLookupTable = new int[] {
0x00FFFFFF, 0x00FBFBFB, 0x00F7F7F7, 0x00F3F3F3, 0x00EFEFEF, 0x00EBEBEB, 0x00E7E7E7, 0x00E4E4E4, 0x00E0E0E0, 0x00DCDCDC,
0x00D8D8D8, 0x00D4D4D4, 0x00D0D0D0, 0x00CCCCCC, 0x00C8C8C8, 0x00C4C4C4, 0x00C0C0C0, 0x00BCBCBC, 0x00B8B8B8, 0x00B4B4B4,
0x00B1B1B1, 0x00ADADAD, 0x00A9A9A9, 0x00A5A5A5, 0x00A1A1A1, 0x009D9D9D, 0x00999999, 0x00959595, 0x00919191, 0x008D8D8D,
0x00898989, 0x00858585, 0x00828282, 0x007E7E7E, 0x007A7A7A, 0x00767676, 0x00727272, 0x006E6E6E, 0x006A6A6A, 0x00666666,
0x00626262, 0x005E5E5E, 0x005A5A5A, 0x00565656, 0x00525252, 0x004F4F4F, 0x004B4B4B, 0x00474747, 0x00434343, 0x003F3F3F,
0x003B3B3B, 0x000000FF, 0x000005FF, 0x00000AFF, 0x00000FFF, 0x000014FF, 0x000019FF, 0x00001EFF, 0x000023FF, 0x000028FF,
0x00002DFF, 0x000032FF, 0x000037FF, 0x00003CFF, 0x000041FF, 0x000046FF, 0x00004BFF, 0x000050FF, 0x000055FF, 0x00005AFF,
0x00005FFF, 0x000064FF, 0x000069FF, 0x00006EFF, 0x000073FF, 0x000078FF, 0x00007DFF, 0x000082FF, 0x000087FF, 0x00008CFF,
0x000091FF, 0x000096FF, 0x00009BFF, 0x0000A0FF, 0x0000A5FF, 0x0000AAFF, 0x0000AFFF, 0x0000B4FF, 0x0000B9FF, 0x0000BEFF,
0x0000C3FF, 0x0000C8FF, 0x0000CDFF, 0x0000D2FF, 0x0000D7FF, 0x0000DCFF, 0x0000E1FF, 0x0000E6FF, 0x0000EBFF, 0x0000F0FF,
0x0000F5FF, 0x0000FAFF, 0x0000FFFF, 0x0000FFFA, 0x0000FFF5, 0x0000FFF0, 0x0000FFEB, 0x0000FFE6, 0x0000FFE1, 0x0000FFDC,
0x0000FFD7, 0x0000FFD2, 0x0000FFCD, 0x0000FFC8, 0x0000FFC3, 0x0000FFBE, 0x0000FFB9, 0x0000FFB4, 0x0000FFAF, 0x0000FFAA,
0x0000FFA5, 0x0000FFA0, 0x0000FF9B, 0x0000FF96, 0x0000FF91, 0x0000FF8C, 0x0000FF87, 0x0000FF82, 0x0000FF7D, 0x0000FF78,
0x0000FF73, 0x0000FF6E, 0x0000FF69, 0x0000FF64, 0x0000FF5F, 0x0000FF5A, 0x0000FF55, 0x0000FF50, 0x0000FF4B, 0x0000FF46,
0x0000FF41, 0x0000FF3C, 0x0000FF37, 0x0000FF32, 0x0000FF2D, 0x0000FF28, 0x0000FF23, 0x0000FF1E, 0x0000FF19, 0x0000FF14,
0x0000FF0F, 0x0000FF0A, 0x0000FF05, 0x0000FF00, 0x0005FF00, 0x000AFF00, 0x000FFF00, 0x0014FF00, 0x0019FF00, 0x001EFF00,
0x0023FF00, 0x0028FF00, 0x002DFF00, 0x0032FF00, 0x0037FF00, 0x003CFF00, 0x0041FF00, 0x0046FF00, 0x004BFF00, 0x0050FF00,
0x0055FF00, 0x005AFF00, 0x005FFF00, 0x0064FF00, 0x0069FF00, 0x006EFF00, 0x0073FF00, 0x0078FF00, 0x007DFF00, 0x0082FF00,
0x0087FF00, 0x008CFF00, 0x0091FF00, 0x0096FF00, 0x009BFF00, 0x00A0FF00, 0x00A5FF00, 0x00AAFF00, 0x00AFFF00, 0x00B4FF00,
0x00B9FF00, 0x00BEFF00, 0x00C3FF00, 0x00C8FF00, 0x00CDFF00, 0x00D2FF00, 0x00D7FF00, 0x00DCFF00, 0x00E1FF00, 0x00E6FF00,
0x00EBFF00, 0x00F0FF00, 0x00F5FF00, 0x00FAFF00, 0x00FFFF00, 0x00FFFA00, 0x00FFF500, 0x00FFF000, 0x00FFEB00, 0x00FFE600,
0x00FFE100, 0x00FFDC00, 0x00FFD700, 0x00FFD200, 0x00FFCD00, 0x00FFC800, 0x00FFC300, 0x00FFBE00, 0x00FFB900, 0x00FFB400,
0x00FFAF00, 0x00FFAA00, 0x00FFA500, 0x00FFA000, 0x00FF9B00, 0x00FF9600, 0x00FF9100, 0x00FF8C00, 0x00FF8700, 0x00FF8200,
0x00FF7D00, 0x00FF7800, 0x00FF7300, 0x00FF6E00, 0x00FF6900, 0x00FF6400, 0x00FF5F00, 0x00FF5A00, 0x00FF5500, 0x00FF5000,
0x00FF4B00, 0x00FF4600, 0x00FF4100, 0x00FF3C00, 0x00FF3700, 0x00FF3200, 0x00FF2D00, 0x00FF2800, 0x00FF2300, 0x00FF1E00,
0x00FF1900, 0x00FF1400, 0x00FF0F00, 0x00FF0A00, 0x00FF0500, 0x00FF0000
};
//***************************************************************
//【Form】KINECTの初期化が済んだらThreadを立ち上げる
//***************************************************************
public Form1()
{
InitializeComponent();
//【利用可能なKINECTセンサを取得する】
kinectSensor = (from sensorToCheck in KinectSensor.KinectSensors where sensorToCheck.Status == KinectStatus.Connected select sensorToCheck).FirstOrDefault();
if (kinectSensor == null)
{
MessageBox.Show(
"接続可能なKINECTセンサが見当たりません。",
"KINECT Viewer Error Message",
MessageBoxButtons.OK
);
return;
}
kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30); //【ColorStream許可】
kinectSensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30); //【DepthStream許可】
kinectSensor.SkeletonStream.Enable(); //【SkeltonStream許可】
kinectSensor.Start(); //【KINECT動作開始】
kinectSensor.ColorFrameReady += ColorImageReady; //【準備完了でColorImageReadyがcallされる】
kinectSensor.DepthFrameReady += DepthImageReady; //【準備完了でDepthImageReadyがcallされる】
kinectSensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(KinectAllFramesReady);
pixelData = new byte[kinectSensor.ColorStream.FramePixelDataLength]; //【データ受取り用】32bit
int dfpdl = kinectSensor.DepthStream.FramePixelDataLength;
depthData = new short[dfpdl]; //【データ受取り用】16bit
depthFrame32 = new byte[dfpdl * 4]; //【カラー表示用】
skeletonData = new Skeleton[kinectSensor.SkeletonStream.FrameSkeletonArrayLength];
int cw, ch, dw, dh;
cw = kinectSensor.ColorStream.FrameWidth;
ch = kinectSensor.ColorStream.FrameHeight;
dw = kinectSensor.DepthStream.FrameWidth;
dh = kinectSensor.DepthStream.FrameHeight;
bmp1 = new Bitmap(cw, ch,System.Drawing.Imaging.PixelFormat.Format32bppRgb ); //【ColorImage表示用】
bmp2 = new Bitmap(dw, dh,System.Drawing.Imaging.PixelFormat.Format32bppRgb ); //【DepthImage表示用】
rect1 = new Rectangle(0, 0, cw, ch); //【ColorImage用枠】
rect2 = new Rectangle(0, 0, dw, dh); //【DepthImage用枠】
kinectSensor.ElevationAngle = -10; //【KINECTセンサの上下角を-10°に設定】設定範囲±27°
Lmax = kinectSensor.DepthStream.MaxDepth;
Lmin = kinectSensor.DepthStream.MinDepth;
label6.Text = "Max=" + Lmax.ToString() + "mm";
label7.Text = "Min=" + Lmin.ToString() + "mm";
processRun = true; //【Thread実行フラグ】
processThread = new Thread(MainProcessThread); //【Main処理ループのためのThreadを定義】
processThread.Start(); //【Thread開始】
}
//***************************************************************
//【Form終了ボタン】KINECTを停止、Thread停止要求を出してFormを閉じる
//***************************************************************
private void button1_Click(object sender, EventArgs e) //【終了ボタン】
{
processRun = false; //【Thread実行フラグ】
kinectSensor.Stop(); //【KINECTセンサを終了】
kinectSensor = null; //【KINECTセンサのオブジェクトを解放する】
this.Close(); //【Formを閉じる】
}
//***************************************************************
//【上下角変更ボタン】KINECTの上下角を変更。±27°の範囲内。時間を置かないとエラーが発生する
//***************************************************************
private void button2_Click(object sender, EventArgs e) //【上下角変更ボタン】
{
Int32 angle = int.Parse(textBox1.Text); //【設定角度を読み込む】±27°
if (angle < kinectSensor.MaxElevationAngle && angle > kinectSensor.MinElevationAngle)
{
kinectSensor.ElevationAngle = angle; //【上下角の変更】
}
}
//***************************************************************
//【ColorImageの取得イベント】
//***************************************************************
private void ColorImageReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (imageFrame = e.OpenColorImageFrame())
{
if (imageFrame != null)
{
imageFrame.CopyPixelDataTo(pixelData); //【KINECTのColor画像を配列pixelDataにコピーする】
data1 = this.bmp1.LockBits(rect1, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb); //【Lockする】
Marshal.Copy(pixelData, 0, data1.Scan0, pixelData.Length); //【UnmanagedMemoryのBitmapDataに配列pixelDataをコピーする】
bmp1.UnlockBits(data1); //【Lock解除】
if (flagFlip == 1) { bmp1.RotateFlip(RotateFlipType.Rotate180FlipY); } //【左右反転】
pictureBox1.Image = bmp1; //【pictureBox1に画像表示】
Graphics g = Graphics.FromImage(pictureBox1.Image);
SolidBrush newbrush = new SolidBrush(Color.Red);
for (int i = 0; i < 20; i++)
{
g.FillEllipse(newbrush, (float)pos[i].Position.X - 5f, (float)pos[i].Position.Y - 5f, 10f, 10f);
}
//【頭】【肩中央】【背骨】【尻中央】【左肩】【左肘】【左手首】【左手】【右肩】【右肘】【右手首】【右手】
//【左尻】【左膝】【左足首】【左足】【右尻】【右膝】【右足首】【右足】
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[0].X, (int)pix[0].Y), new Point((int)pix[1].X, (int)pix[1].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[1].X, (int)pix[1].Y), new Point((int)pix[2].X, (int)pix[2].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[2].X, (int)pix[2].Y), new Point((int)pix[3].X, (int)pix[3].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[2].X, (int)pix[2].Y), new Point((int)pix[4].X, (int)pix[4].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[2].X, (int)pix[2].Y), new Point((int)pix[8].X, (int)pix[8].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[2].X, (int)pix[2].Y), new Point((int)pix[12].X, (int)pix[12].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[2].X, (int)pix[2].Y), new Point((int)pix[16].X, (int)pix[16].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[12].X, (int)pix[12].Y), new Point((int)pix[16].X, (int)pix[16].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[1].X, (int)pix[1].Y), new Point((int)pix[4].X, (int)pix[4].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[4].X, (int)pix[4].Y), new Point((int)pix[5].X, (int)pix[5].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[5].X, (int)pix[5].Y), new Point((int)pix[6].X, (int)pix[6].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[6].X, (int)pix[6].Y), new Point((int)pix[7].X, (int)pix[7].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[1].X, (int)pix[1].Y), new Point((int)pix[8].X, (int)pix[8].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[8].X, (int)pix[8].Y), new Point((int)pix[9].X, (int)pix[9].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[9].X, (int)pix[9].Y), new Point((int)pix[10].X, (int)pix[10].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[10].X, (int)pix[10].Y), new Point((int)pix[11].X, (int)pix[11].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[3].X, (int)pix[3].Y), new Point((int)pix[12].X, (int)pix[12].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[12].X, (int)pix[12].Y), new Point((int)pix[13].X, (int)pix[13].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[13].X, (int)pix[13].Y), new Point((int)pix[14].X, (int)pix[14].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[14].X, (int)pix[14].Y), new Point((int)pix[15].X, (int)pix[15].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[3].X, (int)pix[3].Y), new Point((int)pix[16].X, (int)pix[16].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[16].X, (int)pix[16].Y), new Point((int)pix[17].X, (int)pix[17].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[17].X, (int)pix[17].Y), new Point((int)pix[18].X, (int)pix[18].Y));
g.DrawLine(new Pen(Color.Red, 3.0f), new Point((int)pix[18].X, (int)pix[18].Y), new Point((int)pix[19].X, (int)pix[19].Y));
g.Dispose();
}
else
{
// imageFrame is null because the request did not arrive in time
}
}
}
//***************************************************************
//【DepthImageの取得イベント】
//***************************************************************
private void DepthImageReady(object sender, DepthImageFrameReadyEventArgs e)
{
using (depthFrame = e.OpenDepthImageFrame())
{
if (depthFrame != null)
{ int i,j,d, playerID;
depthFrame.CopyPixelDataTo(this.depthData);
j=0;
for(i=0; i<320*240; i++)
{
//kinectSensor.DepthStreamに以下のフラグがある。
// depthStream.TooNearDepth 【距離が近すぎる】
// depthStream.TooFarDepth 【距離が遠すぎる】
// depthStream.UnknownDepth 【距離が不明】
//【距離情報】
d = depthData[i]; //【上位13bitがDepth値】
playerID = d & bmsk; //【下位3bitはplayerID】
d = ((d >> bwd) & 0x1FFF); //【realDepth】0mm~8191mm
if (d < Lmin) { d = Lmin; } //【近すぎる】
if (d > Lmax) { d = Lmax; } //【遠すぎる】
d = ColorLookupTable[255-(byte)((d - Lmin) * 0.079688)]; //【256色に変換】//800mm~4000mm⇒赤~白
depthFrame32[j] = (byte)(d & 255); //【青】B
depthFrame32[j+1] = (byte)((d>>8) & 255); //【緑】G
depthFrame32[j+2] = (byte)((d>>16) & 255); //【赤】R
depthFrame32[j+3] = 0;
j=j+4;
}
data2 = this.bmp2.LockBits(rect2, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb); //【Lockする】
Marshal.Copy(depthFrame32, 0, data2.Scan0, depthFrame32.Length); //【UnmanagedMemoryのBitmapDataに配列depthDataをコピーする】
bmp2.UnlockBits(data2); //【Lock解除】
if (flagFlip == 1) { bmp2.RotateFlip(RotateFlipType.Rotate180FlipY); } //【左右反転】
pictureBox2.Image = bmp2; //【pictureBox1に画像表示】
}
else
{
// imageFrame is null because the request did not arrive in time
}
}
}
//***************************************************************
//【SkeletonFrameの取得イベント】SkeletonFrameは6名分。各々がJointsコレクションを持つ。
//***************************************************************
private void KinectAllFramesReady(object sender, AllFramesReadyEventArgs e)
{
using (skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame != null)
{
int i;
skeletonFrame.CopySkeletonDataTo(skeletonData);
for(i=0; i< skeletonData.Length; i++)
{
if (skeletonData[i].TrackingState == SkeletonTrackingState.Tracked)
{
label8.Text = "Tracking No.=" + i.ToString();
pos[0].Position = skeletonData[i].Joints[JointType.Head].Position; //【頭】
pos[1].Position = skeletonData[i].Joints[JointType.ShoulderCenter].Position; //【肩中央】
pos[2].Position = skeletonData[i].Joints[JointType.Spine].Position; //【背骨】
pos[3].Position = skeletonData[i].Joints[JointType.HipCenter].Position; //【尻中央】
pos[4].Position = skeletonData[i].Joints[JointType.ShoulderLeft].Position; //【左肩】
pos[5].Position = skeletonData[i].Joints[JointType.ElbowLeft].Position; //【左肘】
pos[6].Position = skeletonData[i].Joints[JointType.WristLeft].Position; //【左手首】
pos[7].Position = skeletonData[i].Joints[JointType.HandLeft].Position; //【左手】
pos[8].Position = skeletonData[i].Joints[JointType.ShoulderRight].Position; //【右肩】
pos[9].Position = skeletonData[i].Joints[JointType.ElbowRight].Position; //【右肘】
pos[10].Position = skeletonData[i].Joints[JointType.WristRight].Position; //【右手首】
pos[11].Position = skeletonData[i].Joints[JointType.HandRight].Position; //【右手】
pos[12].Position = skeletonData[i].Joints[JointType.HipLeft].Position; //【左尻】
pos[13].Position = skeletonData[i].Joints[JointType.KneeLeft].Position; //【左膝】
pos[14].Position = skeletonData[i].Joints[JointType.AnkleLeft].Position; //【左足首】
pos[15].Position = skeletonData[i].Joints[JointType.FootLeft].Position; //【左足】
pos[16].Position = skeletonData[i].Joints[JointType.HipRight].Position; //【右尻】
pos[17].Position = skeletonData[i].Joints[JointType.KneeRight].Position; //【右膝】
pos[18].Position = skeletonData[i].Joints[JointType.AnkleRight].Position; //【右足首】
pos[19].Position = skeletonData[i].Joints[JointType.FootRight].Position; //【右足】
break;
}
}
if(i==7) {label8.Text = "Lost";}
float Xmin=(+9.0f); //ここのXmin,Xmax,Ymin,Ymaxは不必要です。
float Xmax=(-9.0f); //ちょっと訳がわからなかったので調べるのに使った残骸です。
float Ymin=(+9.0f);
float Ymax=(-9.0f);
for (i = 0; i < 20; i++)
{
pix[i]=kinectSensor.MapSkeletonPointToColor(pos[i].Position, ColorImageFormat.RgbResolution640x480Fps30);
if (flagFlip == 1) { pix[i].X = 640 - pix[i].X; }
if (pos[i].Position.X > Xmax) { Xmax = pos[i].Position.X; }
if (pos[i].Position.X < Xmin) { Xmin = pos[i].Position.X; }
if (pos[i].Position.Y > Ymax) { Ymax = pos[i].Position.Y; }
if (pos[i].Position.Y < Ymin) { Ymin = pos[i].Position.Y; }
}
label9.Text = "Xmin=" + Xmin.ToString();
label10.Text = "Xmax=" + Xmax.ToString();
label11.Text = "Ymin=" + Ymin.ToString();
label12.Text = "Ymax=" + Ymax.ToString();
}
else
{
// skeletonFrame is null because the request did not arrive in time
}
}
}
//***************************************************************
//【拡張現実センサ用のThread】
//***************************************************************
private unsafe void MainProcessThread()
{
while (processRun)
{
}
}
}
}
|
|
![]() |
|
【OpenNI導入編】 |
|
|
■Microsoft社XBOX用モーションキャプチャーセンサKinect |
【Windows7でKinectを使えるようにするには?】4月中旬に私設研究所Neo-Tech-Lab.comのノートパソコンを殆どWindows7に更新したのだが、困った事が発生してしまった。OpenNIを導入しようとするのだが失敗してしまい、唯一3月までに導入していたPCだけが動作する状態なのだ。それもそのはず、4月に入ってOpenNIがバージョンアップしているではないか。そこで試行錯誤の末、現状十分とは言えないがKinectが動作する導入条件を何とか見つけることができたので記載する。 【手順1:ダウンロード】以下の3つのダウンロードを行う。 ■2011年6月8日修正記載。Windows7(OS64bit版)で動作を確認。1-1.Kinect用デバイスドライバー(PrimeSensor Modules for OpenNI)をここ(github Avin2)からダウンロードする。少し見辛いが、青い字で8f199caと書かれたリンクをクリックする。 他のリンクをクリックすると違うファイルになるので注意の事。 avin2 pushed to master at avin2/SensorKinect April 16, 2011 [Tree: 8f199ca] ダウンローダーの負荷の関係でマウスカーソルがぐるぐる回っている間はダウンロードできない。30秒以上待たされる事も珍しくないので、気長に待とう。ダウンローダーのダウンロード準備が整うとDownloadsボタンの上にマウスカーソルを持っていくと青いボタンに変わる。 青いDownloadsボタンをクリックした後で表示されるダイアログボックスの『Download.zip』ボタンでzip圧縮ファイル『avin2-SensorKinect-8f199ca.zip』をダウンロード。(解凍すると17.6MB) ![]() ![]() 1-2.OpneNI.orgのダウンロードページから『OpenNI Binaries』の最新暫定(Latest Unstable)版の『OpenNI Unstable Build for Windows x86 (32-bit) v1.1.0.41 Development Edition』をダウンロードする。 たとえ、あなたのPCがWindows7の64bit版OSであったとしても32bit版をダウンロードする必要がある。現状、上記のavin2からダウンロードしたファイルに同梱のSensorモジュールがエラーを発生するからだ。 インストーラ『OpenNI-Win32-1.1.0.41-Dev.msi』 アップロード日付:2011年4月18日 ファイルサイズ:14 MB ![]() 1-3.OpneNI.orgのダウンロードページから『OpenNI Compliant Middleware Binaries』の最新暫定(Latest Unstable)版の『PrimeSense NITE Unstable Build for Windows x86 (32-bit) v1.3.1.5 Development Edition』をダウンロードする。 たとえ、あなたのPCがWindows7の64bit版OSであったとしても32bit版をダウンロードする必要がある。現状、上記のavin2からダウンロードしたファイルに同梱のSensorモジュールがエラーを発生するからだ。 インストーラ『NITE-Win32-1.3.1.5-Dev.msi』 アップロード日付:2011年4月18日 ファイルサイズ:37 MB ![]() 1-4. ステップ1-1でダウンロードしたzipファイルは解凍する。(中身のフォルダavin2-SensorKinect-8f199caごとデスクトップにでもコピーする) 【手順2:デバイスドライバのインストール】2-1.Kinect Sensorを電源ONの状態でUSBケーブルをPCに接続。直後にWindows7がデバイスインストールを試みるが確実に失敗するはず。(これで動作は正常。)2-2.『コンピュータ』⇒『システムのプロパティ』⇒『デバイスマネージャー』で状態を確認すると、ほかのデバイス欄にXbox NUI Motorと表示されているはずだ。 2-3.Windows7の64bitOSの場合は、ダウンロードした『avin2-SensorKinect-8f199ca.zip』を解凍すると、『avin2-SensorKinect-8f199ca』-『Platform』-『Win32』-『Driver』フォルダ内に『dpinst-amd64.exe』があるので実行する。これはWindows7にKinect Sensorデバイスの自動認識をさせるための設定プログラムだ。 ちなみにWindows7の32bitOSの場合、あるいはVistaの場合には、『dpinst-x86.exe』を使用すれば良い。 2-4.実行すると、Kinect Audio, Kinect Camera, Kinect Motorが認識される。 (以前のバージョンではAudioは対応されていなかった。) ![]() ![]() ![]() ![]() 【手順3:OpenNIのインストール】3-1.インストーラ『OpenNI-Win32-1.1.0.41-Dev.msi』を使い、デフォルト設定のままでOpenNIをインストールする。残念なことに64bit版では動作させることができなかった。(これは、手順5でインストールするPrimeSensorのインストールプログラムがインストール済みOpenNIを検証する際に、32bit版を仮定しているためにエラーメッセージが出てしまうためだ。ver.1.1.0.39以上のバージョンのOpenNIをインストールするようにエラーが出力される。自分で修正・コンパイルするのは面倒なのでトライしていない。)『C:\Program Files (x86)』に『OpenNI』フォルダが作成される。 ![]() ![]() 【手順4:NITEのインストール】4-1.インストーラ『NITE-Win32-1.3.1.5-Dev.msi』を使い、デフォルト設定のままでPrimeSense社のNITEをインストールする。これも32bit版である。途中、PrimeSenseのライセンス・キーの入力が求められるが、キーは『0KOIk2JeIBYClPWVnMoRKn5cdY4=』である。 (ダウンロードのときに直前のページに記述されている。) 『C:\Program Files (x86)』に『PrimeSense』フォルダが作成され、その下に『NITE』フォルダが作成される。 ![]() ![]() ![]() ![]() 【手順5:Sensorのインストール】5-1.OpenNIを使用するためのPrimeSensorモジュールをインストールする。『avin2-SensorKinect-8f199ca』-『Bin』フォルダに格納されたインストーラ『SensorKinect-Win-OpenSource32-5.0.1.msi』を使う。 『C:\Program Files (x86)\PrimeSense』フォルダの下に『Sensor』フォルダが作成される。 ![]() 【手順6:環境変数の修正】6-1.インストーラのせいだろうと思うが、環境変数OPEN_NI_BINの示すフォルダ名がBinでなければならないところがbinになる不具合があるので修正する。『システムの詳細設定』から『システムのプロパティ』を表示し、『環境変数(N)』ボタンをクリック。表示されるシステム環境変数(S)からOPEN_NI_BINを探し、選択して『編集(I)』ボタンをクリック。修正を行って、OKボタンで入力確定する。 ![]() ![]() ![]() ![]() 【手順7:OpenNI用xmlファイルの置き換え】7-1.デスクトップにメモ帳で下に示すようなテキストファイルを作り、SamplesConfig.xmlと名前を付けて一旦保存し、『C:\Program Files (x86)\OpenNI\Data』フォルダにコピー(上書き)する。既存ファイルを修正せずに上書きするのは、私の所有するWindows7機はどれもセキュリティー設定せいで『C:\Program Files (x86)\』フォルダ内のメモ帳による変更が制限されているためだ。ちなみに私のPCはノートパソコンだったので画面解像度の関係で『xRes="640" yRes="480" FPS="30"』と書いたが、デスクトップなら『xRes="1280" yRes="1024" FPS="15"』でも動作できるようだ。英文掲示板でそのような記述を見かけた。(動作検証は行っていません。未確認です。) 『SamplesConfig.xml』<OpenNI> <Licenses> <License vendor="PrimeSense" key="0KOIk2JeIBYClPWVnMoRKn5cdY4="/> </Licenses> <Log writeToConsole="false" writeToFile="false"> <!-- 0 - Verbose, 1 - Info, 2 - Warning, 3 - Error (default) --> <LogLevel value="3"/> <Masks> <Mask name="ALL" on="true"/> </Masks> <Dumps> </Dumps> </Log> <ProductionNodes> <!-- Normal Image --> <Node type="Image" name="Image1" stopOnError="false"> <Configuration> <Mirror on="true"/> </Configuration> </Node> <!-- HighRes Image --> <!-- <Node type="Image" name="Image1" stopOnError="false"> <Configuration> <MapOutputMode xRes="640" yRes="480" FPS="30"/> <Mirror on="true"/> </Configuration> </Node> --> <!-- Normal IR --> <!-- <Node type="IR" name="IR1"> <Configuration> <MapOutputMode xRes="640" yRes="480" FPS="30"/> <Mirror on="true"/> </Configuration> </Node> --> <!-- HighRes IR --> <!-- <Node type="IR" name="IR1"> <Configuration> <MapOutputMode xRes="640" yRes="480" FPS="30"/> <Mirror on="true"/> </Configuration> </Node> --> <Node type="Depth" name="Depth1"> <Configuration> <Mirror on="true"/> </Configuration> </Node> <!-- <Node type="Audio" name="Audio1"> </Node> --> </ProductionNodes> </OpenNI> 【手順8:NITE用xmlファイルの置き換え】8-1.上記と同様に、デスクトップにメモ帳で下に示すような3つのテキストファイルを作り、Sample-Scene.xml、Sample-Tracking.xml、Sample-User.xmlと名前を付けて一旦保存し、『C:\Program Files (x86)\PrimeSense\NITE\Data』フォルダにコピー(上書き)する。『Sample-Scene.xml』<OpenNI> <Licenses> <License vendor="PrimeSense" key="0KOIk2JeIBYClPWVnMoRKn5cdY4="/> </Licenses> <Log writeToConsole="true" writeToFile="false"> <!-- 0 - Verbose, 1 - Info, 2 - Warning, 3 - Error (default) --> <LogLevel value="3"/> <Masks> <Mask name="ALL" on="false"/> </Masks> <Dumps> </Dumps> </Log> <ProductionNodes> <Node type="Depth"> <Configuration> <Mirror on="true"/> </Configuration> </Node> <Node type="Scene" /> </ProductionNodes> </OpenNI> 『Sample-Tracking.xml』<OpenNI> <Licenses> <License vendor="PrimeSense" key="0KOIk2JeIBYClPWVnMoRKn5cdY4="/> </Licenses> <Log writeToConsole="true" writeToFile="false"> <!-- 0 - Verbose, 1 - Info, 2 - Warning, 3 - Error (default) --> <LogLevel value="3"/> <Masks> <Mask name="ALL" on="false"/> </Masks> <Dumps> </Dumps> </Log> <ProductionNodes> <Node type="Depth"> <Configuration> <Mirror on="true"/> </Configuration> </Node> <Node type="Gesture" /> <Node type="Hands" /> </ProductionNodes> </OpenNI> 『Sample-User.xml』<OpenNI> <Licenses> <License vendor="PrimeSense" key="0KOIk2JeIBYClPWVnMoRKn5cdY4="/> </Licenses> <Log writeToConsole="true" writeToFile="false"> <!-- 0 - Verbose, 1 - Info, 2 - Warning, 3 - Error (default) --> <LogLevel value="3"/> <Masks> <Mask name="ALL" on="false"/> </Masks> <Dumps> </Dumps> </Log> <ProductionNodes> <Node type="Depth"> <Configuration> <Mirror on="true"/> </Configuration> </Node> <Node type="User" /> </ProductionNodes> </OpenNI> 【動作確認】以上でKinectのデモプログラムが動作するはずだ。『c:\Program Files (x86)\OpenNI\Samples\Bin\Release\』フォルダにあるNIUserTracker.exeの実行例を示す。 ユーザー認識の後で、両腕をあげてガッツポーズを取ると骨格が表示される。 ![]() 【OpenNI.Net.dllが激変しているではないか?!】ここまで、やってOpenNIやNITEのデモプログラムは全部動作するようになったはずだが、2011年3月までのOpenNI.net.dllの仕様を知っている人にはショックが待ち構えている。OpenNI.net.dll⇒OpenNI.Net.dllと1文字大文字に変わっている。 あれっと思って、以前のプログラムをコンパイルしようとすると、名前空間『xn』が見当たらないと言う。 『じゃあ、参照取りなおすか。』と思ってしてみると、名前空間はOpenNI.Netに変更されている。 コンパイルしてみると一杯エラーが出てくる?? これは!!......\(-o-)/ 仕様が大変更されているではないか......orz......<(-_-;)> やられた.....定数とか参照とれてないし。また一から調べるとするか......(T_T) まぁ、確かにOpenNIサイトに骨格データが分かれているとか兆候はあったなぁ..... なので、以前に作ったサンプルプログラムを修正しました。【2011年5月15日午後14:14】 |
【注意】 以下のコードは、2011年4月版のOpenNI.Net.dllを使った場合のコードです2011年1月のC# Wapperから見て、かなり構造的な変更が何か所もあったようです。どうやらプロパティーでもメソッドでもなく、Public変数になっている部分とか、型の変更とか... 一から調べなおすのは面倒だったので、コンパイルしてエラー箇所を機嫌が良くなるように修正するという後ろ向きな修正方法で、以前に作成したサンプルプログラムを動くように修正しました。 取り敢えず、handsGeneratorはまだ復旧できていませんが、RGBカメラとかDepthバッファとかは以前のように動かすことができました。handsGenerator関連は外してもらっても問題ありません。 下にサンプルプログラムのソースを掲載しておきます。 【サンプルプログラムのプロジェクトファイル一式(zip形式) 】 |
|
【Kinect Sensorの使い方】C# WapperのOpenNI.net.dllを使えば、Visual C#でKinect Sensorを使うことができます。以下に最もポピュラーなRGBカメラの読出/表示、デプスバッファの読出/表示、ユーザー切り出し、骨格情報の読出しのサンプルプログラムを示します。スクリーンショットのデプスバッファのカラー表示画面で、最も手前にあるはずの左手の横に白い部分がありますが、これはデプスバッファの数値として0が返されてくる部分なのです。対象がカメラに近いほどこの白い領域がずれて目立ちます。Prime Sense社の3次元座標検出方法に強く関係していると考えられますが、詳細は不明です。 3次元座標の取得処理時には赤色レーザーが発光しますので、もしかするとレーザー光線をスキャンして2つのカメラを使った視差を求める方法かもしれません。この場合、片側のカメラに光点がなければ3次元座標(深さz値)を求めることができないので、値が0になるのかもしれません。 ![]() 【図xx】RGBカメラ、デプスバッファのカラー表示、ユーザー切り出し、骨格情報読出し表示の例 ![]() 【図xx】RGBカメラ、デプスバッファのモノクロ表示、ユーザー切り出し、骨格情報読出し表示の例 ![]() 【図xx】近くほど白い領域がずれて目立つ。Prime Sense社の3次元座標検出方法に関係していると考えられる。 |
|
【Program.cs】Kinect SensorをC#で利用する場合のサンプル・プログラム
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace KinectSimpleViewer.net
{
static class Program
{
///
|
|
【Form1.cs】Kinect SensorをC#で利用する場合のサンプル・プログラム
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using OpenNI;
using System.Threading;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace KinectSimpleViewer
{
public partial class Form1 : Form
{
private readonly string SAMPLE_XML_FILE = @"../../Data/SamplesConfig.xml";
private Context context;
private DepthGenerator depth;
private ImageGenerator image;
private UserGenerator userGenerator;
private HandsGenerator handsGenerator;
private SkeletonCapability skeletonCapbility;
private PoseDetectionCapability poseDetectionCapability;
private string calibPose;
private Thread readerThread;
private bool shouldRun;
private Bitmap bitmap;
private Bitmap bitmap2;
private int[] histogram;
private Point3D handsPos;
private Dictionary<int, Dictionary<SkeletonJoint, SkeletonJointPosition>> joints;
private static Int32 nJoints = 16; //【実際には15個しか戻されていない】
private SkeletonJointPosition[] SkeletonJointData = new SkeletonJointPosition[nJoints];
private bool shouldDrawPixels = true; //【画像を更新する】
private bool shouldDrawBackground = true; //【背景を描画する】
private bool shouldPrintID = true; //【UserIDを表示する】
private bool shouldPrintState = true; //【検出状態を表示する】
private bool shouldDrawSkeleton = true; //【骨格を表示する】
private Color[] colors = { Color.Red, Color.Blue, Color.ForestGreen, Color.Yellow, Color.Orange, Color.Purple, Color.White };
private Color[] anticolors = { Color.Green, Color.Orange, Color.Red, Color.Purple, Color.Blue, Color.Yellow, Color.Black };
private int ncolors = 6;
//【カラーインデックスデータ】A/Dコンバータの計測値に対応する球体表示色を与える配列
public static Int16 nColors = 256;
public static int[] ColorLookupTable = new int[] {
0x00FFFFFF, 0x00FBFBFB, 0x00F7F7F7, 0x00F3F3F3, 0x00EFEFEF, 0x00EBEBEB, 0x00E7E7E7, 0x00E4E4E4, 0x00E0E0E0, 0x00DCDCDC,
0x00D8D8D8, 0x00D4D4D4, 0x00D0D0D0, 0x00CCCCCC, 0x00C8C8C8, 0x00C4C4C4, 0x00C0C0C0, 0x00BCBCBC, 0x00B8B8B8, 0x00B4B4B4,
0x00B1B1B1, 0x00ADADAD, 0x00A9A9A9, 0x00A5A5A5, 0x00A1A1A1, 0x009D9D9D, 0x00999999, 0x00959595, 0x00919191, 0x008D8D8D,
0x00898989, 0x00858585, 0x00828282, 0x007E7E7E, 0x007A7A7A, 0x00767676, 0x00727272, 0x006E6E6E, 0x006A6A6A, 0x00666666,
0x00626262, 0x005E5E5E, 0x005A5A5A, 0x00565656, 0x00525252, 0x004F4F4F, 0x004B4B4B, 0x00474747, 0x00434343, 0x003F3F3F,
0x003B3B3B, 0x000000FF, 0x000005FF, 0x00000AFF, 0x00000FFF, 0x000014FF, 0x000019FF, 0x00001EFF, 0x000023FF, 0x000028FF,
0x00002DFF, 0x000032FF, 0x000037FF, 0x00003CFF, 0x000041FF, 0x000046FF, 0x00004BFF, 0x000050FF, 0x000055FF, 0x00005AFF,
0x00005FFF, 0x000064FF, 0x000069FF, 0x00006EFF, 0x000073FF, 0x000078FF, 0x00007DFF, 0x000082FF, 0x000087FF, 0x00008CFF,
0x000091FF, 0x000096FF, 0x00009BFF, 0x0000A0FF, 0x0000A5FF, 0x0000AAFF, 0x0000AFFF, 0x0000B4FF, 0x0000B9FF, 0x0000BEFF,
0x0000C3FF, 0x0000C8FF, 0x0000CDFF, 0x0000D2FF, 0x0000D7FF, 0x0000DCFF, 0x0000E1FF, 0x0000E6FF, 0x0000EBFF, 0x0000F0FF,
0x0000F5FF, 0x0000FAFF, 0x0000FFFF, 0x0000FFFA, 0x0000FFF5, 0x0000FFF0, 0x0000FFEB, 0x0000FFE6, 0x0000FFE1, 0x0000FFDC,
0x0000FFD7, 0x0000FFD2, 0x0000FFCD, 0x0000FFC8, 0x0000FFC3, 0x0000FFBE, 0x0000FFB9, 0x0000FFB4, 0x0000FFAF, 0x0000FFAA,
0x0000FFA5, 0x0000FFA0, 0x0000FF9B, 0x0000FF96, 0x0000FF91, 0x0000FF8C, 0x0000FF87, 0x0000FF82, 0x0000FF7D, 0x0000FF78,
0x0000FF73, 0x0000FF6E, 0x0000FF69, 0x0000FF64, 0x0000FF5F, 0x0000FF5A, 0x0000FF55, 0x0000FF50, 0x0000FF4B, 0x0000FF46,
0x0000FF41, 0x0000FF3C, 0x0000FF37, 0x0000FF32, 0x0000FF2D, 0x0000FF28, 0x0000FF23, 0x0000FF1E, 0x0000FF19, 0x0000FF14,
0x0000FF0F, 0x0000FF0A, 0x0000FF05, 0x0000FF00, 0x0005FF00, 0x000AFF00, 0x000FFF00, 0x0014FF00, 0x0019FF00, 0x001EFF00,
0x0023FF00, 0x0028FF00, 0x002DFF00, 0x0032FF00, 0x0037FF00, 0x003CFF00, 0x0041FF00, 0x0046FF00, 0x004BFF00, 0x0050FF00,
0x0055FF00, 0x005AFF00, 0x005FFF00, 0x0064FF00, 0x0069FF00, 0x006EFF00, 0x0073FF00, 0x0078FF00, 0x007DFF00, 0x0082FF00,
0x0087FF00, 0x008CFF00, 0x0091FF00, 0x0096FF00, 0x009BFF00, 0x00A0FF00, 0x00A5FF00, 0x00AAFF00, 0x00AFFF00, 0x00B4FF00,
0x00B9FF00, 0x00BEFF00, 0x00C3FF00, 0x00C8FF00, 0x00CDFF00, 0x00D2FF00, 0x00D7FF00, 0x00DCFF00, 0x00E1FF00, 0x00E6FF00,
0x00EBFF00, 0x00F0FF00, 0x00F5FF00, 0x00FAFF00, 0x00FFFF00, 0x00FFFA00, 0x00FFF500, 0x00FFF000, 0x00FFEB00, 0x00FFE600,
0x00FFE100, 0x00FFDC00, 0x00FFD700, 0x00FFD200, 0x00FFCD00, 0x00FFC800, 0x00FFC300, 0x00FFBE00, 0x00FFB900, 0x00FFB400,
0x00FFAF00, 0x00FFAA00, 0x00FFA500, 0x00FFA000, 0x00FF9B00, 0x00FF9600, 0x00FF9100, 0x00FF8C00, 0x00FF8700, 0x00FF8200,
0x00FF7D00, 0x00FF7800, 0x00FF7300, 0x00FF6E00, 0x00FF6900, 0x00FF6400, 0x00FF5F00, 0x00FF5A00, 0x00FF5500, 0x00FF5000,
0x00FF4B00, 0x00FF4600, 0x00FF4100, 0x00FF3C00, 0x00FF3700, 0x00FF3200, 0x00FF2D00, 0x00FF2800, 0x00FF2300, 0x00FF1E00,
0x00FF1900, 0x00FF1400, 0x00FF0F00, 0x00FF0A00, 0x00FF0500, 0x00FF0000
};
public Form1()
{
InitializeComponent();
this.context = new Context(SAMPLE_XML_FILE);
this.depth = context.FindExistingNode(NodeType.Depth) as DepthGenerator;
if (this.depth == null)
{
throw new Exception("Viewer must have a depth node!");
}
this.image = context.FindExistingNode(NodeType.Image) as ImageGenerator;
if (this.image == null)
{
throw new Exception("Viewer must have a image node!");
}
this.userGenerator = new UserGenerator(this.context);
this.userGenerator.NewUser += userGenerator_NewUser;
this.userGenerator.LostUser += userGenerator_LostUser;
this.userGenerator.StartGenerating();
this.skeletonCapbility = this.userGenerator.SkeletonCapability;
this.calibPose = this.skeletonCapbility.CalibrationPose;
this.skeletonCapbility.CalibrationEnd += skeletonCapbility_CalibrationEnd;
this.skeletonCapbility.SetSkeletonProfile(SkeletonProfile.All);
this.joints = new Dictionary<int, Dictionary<SkeletonJoint, SkeletonJointPosition>>();
this.handsGenerator = new HandsGenerator(this.context);
this.handsGenerator.StartGenerating();
handsPos.X = 0; handsPos.Y = 0; handsPos.Z = 0;
this.handsGenerator.SetSmoothing(1.3F);
this.poseDetectionCapability = this.userGenerator.PoseDetectionCapability;
this.poseDetectionCapability.PoseDetected += poseDetectionCapability_PoseDetected;
this.histogram = new int[this.depth.DeviceMaxDepth]; //【MaxDepth:10000】
// MapOutputMode mapMode = this.depth.GetMapOutputMode();
MapOutputMode mapMode = this.image.MapOutputMode;
this.bitmap = new Bitmap((int)mapMode.XRes, (int)mapMode.YRes/*, System.Drawing.Imaging.PixelFormat.Format24bppRgb*/);
this.bitmap2 = new Bitmap((int)mapMode.XRes, (int)mapMode.YRes/*, System.Drawing.Imaging.PixelFormat.Format24bppRgb*/);
this.context.GlobalMirror = false; //【実写・非鏡像表示】
this.depth.AlternativeViewpointCapability.SetViewpoint(image); //【骨格位置を実写画像に一致させる】
this.shouldRun = true;
this.readerThread = new Thread(ReaderThread);
this.readerThread.Start();
}
void skeletonCapbility_CalibrationEnd(object sender, CalibrationEndEventArgs e)
{
if (e.Success)
{
this.skeletonCapbility.StartTracking(e.ID);
this.joints.Add(e.ID, new Dictionary<SkeletonJoint, SkeletonJointPosition>());
}
else
{
this.poseDetectionCapability.StartPoseDetection(calibPose, e.ID);
}
}
void poseDetectionCapability_PoseDetected(object sender, PoseDetectedEventArgs e)
{
this.poseDetectionCapability.StopPoseDetection(e.ID);
this.skeletonCapbility.RequestCalibration(e.ID, true);
}
void userGenerator_NewUser(object sender, NewUserEventArgs e)
{
this.poseDetectionCapability.StartPoseDetection(this.calibPose, e.ID);
}
void userGenerator_LostUser(object sender, UserLostEventArgs e)
{
this.joints.Remove(e.ID);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
lock (this)
{
e.Graphics.DrawImage(this.bitmap,
this.panel1.Location.X,
this.panel1.Location.Y,
this.panel1.Size.Width,
this.panel1.Size.Height);
e.Graphics.DrawImage(this.bitmap2,
this.panel2.Location.X,
this.panel2.Location.Y,
this.panel2.Size.Width,
this.panel2.Size.Height);
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//Don't allow the background to paint
}
protected override void OnClosing(CancelEventArgs e)
{
this.shouldRun = false;
this.readerThread.Join();
base.OnClosing(e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
switch (e.KeyChar)
{
case (char)27:
Close();
// exit(1);
break;
case 'b':
this.shouldDrawBackground = !this.shouldDrawBackground;
break;
case 'x':
this.shouldDrawPixels = !this.shouldDrawPixels;
break;
case 's':
this.shouldDrawSkeleton = !this.shouldDrawSkeleton;
break;
case 'i':
this.shouldPrintID = !this.shouldPrintID;
break;
case 'l':
this.shouldPrintState = !this.shouldPrintState;
break;
case '1':
// g_nViewState = DISPLAY_MODE_OVERLAY;
depth.AlternativeViewpointCapability.SetViewpoint(image);
break;
case '2':
// g_nViewState = DISPLAY_MODE_DEPTH;
depth.AlternativeViewpointCapability.ResetViewpoint();
break;
case '3':
// g_nViewState = DISPLAY_MODE_IMAGE;
depth.AlternativeViewpointCapability.ResetViewpoint();
break;
case 'm':
context.GlobalMirror = !context.GlobalMirror;
break;
}
base.OnKeyPress(e);
}
private unsafe void CalcHist(DepthMetaData depthMD)
{
// reset
for (int i = 0; i < this.histogram.Length; ++i) { this.histogram[i] = 0; }
ushort* pDepth = (ushort*)depthMD.DepthMapPtr.ToPointer();
int points = 0;
for (int y = 0; y < depthMD.YRes; ++y)
{
for (int x = 0; x < depthMD.XRes; ++x, ++pDepth)
{
ushort depthVal = *pDepth;
if (depthVal != 0)
{
this.histogram[depthVal]++;
points++;
}
}
}
for (int i = 1; i < this.histogram.Length; i++) { this.histogram[i] += this.histogram[i - 1]; }
if (points > 0)
{
for (int i = 1; i < this.histogram.Length; i++) { this.histogram[i] = (int)(256 * (1.0f - (this.histogram[i] / (float)points))); }
}
}
private void GetJoint(int user, SkeletonJoint joint, int k)
{
SkeletonJointPosition pos = this.skeletonCapbility.GetSkeletonJointPosition(user, joint);
if (pos.Position.Z == 0)
{
pos.Confidence = 0;
}
else
{
pos.Position = this.depth.ConvertRealWorldToProjective(pos.Position);
}
this.joints[user][joint] = pos;
SkeletonJointData[k] = pos; //pos.fConfidenceも含めて保存される
}
private void GetJoints(int user)
{
GetJoint(user, SkeletonJoint.Head, 0); //【頭】
GetJoint(user, SkeletonJoint.Neck, 1); //【首】
GetJoint(user, SkeletonJoint.LeftShoulder, 2); //【左肩】
GetJoint(user, SkeletonJoint.LeftElbow, 3); //【左ひじ】
GetJoint(user, SkeletonJoint.LeftHand, 4); //【左手】
GetJoint(user, SkeletonJoint.RightShoulder, 5); //【右肩】
GetJoint(user, SkeletonJoint.RightElbow, 6); //【右ひじ】
GetJoint(user, SkeletonJoint.RightHand, 7); //【右手】
GetJoint(user, SkeletonJoint.Torso, 8); //【胴】
GetJoint(user, SkeletonJoint.LeftHip, 9); //【左尻】
GetJoint(user, SkeletonJoint.LeftKnee, 10); //【左膝】
GetJoint(user, SkeletonJoint.LeftFoot, 11); //【左足首】
GetJoint(user, SkeletonJoint.RightHip, 12); //【右尻】
GetJoint(user, SkeletonJoint.RightKnee, 13); //【右膝】
GetJoint(user, SkeletonJoint.RightFoot, 14); //【右足首】
if (SkeletonJointData[9].Confidence != 0 && SkeletonJointData[12].Confidence != 0) //【下腹部】
{
Point3D pos = new Point3D();
SkeletonJointData[15] = SkeletonJointData[9];
pos.X = 0.5f * (SkeletonJointData[9].Position.X + SkeletonJointData[12].Position.X);
pos.Y = 0.5f * (SkeletonJointData[9].Position.Y + SkeletonJointData[12].Position.Y);
pos.Z = 0.5f * (SkeletonJointData[9].Position.Z + SkeletonJointData[12].Position.Z);
SkeletonJointData[15].Position = pos;
}
}
private void DrawLine(Graphics g, Color color, Dictionary<SkeletonJoint, SkeletonJointPosition> dict, SkeletonJoint j1, SkeletonJoint j2)
{
Point3D pos1 = dict[j1].Position;
Point3D pos2 = dict[j2].Position;
if (dict[j1].Confidence == 0 || dict[j2].Confidence == 0) return;
if ((int)pos1.X < 0 || (int)pos1.X > 640 || (int)pos1.Y < 0 || (int)pos1.Y > 480) return;
if ((int)pos2.X < 0 || (int)pos2.X > 640 || (int)pos2.Y < 0 || (int)pos2.Y > 480) return;
//【線幅3に設定】
g.DrawLine(new Pen(color, 3.0f), new Point((int)pos1.X, (int)pos1.Y), new Point((int)pos2.X, (int)pos2.Y));
}
private void DrawSkeleton(Graphics g, Color color, int user)
{
GetJoints(user);
Dictionary<SkeletonJoint, SkeletonJointPosition> dict = this.joints[user];
DrawLine(g, color, dict, SkeletonJoint.Head, SkeletonJoint.Neck);
DrawLine(g, color, dict, SkeletonJoint.LeftShoulder, SkeletonJoint.Torso);
DrawLine(g, color, dict, SkeletonJoint.RightShoulder, SkeletonJoint.Torso);
DrawLine(g, color, dict, SkeletonJoint.Neck, SkeletonJoint.LeftShoulder);
DrawLine(g, color, dict, SkeletonJoint.LeftShoulder, SkeletonJoint.LeftElbow);
DrawLine(g, color, dict, SkeletonJoint.LeftElbow, SkeletonJoint.LeftHand);
DrawLine(g, color, dict, SkeletonJoint.Neck, SkeletonJoint.RightShoulder);
DrawLine(g, color, dict, SkeletonJoint.RightShoulder, SkeletonJoint.RightElbow);
DrawLine(g, color, dict, SkeletonJoint.RightElbow, SkeletonJoint.RightHand);
DrawLine(g, color, dict, SkeletonJoint.LeftHip, SkeletonJoint.Torso);
DrawLine(g, color, dict, SkeletonJoint.RightHip, SkeletonJoint.Torso);
DrawLine(g, color, dict, SkeletonJoint.LeftHip, SkeletonJoint.RightHip);
DrawLine(g, color, dict, SkeletonJoint.LeftHip, SkeletonJoint.LeftKnee);
DrawLine(g, color, dict, SkeletonJoint.LeftKnee, SkeletonJoint.LeftFoot);
DrawLine(g, color, dict, SkeletonJoint.RightHip, SkeletonJoint.RightKnee);
DrawLine(g, color, dict, SkeletonJoint.RightKnee, SkeletonJoint.RightFoot);
for (var i = 0; i < nJoints; i++)
{
if (SkeletonJointData[i].Confidence != 0
&& SkeletonJointData[i].Position.X >= 0
&& SkeletonJointData[i].Position.X < 640
&& SkeletonJointData[i].Position.Y >= 0
&& SkeletonJointData[i].Position.Y < 480)
{ //【関節を円で表示】
SolidBrush newbrush = new SolidBrush(Color.Red);
g.FillEllipse(newbrush, SkeletonJointData[i].Position.X - 5f, SkeletonJointData[i].Position.Y - 5f, 10f, 10f);
}
}
}
private unsafe void ReaderThread()
{
DepthMetaData depthMD = new DepthMetaData();
ImageMetaData imageMD = new ImageMetaData();
while (this.shouldRun)
{
try
{
this.context.WaitOneUpdateAll(this.depth);
}
catch (Exception)
{
}
this.depth.GetMetaData(depthMD);
this.image.GetMetaData(imageMD);
CalcHist(depthMD);
lock (this)
{
Rectangle rect = new Rectangle(0, 0, this.bitmap.Width, this.bitmap.Height);
BitmapData data = this.bitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
BitmapData data2 = this.bitmap2.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
if (this.shouldDrawPixels)
{
//【image情報】RGBカメラ画像を左側パネルに表示する
byte* pImage = (byte*)this.image.ImageMapPtr.ToPointer(); //【RGB24bitBitMap】
for (int y = 0; y < imageMD.YRes; ++y)
{
byte* pDest = (byte*)data.Scan0.ToPointer() + y * data.Stride;
for (int x = 0; x < imageMD.XRes; ++x, pImage += 3, pDest += 3)
{
pDest[0] = *(pImage + 2); //【青】
pDest[1] = *(pImage + 1); //【緑】
pDest[2] = *(pImage); //【赤】
}
}
//【depth情報】デプスバッファ情報を右側パネルに表示する
ushort* pDepth = (ushort*)this.depth.DepthMapPtr.ToPointer(); //【depth16bit】
ushort* pLabels = (ushort*)this.userGenerator.GetUserPixels(0).LabelMapPtr.ToPointer();
for (int y = 0; y < depthMD.YRes; ++y)
{
byte* pDest = (byte*)data2.Scan0.ToPointer() + y * data.Stride;
for (int x = 0; x < depthMD.XRes; ++x, ++pDepth, ++pLabels, pDest += 3)
{
pDest[0] = pDest[1] = pDest[2] = 0;
ushort label = *pLabels;
if (this.shouldDrawBackground || *pLabels != 0)
{
if (checkBox1.Checked)
{ //depthに応じて着色表示
byte dL = (byte)(255 - (int)((float)(*pDepth) * 0.0255));
if (*pDepth == 0) { dL = 0; }
Color depthColor = Color.FromArgb(ColorLookupTable[dL]);
pDest[0] = (byte)depthColor.B;
pDest[1] = (byte)depthColor.G;
pDest[2] = (byte)depthColor.R;
}
else
{ //depth bufferにuser表示
Color labelColor = Color.White;
if (label != 0) { labelColor = colors[label % ncolors]; }
byte pixel = (byte)this.histogram[*pDepth];
pDest[0] = (byte)(pixel * (labelColor.B / 256.0));
pDest[1] = (byte)(pixel * (labelColor.G / 256.0));
pDest[2] = (byte)(pixel * (labelColor.R / 256.0));
}
}
}
}
}
this.bitmap.UnlockBits(data);
this.bitmap2.UnlockBits(data2);
//【Kinect Sensorの検出状態と骨格情報の表示】
Graphics g = Graphics.FromImage(this.bitmap);
// this.handsGenerator.
string hand = "Hand";
if (handsPos.Z != 0)
{
g.DrawString(hand, new Font("Arial", 6), new SolidBrush(anticolors[0]), handsPos.X, handsPos.Y);
label5.Text = "x = " + handsPos.X.ToString();
label6.Text = "y = " + handsPos.Y.ToString();
label7.Text = "z = " + handsPos.Z.ToString();
}
int[] users = this.userGenerator.GetUsers();
foreach (int user in users) //【各ユーザーに関して実施】
{
if (this.shouldPrintID)
{
Point3D com = this.userGenerator.GetCoM(user);
com = this.depth.ConvertRealWorldToProjective(com);
string label = "";
if (!this.shouldPrintState)
{ label += user; }
else if (this.skeletonCapbility.IsTracking(user))
{ label += user + " - Tracking"; }
else if (this.skeletonCapbility.IsCalibrating(user))
{ label += user + " - Calibrating..."; }
else
{
label += user + " - Looking for pose";
// this.handsGenerator.StartTracking(ref handsPos);
}
g.DrawString(label, new Font("Arial", 6), new SolidBrush(anticolors[user % ncolors]), com.X, com.Y);
}
// if (this.shouldDrawSkeleton && this.skeletonCapbility.IsTracking(user))
if (this.skeletonCapbility.IsTracking(user))
DrawSkeleton(g, colors[0], user); //【赤で表示】
}
g.Dispose();
}
this.Invalidate();
}
}
}
}
|
|
![]() 【図xx】Form1のデザイン |
【Form1.Designer.cs】Kinect SensorをC#で利用する場合のサンプル・プログラム
namespace KinectSimpleViewer
{
partial class Form1
{
///
|
|
【Kinectセンサについて】2011/04/03 追記■Microsoft社XBOX用モーションキャプチャーセンサKinect ![]() ■Visual C#でKinectアプリケーション開発が行えます。 |
【注】Kinect(キネクト)センサは、Microsoft社のXBOX360用のモーションキャプチャーセンサ(1万5千円弱)で単独で購入することができます。640×480画素のRGBカメラのほか、赤外線カメラも搭載しており、毎秒30フレームの速度で深さ情報(depth buffer)を得る事ができます。USBインターフェースなのでWindowsパソコンに接続可能です。2011年1月6日にC#で使える暫定版OpenNI.net.dllがオープンソースで公開されています。OpenNI.net.dllを使えば上の写真のように基本的な骨格情報(関節の3次元座標)をリアルタイムに得られます。下の動画は磁界・超音波センサに応用してみた事例です。トランジスタ技術2011年2月号の拡張現実センサの進化形に該当します。(詳細は別ページで記載の予定。現在、導入メモ等と一部ソースを記載。[2011/02/05 - 2011/03/31]) |
【各種拡張現実センサの開発】
どちらかと言うと、専用センサを開発するのがメインで、拡張現実センサの機能を付加しているという程度です。 |
|
|
|
|