AppleのARKitからRGB色空間でCVPixelBuffer
を取得しようとしています。 ARSessionDelegate
のfunc session(_ session: ARSession, didUpdate frame: ARFrame)
メソッドで、ARFrame
のインスタンスを取得します。ページ上 Metalを使用したARエクスペリエンスの表示 このピクセルバッファーはYCbCr(YUV)色空間にあることがわかりました。
これをRGB色空間に変換する必要があります(実際にはCVPixelBuffer
ではなくUIImage
が必要です)。 iOSでの色変換について 何か を見つけましたが、Swift 3でこれを機能させることができませんでした。
あなたが何を求めているかに応じて、これを行うにはいくつかの方法があります。これをリアルタイムで行う(つまり、バッファーをビューにレンダリングする)最良の方法は、カスタムシェーダーを使用してYCbCrCVPixelBufferをRGBに変換することです。
Metalの使用:新しいプロジェクトを作成する場合は、[拡張現実アプリ]を選択し、コンテンツテクノロジーとして[Metal]を選択すると、生成されるプロジェクトには次のものが含まれます。この変換を行うために必要なコードとシェーダー。
OpenGLの使用:GLCameraRippleの例 from AppleはAVCaptureSessionを使用してカメラをキャプチャし、そして、結果のCVPixelBufferをGLテクスチャにマッピングする方法を示します。テクスチャは、シェーダーでRGBに変換されます(これも例で提供されています)。
非リアルタイム:このスタックオーバーフローの質問 への答えは、バッファをUIImageに変換することを扱い、非常に簡単な方法を提供しますそれ。
私も数日間この質問にこだわっています。initernetで見つけたすべてのコードスニペットは、CVPixelBufferのUIImageへの変換に関して、Swiftではなくobjective-cで書かれています。
最後に、次のコードスニペットは、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
}
docs は、輝度平面と彩度平面にアクセスする必要があることを明示的に示しています。
ARKitは、平面YCbCr形式(YUVとも呼ばれます)形式でピクセルバッファーをキャプチャします。これらの画像をデバイスディスプレイにレンダリングするには、ピクセルバッファーの輝度面と彩度面にアクセスし、ピクセル値をRGB形式に変換する必要があります。
したがって、RGBプレーンを直接取得する方法はなく、@ joshueで説明されているように、MetalまたはopenGLのいずれかでシェーダーでこれを処理する必要があります。
Accelerateフレームワークの 画像変換関数 が必要な場合があります。おそらくvImageConvert_420Yp8_Cb8_Cr8ToARGB8888
とvImageConvert_ARGB8888toRGB888
の組み合わせ(アルファチャンネルが必要ない場合)。私の経験では、これらはリアルタイムで機能します。
これにも長い間苦労し、私は次のコードを書くことになりました。これは私にとってはうまくいきます。
// 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;
}