KinectでMangoにPush!
8/27のTech Party 2011 広島で紹介したKinectとWindows Phone 7を組合せたPush Notification活用事例です。
Windows Phone 7ではPush Notificationという機能があることはご存知ですね。
Kinect for Windows SDKでは人間7人までのインデックスつき深度情報を取得できるので、Kinectセンサーの前を人が通ったら、それをPush NotificationでWindows Phoneデバイスに通知してやろうってーいう寸法です。
まぁイメージとしては、出入り口(部屋でもよし)にKinectが設置してあって、その前を誰かが通過したら、通過したよと、離れた場所にいる誰かに通知が行くわけですね。
Windows PhoneのPush Notificationの詳細はサンプルなどと共に
https://msdn.microsoft.com/en-us/library/ff402537(v=VS.92).aspx
で紹介されています。Windows Phone側のサンプル Toast Notification Clientはそのまま使えます。
そしてKinect for Windows SDKをインストールしたWindows 7 PCとKinectセンサーを用意します。Windowsアプリケーションを一つ作成して、Microsoft.Research.Kinectアセンブリを参照設定に追加します。
そして、クラスに KinectのRuntime型のメンバーを
private Runtime kinectRuntime;
で定義して、
kinectRuntime = new Runtime();
kinectRuntime.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking);
kinectRuntime.DepthStream.Open(
ImageStreamType.Depth, 2,
ImageResolution.Resolution320x240,
ImageType.DepthAndPlayerIndex);
kinectRuntime.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(kinectRuntime_DepthFrameReady);
と初期化処理を書けば、kinectRuntime_DepthFrameReadyメソッドがDepthFrameのデータが更新されるたびに呼ばれます。そしてこのメソッドの中で、
void kinectRuntime_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
byte[] depthImageBytes = e.ImageFrame.Image.Bytes;
で、1ピクセルあたり2バイトで、320ピクセル(=1ライン)×240ラインの深度情報が取得できます。depthImageBytesは各奇数バイトの下位3ビットが、人の識別情報なので、0x3で&をとって、1、2,3,4,5,6,7の値になっているピクセル数をカウントアップします。ノイズもあるので、ある一定のピクセル数を超えたら、それぞれの識別インデックスに割り振られた人が今Kinectセンサーの前にいると判断できます。
そして、このメソッドが1つ前にコールされた状態を保持しておいて比較します。前回いなくて今回居たら通過を始めて、逆に前回いたけど今回いないなら通過終了と判断できるわけです。コードにすると、以下の様になります。
int[] currentPlayers = { 0, 0, 0, 0, 0, 0, 0 };
foreach (byte depthRaw in depth16)
{
int playerIndex = depthRaw & 0x7;
if (playerIndex > 0)
{
currentPlayers[playerIndex + 1]++;
}
}
bool[] currentPlayerStates = { false, false, false, false, false, false, false };
for (int i = 0; i < currentPlayers.Length; i++)
{
if (currentPlayers[i] > depthLimit)
{
currentPlayerStates[i] = true;
}
}
状態が変化したインデックスがあれば、Push NotificationにパラメータつきでPush Notification Serviceに通知を送ればよろしい。これはWindows PhoneのPush Notificationのサンプルで提供されているクラウド側のコードを参考にして、
HttpWebRequest request = HttpWebRequest.Create(PushURL) as HttpWebRequest;
if (!String.IsNullOrWhiteSpace(ProxyURL))
{
request.Proxy = new WebProxy(ProxyURL);
}
request.Method = "POST";
string toastMessage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>" + "Kinect" + "</wp:Text1>" +
"<wp:Text2>" + Status + "</wp:Text2>" +
"<wp:Param>/Page2.xaml?NavigatedFrom=Toast Notification</wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
byte[] notifyMessage = Encoding.Default.GetBytes(toastMessage);
request.ContentLength = notifyMessage.Length;
request.ContentType = "text/xml";
request.Headers.Add("X-WindowsPhone-Target", "toast");
request.Headers.Add("X-NotificationClass", "2");
try
{
using (var stream = request.GetRequestStream())
{
stream.Write(notifyMessage, 0, notifyMessage.Length);
stream.Close();
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
if (response.StatusCode != HttpStatusCode.OK)
{
StatusはPlayer Indexが現れた、消えたというメッセージにして、Windows Phone側でPush Notificationチャネルを開いた時に取得したURLをPushURLにセットしてあげれば 、MPNSから、Windows Phoneデバイスに通知がいきます。
Kinectをつないでアプリを動かしているのと同じPC上で、Windows PhoneのEmulator上でToast Notification Clientを動かしてみると良いでしょう。
実用で使う場合、PhoneとWinPCでPushのURLを交換する方法が必要になりますが、クラウドサービスを介する方法や、ローカルネット内でのUDPマルチキャストによる交換などいろいろな方法が考えられます。こちらはまた別のお話という事で。
以上、KinectとPhoneの簡単なコラボレーションを紹介しました。センサーは所詮データを取り出すだけのものなので、こんな感じで他のサービスやデバイスと組合わせてシナリオを考えると、結構おもしろいものアイデアが浮かんでくると思うので、皆さんも是非、色々と考えてみてください。