Subscribe RSS Join our Facebook Group Follow us on Twitter!
in Search

Samsu's passion (.NET)

Blog mengenai kegiatan sebagai MSP, MIC, dan juga penyuka teknologi Microsoft

Kinect Body Tracking

Setelah dalam posting-posting sebelumnya membahas mengenai apa itu dan apa yang sedang dilakukan dengan Kinect. Pada post ini saya mencoba untuk membuat suatu program dengan Microsoft Visual Studio C++ (saya menggunakan VS2010) dan OpenNI library untuk melakukan body tracking yaitu melacak posisi seseorang di depan Kinect.

Teknologi di balik ini sebenarnya dipelajari dalam bidang computer vision. Terdapat beberapat pendekatan utama untuk mendeteksi dan tracking posisi manusia. Teknik yang umum digunakan yaitu background subtraction dan HOG (Histogram of Oriented Gradient). Bagi yang tertarik dapat membaca survey paper yang membahas mengenai hal ini atau langsung membaca beberapa paper terkait. Beberapa jurnal yang mungkin menarik untuk dibaca:

Post saya kali ini hanya akan membahas secara ringkasi dari sisi teknologi yang digunakan dan hasil yang diperoleh. Untuk info lebih lanjut mengenai implementasi dan hal-hal teknis lainnya via PM saja, ok?

Prerequisite : Microsoft Kinect, C/C++ basic knowledge

Pertama-tama, kita harus melakukan setting awal untuk membuat Kinect dapat digunakan bersama Windows termasuk pengaturan dalam visual studio C++. Tutorial untuk melakukan pengaturan saya lampirkan di akhir post ini.

