OpenNIで複数のKinectから1つを選択して起動する方法 - TK's HP

TK's HP ホーム » スポンサー広告 » kinect » OpenNIで複数のKinectから1つを選択して起動する方法

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

OpenNIで複数のKinectから1つを選択して起動する方法

正しい方法でデータ取得する方法がありましたね。
世界で最初の Kinect + OpenNI + NITE の本を書きました の 5.5.複数のKinectを操作する に書かれていると思われます。
サンプルコードgit内で確認できます。
解説が見たかったら本を買ってください。

他のページとしては、
OpenNI Tipsの中のサンプルソースコードとかでもできます。(Twitterでつぶやいていたダイレクトリンクです)


このページの内容は微妙なので、上の方法を使ってください。
需要はあまり無いと思いますが、せっかく書いたのでの残しておきます。


OpenNIを使って複数のKinectから情報を取る方法がどこにも載っていないので、OpenNIを改造して1つのPCでKinectを複数台接続したときに、選んで起動する方法を書いておきます。
(かなり無理矢理です。これまでのプログラムが使えるようには書いていますが、本家が更新したらもちろん同じ作業をしないと複数台接続できるようになりません。また、エラー処理は適当なので、簡単にセグフォします)

やりかたとしては、xmlファイルで読み込むときのstream名でどのセンサを読み込むかを指定する方法です。
その部分が一番読みやすく、Sampleとかxmlファイル読んでいるからユーザプログラムをいじる必要が無いといった理由です。
センサの台数分xmlファイルを用意する必要があります。

とりあえずできるというだけですので、初心者の方は公式の方法があればそちらを使用してください。

ちなみにこの方法では、使用するKinectを選択するだけなので、複数のKinectを処理したいならマルチプロセスで処理する必要があります。

外部      | PC内
kinect1 - | handler1 ┐
| ├ viewer
kinect2 - | handler2 ┘

こんな感じでプロセスを3つとか使えば、ちゃんと動きます。
一応ここまでは確認しています。
まあ、OpenNIが本気をだせば、こんなことしなくても使えるようになると思います。

Kinectを複数台接続するときに注意しなければならないのは、USBの帯域です。
Kinect自体がかなりデータ量が多いので、1つのUSBチップで1つのKinectしか通信できません(640x480でRGBD取得した場合)。
複数のKinectを使いたいときは、USBのチップ(なんていうか知らない、もちろんUSBハブはダメ)を増設して下さい。
とりあえずノートパソコンなどだと、右と左のUSBポートでチップが異なることが多いようです。



以下、OpenNIの改造方法。
インストールがなんて終わっているぜって人は「インストール」を飛ばして「OpenNIの改造」を見て下さい。

インストール


なんかいつの間にかOpenNIがバージョンアップしていたので、インストール方法のおさらいから。
ちなみに私の環境ですが、ThinkPad X200 Linux Ubuntu 10.04 (32bit)です。
一応WindowsでもOpenNI Windowsに開発環境を構築とか見ながら自分でコンパイルすれば大丈夫だと思います。

gitの使い方とか知らないので、これまでに入れたプログラムは一度全部消去しています。
入れ方変わっていたらごめんなさい。


OpenNIのインストール (commit 8c24e1687d429036d803)


mkdir ~/kinect
cd ~/kinect
git clone https://github.com/OpenNI/OpenNI.git
cd OpenNI/Platform/Linux-x86/Build/
make && sudo make install



Kinectドライバのインストール(avin2 commit 28738dc12c7d653f45ce)


avin2さん流石の更新の速さ。
Linuxでも動くし、Windows用のバイナリも配布しているので、初心者はこれが良さそうですね。

cd ~/kinect
git clone https://github.com/avin2/SensorKinect.git
cd SensorKinect/Platform/Linux-x86/Build/
make && sudo make install




NITEのインストール(ver1.3.1.3)


以下のURLから自分にあったものをダウンロードする
http://www.openni.org/downloadfiles/openni-compliant-middleware-binaries/34-stable

