2014年10月1日水曜日

【kinect使ってみる。】座標のずれ

kinect記事前回では、depth画像とcolor画像の座標のズレを合わすところまで入てやりました。

↓これで座標を変換しているらしい
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution()

しかし、それでも若干ずれている気がする~で終わりました。 

今回はそれをとりあえず直すことをやってみます。



※プログラミングスキルは初心者なので間違いや無駄等あると思います。


事前にに保存した画像でなんやかんややるので下記のプログラムではKinectは使っていません。


#include <iostream>
#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char* argv[]){

 Mat depth, depth2; //depth2:ズラす用
 int counter = 0;
 char readBuf[256];
 char writeBuf[256];

 //動画で書き出す用
 //double fps = 15;
 //VideoWriter wDepth("wDepth.avi", CV_FOURCC('D', 'I', 'B', ' '), fps, cv::Size(640, 480), true);

 while (1){

  //事前に保存した画像の読み込み
  /*
  保存時にファイル名に連番を振っていたので
  counterで番号を回しながら読み込んで行きます。
  自分の場合、別のディレクトリに保存していました。
  */
  sprintf_s(readBuf, "..\\元画像保存ディレクトリ\\ファイル名%04d.jpg", counter);

  depth = imread(readBuf);
  depth2 = Mat::zeros(480, 640, CV_8UC3); //とりあえず真っ黒を用意
 if (depth.data == NULL) break;

  for (int y = 0; y < depth.rows; y++){

   Vec3b* ptrDepth = depth.ptr<Vec3b>(y);
   Vec3b* ptrDepth2 = depth2.ptr<Vec3b>(y); //ズラす用

   for (int x = 0; x < depth.cols; x++){

    if (x + 13 < depth2.cols)
     ptrDepth2[x + 13] = ptrDepth[x]; //ここで右に13ズラす

   }
  }


  //表示
  cv::imshow("depth", depth);
  cv::imshow("depth2", depth2);


  //静止画で書き出す
  sprintf_s(writeBuf, "保存先ディレクトリ\\保存ファイル名%04d.jpg", counter); 
  imwrite(writeBuf, depth2);

  //動画で書き出す
  //wDepth << depth2;

  //キー入力
  int key = cv::waitKey(10);
  if (key == 'q') break;


  counter++;
 }//while

 //終了
 //wDepth.release();
 cv::destroyAllWindows();
}








実行結果
元画像:



ずらした画像:





別途編集ソフト使って重ね合わせてみる
color画像:



color画像と元depth画像:




color画像とズラしたdepth画像:







画素にアクセスして x について右にズラしただけです。

ずらしたものを静止画で保存しています。

元の深度画像とカラー画像を合わせてみると横方向にずれているように見えます。

それを単純に右にずらしました。なんとなく合ってるよね?

(もし、元画像の方でdepthとcolorで取得のタイミングが異なる場合は時間とかでラベル付けしたり肉眼で比較してみて合わせてだいたい同じタイミングの画像を探してみるとか。)


きっともっとスマートな方法があるのだと思いますが、勉強不足ゆえこんな感じです。

肉眼を頼りにずれを調節しているので、正確さは保障できないと思います。とりあえずです。



今回は以上。


---[追記]-----

ある論文(出典見失った;・・・たしか阪大かどこかだったような)によると
Kinectはカラーカメラとデプスカメラは非同期らしいので、そもそもズレがあるようです。
ただ、フレームを取得する間隔はどちらも一定で、その幅がカラーとデプスでは異なるようなのでズレが生じているらしいです。

まぁだから↑のように単純に座標をズラしただけじゃダメってことだと思います。

ただXBOX版の時の話なので、今はどうなのか・・・。また調べてみようと思います。

--------


参考:
http://d.akiroom.com/2012-06/opencv-pixel-read-write-get-set/
http://kassymemo.blogspot.jp/2011/09/opencv2cvmat.html











2014年9月25日木曜日

【kinect使ってみる。】

kinect v2だっけ?新しいバージョンが発売するそうだけど、それはwindows8専用らしいです。

でとりあえずwindows版とxbox版が手元に転がっていたのでこれらを使ってこれから遊んでいこうと思います。





試しにこのサンプルを使ってみます。

プレイヤーの認識とかは今回は使わないのでカットして、colorカメラの表示、depth座標をcolor座標に変換するのを加えてみました。

だいたいコピペです。
でも正しいかどうかは素人勉強中のためよくわかっていません。
プログラミングスキルもカスなのでご了承を。
逆にアドバイスもらえると嬉しいです。