Selanjutnya kode berikut dapat digunakan untuk memanggil fungsi deteksi dan tracking dari OpenNI. Langkah-langkahnya dapat dikelompokkan sebagai berikut :

  • Inisialisasi : membuat objek-objek generator seperti user generator, depth generator, dan image generator. Namun untuk melakukan deteksi user sebenarnya cukup menggunakan user generator. (baris 1-77)
  • Proses     : pada tahap ini, data dari setiap generator diupdate. Pada tahap ini juga dilakukan proses yang diinginkan, misalnya menggambarkan ke layar kondisi terbaru dari tiap user seperti yang dilakukan di program ini. (baris 79-135)
  • Terminasi  : melepaskan setiap resource yang digunakan. (baris 136-139)
    Keterangan lebih lanjut dapat mengecek dokumentasi OpenNI.
   1: #include <cvaux.h>
   2: #include <highgui.h>
   3: #include <XnCppWrapper.h>
   4: #include <string>
   5: #include <conio.h>
   6:  
   7: using namespace cv;
   8: using namespace xn;
   9: using namespace std;
  10:  
  11: #define SAMPLE_XML_PATH "XML_Tracking.xml"
  12: #define CHECK_RC(rc, what)                                        \
  13: if (rc != XN_STATUS_OK)                                            \
  14: {                                                                \
  15:     printf("%s failed: %s\n", what, xnGetStatusString(rc));        \
  16:     return rc;                                                    \
  17: }
  18:  
  19: int main(int argc, char** argv)
  20: {
  21:     Context context;
  22:     EnumerationErrors errors;
  23:     XnStatus nRetVal;
  24:  
  25:     nRetVal = context.InitFromXmlFile(SAMPLE_XML_PATH);
  26:     CHECK_RC(nRetVal,"Initialize Context");
  27:  
  28:     //DEPTH
  29:     DepthGenerator depth;    //depth context
  30:     DepthMetaData depthMD;    //depth metadata
  31:     nRetVal = context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth);
  32:     CHECK_RC(nRetVal,"Initialize Depth Node");
  33:     
  34:     //RGB IMAGE
  35:     ImageGenerator image;    //image context
  36:     ImageMetaData imageMD;    //image metadata
  37:     nRetVal = context.FindExistingNode(XN_NODE_TYPE_IMAGE, image);
  38:     CHECK_RC(nRetVal,"Initialize Image Node");
  39:     
  40:     //USER
  41:     UserGenerator user;
  42:     XnCallbackHandle hUserCallbacks;
  43:     nRetVal = context.FindExistingNode(XN_NODE_TYPE_USER, user);
  44:     CHECK_RC(nRetVal,"Initialize User Node");
  45:     
  46:     //Scene
  47:     SceneMetaData scene;
  48:     SceneMetaData sceneUser;
  49:  
  50:     //OPENCV VARIABLE
  51:     Mat depthshow;
  52:     Mat show;
  53:     IplImage* tracked = cvCreateImage(cvSize(500,500), 8, 3);    
  54:     imshow("tracked",tracked);
  55:     int numframe = 0;
  56:     CvFont font;
  57:     cvInitFont( &font, CV_FONT_HERSHEY_COMPLEX_SMALL, 1.0, 1.0, 0, 1);
  58:     
  59:     //DRAW GRID PATTERN IN THE OUTPUT TRAJECTORY WINDOW
  60:     cvLine(tracked,cvPoint( 50,0),cvPoint( 50,500),CV_RGB(150,150,150),2);
  61:     cvLine(tracked,cvPoint(150,0),cvPoint(150,500),CV_RGB(150,150,150),2);
  62:     cvLine(tracked,cvPoint(250,0),cvPoint(250,500),CV_RGB(150,150,150),2);
  63:     cvLine(tracked,cvPoint(350,0),cvPoint(350,500),CV_RGB(150,150,150),2);
  64:     cvLine(tracked,cvPoint(450,0),cvPoint(450,500),CV_RGB(150,150,150),2);
  65:     cvLine(tracked,cvPoint(0,100),cvPoint(500,100),CV_RGB(150,150,150),2);
  66:     cvLine(tracked,cvPoint(0,200),cvPoint(500,200),CV_RGB(150,150,150),2);
  67:     cvLine(tracked,cvPoint(0,300),cvPoint(500,300),CV_RGB(150,150,150),2);
  68:     cvLine(tracked,cvPoint(0,400),cvPoint(500,400),CV_RGB(150,150,150),2);
  69:     
  70:     //PATH 
  71:     cvLine(tracked,cvPoint(350,300),cvPoint(150,300),CV_RGB(255,255,0),2);
  72:     cvLine(tracked,cvPoint(150,300),cvPoint(150,200),CV_RGB(255,255,0),2);
  73:     cvLine(tracked,cvPoint(150,200),cvPoint(350,200),CV_RGB(255,255,0),2);
  74:     cvLine(tracked,cvPoint(350,200),cvPoint(350,100),CV_RGB(255,255,0),2);
  75:     cvLine(tracked,cvPoint(350,100),cvPoint(150,100),CV_RGB(255,255,0),2);
  76:     cvLine(tracked,cvPoint(150,100),cvPoint(150,0),CV_RGB(255,255,0),2);
  77:     cvLine(tracked,cvPoint(150,0),cvPoint(350,0),CV_RGB(255,255,0),2);
  78:  
  79:     while (kbhit() == 0)
  80:     {
  81:         context.WaitAnyUpdateAll();
  82:         image.GetMetaData(imageMD);
  83:         depth.GetMetaData(depthMD);
  84:         numframe++;
  85:             
  86:         ///Converting to OpenCV Matrix
  87:         Mat depth16(480,640,CV_16SC1,(unsigned short*)depthMD.WritableData());
  88:         depth16.convertTo(depthshow,CV_8U,-255/4096.0,255); //convert depth 16 bit image to 8 bit image
  89:         Mat imni(480,640,CV_8UC3,(uchar*)imageMD.WritableData());
  90:         cvtColor(imni,show,CV_RGB2BGR);                        //convert color space : RGB2BGR
  91:  
  92:         int number_of_user = user.GetNumberOfUsers();
  93:         if (number_of_user > 0) {
  94:             nRetVal = user.GetUserPixels(0,scene);
  95:             CHECK_RC(nRetVal, "Get Scene Meta Data");
  96:             Mat sceneMap(480,640,CV_16SC1,(unsigned short*)scene.WritableData());
  97:             sceneMap.convertTo(sceneMap,8,60,0);
  98:             
  99:             for (int i=1;i<=number_of_user;i++) {
 100:                 XnPoint3D CoM;
 101:                 xnGetUserCoM(user,i,&CoM);
 102:  
 103:                 if (CoM.X != 0 && CoM.Y != 0 && CoM.Z != 0) {
 104:                     int R=255,G=255,B=255;
 105:  
 106:                     //APPROXIMATION OF HUMAN CENTRAL POINT
 107:                     int x = (int) CoM.X/10;
 108:                     int z = (int) CoM.Z/10;
 109:                     int xx,zz;
 110:                     if (x == 0) {
 111:                         xx = 0;
 112:                         zz = z + 20;
 113:                     }
 114:                     else {
 115:                         xx = (int) x + (20 * x / sqrt((double) x*x + z*z));
 116:                         zz = (int) z + (20 * z / sqrt((double) x*x + z*z));
 117:                     }
 118:                     cvCircle(tracked,cvPoint(250 + xx, 500 - zz),2,CV_RGB(R,G,B),-1);
 119:                     printf("%d\t%d\n", xx, zz);
 120:  
 121:  
 122:                     ///CENTER OF MASS
 123:                     XnPoint3D imagePoint;
 124:                     depth.ConvertRealWorldToProjective(1,&CoM,&imagePoint);
 125:                     cvCircle(&show.operator IplImage(),cvPoint((int)imagePoint.X,(int)imagePoint.Y),5,CV_RGB(R,G,B),-1);
 126:                 }
 127:             }
 128:             imshow("scene",sceneMap);
 129:             imshow("tracked", tracked);
 130:         }
 131:         imshow("image",show);
 132:         imshow("depth",depthshow);
 133:  
 134:         waitKey(33);
 135:     }
 136:     cvReleaseImage(&tracked);
 137:     context.Shutdown();
 138:     return 0;
 139: }

