【问题标题】:Get RGB "CVPixelBuffer" from ARKit从 ARKit 获取 RGB“CVPixelBuffer”
【发布时间】:2017-11-09 00:14:22
【问题描述】:

我正在尝试从 Apple 的 ARKit 中获取 RGB 颜色空间中的 CVPixelBuffer。在ARSessionDelegatefunc session(_ session: ARSession, didUpdate frame: ARFrame) 方法中,我得到了ARFrame 的一个实例。在Displaying an AR Experience with Metal 页上,我发现这个像素缓冲区位于 YCbCr (YUV) 颜色空间中。

我需要将其转换为 RGB 颜色空间(我实际上需要 CVPixelBuffer 而不是 UIImage)。我发现 something 关于 iOS 上的颜色转换,但我无法在 Swift 3 中使用它。

【问题讨论】:

  • 什么用例需要 RGB over YUV?
  • 我有一个需要 RGB 的自定义处理管道
  • 好的。如果您的管道涉及某种 opengl / Metal,那么在那里进行转换很容易。我不确定是否有直接获取 RGB 的好方法。

标签: ios swift arkit metal cvpixelbuffer


【解决方案1】:

有几种方法可以做到这一点,具体取决于您所追求的。实时执行此操作(也就是说,将缓冲区渲染到视图)的最佳方法是使用自定义着色器将 YCbCr CVPixelBuffer 转换为 RGB。

使用金属: 如果您创建一个新项目,选择“Augmented Reality App”并选择“Metal”作为内容技术,生成的项目将包含进行此转换所需的代码和着色器。

使用 OpenGL: 来自 Apple 的 GLCameraRipple example 使用 AVCaptureSession 来捕获相机,并展示了如何将生成的 CVPixelBuffer 映射到 GL 纹理,然后在着色器中将其转换为 RGB(同样,在示例中提供)。

非实时: this stackoverflow question 的答案解决了将缓冲区转换为 UIImage 的问题,并提供了一种非常简单的方法。

