前回のあらすじ: OpenNI を入れた。
Kinect のカメラ、及び深度データを OpenCV で使うための最小限のソースコードをこの辺で見つけたので、さっそく試してみた。
自分の環境に合わせてじゃっかんソースコードを書き換えたが、概ねそのままである(下記のコードは、Visual Studio と OpenCV 2.0 のコンビネーションじゃないとうまく動かない気がする。特にライブラリのバージョンに注意)。
#include<cv.h> #include<highgui.h> // ※インクルードパスに // 「C:\Program files\OpenNI\Include」 // を追加しておこう! #include<XnCppWrapper.h> #pragma comment(lib,"C:/Program files/OpenNI/Lib/openNI.lib") #define SAMPLE_XML_PATH "C:/Program Files/OpenNI/Data/SamplesConfig.xml" int main() { xn::Context context; xn::EnumerationErrors errors; context.InitFromXmlFile(SAMPLE_XML_PATH); xn::DepthGenerator depth; // 深度コンテキスト context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth); xn::ImageGenerator image; // イメージコンテキスト context.FindExistingNode(XN_NODE_TYPE_IMAGE, image); xn::DepthMetaData depthMD; xn::ImageMetaData imageMD; cv::Mat depthshow; cv::Mat show; int key = 0; bool isWarp=false; while (key!='q') { // waitとエラー処理 context.WaitAnyUpdateAll(); image.GetMetaData(imageMD); depth.GetMetaData(depthMD); // 画像と深度はOpenCVのMatに格納 cv::Mat depth16(480,640,CV_16SC1,(unsigned short*)depthMD.WritableData()); cv::Mat imni(480,640,CV_8UC3,(uchar*)imageMD.WritableData()); // RGB色空間をBGRに変換 cv::cvtColor(imni,show,CV_RGB2BGR); // 11ビット画像を8ビット画像に変換 depth16.convertTo(depthshow,CV_8U,-255/4096.0,255); cv::imshow("depth",depthshow); cv::imshow("image",show); key = cv::waitKey(33); } context.Shutdown(); return 0; }これで、Kinect から労せずしてカメラ映像を取得する事ができるようになった。
……と見せかけて、上記のサンプルコードのままでは(僕にとって)多少問題がある。
この段階で、画像データは OpenCV の汎用行列クラスである cv::Mat のオブジェクトとして扱えるようになるのだが、このページの情報によると、 cv::Mat のメンバである step の値が、4 バイト単位に調整されなくなるという不都合が残っているらしい。
以前書いたような Windows API との連携を考えた場合、画像の管理には素性がはっきりしている IplImage 構造体を用いた方が、面倒も少なくて済むだろう。
僕がてきとうに調べた限り、Kinect から取ってきた画像データを直に IplImage に変換するような先人のソースコードを見つける事はできなかった。
それでも、恐らく需要はあるかも知れないと思い、ちょっと作ってみることに。
もしかしたら少々面倒かなぁと思ったのだが、意外とあっけなくできたので掲載しておく。
#include<cv.h> #include<highgui.h> #include<XnCppWrapper.h> #pragma comment(lib,"C:/Program files/OpenNI/Lib/openNI.lib") const char* SAMPLE_XML_PATH = "C:/Program Files/OpenNI/Data/SamplesConfig.xml"; int main() { xn::Context context; xn::EnumerationErrors errors; context.InitFromXmlFile(SAMPLE_XML_PATH); xn::ImageGenerator image; // イメージコンテキスト context.FindExistingNode(XN_NODE_TYPE_IMAGE, image); xn::ImageMetaData imageMD; int key = 0; // 大人の事情で、IplImageで管理することに。 cv::Ptr<IplImage> iplimage; iplimage = cvCreateImage(cvSize(640, 480),IPL_DEPTH_8U,3); while (key!='q') { // waitとエラー処理 context.WaitAnyUpdateAll(); // 画像メタデータ取得 image.GetMetaData(imageMD); // IplImageにデータを格納 iplimage->imageData = (char *)imageMD.WritableData(); cvCvtColor(iplimage, iplimage, CV_RGB2BGR); // iplImageはcvShowImageで表示 cvShowImage("hoge", iplimage); key = cv::waitKey(33); } context.Shutdown(); return 0; }なお、cv::Mat オブジェクト宣言時、コンストラクタに IplImage 構造体を渡す事によって、事実上 IplImage から cv::Mat への変換は容易に可能である。 Kinect から取ってきた映像データは一度 IplImage に格納しておくのがよいだろう。
cv::imshow ではなく、cvShowImage 関数で IplImage を表示した結果は次の通りである。
ちゃんと画像を取ってくる事ができたので、あとは OpenCV で煮るなり焼くなりできる。
0 件のコメント:
コメントを投稿
ひとことどうぞφ(・ω・,,)