環境:
windows7 64bit home premium
visual stadio 2013
openCV2.4.8
kinect for windows SDK v1.8


#include <Windows.h>
#include <NuiApi.h>

#include <iostream>
#include <conio.h>

#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;

void main()
{
 try {
  int count = 0;
  ::NuiGetSensorCount(&count);
  if (count == 0) {
   throw std::runtime_error("利用可能なKinectがありません");
  }

  INuiSensor* kinect;
  ::NuiCreateSensorByIndex(0, &kinect);

  //変更
  kinect->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH | NUI_INITIALIZE_FLAG_USES_COLOR);

  //追加:colorHandle
  HANDLE depthHandle;
  HANDLE colorHandle;

  //追加
  //depth
  kinect->NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH, NUI_IMAGE_RESOLUTION_640x480, 0, 2, 0, &depthHandle);
  //color
  kinect->NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480, 0, 2, 0, &colorHandle);

  DWORD width = 0;
  DWORD height = 0;
  ::NuiImageResolutionToSize(NUI_IMAGE_RESOLUTION_640x480, width, height);

  while (true) {
   // 距離カメラのフレームデータを取り出す
   NUI_IMAGE_FRAME depthFrame = { 0 };
   kinect->NuiImageStreamGetNextFrame(depthHandle, INFINITE, &depthFrame);
   
   //追加:colorカメラのフレームデータを取り出す
   NUI_IMAGE_FRAME colorFrame = { 0 };
   kinect->NuiImageStreamGetNextFrame(colorHandle, INFINITE, &colorFrame);

   // フレームデータを元に、拡張距離データを取得する
   BOOL nearMode = FALSE;
   INuiFrameTexture *frameTexture = 0;
   kinect->NuiImageFrameGetDepthImagePixelFrameTexture(
    depthHandle, &depthFrame, &nearMode, &frameTexture);
   //追加:color
   INuiFrameTexture *colorframeTexture = colorFrame.pFrameTexture;

   // NUI_DEPTH_IMAGE_PIXELを取り出す
   NUI_LOCKED_RECT rectDepth = { 0 }; //名前変えたrect→rectDepth
   frameTexture->LockRect(0, &rectDepth, 0, 0);

   //追加:カラー
   NUI_LOCKED_RECT rectColor = { 0 };
   colorframeTexture->LockRect(0, &rectColor, 0, 0);

   NUI_DEPTH_IMAGE_PIXEL* depthPixel = (NUI_DEPTH_IMAGE_PIXEL*)rectDepth.pBits;

   // 表示する
   cv::Mat cameraDepth(height, width, CV_8UC3);
   //追加
   cv::Mat cameraColor(height, width, CV_8UC4, reinterpret_cast<ushort*>(rectColor.pBits));

   for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
     int depthIndex = (y * width) + x;

     //追加
     unsigned char iro = depthPixel[depthIndex].depth * 255 / 1000;

     //追加:depthとcolorの位置合わせ    
     LONG colorX = 0;
     LONG colorY = 0;
     kinect->NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(
      NUI_IMAGE_RESOLUTION_640x480, NUI_IMAGE_RESOLUTION_640x480,
      nullptr, x, y, 0, &colorX, &colorY );
     int cameraIndex = ((colorY * width) + colorX) * 3; 
     

     //追加
     //1mより遠い場合
     if (depthPixel[depthIndex].depth > 1000) {
      cameraDepth.data[cameraIndex] = 0;
      cameraDepth.data[cameraIndex + 1] = 0;
      cameraDepth.data[cameraIndex + 2] = 0;
     }
     else{ // 1mより近い場合
      cameraDepth.data[cameraIndex] = iro;
      cameraDepth.data[cameraIndex + 1] = iro;
      cameraDepth.data[cameraIndex + 2] = iro;
     }
    }
   } 

   imshow("depth", cameraDepth);
   //追加:カラー表示
   imshow("color", cameraColor);

   kinect->NuiImageStreamReleaseFrame(depthHandle, &depthFrame);
   //追加  
   kinect->NuiImageStreamReleaseFrame(colorHandle, &colorFrame);

   int key = cv::waitKey(10);
   if (key == 'q') {
    break;
   }
  }
 }
 catch (exception& ex) {
  cout << ex.what() << endl;
 }
}




実行結果(あるフレームでの)
Color:


Depth:


1mより先は黒で塗りつぶしています。
あと若干まだ座標のズレを感じます。距離画像の方が左に10画素くらい。


参考文献