cd ~/kinect
wget --content-disposition http://www.openni.org/downloadfiles/openni-compliant-middleware-binaries/stable/72-primesense-nite-stable-build-for-ubuntu-10-10-x86-32-bit-v1-3-1/download
tar -xf NITE-Bin-Linux32-v1.3.1.3.tar.bz2
cd Nite-1.3.1.3/
sh install.sh


残念ながら私の環境ではコンパイルが通りませんでした。
まずubuntu10.04でGLIBCXXのバージョンが足りないことと、
あとMakefileの間違いNite-1.3.1.3/Samples/NiteSampleMakefile内の

BIN_DIR = ../Bin -> BIN_DIR = ../../Bin


を直せばコンパイルできるかも。
流石にライブラリのバージョンを手動で上げる気はしなかった。
というわけでNITEライブラリはver1.3.0.17を使用します。
コンパイル通るなら新しい方を使った方が良いと思います。


OpenNIで複数のセンサを選択できるように改造


OpenNIの改造


xmlファイルを読み込む部分を改造します。
ある程度バージョンが変わっても同様の方法で改造できると思います。

以下 OpenNIのディレクトリ上で行います。

○Include/XnContext.h を編集
290行周辺、xnFindExistingNodeByType()の上に追加

/**
* @brief Returns the first found existing node of the specified type.
*
* @param pContext [in] OpenNI context.
* @param type [in] Type to look for.
* @param phNode [out] A handle to the found node.
* @param n [in] sensor number
*/
XN_C_API XnStatus xnFindExistingNodeByTypeEx(
XnContext* pContext,
XnProductionNodeType type,
XnNodeHandle* phNode,
XnInt32 n
);


○Include/XnCppWrapper.h を編集
4866行周辺、Context::FindExistingNode()関数を置き換え
関数ごと置き換えて下さい。

/** @copybrief xnFindExistingNodeByType

* For full details and usage, see @ref xnFindExistingNodeByType

*/

XnStatus FindExistingNode(XnProductionNodeType type, ProductionNode& node, XnInt32 n = 0) const
{

XnStatus nRetVal = XN_STATUS_OK;



XnNodeHandle hNode;

nRetVal = xnFindExistingNodeByTypeEx(m_pContext, type, &hNode, n);

XN_IS_STATUS_OK(nRetVal);



node.SetHandle(hNode);



return (XN_STATUS_OK);

}



Source/XnOpenNI.cppを編集
2302行目周辺xnFindExistingNodeByType()を置き換え、新たにxnFindExistingNodeByTypeEx()を追加

XN_C_API XnStatus xnFindExistingNodeByTypeEx(XnContext* pContext, XnProductionNodeType type, XnNodeHandle* phNode, XnInt32 n )
{
XnStatus nRetVal = XN_STATUS_OK;

XN_VALIDATE_INPUT_PTR(pContext);
XN_VALIDATE_OUTPUT_PTR(phNode);

XnNodeInfoList* pList;
nRetVal = xnEnumerateExistingNodesByType(pContext, type, &pList);
XN_IS_STATUS_OK(nRetVal);

// take first
XnNodeInfoListIterator it = xnNodeInfoListGetFirst(pList);
for( XnInt32 i = 0; i < n; i++ )
{
// make sure it's valid
if (!xnNodeInfoListIteratorIsValid(it))
{
xnNodeInfoListFree(pList);
return XN_STATUS_NO_MATCH;
}
it = xnNodeInfoListGetNext(it);
}

// make sure it's valid
if (!xnNodeInfoListIteratorIsValid(it))
{
xnNodeInfoListFree(pList);
return XN_STATUS_NO_MATCH;
}

XnNodeInfo* pNodeInfo = xnNodeInfoListGetCurrent(it);
*phNode = xnNodeInfoGetHandle(pNodeInfo);
xnNodeInfoListFree(pList);

return (XN_STATUS_OK);
}

XN_C_API XnStatus xnFindExistingNodeByType(XnContext* pContext, XnProductionNodeType type, XnNodeHandle* phNode)
{
return xnFindExistingNodeByTypeEx( pContext, type, phNode, 0 );
}




