Showing posts with label raspberrypi. Show all posts
Showing posts with label raspberrypi. Show all posts

Friday, March 29, 2013

OpenNI + Depth & IR Compression + Pandaboard

[Update 4/14] I do not have access to a pandaboard yet, therefore I am working on this in Ubuntu with a Kinect.

I am looking to compress depth images from a Kinect or Asus Xtion using OpenNI. Currently I am trying to modify NiViewer to capture and save depth frames as images.

See https://groups.google.com/forum/?fromgroups=#!msg/openni-dev/iYtcrrA365U/wEFT2_-mH0wJ
I edited the files as listed on the google groups post. Alternate link: http://gremsi.blogspot.com/2013/04/saving-rgb-and-depth-data-from-kinect.html

Install OpenCV: http://mitchtech.net/raspberry-pi-opencv/

Edit the make file for NiViewer using this: http://ubuntuforums.org/showthread.php?t=1895678

[Update 4/7]
I had a little trouble with the makefile for NiViewer using the link above. Here is what I have for  OpenNI/Platform/Linux/Build/Samples/NiViewer/Makefile


include ../../Common/CommonDefs.mak
BIN_DIR = ../../../Bin
INC_DIRS = \
../../../../../Include \
../../../../../Samples/NiViewer \
/usr/local/lib \
/usr/local/include/opencv
SRC_FILES = ../../../../../Samples/NiViewer/*.cpp
ifeq ("$(OSTYPE)","Darwin")
LDFLAGS += -framework OpenGL -framework GLUT
else
USED_LIBS += glut GL opencv_core opencv_highgui
endif
USED_LIBS += OpenNI
EXE_NAME = NiViewer
CFLAGS        = -pipe -O2 -I/usr/local/include/opencv  -D_REENTRANT $(DEFINES)
CXXFLAGS      = -pipe -O2 -I/usr/local/include/opencv  -D_REENTRANT $(DEFINES)
include ../../Common/CommonCppMakefile


[Update 4/14]
I ran NiViewer, right click and start capture. So as of now, it saves depth images (in OpenNI/Platform/Linux/Bin/(your platform)/CaptureFrames). Remember to create the CapturedFrames folder.

Now, I want it to start capturing as soon as I ran the program so I edited the NiViewer.cpp file to this:

Comment this part out in the main method (the part that handles the user interface):
reshaper.zNear = 1;
reshaper.zFar = 100;

glut_add_interactor(&reshaper);
cb.mouse_function = MouseCallback;
cb.motion_function = MotionCallback;
cb.passive_motion_function = MotionCallback;
cb.keyboard_function = KeyboardCallback;
cb.reshape_function = ReshapeCallback;
glut_add_interactor(&cb);
glutInit(&argc, argv);
glutInitDisplayString("stencil double rgb");
glutInitWindowSize(WIN_SIZE_X, WIN_SIZE_Y);
glutCreateWindow("OpenNI Viewer");
glutFullScreen();
glutSetCursor(GLUT_CURSOR_NONE);
init_opengl();
glut_helpers_initialize();
glutIdleFunc(IdleCallback);
glutDisplayFunc(drawFrame);
drawInit();
createKeyboardMap();
createMenu();
atexit(onExit);
glutMainLoop();
 before the audioShutdown() command is called, I added these lines:

captureStart(0);
int i = 0;
while (i<10)
{
captureFrame();
i++;
}
captureStop(0);
I compiled and ran NiViewer, but I was only getting blank images. This is because the saveFrame_depth() function (from the google groups post) is using a Linear Histogram. It seems that the calculateHistogram method needs to be called before the Linear Histogram is used. Before, drawFrame() was calling the calculateHistogram function and that is why it worked. I just wanted to see if this worked in general so inside the saveFrame_depth() function, there is a line that says switch(g_DrawConfig.Streams.Depth.Coloring), change this to switch(PSYCHEDELIC); Now I am able to see something in my saved depth image.

[Update 4/17]
I wanted to see how much space it would take for depth data to be stored without any compression. I first started out writing the depth values to a binary file (in plain ascii). This is pretty simple but I am just writing everything out incase if anyone is confused. To do this, I created a new file in the beginning of the saveFrame_depth function:
ofstream myfile;
myfile.open("depthData_ascii");
Then inside the nested for loops (after the switch case statements), you will see data being assigned to red, blue and green pointers (e.g. Bptr[nX] = nBlue and etc). I added these lines under the red, green, blue pointer assignments:
myfile << *pDepth
myfile << " "; 
The lines above should be inside the 2nd for loop. Then I added
myfile << "\n" 
at the end of the first for loop. So basically it should look like this:

saveFrame_depth(...)
{
   ofstream myfile;
   myfile.open("depthData_ascii");
   //code
   for(...)
   {
      for(...)
      {
         //code
         myfile << *pDepth
         myfile << " ";
      }
      myfile << "\n"
   } 
   myfile.close()
   //code 

This file seems to take up around 1.2 mb of space per frame.

[Update 4/21]
Saving it as a simple binary file seems to take up too much space. 1.2mb * 30fps * 60seconds/min * 60mins/hr is 126.562gb per hour. I tried to save the depth data inside a png as 16bit unsigned short integers. To do this,  create a new matrix at the beginning of saveFrame_depth file. The dimensions of the matrix should be pDepthMD->YRes() by pDepthMD->XRes(). Instead of creating it as an 8bit unsigned, do 16bit unsigned (CV_16U). The code for this would look like:
cv::Mat depthArray = cv::Mat(pDepthMD->YRes(), pDepthMD->XRes(), CV_16U)
Inside the first for loop, there are RGB pointers being created with the colorArr variable. Create a pointer for for your depthArray in that same location.
uchar* depthArrayPtr = depthArray.ptr<uchar>(nY); 
ushort* depthArrayPtr = depthArray.ptr<ushort>(nY);
 edit: I had to use ushort for the pointer type. Im not sure exactly why because the pointer type should stay the same length whether or not we are creating a depthArray of 16bit unsigned short or 8bit unsigned char. But using ushort works, where as if I used uchar it saved the image on the left half.

image when using uchar*
image when using  ushort*



Inside the second for loop (toward the end of it), there are values being assigned to the locations of those pointers (e.g. Bptr[nX] = nBlue), under those lines, add this:
depthArrayPtr[nX] = *pDepth
All that is happening is, I am storing the raw depth value in an matrix. Save this array as a png image (add these lines at the end of the function saveFrame_depth):
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(0);
imwrite(str_aux_raw, depthArray, compression_params);
I had to #include "cv.h", "highgui.h", <vector>, <iostream>, and <fstream>. In addition to that, I added using namespace std; after my include statements.

This will save the image as a png and it should have the original depth values. The total size comes out to be around 100kb - 200kb (depending on the depth image).

124kb


To see if you are saving your png image correctly and the values are correct, I used MatLab. In MatLab enter this command:
image = imread('/path/to/image.png'); imagesc(image); colorbar;
This should give you a scale of the values represented in your depth image. You could even click Tools->Data Cursor (in the image window) and select a point in that image to get the specific value at that point.


other information: ~1 min of recording depth as a png (with 0 compression) =  14.5mb. It could be that it is not doing 30fps. There were over all of 117 images so thats about 2 images/frames per second.

[Update 4/21]
I am now trying to get IR data and store it into the image. I found some code that could help: https://groups.google.com/d/msg/openni-dev/ytk-dRPDkoM/XKgoIhOxsv8J

[Update 4/26]
I dont think the code is the problem here, because viewing IR data is not working at all in NiViewer. However I do have some information about compression with depth data. I was able to use PNG_COMPRESSION in imwrite and set that value to 50. The image lowered about 10kb (so not that much). However if I had a binary/text file of 1.5mb and zipped that, it would go down to 67kb. So maybe I could use gzip to zip the files as I am saving them.

Updating...

Saturday, February 23, 2013

Kinect + Raspberry Pi

I have been trying to get the Kinect to work with Raspberry Pi (attempting to get raw depth data) but still keep on hitting roadblocks. The motor was able to tilt when using libfreenect. As for Open NI, I was able to get it to recognize the kinect but unable to get data from the camera.


Install libraries using this tutorial (use unstable) http://mewgen.com/Ge107_files/20120921%20Setting%20up%20Rasberry%20pi%20for%20the%20Xtion%20and%20kinect.html

alternate link: http://gremsi.blogspot.com/2013/04/installing-openni-sensorkinect-and.html

Status: Recognizes the kinect but cannot transfer data.

General Commands:
compile sample files
~/Desktop/kinect/OpenNI/Platform/Linux/Build $ make

build/install open ni
~/Desktop/kinect/unstable/OpenNI/Platform/Linux/CreateRedist $ sudo ./RedistMaker.Arm
~/Desktop/kinect/unstable/OpenNI/Platform/Linux/Redist $ sudo ./install.sh

Trial and Error

  1. used this site to change usbinterface line in unstable sensor kinect: http://daybydaylinux.blogspot.com/2012/12/how-to-compile-openni-and-sensorkinect.html
    1. cd ~/kinect/SensorKinect/Platform/Linux/Redist/Sensor-Bin-Linux-Arm-v5.1.2.1/Config/

      sudo vi GlobalDefaultsKinect.ini
      modify`;UsbInterface=2` into `UsbInterface=1`
    2. Status: instead of saying usb interface not supported, it says: UpdateData failed: A timeout has occurred when waiting for new data!
  2. Attempt to fix timeout issue: change line in Include/XnTypes.h for data_timeout from 2000 to 20,000 and build open ni
    1. status: changing it to 20seconds and 1 minute and did not work
  3. Was not able to change the FPS for depth because it only supports 15fps (http://openni-discussions.979934.n3.nabble.com/OpenNI-dev-How-to-change-FPS-td2500973.html)

Trace Back: NiSimpleRead

Attempting to trace the problem to see where it occurs.


  1. Samples/NiSimpleRead/NiSimpleRead.cpp
    line 103: context.
    WaitOneUpdateAll(depth)
  2. Include/XnCppWrapper.h
    line 9421: return
    xnWaitOneUpdateAll(...)
  3. Source/OpenNI.cpp
    line 2601:
    xnWaitForCondition(...)
  4. Source/OpenNI.cpp
    line 2552:
    xnOSWaitForCondition(...)
  5. Source/XnOS.cpp
    line 236:
    xnOSWaitEvent(...)
  6. Source/OpenNI/Linux/LinuxEvents.cpp
    line 160: pEvent->Wait(...)


Somehow xnOSWaitEvent is linked with Source/OpenNI/Linux/XnUSBLinux.cpp: xnUSBReadThreadMain. I am assuming its linked because I got an warning (after i set the usb interface to 1): USB events thread - failed to set priority. And this error message originates in the file XnUSBLinux

I'm currently trying to see if data is being transferred at all or if the Kinect is just waiting and not sending data.

Side note: you can change the option to print out the logs for OpenNI in the Data/SampleConfig.xml file.


[Update: 2/24]: Some people had some success with the beagleboard and the kinect:

  http://www.pansenti.com/wordpress/?page_id=1772
  http://instructionalrobotics.blogspot.com/2013/01/kinect-under-beagleboard-c4.html 

[Update: 2/27]:
I updated the file XnUSBLinux.cpp to printout the transfer status. In the xnUSBReadThreadMain method, I added these lines (in bold):

//more code...

else // transfer done
{

if (pBufferInfo->nLastStatus == LIBUSB_TRANSFER_COMPLETED || // read succeeded
pBufferInfo->nLastStatus == LIBUSB_TRANSFER_CANCELLED)   // cancelled, but maybe some data arrived
{
if(pBufferInfo->nLastStatus == LIBUSB_TRANSFER_COMPLETED)
{
printf("***** pBufferInfo->nLastStatus = LIBUSB_TRANSFER_COMPLETED\n");
}
else if(pBufferInfo->nLastStatus == LIBUSB_TRANSFER_CANCELLED)
{
printf("***** pBufferInfo->nLastStatus == LIBUSB_TRANSFER_CANCELLED\n");
}
if (pTransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
{
XnUInt32 nTotalBytes = 0;
// some packets may return empty, so we need to remove spaces, and make the buffer sequential
for (XnUInt32 i = 0; i < pTransfer->num_iso_packets; ++i)
{
struct libusb_iso_packet_descriptor* pPacket = &pTransfer->iso_packet_desc[i];
if (/*pPacket->status == LIBUSB_TRANSFER_COMPLETED && */pPacket->actual_length != 0)
{
XnUChar* pBuffer = libusb_get_iso_packet_buffer_simple(pTransfer, i);
// if buffer is not at same offset, move it
if (pTransfer->buffer + nTotalBytes != pBuffer)
{
// printf("buffer %d has %d bytes. Moving to offset %d...\n", i, pPacket->actual_length, nTotalBytes);
memmove(pTransfer->buffer + nTotalBytes, pBuffer, pPacket->actual_length);
}
nTotalBytes += pPacket->actual_length;
}
else if (pPacket->status != LIBUSB_TRANSFER_COMPLETED)
{
xnLogWarning(XN_MASK_USB, "2 Endpoint 0x%x, Buffer %d, packet %d Asynch transfer failed (status: %d)", pTransfer->endpoint, pBufferInfo->nBufferID, i, pPacket->status);
}

