Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get RGB "CVPixelBuffer" from ARKit

I'm trying to get a CVPixelBuffer in RGB color space from the Apple's ARKit. In func session(_ session: ARSession, didUpdate frame: ARFrame) method of ARSessionDelegate I get an instance of ARFrame. On page Displaying an AR Experience with Metal I found that this pixel buffer is in YCbCr (YUV) color space.

I need to convert this to RGB color space (I actually need CVPixelBuffer and not UIImage). I've found something about color conversion on iOS but I was not able to get this working in Swift 3.

like image 989
tomas789 Avatar asked Jun 07 '17 14:06

tomas789


3 Answers

There's several ways to do this, depending on what you're after. The best way to do this in realtime (to say, render the buffer to a view) is to use a custom shader to convert the YCbCr CVPixelBuffer to RGB.

Using Metal: If you make a new project, select "Augmented Reality App," and select "Metal" for the content technology, the project generated will contain the code and shaders necessary to make this conversion.

Using OpenGL: The GLCameraRipple example from Apple uses an AVCaptureSession to capture the camera, and shows how to map the resulting CVPixelBuffer to GL textures, which are then converted to RGB in shaders (again, provided in the example).

Non Realtime: The answer to this stackoverflow question addresses converting the buffer to a UIImage, and offers a pretty simple way to do it.

like image 184
joshue Avatar answered Oct 23 '22 11:10

joshue


I have also stuck on this question for several days. All of the code snippet I could find on the Internet is written in Objective-C rather than Swift, regarding converting CVPixelBuffer to UIImage.

Finally, the following code snippet works perfect for me, to convert a YUV image to either JPG or PNG file format, and then you can write it to the local file in your application.

func pixelBufferToUIImage(pixelBuffer: CVPixelBuffer) -> UIImage {
    let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
    let context = CIContext(options: nil)
    let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
    let uiImage = UIImage(cgImage: cgImage!)
    return uiImage
}
like image 5
SteveGuanqi Avatar answered Oct 23 '22 10:10

SteveGuanqi


The docs explicitly says that you need to access the luma and chroma planes:

ARKit captures pixel buffers in a planar YCbCr format (also known as YUV) format. To render these images on a device display, you'll need to access the luma and chroma planes of the pixel buffer and convert pixel values to an RGB format.

So there's no way to directly get the RGB planes and you'll have to handle this in your shaders, either in Metal or openGL as described by @joshue

like image 2
Guig Avatar answered Oct 23 '22 11:10

Guig