Saving RGB and Depth Data from Kinect as an Image (with OpenNI)
29 Mar 2013This is not my work, I am reposting it just incase if it gets removed. Source: https://groups.google.com/forum/?fromgroups=#!msg/openni-dev/iYtcrrA365U/wEFT2_-mH0wJ
Hi all,
I am working on foreground segmentation using kinect. I needed to
extract the color and depth images in a synchronized and registerd way
and This thread has been very useful for me. I write you the code I
have used to do it, if someone need to do the same:
I started modifying the NiViewer sample code you can find in:
/OpenNI/Platform/Linux-x86/Redist/Samples/NiViewer
Then, I modified some of their files to achieve the .jpg recording
jointly with the .oni file. To save the images, I have used opencv
library.
extract RGB images (in Device.cpp):
//new includes:
#include "cv.h"
#include "highgui.h"
#include "sstream"
#include "string"
//jaume. New function to save RGB frames in jpg format.
void saveFrame_RGB(int num)
{
cv::Mat colorArr[3];
cv::Mat colorImage;
const XnRGB24Pixel* pImageRow;
const XnRGB24Pixel* pPixel;
// ImageMetaData* g_ImageMD = getImageMetaData();
g_Image.GetMetaData(g_ImageMD);
pImageRow = g_ImageMD.RGB24Data();
colorArr[0] = cv::Mat(g_ImageMD.YRes(),g_ImageMD.XRes(),CV_8U);
colorArr[1] = cv::Mat(g_ImageMD.YRes(),g_ImageMD.XRes(),CV_8U);
colorArr[2] = cv::Mat(g_ImageMD.YRes(),g_ImageMD.XRes(),CV_8U);
for (int y=0; y<g_ImageMD.YRes(); y++)
{
pPixel = pImageRow;
uchar* Bptr = colorArr[0].ptr<uchar>(y);
uchar* Gptr = colorArr[1].ptr<uchar>(y);
uchar* Rptr = colorArr[2].ptr<uchar>(y);
for(int x=0;x<g_ImageMD.XRes();++x , ++pPixel)
{
Bptr[x] = pPixel->nBlue;
Gptr[x] = pPixel->nGreen;
Rptr[x] = pPixel->nRed;
}
pImageRow += g_ImageMD.XRes();
}
cv::merge(colorArr,3,colorImage);
char framenumber[10];
sprintf(framenumber,"%06d",num);
std::stringstream ss;
std::string str_frame_number;
// char c = 'a';
ss << framenumber;
ss >> str_frame_number;
std::string str_aux = "CapturedFrames/image_RGB_"+
str_frame_number +".jpg";
IplImage bgrIpl = colorImage; // create a
IplImage header for the cv::Mat bgrImage
cvSaveImage(str_aux.c_str(),&bgrIpl); // save it with the
old
}
extract Depth images (in Draw.cpp):
//new includes:
#include "cv.h"
#include "highgui.h"
//jaume. New function to save depth map in jpg format. I have based
this implementation on the draw images function.
void saveFrame_depth(int num)
{
const DepthMetaData* pDepthMD = getDepthMetaData();
const XnDepthPixel* pDepth = pDepthMD->Data();
XN_ASSERT(pDepth);
cv::Mat depthImage;
cv::Mat colorArr[3];
colorArr[0] = cv::Mat(pDepthMD->YRes(),pDepthMD->XRes(),CV_8U);
colorArr[1] = cv::Mat(pDepthMD->YRes(),pDepthMD->XRes(),CV_8U);
colorArr[2] = cv::Mat(pDepthMD->YRes(),pDepthMD->XRes(),CV_8U);
for (XnUInt16 nY = pDepthMD->YOffset(); nY < pDepthMD->YRes() +
pDepthMD->YOffset(); nY++)
{
XnUInt8* pTexture = TextureMapGetLine(&g_texDepth, nY) + pDepthMD-
>XOffset()*4;
uchar* Bptr = colorArr[0].ptr<uchar>(nY);
uchar* Gptr = colorArr[1].ptr<uchar>(nY);
uchar* Rptr = colorArr[2].ptr<uchar>(nY);
for (XnUInt16 nX = 0; nX < pDepthMD->XRes(); nX++, pDepth++,
pTexture+=4)
{
XnUInt8 nRed = 0;
XnUInt8 nGreen = 0;
XnUInt8 nBlue = 0;
XnUInt8 nAlpha = g_DrawConfig.Streams.Depth.fTransparency*255;
XnUInt16 nColIndex;
switch (g_DrawConfig.Streams.Depth.Coloring)
{
case LINEAR_HISTOGRAM:
nBlue = nRed = nGreen = g_pDepthHist[*pDepth]*255;
break;
case PSYCHEDELIC_SHADES:
nAlpha *= (((XnFloat)(*pDepth % 10) / 20) + 0.5);
case PSYCHEDELIC:
switch ((*pDepth/10) % 10)
{
case 0:
nRed = 255;
break;
case 1:
nGreen = 255;
break;
case 2:
nBlue = 255;
break;
case 3:
nRed = 255;
nGreen = 255;
break;
case 4:
nGreen = 255;
nBlue = 255;
break;
case 5:
nRed = 255;
nBlue = 255;
break;
case 6:
nRed = 255;
nGreen = 255;
nBlue = 255;
break;
case 7:
nRed = 127;
nBlue = 255;
break;
case 8:
nRed = 255;
nBlue = 127;
break;
case 9:
nRed = 127;
nGreen = 255;
break;
}
break;
case RAINBOW:
nColIndex = (XnUInt16)((*pDepth / (g_fMaxDepth / 256)));
nRed = PalletIntsR[nColIndex];
nGreen = PalletIntsG[nColIndex];
nBlue = PalletIntsB[nColIndex];
break;
case CYCLIC_RAINBOW:
nColIndex = (*pDepth % 256);
nRed = PalletIntsR[nColIndex];
nGreen = PalletIntsG[nColIndex];
nBlue = PalletIntsB[nColIndex];
break;
}
Bptr[nX] = nBlue ;
Gptr[nX] = nGreen;
Rptr[nX] = nRed;
}
}
cv::merge(colorArr,3, depthImage);
char framenumber[10];
sprintf(framenumber,"%06d",num);
std::stringstream ss;
std::string str_frame_number;
ss << framenumber;
ss >> str_frame_number;
//CapturedFrames folder must exist!!!
std::string str_aux = "CapturedFrames/image_depth_"+
str_frame_number +".jpg";
IplImage bgrIpl = depthImage; // create a
IplImage header for the cv::Mat bgrImage
cvSaveImage(str_aux.c_str(),&bgrIpl); // save it with the
old
}
File where I use these new functionalities: Capture.cpp.
//New include:
#include <iostream>
//Function modified to save frames in jpg format:
XnStatus captureFrame()
{
XnStatus nRetVal = XN_STATUS_OK;
if (g_Capture.State == SHOULD_CAPTURE)
{
XnUInt64 nNow;
xnOSGetTimeStamp(&nNow);
nNow /= 1000;
if (nNow >= g_Capture.nStartOn)
{
g_Capture.nCapturedFrames = 0;
g_Capture.State = CAPTURING;
}
}
if (g_Capture.State == CAPTURING)
{
nRetVal = g_Capture.pRecorder->Record();
XN_IS_STATUS_OK(nRetVal);
//start.jaume
saveFrame_RGB(g_Capture.nCapturedFrames);
saveFrame_depth(g_Capture.nCapturedFrames);
//end.jaume
g_Capture.nCapturedFrames++;
}
return XN_STATUS_OK;
}
To test the code, you must execute the new NiViewer app, and use the
options Capture->start that appear just clicking in the left mouse
button.
It's all, I hope this code will be useful.
Jaume