背景
Windowsはなぜかローカルホストでのソケット通信を許可してくれない。そのため、OSCでプロセス間通信をしていたアプリをWindowsに持ってくると全滅する。Windows使わないという手はあるのだが、今回はKinect SDKを利用したいので、OSC以外の方法でプロセス間通信を実装した。
システム概要
Kinect SDKをC#で利用し、その情報をプロセス間通信を利用してProcessing(つまりJava)に投げてProcessing側でメインの処理をする。
プロセス間通信は色々な方法があるが、今回利用したプロセス間通信の手法は『名前付きパイプ(またの名前をFIFO)』である。
送信する情報は"顔の位置と向き"だ。数値を文字列にして渡している。
サーバー側(C#)
Kinect SDKを利用して情報を送信するサーバー側はC#である。コードは著作権的ゴニョゴニョにより一部のみしか載せられないので、詳しくは下記のサイトなどを参照だ。なので動作は確認していない。
方法: ネットワークのプロセス間通信で名前付きパイプを使用する
以下のコードのStart()は起動時に一度呼ばれるメソッド。Stop()は止まった時、OnFaceFrameArrived()は顔認識が出来た時に毎回呼び出されるメソッドである。OnFaceFrameArrived()で顔の位置と向きを送信している。クライアント側を止めると例外が投げられるので、パイプの送信するメソッドたちは本来tryを利用して例外をキャッチする必要がある。
NamedPipeServerStream pipeServer; public void Start() { pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); Console.WriteLine("[PIPE SERVER] thread created"); pipeServer.BeginWaitForConnection(ConnectCallback, null); } public void ConnectCallback(IAsyncResult result) { Console.WriteLine("[PIPE SERVER] Client Connected"); pipeServer.EndWaitForConnection(result); if (pipeServer.IsConnected) { byte[] message = Encoding.ASCII.GetBytes("START\n"); pipeServer.BeginWrite(message, 0, message.Length, WriteCallback, null); } } public void Stop() { this.pipeServer.Close(); } private void OnFaceFrameArrived(object sender, HighDefinitionFaceFrameArrivedEventArgs e) { using (HighDefinitionFaceFrame faceFrame = e.FrameReference.AcquireFrame()) { if (faceFrame != null && faceFrame.IsFaceTracked) { faceFrame.GetAndRefreshFaceAlignmentResult(this.faceAlignment); if (pipeServer.IsConnected) { string s = this.faceAlignment.FaceOrientation.Y.ToString() + "," + this.faceAlignment.HeadPivotPoint.X.ToString() + "," + this.faceAlignment.HeadPivotPoint.Z.ToString() + "\n"; byte[] message = Encoding.ASCII.GetBytes(s); pipeServer.BeginWrite(message, 0, message.Length, WriteCallback, null); } } } } private void WriteCallback(IAsyncResult ar) { pipeServer.EndWrite(ar); }
クライアント側(Processing)
サーバー側と同じくプロセス間通信以外のコードを削除したので動作は確認していない。Javaで普通にファイルを読み込む時に利用する"BufferdReader"を利用している。特殊なのは"FileReader"に渡すパスがパイプを指すものになっている。
Processingの仕様でdraw()は毎フレーム呼ばれるので、draw()でポーリングをしているわけだが、この辺りObserverパターンでコールバック関数を呼び出す感じのヤツの方が美しいよな〜と思いながら実装していたので、そういうヤツを実装した人は是非教えてほしい。今回は時間の関係でポーリングになった。
import java.io.FileReader; import java.io.BufferedReader; BufferedReader pipeBuffer; void setup() { try { pipeBuffer = new BufferedReader(new FileReader("\\\\.\\pipe\\testpipe")); } catch (Exception e) { e.printStackTrace(); } } void draw() { if (pipeBuffer != null) { try { while (pipeBuffer.ready ()) { String line = pipeBuffer.readLine(); parseKinectValue(line); } } catch (Exception e) { e.printStackTrace(); } } } void exit() { if (pipeBuffer != null) { try { pipeBuffer.close(); } catch (Exception e) { e.printStackTrace(); } } super.exit(); } void parseKinectValue(String str) { String[] strs = str.split(","); if (strs.length == 3) { println(str); float faceAngle = Float.parseFloat(strs[0]); float x = Float.parseFloat(strs[1]); float z = Float.parseFloat(strs[2]); } }