Kinect OpenNIによる3次元ポイントクラウド - 3次元描画 高速化
今回はマイクロソフトが公式で公開しているKinect for Windows SDKを使ってポイントクラウドを行ってみます。
描画にはいつもどおりOpenGLを使い、その過程でOpenCVを使っています。
まず初めにKinect SDKでRGB画像とDepth画像の取得方法について簡単に説明します。
細かい説明なんてどうでもいいんだよ!って人は 下のほうでサンプルプログラムがダウンロードできるので、下の方へスクロールしてってください。
では、プログラムの初期化処理は以下のようになる
初期化処理では
HRESULT NuiInitialize( DWORD dwFlags )
で初期化します。
引数はRGB画像、Depth画像、骨格情報を得る場合
NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON
とします 次に画像取得のイベントハンドルを設定します。
Depth画像の大きさによって使用するNuiImageStreamOpen関数の引数が違うので
KINECT_DEPTH_WIDTHを#defineで事前に定義して、ここで条件分岐しています。
KinectSDKはBetaの時はDepth画像の解像度を320x240しか設定できなかったのですがv1.0になり、640x480に設定することが可能となりました。
上記で設定したイベントで以下のようにしてRGB画像とDepth画像を取得します
扱いやすい形にするため、OpenCVのMatに格納します
また、その処理を関数化します
ここではGetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle)
という関数を作りました。
この関数は以下のように用います。
関数の戻り値が-1の時は取得失敗ということです。
Kinect SDKではOpenNIを違い、Matへの格納が少しめんどうですね。
次にポイントクラウドを行う上で、画像のピクセルが現実座標ではどこにあるのかを知る必要があります。
OpenNIではConvertProjectiveToRealWorldという関数がありました。
Kinect SDKでは以下の関数を用います
Vector4 NuiTransformDepthImageToSkeleton(
LONG lDepthX,
LONG lDepthY,
USHORT usDepthValue
)
この関数はDepth画像のピクセルの位置(x,y)とその場所の値を与えることで現実座標系で帰ってきます。
では、Depth画像から現実座標配列を格納する関数を以下のように作ります。
NuiTransformDepthImageToSkeletonはDepth画像の解像度により引数を追加します。
これはbetaの時にDepth画像が640x480に対応していなかったからかなぁっと思います。
で、最後にポイントクラウドで描画します。
drawPointCloudではRGB画像とretrievePointCloudMapで取得した三次元位置を渡し、描画します。
また、この関数内でRGBとDepthのズレを補正しています。
OpepNIでいう
DepthGenerator.GetAlternativeViewPointCap().SetViewPoint は
KinectSDKでは
NuiImageGetColorPixelCoordinatesFromDepthPixel
または
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution
を使います
これも、Depthの解像度により関数を変える必要があります。
まあ、NuiImageGetColorPixelCoordinatesFromDepthPixelのなかでNuiImageGetColorPixelCoordinatesFromDepthPixelAtResolutionが呼ばれてるんですけどね
これまで長く説明してきましたけど、まあ、こんな説明読むよりサンプルプログラムを見たほうが早いかな?
全体のプログラムはこんな感じです。
github
サンプルダウンロード