if(pPacket->status == LIBUSB_TRANSFER_COMPLETED)
{
printf("*****pPacket->status = LIBUSB_TRANSFER_COMPLETED. Length: %d\n", pPacket->actual_length);
}
else if(pPacket->status == LIBUSB_TRANSFER_CANCELLED)
{
printf("*****pPacket->Status = TRANSFER CANCELLED Length: %d\n", pPacket->actual_length);
}
}
if (nTotalBytes != 0)
{
// call callback method
pBufferInfo->pThreadData->pCallbackFunction(pTransfer->buffer, nTotalBytes, pBufferInfo->pThreadData->pCallbackData);
}
}
else
{
// call callback method
pBufferInfo->pThreadData->pCallbackFunction(pTransfer->buffer, pTransfer->actual_length, pBufferInfo->pThreadData->pCallbackData);
}
}
//more code...


After building and installing, I ran ./Sample-NiSimpleRead again. Click here to see the output (the lines that begin with '*****' are my print line statements). 

The main things to focus on are these lines:
*****pPacket->status = LIBUSB_TRANSFER_COMPLETED. Length: 1760 
*****pPacket->status = LIBUSB_TRANSFER_COMPLETED. Length: 1920 
*****pPacket->status = LIBUSB_TRANSFER_COMPLETED. Length: 0

The transfer seems to be completed but the length of the packet is 0. Not sure why this is happening. 

I left Sample-NiSimpleRead running longer, and noticed I was getting data back from the Kinect. The packet would hold 1760 or 1920 bytes at a time. (the log file has been updated with the new log)

[Update 3/29]
Unfortunately, I wasn't able to get it working with the Kinect. I am currently looking into other options. 

  • Asus Xtion + Raspberry Pi: I was able to get frames back from an Asus Xtion.
  • Kinect + Beagle Board (or Panda Board): I haven't had the chance to try this out but in theory since this is a little more powerful than the RPi, it could work with the kinect. 
I am currently working on getting and compressing the depth images using OpenNI. See OpenNI + Depth Compression