実際にVisual Studio 2015を用いて、Kinect v2プログラミングを行っていきたいと思います。
環境構築が済んでいない方は、前回までの記事を参考にしてください。ここからは実際にVisual Studio上でプログラミングをしていきます。
Microsoft Kinect v2 環境構築 ① (Windows 10)
Microsoft Kinect v2 環境構築 ② (Windows 10)
今回の記事で目指すゴール
① Visual Studio 2015で、Kinectを用いたプログラミングを実施
② RGB画像とデプス画像を出力し、表示を行う
まずは基本中の基本ということで、RGB画像と深度画像の表示を行ってみたいと思います。
今回の環境
・OS : Windows10(64bit)
・Visual Studio Community 2015 (インストール済)
・Microsoft Kinect SDK 2.0(インストール済)
手順
(1) Visual Studio 2015を起動し、プロジェクトを作成
Visual Studio Community 2015を起動し、新しいプロジェクトを作成しましょう。
「Win32コンソールアプリケーション」を選択し、保存場所や名前は自由に決定してください。私は名前は「KinectTestProgram」としました。
OKを押して次に進みましょう。
次にWin32アプリケーションウィザードが開くので、「次へ」を押してください。
「空のプロジェクト」にチェックを入れて、完了してください。これでデフォルトでソースコードなどが何も入っていないプロジェクトができると思います。
これで無事ソリューションが作成されました。
今回は取りあえず64bitでいきましょう。「Release/x64」にしておきます。
また、適当な.cppファイルを一つ追加してください。取りあえず、今回は「kinectColorDepth.cpp」としました。
ソースファイルのフォルダ上で右クリックして「追加->新しい項目」より追加することが可能です。
(2) Kinect SDKを使うための設定
ここからは、このプロジェクトにKinect開発に必要なファイルへのパスを通していきます。
慣れている人のためにまとめると、
<1> 追加のインクルードディレクトリ
C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\inc
<2> 追加のライブラリファイル
C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\Lib\x64
<3> 依存ファイル
Kinect20.lib
<4> 環境変数
「C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\bin」にパスを通す
と設定してください(ただし、場所はMicrosoft SDKのインストール場所を変更している場合は随時読み替えのこと)。
ここでは少し細かく、画像を使って上記手順を説明していきます。
まず、ソリューションエクスプローラーのプロジェクト名の上で右クリックをして、出たウィンドウの最下部にある「プロパティ」をクリックしてください。
構成がRelease、プラットフォームがx64になっていることを確認してください。もしなっていなければ変更しましょう。
<1> インクルードファイルの追加
左側のタブ「C/C++」→「全般」を開き、「追加のインクルードディレクトリ」に「C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\inc」を追加してください。
あくまでも私は「C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409」というフォルダにKinect開発用のSDK一式をダウンロードしているだけなので、もし違う場所にダウンロードされているのであれば、その場所のファイルパスで読み替えてください。
<2> 追加のライブラリディレクトリと追加の依存ファイルを設定
次に、左側の「リンカー」→「全般」→「追加のライブラリディレクトリ」に「C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\Lib\x64」を追加してください。
また、「入力」→「追加の依存ファイル」に「Kinect20.lib」を追加しましょう。
<3> 環境変数の設定
これは毎回のプロジェクトではなく1回やればOKですが、Kinect SDKのbinにパスを通します。
いろんな記事で書いているので詳細は省略しますが、システム環境変数の設定から環境変数の編集ページを開きます。
環境変数を開いて設定します。
Pathに「C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\bin」を設定します。
以上で、KinectのSDK周りの設定は完了です。
(3) NugetでOpenCVの設定
今回使うソースコードでは、Kinectから得られたRGB画像やデプス画像を表示したり、保存を行うためにOpenCVを使うこととしました。
特に複雑な関数は使っていないのでOpenCV2.0以降ならどのバージョンでも大丈夫そうではありますが、環境構築が簡単なので今回はNugetから入手できるOpenCV2.4.11を使うことにしました。
NuGetを使ってOpenCVの環境構築(Visual Studio 2015)
上記の記事の通りにNuGetからOpenCV2.4.11をインストールしました。
(4) コードの実行
それでは、コードを実行してみましょう。
KinectはPCに接続しておくようにしてください。
今回はカラーカメラの映像とDepthカメラからの結果を同時にウィンドウに表示します。
#include <kinect.h> | |
#include <iostream> | |
#include <opencv2/opencv.hpp> | |
using namespace std; | |
using namespace cv; | |
int main(int argc, char* argv[]) { | |
// (1) Sensorを開く | |
IKinectSensor* kinect; | |
GetDefaultKinectSensor(&kinect); | |
kinect->Open(); | |
// (2-1) ColorFrameSourceを得る | |
IColorFrameSource* p_color_source; | |
kinect->get_ColorFrameSource(&p_color_source); | |
// (2-2) DepthFrameSourceを得る | |
IDepthFrameSource* p_depth_source; | |
kinect->get_DepthFrameSource(&p_depth_source); | |
// (3-1) ColorFrameReaderを開く | |
IColorFrameReader* p_color_reader; | |
p_color_source->OpenReader(&p_color_reader); | |
// (3-2) DepthFrameReaderを開く | |
IDepthFrameReader* p_depth_reader; | |
p_depth_source->OpenReader(&p_depth_reader); | |
// (4) 画像サイズの取得 | |
int color_width, color_height, depth_width, depth_height; | |
IFrameDescription* p_frame_desc; | |
p_color_source->get_FrameDescription(&p_frame_desc); | |
p_frame_desc->get_Width(&color_width); | |
p_frame_desc->get_Height(&color_height); | |
p_depth_source->get_FrameDescription(&p_frame_desc); | |
p_frame_desc->get_Width(&depth_width); | |
p_frame_desc->get_Height(&depth_height); | |
cout << "color_width : " << color_width << endl; | |
cout << "color_height : " << color_height << endl; | |
cout << "depth_width : " << depth_width << endl; | |
cout << "depth_height : " << depth_height << endl; | |
// フレーム数カウント用 | |
int frame_counter = 0; | |
// ウィンドウの用意 | |
cv::namedWindow("Color"); | |
cv::namedWindow("Depth"); | |
cout << endl << "--------------------------------------------------------" << endl; | |
cout << " [Esc]:終了" << endl; | |
cout << " [c]:カラー画像の保存" << endl; | |
cout << " [d]:デプス画像の保存" << endl; | |
cout << "--------------------------------------------------------" << endl; | |
while (1) { | |
// (5-1) kinect v2からのカラー画像の取得 | |
IColorFrame* p_color_frame = nullptr; | |
Mat color_image(color_height, color_width, CV_8UC4); | |
int color_buffer_size = color_width * color_height * 4 * sizeof(unsigned char); | |
HRESULT color_result = p_color_reader->AcquireLatestFrame(&p_color_frame); | |
if (SUCCEEDED(color_result)) { | |
color_result = p_color_frame->CopyConvertedFrameDataToArray(color_buffer_size, reinterpret_cast<BYTE*>(color_image.data), ColorImageFormat_Bgra); | |
if (SUCCEEDED(color_result)) { | |
resize(color_image, color_image, cv::Size(), 0.5, 0.5); | |
flip(color_image, color_image, 1); // 水平反転 | |
} | |
} | |
if (p_color_frame != nullptr) { | |
p_color_frame->Release(); | |
} | |
if (SUCCEEDED(color_result)) { | |
cv::imshow("Color", color_image); | |
} | |
// (5-2) kinect v2からのデプス画像の取得 | |
IDepthFrame* p_depth_frame = nullptr; | |
Mat depth_image(depth_height, depth_width, CV_8UC1); | |
int depth_buffer_size = depth_width * depth_height; | |
unsigned short *depth_buffer; | |
depth_buffer = new unsigned short[depth_buffer_size]; | |
HRESULT depth_result = p_depth_reader->AcquireLatestFrame(&p_depth_frame); | |
if (SUCCEEDED(depth_result)) { | |
p_depth_frame->CopyFrameDataToArray(depth_buffer_size, depth_buffer); | |
// 深度0~8000[mm]で取得されるので、0~255で正規化 | |
for (int y = 0; y < depth_buffer_size; y++) { | |
depth_image.data[y] = (unsigned int)(depth_buffer[y] * 255.0f / 8000.0f); | |
} | |
flip(depth_image, depth_image, 1); // 水平反転 | |
} | |
// 使わないので解放 | |
if (p_depth_frame != nullptr) { | |
p_depth_frame->Release(); | |
} | |
if (SUCCEEDED(depth_result)) { | |
cv::imshow("Depth", depth_image); | |
} | |
// (6) キーが押されたときの処理 | |
char key = cv::waitKey(30); | |
if (key == VK_ESCAPE) { | |
break; | |
} | |
else if (key == 'c') { | |
string filename = "color_" + to_string(frame_counter) + ".png"; | |
imwrite(filename, color_image); | |
cout << filename << "を保存しました" << endl; | |
} else if (key == 'd') { | |
string filename = "depth_" + to_string(frame_counter) + ".png"; | |
imwrite(filename, depth_image); | |
cout << filename << "を保存しました" << endl; | |
} | |
frame_counter++; | |
} | |
kinect->Close(); | |
return 0; | |
} |
実際にプログラムをビルド、実行できることを確認してください。
また、今回はプログラムをコンパクトにするためにエラー処理等をあまり書いていないので、実際は書いた方がいいでしょう。
少々変な方向を向いていますが、実行すると上記のようにカラー画像用のウィンドウと、デプス画像用のウィンドウが同時に出ます。
[c]ボタンを押したときにカラー画像を保存し、[d]ボタンを押したときにデプス画像が保存できるようにしてあります。
これで、試しに部屋の一部分を撮影してみました。
カラー画像とデプス画像で撮影領域が若干異なっていることもわかる気がします。
また、デプスは今回0m~8mの深さを0~255の値で正規化したのですが、遠いところがないため全体的に暗い感じになっていますね。
コードの解説等もしていきたいところですが、長くなってしまったのでコード部分の説明は記事を分けたいと思います。
まとめ
カラー画像とデプス画像をウィンドウに同時に出し、任意のフレームを保存できるプログラムについて実行を確認しました。
OpenCV等と組み合わせることで、様々な利用方法が検討できそうです。
それでは、今回はこれで終わりとしたいと思います。