我正试图从Apple的ARKit中获取RGB色彩空间中的CVPixelBuffer
。在func session(_ session: ARSession, didUpdate frame: ARFrame)
的{{1}}方法中,我得到了ARSessionDelegate
的实例。在页面Displaying an AR Experience with Metal上,我发现此像素缓冲区位于YCbCr(YUV)颜色空间中。
我需要将其转换为RGB色彩空间(实际上我需要ARFrame
而不是CVPixelBuffer
)。我在iOS上找到了关于颜色转换的something,但我无法在Swift 3中使用它。
答案 0 :(得分:7)
根据您的追求,有几种方法可以做到这一点。实时执行此操作的最佳方法(比如,将缓冲区渲染到视图)是使用自定义着色器将YCbCr CVPixelBuffer转换为RGB。
使用金属: 如果您创建一个新项目,选择“增强现实应用程序”,并为内容技术选择“金属”,生成的项目将包含进行此转换所需的代码和着色器。
使用OpenGL: 来自Apple的GLCameraRipple example使用AVCaptureSession捕获相机,并显示如何将生成的CVPixelBuffer映射到GL纹理,然后在着色器中将其转换为RGB(再次,在示例中提供)。
非实时: this stackoverflow question解决了将缓冲区转换为UIImage的问题,并提供了一种非常简单的方法。
答案 1 :(得分:3)
我也坚持了几天这个问题..我在initernet上找到的所有代码片段都是用objective-c而不是swift编写的,关于将CVPixelBuffer转换为UIImage。
最后,以下代码片段非常适合我,将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
}
答案 2 :(得分:2)
docs明确表示您需要访问亮度和色度平面:
ARKit以平面YCbCr格式(也称为YUV)格式捕获像素缓冲区。要在设备显示器上渲染这些图像,您需要访问像素缓冲区的亮度和色度平面,并将像素值转换为RGB格式。
因此无法直接获取RGB平面,您必须在着色器中处理此问题,无论是使用Metal还是openGL,如@joshue所述
答案 3 :(得分:0)
这一段时间也在苦苦挣扎,我最终编写了以下代码,这对我有用:
// 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;
}
答案 4 :(得分:0)
您可能需要Accelerate框架的image conversion functions。可能是vImageConvert_420Yp8_Cb8_Cr8ToARGB8888
和vImageConvert_ARGB8888toRGB888
的组合(如果您不希望使用Alpha通道)。以我的经验,这些都是实时的。