Source/OpenNI/XnXmlConfig.cppを編集
776行目周辺xnConfigureSetOpcode()内pNode->Attribute("name")をチェックしているif文を置き換え

// check if a name was requested

if (NULL != pNode->Attribute("name"))

{

const XnChar* strName = NULL;

nRetVal = xnXmlReadStringAttribute(pNode, "name", &strName);

if (nRetVal != XN_STATUS_OK)

{

xnNodeInfoListFree(pTrees);

return (nRetVal);

}

#if 1
int sensorNum = 0;
sensorNum = atoi(strName);
for( int i = 0; i < sensorNum; i++ )
{
if(!xnNodeInfoListIteratorIsValid(itChosen))
{
xnNodeInfoListFree(pTrees);
return (XN_STATUS_NO_MATCH);
}
itChosen = xnNodeInfoListGetNext(itChosen);
pChosenInfo = xnNodeInfoListGetCurrent(itChosen);
}
#endif
nRetVal = xnNodeInfoSetInstanceName(pChosenInfo, strName);

if (nRetVal != XN_STATUS_OK)

{

xnNodeInfoListFree(pTrees);

return (nRetVal);

}

}




○再コンパイル&再インストール
OpenNIを再度コンパイル・インストールしなおして下さい。
ちなみに共有ライブラリ・ダイナミックライブラリを使用しているので、ユーザープログラムは(恐らく)コンパイルしなおす必要は無いと思います。

センサの選択方法


最初に読み込むXMLファイルを使って、接続するセンサを制御します。
この改造の方法では、Nodeのnameをatoi()に突っ込むことで番号を選択しているので、nameの最初の部分に0から始まる数字を付加します。


<ProductionNodes>
<Node type="Image" name="0image">
<Configuration>
<MapOutputMode xRes="640" yRes="480" FPS="30"/>
<Mirror on="false"/>
</Configuration>
</Node>
<Node type="Depth" name="1depth">
<Configuration>
<MapOutputMode xRes="640" yRes="480" FPS="30"/>
<Mirror on="false"/>
</Configuration>
</Node>
<Node type="User" />
</ProductionNodes>



このとき注意してほしいのが、同じ番号の組み合わせが同じセンサを表しているとは限らないことです。

私のPCではimageの0番とdepthの1番が正しい組み合わせとなりました。
この番号は挿す場所によって固定のようなので、この番号を変えたxmlファイルを複数用意して、プログラムの起動時にxmlファイルを代えて起動すれば、接続するセンサを制御できます。

int main(int argc, char **argv)
{
XnStatus nRetVal = XN_STATUS_OK;

Context context;
EnumerationErrors errors;

nRetVal = context.InitFromXmlFile(argv[1], &errors);


のようにでもして、起動時にxmlファイルを選択できるようにでもしておけば良いと思います。

ちなみに今回の改造はまじめにエラー処理をしていないので、xmlファイルで1以上の値を指定したときに、その台数分センサがPCにささっていないとセグメンテーションフォールトで終了するので気をつけて下さい。




反省・・・


以上、OpenNIを使ったときに複数のKinectを選択する方法でした。
個人的にはやったは良いけど、ノートPCでは複数接続しても処理が追いつかないので、意味が無く、ちょっと後悔していたり。

この方法1月くらいに考えたのですが、すぐに公式が対応すると思って放置していたらいつの間にかもう4月。
この間に普通にOpenNI単体でmulti kinectできるようになっていたらちょっとショック

あと、プログラムの改造方法を公開しても良いのか疑問。
LGPLだから大丈夫だよね、きっと
関連記事
記事に間違いなどがあればすぐに訂正いたしますので、コメントしてください。
コメント
非公開コメント

承認待ちコメント

このコメントは管理者の承認待ちです

2011-12-28 14:55 │ from URL

トラックバック

http://tclip.blog.fc2.com/tb.php/113-5e6e0733

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。