【讨论】:

    【解决方案2】:

    我也坚持这个问题好几天了。我在 Internet 上找到的所有关于将 CVPixelBuffer 转换为 UIImage 的代码 sn-p 都是用 Objective-C 而不是 Swift 编写的。

    最后,以下代码 sn-p 非常适合我,将 YUV 图像转换为 JPG 或 PNG 文件格式,然后您可以将其写入应用程序中的本地文件。

    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
    }
    

    【讨论】:

    • 这确实不能回答问题。
    • 这是最好的答案,因为它可以正确处理色彩空间和伽马转换,但请记住,在每次调用时分配这样的 CIContext 真的非常慢。在重复调用的生命周期内保持对 CIContext 的引用,您的应用程序将有效执行。如果您对 RGB->YCbCr 转换的完整 Metal impl 感兴趣,请查看:github.com/mdejong/MetalBT709Decoder
    • 带有 SceneKit 的 ARKit 提供了一个 snapshot 方法来直接获取当前帧到 UIImage
    【解决方案3】:

    docs 明确表示您需要访问亮度和色度平面:

    ARKit 以平面 YCbCr 格式(也称为 YUV)格式捕获像素缓冲区。要在设备显示器上渲染这些图像,您需要访问像素缓冲区的亮度和色度平面并将像素值转换为 RGB 格式。

    因此没有办法直接获取 RGB 平面,您必须在着色器中处理这个问题,如 @joshue 所述,无论是在 Metal 还是 openGL 中

    【讨论】:

      【解决方案4】:

      您可能需要 Accelerate 框架的 image conversion functions。也许是vImageConvert_420Yp8_Cb8_Cr8ToARGB8888vImageConvert_ARGB8888toRGB888 的组合(如果您不想要alpha 通道)。根据我的经验,这些工作是实时的。

      【讨论】:

        【解决方案5】:

        为此苦苦挣扎了很长时间,我最终编写了以下代码,这对我有用:

        // Helper macro to ensure pixel values are bounded between 0 and 255
        #define clamp(a) (a > 255 ? 255 : (a < 0 ? 0 : a));
        
        - (void)processImageBuffer:(CVImageBufferRef)imageBuffer
        {
            OSType type  = CVPixelBufferGetPixelFormatType(imageBuffer);
            if (type == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
            {
                CVPixelBufferLockBaseAddress(imageBuffer, 0);
                // We know the return format of the base address based on the YpCbCr8BiPlanarFullRange format (as per doc)
                StandardBuffer baseAddress = (StandardBuffer)CVPixelBufferGetBaseAddress(imageBuffer);
        
                // Get the number of bytes per row for the pixel buffer, width and height
                size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
                size_t width = CVPixelBufferGetWidth(imageBuffer);
                size_t height = CVPixelBufferGetHeight(imageBuffer);
        
                // Get buffer info and planar pixel data
                CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
                uint8_t* cbrBuff = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1);
                // This just moved the pointer past the offset
                baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
                int bytesPerPixel = 4;
                uint8_t *rgbData =  rgbFromYCrCbBiPlanarFullRangeBuffer(baseAddress,
                                                                        cbrBuff,
                                                                        bufferInfo,
                                                                        width,
                                                                        height,
                                                                        bytesPerRow);
        
                [self doStuffOnRGBBuffer:rgbData width:width height:height bitsPerComponent:8 bytesPerPixel:bytesPerPixel bytesPerRow:bytesPerRow];
        
                free(rgbData);
                CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
            }
            else
            {
                NSLog(@"Unsupported image buffer type");
            }
        }
        
        uint8_t * rgbFromYCrCbBiPlanarFullRangeBuffer(uint8_t *inBaseAddress,
                                                      uint8_t *cbCrBuffer,
                                                      CVPlanarPixelBufferInfo_YCbCrBiPlanar * inBufferInfo,
                                                      size_t inputBufferWidth,
                                                      size_t inputBufferHeight,
                                                      size_t inputBufferBytesPerRow)
        {
            int bytesPerPixel = 4;
            NSUInteger yPitch = EndianU32_BtoN(inBufferInfo->componentInfoY.rowBytes);
            uint8_t *rgbBuffer = (uint8_t *)malloc(inputBufferWidth * inputBufferHeight * bytesPerPixel);
            NSUInteger cbCrPitch = EndianU32_BtoN(inBufferInfo->componentInfoCbCr.rowBytes);
            uint8_t *yBuffer = (uint8_t *)inBaseAddress;
        
            for(int y = 0; y < inputBufferHeight; y++)
            {
                uint8_t *rgbBufferLine = &rgbBuffer[y * inputBufferWidth * bytesPerPixel];
                uint8_t *yBufferLine = &yBuffer[y * yPitch];
                uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
                for(int x = 0; x < inputBufferWidth; x++)
                {
                    int16_t y = yBufferLine[x];
                    int16_t cb = cbCrBufferLine[x & ~1] - 128;
                    int16_t cr = cbCrBufferLine[x | 1] - 128;
        
                    uint8_t *rgbOutput = &rgbBufferLine[x*bytesPerPixel];
        
                    int16_t r = (int16_t)roundf( y + cr *  1.4 );
                    int16_t g = (int16_t)roundf( y + cb * -0.343 + cr * -0.711 );
                    int16_t b = (int16_t)roundf( y + cb *  1.765);
        
                    // ABGR image representation
                    rgbOutput[0] = 0Xff;
                    rgbOutput[1] = clamp(b);
                    rgbOutput[2] = clamp(g);
                    rgbOutput[3] = clamp(r);
                }
            }
        
            return rgbBuffer;
        }
        

        【讨论】:

        • 有没有办法在 SWIFT 应用程序中利用此代码?
        猜你喜欢
        • 2018-03-28
        • 1970-01-01
        • 2019-12-11
        • 2023-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-01
        • 2013-07-10
        相关资源
        最近更新 更多