Xtion と Flash をくっつけてちょっとだけ遊んでみた


仕事がアレで全然触れてなかった Xtion(例のKinectのちっこいヤツね)を、OpenNI(NiHandTracker)と Flash(AIR 2.0 NativeProcess))で結合させて、以前 Wonderfl にポストしていた WebCmera を使ったモーショントラックで遊ぶヌンチャクごっこを、精度が高い Xtion版として作り直してみました。

動作のデモ映像はこちら。


(ヌンチャクの綴りが間違っているようですが気にしない気にしないw)

で、Flash と Xtion をどうやって結合したかといえば…
AIR 3.0 の ANE(ネイティブ拡張)を使用してくっつけようかとも思ったのですが、今日一日しか触れる時間が取れそうになかったので、安全策(?)をとって、AIR 2.0 の NativeProcess API を使用して C++(OpenNI)のコンソールアプリケーションと Flash を結合してみました。

というわけで、NativeProcess で Xtion から値を取得するために書いた AS のコードはこちらです。

package 
{
	import flash.desktop.*;
	import flash.filesystem.*;
	import flash.events.*;
	import flash.geom.Point;

	public class GetXtion2DPoint
	{

		private var _process:NativeProcess;
		
		public var xtionPoint:Point = new Point();
		public var msg:String = "";
		
		public function GetXtion2DPoint()
		{
			try
			{
				init();
			}
			catch (e:Error)
			{
				trace(e);
			}
			trace(NativeProcess.isSupported);
		}
		
		private function init():void
		{
			var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
			var file:File = File.applicationDirectory.resolvePath("bin/start.sh");

			info.executable = file;

			var args:Vector.<String> = new Vector.<String>();
			info.arguments = args;

			_process = new NativeProcess();
			_process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, outputHandler);
			_process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, progressHandler);
			_process.start(info);

		}
		
		private function outputHandler(e:ProgressEvent):void
		{
			var data:String = e.target.standardOutput.readUTFBytes(_process.standardOutput.bytesAvailable);
			dataToPoint(data);
		}

		private function progressHandler(e:ProgressEvent):void
		{
			trace(e);
		}
		
		
		private function dataToPoint(data:String):void
		{
			var list:Array = data.split(" ");

			var p:Point = new Point(0,0);
			if (list.length > 1)
			{
				p.x = int(list[0]);
				p.y = int(list[1]);
			}
			
			msg = p.toString();
			xtionPoint = p;
		}

	}

}

Xtion(OpenNI)側のコードは、OpenIN のサンプルにある「NiHandTracker(NiHandTracker.cpp の DisplayPostDraw 関数)」を一部書き直して使用しました。
書き直した部分はこちら

void HandViewer::DisplayPostDraw()
{
	typedef TrailHistory			History;
	typedef History::ConstIterator	HistoryIterator;
	typedef History::Trail			Trail;
	typedef Trail::ConstIterator	TrailIterator;

	static const float colours[][3] =
	{
		{ 0.5f, 0.5f, 0.5f},
		{ 0.0f, 1.0f, 0.0f},
		{ 0.0f, 0.5f, 1.0f},
		{ 1.0f, 1.0f, 0.0f},
		{ 1.0f, 0.5f, 0.0f},
		{ 1.0f, 0.0f, 1.0f}
	};
	const TrailHistory&	history = m_HandTracker.GetHistory();

	// History points coordinates buffer
	XnFloat	coordinates[3 * MAX_HAND_TRAIL_LENGTH];

	const HistoryIterator	hend = history.end();
	for(HistoryIterator		hit = history.begin(); hit != hend; ++hit)
	{

		// Dump the history to local buffer
		int				numpoints = 0;
		const Trail&	trail = hit.GetTrail();

		const TrailIterator	tend = trail.end();
		for(TrailIterator	tit = trail.begin(); tit != tend; ++tit)
		{
			XnPoint3D	point = *tit;
			m_depth.ConvertRealWorldToProjective(1, &point, &point);
			ScalePoint(point);
			coordinates[numpoints * 3] = point.X;
			coordinates[numpoints * 3 + 1] = point.Y;
			coordinates[numpoints * 3 + 2] = 0;
			++numpoints;
		}
		assert(numpoints <= MAX_HAND_TRAIL_LENGTH);

		// Draw the hand trail history
		XnUInt32 nColor = hit.GetKey() % LENGTHOF(colours);
		glColor4f(colours[nColor][0],
			colours[nColor][1],
			colours[nColor][2],
			1.0f);
		glPointSize(2);
		glVertexPointer(3, GL_FLOAT, 0, coordinates);
		glDrawArrays(GL_LINE_STRIP, 0, numpoints);
		// Current point as a larger dot
		glPointSize(8);
		glDrawArrays(GL_POINTS, 0, 1);
		glFlush();
	}
	printf("%f %f\n",coordinates[3], coordinates[4]); // 座標を出力
	fflush(stdout); // 出力バッファを削除
}

これで、NiHandTracker から出力される値を Flash 側で取得できるようになりました。

そんなこんなで、こういうモーションキャプチャデバイスで遊ぶのは結構楽しいので、今後もヒマを見つけて Xtion で遊んでみたいと思います。