OpenNIをOpenframeworksで使う

LINEで送る
Pocket

このエントリはOpenNI Advent Calendar 2011 : ATNDの12月23日分です!!

OpenframeworksでOpenNIの連携について書きます。

Openframeworks ( http://www.openframeworks.cc/ )(以下、oF)とは、メディアアート界隈で使われている描画や動画の扱いやプログラムの書き方がprocessingっぽく作られている、クロスプラットフォームで動作するC++のフレームワークです。
oFはたくさんの有志により様々なアドオンが配布されており、それを使うとソケットやOSC、OpenCVなどの機能を簡単に使う事ができます。

oFで使えるアドオン

oFで使えるKinect関連のアドオンがいくつか、下記のものがあります。
・ofxKinect: ( https://github.com/ofTheo/ofxKinect )
→深度情報とRGBが取得できます。スケルトンの取得機能はありません。
・ofxOpenNI:( https://github.com/gameoverhack/ofxOpenNI )
→OpenNIをoFで使えるように作ったもの。

ただ、OpenNI自体バージョンアップが早い事もあるので今回は普通のOpenNIをoFで使う方法を紹介します。

oFを使えるようにする

oFのWebサイトから必要なファイルをダウンロードします。私の場合はMacなのでosxバージョンをダウンロードします。
http://www.openframeworks.cc/download/

ダウンロードしたファイルを解凍すると下記のフォルダ構成になっています。

  • add ons / oFのアドオンが格納されています。
  • apps / サンプルプログラムがたくさん入っています。実際にoFでアプリを開発する場合はこの中のexamples/EmptyExampleをベースにします。
  • libs / oF自体のライブラリやoFが使っているライブラリが格納されています。

of_directory

では、oFの基本を理解するために EmptyExampleをみてみます。

testApp.cppをみてみましょう。
はじめに定義されているメソッドの実装を進めて開発を進めます。

setup() :
アプリケーションの起動時あるいはnewした直後に呼ばれる関数です。初期化などはここで行います。
update():
毎フレーム毎draw()の前に呼ばれます。ここでデータや座標計算の更新をすると良いでしょう。
draw():
毎フレーム毎、update()の後に呼ばれます。ここには描画処理をかきます。

基本的にはこれらを使うのですが、マウスやキーボードのユーザー入力にも対応しています。

keyPressed, keyReleased:
キーボードを押したとき、離したときに呼ばれます。
mouseMoved, mouseDragged, mousePressed, mouseReleased:
マウスの移動、ドラッグ、マウスダウン、マウスを離した時に呼ばれます。

OpenNIの設定:

準備

今回はとにかく簡単に使えるようになる事を考えて説明します。
まず、OpenNIのIncludeとLibフォルダをプロジェクト直下に”OpenNI”というフォルダを作成し、そこに移動します。

次にプロジェクトにヘッダー参照パスとライブラリパスの設定をします。
プロジェクト設定の “Build Settings” → “Search Paths”の

Header Search Pathsに以下を設定します。
“$(SRCROOT)/../OpenNI/Include
Library Search Pathsに以下を設定します。
“$(SRCROOT)/../OpenNI/Lib

※設定後、自動的に別の項目に変更される場合がありますが、ビルドが通れば問題はありません。
私の環境では、
HEADER_SEARCH_PATHS_QUOTED_FOR_TARGET_1
LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1
の項目が追加されていました。

さて、それではOpenNIのコードを書いてみましょう。NiSimpleViewerをパク、、いやもとにして、深度情報を表示させてみましょう。
表示にはoFTextureクラスを使い、テクスチャとして表示させます。

今回は簡単に移植するため、NiSimpleViewerからコピペする事にします。
まず、

testApp.h

testApp.hに下記を追加します。

#include <XnCppWrapper.h>
using namespace xn;
//---------------------------------------------------------------------------
// Defines
//---------------------------------------------------------------------------
#define SAMPLE_XML_PATH "../../../data/SamplesConfig.xml"

#define DISPLAY_MODE_DEPTH		2
#define DEFAULT_DISPLAY_MODE	DISPLAY_MODE_DEPTH

#define MAX_DEPTH 10000

SamplesConfig.xmlはbin/data/以下に設置したため、
SAMPLE_XML_PATHを変更しています。

——-

testApp.cpp

testApp.cppに”Globals”の内容をもとに移植します。

//---------------------------------------------------------------------------
// Globals
//---------------------------------------------------------------------------
float g_pDepthHist[MAX_DEPTH];
XnRGB24Pixel* g_pTexMap = NULL;
unsigned int g_nTexMapX = 0;
unsigned int g_nTexMapY = 0;

unsigned int g_nViewState = DEFAULT_DISPLAY_MODE;

Context g_context;
ScriptNode g_scriptNode;
DepthGenerator g_depth;
DepthMetaData g_depthMD;

——–
NiSimpleViewerのmain関数が初期化に関する内容なので、setupに移植します。

	XnStatus rc;
	
	EnumerationErrors errors;
	rc = g_context.InitFromXmlFile(SAMPLE_XML_PATH, g_scriptNode, &errors);
	if (rc == XN_STATUS_NO_NODE_PRESENT)
	{
		XnChar strError[1024];
		errors.ToString(strError, 1024);
		printf("%s\n", strError);
		return ;
	}
	else if (rc != XN_STATUS_OK)
	{
		printf("Open failed: %s\n", xnGetStatusString(rc));
		return;
	}
	
	rc = g_context.FindExistingNode(XN_NODE_TYPE_DEPTH, g_depth);
	if (rc != XN_STATUS_OK)
	{
		printf("No depth node exists! Check your XML.");
		return;
	}
	
	g_depth.GetMetaData(g_depthMD);
	
	// Texture map init
	g_nTexMapX = (((unsigned short)(g_depthMD.FullXRes()-1) / 512) + 1) * 512;
	g_nTexMapY = (((unsigned short)(g_depthMD.FullYRes()-1) / 512) + 1) * 512;
	g_pTexMap = (XnRGB24Pixel*)malloc(g_nTexMapX * g_nTexMapY * sizeof(XnRGB24Pixel));

——-
setup関数の最後にofTextureクラスとofTextureで表示させるためのunsigned char*のデータをを初期化します。

	pixels = (unsigned char*)malloc(640*480*3*sizeof(unsigned char));
	tex.allocate(640, 480, GL_RGB);

——-
OpenNIでのデータを更新する処理をupdateに移植します。

	XnStatus rc = XN_STATUS_OK;
	
	// Read a new frame
	rc = g_context.WaitAnyUpdateAll();
	if (rc != XN_STATUS_OK)
	{
		printf("Read failed: %s\n", xnGetStatusString(rc));
		return;
	}
	
	g_depth.GetMetaData(g_depthMD);
	//g_image.GetMetaData(g_imageMD);
	
	const XnDepthPixel* pDepth = g_depthMD.Data();
	
	// Calculate the accumulative histogram (the yellow display...)
	xnOSMemSet(g_pDepthHist, 0, MAX_DEPTH*sizeof(float));
	
	unsigned int nNumberOfPoints = 0;
	for (XnUInt y = 0; y < g_depthMD.YRes(); ++y)
	{
		for (XnUInt x = 0; x < g_depthMD.XRes(); ++x, ++pDepth)
		{
			if (*pDepth != 0)
			{
				g_pDepthHist[*pDepth]++;
				nNumberOfPoints++;
			}
		}
	}
	for (int nIndex=1; nIndex<MAX_DEPTH; nIndex++)
	{
		g_pDepthHist[nIndex] += g_pDepthHist[nIndex-1];
	}
	if (nNumberOfPoints)
	{
		for (int nIndex=1; nIndex<MAX_DEPTH; nIndex++)
		{
			g_pDepthHist[nIndex] = (unsigned int)(256 * (1.0f - (g_pDepthHist[nIndex] / nNumberOfPoints)));
		}
	}
	
	xnOSMemSet(g_pTexMap, 0, g_nTexMapX*g_nTexMapY*sizeof(XnRGB24Pixel));
	
	// check if we need to draw depth frame to texture
	const XnDepthPixel* pDepthRow = g_depthMD.Data();
	XnRGB24Pixel* pTexRow = g_pTexMap + g_depthMD.YOffset() * g_nTexMapX;
	
	for (XnUInt y = 0; y < g_depthMD.YRes(); ++y)
	{
		const XnDepthPixel* pDepth = pDepthRow;
		XnRGB24Pixel* pTex = pTexRow + g_depthMD.XOffset();
		
		for (XnUInt x = 0; x < g_depthMD.XRes(); ++x, ++pDepth, ++pTex)
		{
			int idx = (x + y * g_depthMD.XRes()) * 3;
			if (*pDepth != 0)
			{
				int nHistValue = g_pDepthHist[*pDepth];
				
				pixels[idx] = nHistValue;
				pixels[idx+1] = nHistValue;
				pixels[idx+2] = nHistValue;
			}
			else
			{
				pixels[idx] = 0;
				pixels[idx+1] = 0;
				pixels[idx+2] = 0;
			}
		}
		
		pDepthRow += g_depthMD.XRes();
		pTexRow += g_nTexMapX;
	}
	
	tex.loadData(pixels, 640, 480, GL_RGB);

ofTextureのloadDataメソッドを使い、深度データをテクスチャとして読み込ませます。
——
最後にdrawメソッドにofTexetureのdrawを使って実際に描画します。

引数は、x座標、y座標、幅、高さを示しています。
—–

これでビルドすると表示できるようになります。
たくさんの警告はありますが、ライブラリ内部的なものもあるので無視する事にします。

advant_of

ファイル一式をこちらにアップしましたのでダウンロードして試してみてください。
https://github.com/mmlemon/OpenNI_Advent_oFSample

※OpenNIは1.4.0.2、oFは007で記述しています。

3 Thoughts on “OpenNIをOpenframeworksで使う

  1. Pingback: Kinect Hackに必要なフレームワーク | Interactionize. ~ Prototyping of Art & Tech ~

  2. Pingback: OpenFrameworksでKinectを使う | Interactionize. ~ Prototyping of Art & Tech ~

  3. Pingback: Kinect Hackに必要なフレームワーク | Interactionize.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Post Navigation