File XML_Tracking.xml yang digunakan sebagai berikut :

   1: <OpenNI>
   2:     <Licenses></Licenses>
   3:     <Log writeToConsole="false" writeToFile="false">
   4:         <!-- 0 - Verbose, 1 - Info, 2 - Warning, 3 - Error (default) -->
   5:         <LogLevel value="3"/>
   6:         <Masks> <Mask name="ALL" on="true"/> </Masks>
   7:         <Dumps></Dumps>
   8:     </Log>
   9:     <ProductionNodes>
  10:         <!--<Recording file="test.oni" />--> <!-- jika menggunakan file recording-->
  11:         <Node type="Image" name="Image1">
  12:             <Configuration>
  13:                 <MapOutputMode xRes="640" yRes="480" FPS="30"/>
  14:                 <Mirror on="false"/>
  15:             </Configuration>
  16:         </Node>
  17:         <Node type="Depth" name="Depth1">
  18:             <Configuration>
  19:                 <MapOutputMode xRes="640" yRes="480" FPS="30"/>
  20:                 <Mirror on="false"/>
  21:                 <AlternativeViewPoint>Image1</AlternativeViewPoint>
  22:             </Configuration>
  23:         </Node>
  24:         <Node type="User"/>
  25:     </ProductionNodes>
  26: </OpenNI>

hasil dari body tracking ini dapat dilihat dari video berikut. tujuan dari program ini selain melakukan human tracking juga mengecek akurasi Kinect (dengan mensimulasikan pada ruangan berukuran 5x5 meter). Ternyata didapatkan hasil yang cukup memuaskan yaitu selisih 5-10 cm pada kedua sumbu. (lihat detailnya di blog tambahan saya)

obtaining human trajectory with Microsoft Kinect

Keterangan :

  • Window kiri : siluet manusia (dari depth camera),
  • window kanan : hasil tracking dalam koordinat real-world (kuning :ground truth, putih : trajectory yang didapat)
  • Panduan setting Kinect, OpenNI, dan IDE Microsoft Visual Studio dapat diunduh di sini.

    870 Views, 1 Comment(s), Published on: 04-18-2011 23:06 by samsu.sempena to Samsu's passion (.NET)
    | More
    Filed under: ,

    Comments

     

    Samsu's passion (.NET) said:

    XNA seperti yang telah dibahas pada post sebelumnya, merupakan framework yang disediakan Microsoft untuk

    May 30, 2011 12:31 PM

    About samsu.sempena

    2007-now Faculty of Informatics, Institute Teknologi Bandung