1

I am trying to produce a point cloud where each point has a colour. I can get just the point cloud or I can get the camera to take a picture, but I need them to be as simultaneous as possible. If I could look up an RGB image with a timestamp or call a function to get the current frame when onXYZijAvailable() is called I would be done. I could just go over the points, find out where it would intersect with the image plane and get the colour of that pixel.

As it is now I have not found any way to get the pixel info of an image or get coloured points. I have seen AR apps where the camera is connected to the CameraView and then things are rendered on top, but the camera stream is never touched by the application.

According to this post it should be possible to get the data I want and synchronize the point cloud and the image plane by a simple transformation. This post is also saying something similar. However, I have no idea how to get the RGB data. I cant find any open source projects or tutorials.

The closest I have gotten is finding out when a frame is ready by using this:

     public void onFrameAvailable(final int cameraId) {
          if (cameraId == TangoCameraIntrinsics.TANGO_CAMERA_COLOR) {
               //Get the new rgb frame somehow.
          }
     }

I am working with the Java API and I would very much like to not delve into JNI and the NDK if at all possible. How can I get the frame that most closely matches the timestamp of my current point cloud?

Thank you for your help.

Update:

I implemented a CPU version of it and even after optimising it a bit I only managed to get .5 FPS on a small point cloud. This is also due to the fact that the colours have to be converted from the android native NV21 colour space to the GPU native RGBA colour space. I could have optimized it further, but I am not going to get a real time effect with this. The CPU on the android device simply can not perform well enough. If you want to do this on more than a few thousand points, go for the extra hassle of using the GPU or do it in post.

Community
  • 1
  • 1
Noobs DeSroobs
  • 253
  • 1
  • 6
  • 24

2 Answers2

3

Tango normally delivers color pixel data directly to an OpenGLES texture. In Java, you create the destination texture and register it with Tango.connectTextureId(), then in the onFrameAvailable() callback you update the texture with Tango.updateTexture(). Once you have the color image in a texture, you can access it using OpenGLES drawing calls and shaders.

If your goal is to color a Tango point cloud, the most efficient way to do this is in the GPU. That is, instead of pulling the color image out of the GPU and accessing it in Java, you instead pass the point data into the GPU and use OpenGLES shaders to transform the 3D points into 2D texture coordinates and look up the colors from the texture. This is rather tricky to get right if you're doing it for the first time but may be required for acceptable performance.

If you really want direct access to pixel data without using the C API, you need to render the texture into a buffer and then read the color data from the buffer. It's kind of tricky if you aren't used to OpenGL and writing shaders, but there is an Android Studio app that demonstrates that here, and is further described in this answer. This project demonstrates both how to draw the camera texture to the screen, and how to draw to an offscreen buffer and read RGBA pixels.

If you really want direct access to pixel data but decide that the NDK might be less painful than OpenGLES, the C API has TangoService_connectOnFrameAvailable() which gives you pixel data directly, i.e. without going through OpenGLES. Note, however, that the format of the pixel data is NV21, not RGB or RGBA.

Community
  • 1
  • 1
rhashimoto
  • 15,650
  • 2
  • 52
  • 80
  • I have no problem with OpenGL, luckily. It is android and the tango API that I am struggling with. I ended up with using the NDK to extract the data. I know I can directly link the colour image to the texture, but I need access to the data to modify it and stream the data to a server for analysis. I wrote the TangoService_connectOnFrameAvailable() function before heading home today, so tomorrow I will work on extracting the data. I am so excited to see the performance of crossing the boundary between c and java and how expensive it is to extract the image! Thank you for your answer. – Noobs DeSroobs Dec 10 '15 at 23:22
  • Even if you need the colored points outside the GPU, it is highly likely that the most efficient way to color them is using the GPU. That is, it's going to be less expensive to stream the point cloud data in, do the OpenGL magic into a renderbuffer, and stream the renderbuffer data out, than to do those lookups (with interpolation, possibly) and color space conversions on the CPU. – rhashimoto Dec 10 '15 at 23:38
  • I want to see the peeformance first as GPGPU programming on an android might be more of a hazzle. If it is too slow, then I will try doing that. Do you have some data on this? – Noobs DeSroobs Dec 10 '15 at 23:51
  • I've implemented the GPU path for coloring points (to a renderbuffer but without calling `glReadPixels()` which you would need to do) and it's quite fast. I never implemented it on the CPU so I can't make an apples-to-apples comparison. But based on my experience with other CPU code, my prediction is that lookup by itself (only address generation and memory fetch, without 3D-to-2D mapping, interpolation, and color space conversion) over a full field of XYZ points will be extremely taxing. Good luck, and let us know whether that is the case or not. – rhashimoto Dec 11 '15 at 00:53
  • 1
    I implemented a CPU version of it and even after optimising it a bit I managed to get .5 FPS on a small point cloud. This is also due to the fact that the colours have to be converted from the NV21 colour space to the RGBA colour space. – Noobs DeSroobs Feb 03 '16 at 16:20
1

I am doing this now by capturing depth with onXYZijAvailable() and images with onFrameAvailable(). I am using native code, but the same should work in Java. For every onFrameAvailable() I get the image data and put it in a preallocated ring buffer. I have 10 slots and a counter/pointer. Each new image increments the counter, which loops back from 9 to 0. The counter is an index into an array of images. I save the image timestamp in a similar ring buffer. When I get a depth image, onXYZijAvailable(), I grab the data and the timestamp. Then I go back through the images, starting with the most recent and moving backwards, until I find the one with the closest timestamp to the depth data. As you mentioned, you know that the image data will not be from the same frame as the depth data because they use the same camera. But, using these two calls (in JNI) I get within +/- 33msec, i.e. the previous or next frame, on a consistent basis.

I have not checked how close it would be to just naively use the most recently updated rgb image frame, but that should be pretty close.

Just make sure to use the onXYZijAvailable() to drive the timing, because depth updates more slowly than rgb.

I have found that writing individual images to the file system using OpenCV::imwrite() does not keep up with the real time of the camera. I have not tried streaming to a file using the video codec. That should be much faster. Depending on what you plan to do with the data in the end you will need to be careful how you store your results.

Ken
  • 309
  • 2
  • 11
  • The Java and C APIs do not behave identically. Although the C `onFrameAvailable()` callback delivers pixel data, the Java callback does not. Getting pixel data in pure Java requires going through OpenGL. – rhashimoto Jan 28 '16 at 19:57