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の簡単なコラボレーションを紹介しました。センサーは所詮データを取り出すだけのものなので、こんな感じで他のサービスやデバイスと組合わせてシナリオを考えると、結構おもしろいものアイデアが浮かんでくると思うので、皆さんも是非、色々と考えてみてください。