如何从 vImage_Buffer 到 CVPixelBufferRef

Posted

技术标签:

【中文标题】如何从 vImage_Buffer 到 CVPixelBufferRef【英文标题】:How to go from vImage_Buffer to CVPixelBufferRef 【发布时间】:2015-03-04 15:46:52 【问题描述】:

我正在我的 ios 应用中录制实时视频。在另一个 Stack Overflow page 上,我发现你可以使用 vImage_Buffer 来处理我的框架。

问题是我不知道如何从输出的vImage_buffer 回到CVPixelBufferRef

这是另一篇文章中给出的代码:

NSInteger cropX0 = 100,
          cropY0 = 100,
          cropHeight = 100,
          cropWidth = 100,
          outWidth = 480,
          outHeight = 480;

CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);                   
CVPixelBufferLockBaseAddress(imageBuffer,0);
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);

vImage_Buffer inBuff;                       
inBuff.height = cropHeight;
inBuff.width = cropWidth;
inBuff.rowBytes = bytesPerRow;

int startpos = cropY0 * bytesPerRow + 4 * cropX0;
inBuff.data = baseAddress + startpos;

unsigned char *outImg = (unsigned char*)malloc(4 * outWidth * outHeight);
vImage_Buffer outBuff = outImg, outHeight, outWidth, 4 * outWidth;

vImage_Error err = vImageScale_ARGB8888(&inBuff, &outBuff, NULL, 0);
if (err != kvImageNoError) NSLog(@" error %ld", err);

现在我需要将outBuff 转换为CVPixelBufferRef

我认为我需要使用vImageBuffer_CopyToCVPixelBuffer,但我不确定如何使用。

我的第一次尝试以EXC_BAD_ACCESS: CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 失败

CVPixelBufferRef pixelBuffer;
CVPixelBufferCreate(kCFAllocatorSystemDefault, 480, 480, kCVPixelFormatType_32BGRA, NULL, &pixelBuffer);
    
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    
vImage_CGImageFormat format = 
    .bitsPerComponent = 8,
    .bitsPerPixel = 32,
    .bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,  //BGRX8888
    .colorSpace = NULL,  //sRGB
;
    
vImageBuffer_CopyToCVPixelBuffer(&outBuff,
                                 &format,
                                 pixelBuffer,
                                 NULL,
                                 NULL,
                                 kvImageNoFlags);  // Here is the crash!
    
    
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

有什么想法吗?

【问题讨论】:

EXC_BAD_ACCESS 对应于哪个字节块? vImage_Buffer.data 还是 CVPixelBufferRef 基地址? 嗨,伊恩,我怎么才能找到呢? 在调试器中运行您的代码。当您崩溃时,您应该会看到类似“EXC_BAD_ACCESS (Code=X, address=ADDR)”的内容。此时,使用调试器查看 pixelBuffer 和 outBuff.data 的值。其中之一应与 ADDR 相同或接近。 (这是假设您实际上是在 CopyToCVPixelBuffer 中崩溃,而不是更早;您在什么函数中崩溃?) 好吧,调试器保存在 vImageBuffer_CopyToCVPixelBuffer 但 addr 实际上是 0x0,但所有输入参数 outBuff、format 和 pixelBuffer 都与 0x0 不同。代码是 1 【参考方案1】:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool : YES], kCVPixelBufferCGImageCompatibilityKey,
    [NSNumber numberWithBool : YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
    [NSNumber numberWithInt : 480], kCVPixelBufferWidthKey,
    [NSNumber numberWithInt : 480], kCVPixelBufferHeightKey,
    nil];

status = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
                                      480,
                                      480,
                                      kCVPixelFormatType_32BGRA,
                                      outImg,
                                      bytesPerRow,
                                      NULL,
                                      NULL,
                                      (__bridge CFDictionaryRef)options,
                                      &pixbuffer);

您应该像上面那样生成一个新的pixelBuffer

【讨论】:

【参考方案2】: 以防万一...如果您希望在界面中裁剪实时视频源,请使用AVPlayerLayerAVCaptureVideoPreviewLayer 和/或其他CALayer 子类,使用图层边界,框架和位置为您的 100x100 像素区域到 480x480 区域。

vImage为您的问题备注(不同情况可能不同):

    CVPixelBufferCreateWithBytes 不适用于vImageBuffer_CopyToCVPixelBuffer(),因为您需要将vImage_Buffer 数据复制到“干净”或“空”CVPixelBuffer

    无需锁定/解锁 - 确保您知道何时锁定和何时不锁定像素缓冲区。

    您的inBuff vImage_Buffer 只需要从像素缓冲区数据初始化,而不是手动(除非您知道如何使用CGContexts 等来初始化像素网格)

    使用vImageBuffer_InitWithCVPixelBuffer()

    vImageScale_ARGB8888 会将整个 CVPixel 数据缩放为更小/更大的矩形。它不会将缓冲区的一部分/裁剪区域缩放到另一个缓冲区。

    当你使用vImageBuffer_CopyToCVPixelBuffer()时, vImageCVImageFormatRef & vImage_CGImageFormat 需正确填写。

    CGColorSpaceRef dstColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
    
    vImage_CGImageFormat format = 
        .bitsPerComponent = 16,
        .bitsPerPixel = 64,
        .bitmapInfo = (CGBitmapInfo)kCGImageAlphaPremultipliedLast  |  kCGBitmapByteOrder16Big ,
        .colorSpace = dstColorSpace
    ;
    vImageCVImageFormatRef vformat = vImageCVImageFormat_Create(kCVPixelFormatType_4444AYpCbCr16,
                                                                kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2,
                                                                kCVImageBufferChromaLocation_Center,
                                                                format.colorSpace,
                                                                0);
    
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                          480,
                                          480,
                                          kCVPixelFormatType_4444AYpCbCr16,
                                          NULL,
                                          &destBuffer);
    
    NSParameterAssert(status == kCVReturnSuccess && destBuffer != NULL);
    
    err = vImageBuffer_CopyToCVPixelBuffer(&sourceBuffer, &format, destBuffer, vformat, 0, kvImagePrintDiagnosticsToConsole);
    

注意:这些是带 Alpha 的 64 位 ProRes 的设置 - 调整为 32 位。

【讨论】:

别忘了 NB,NB,非常 NB - Memory Leak City 1. 你必须自己释放 CG 对象(色彩空间等) 2.您还必须从缓冲区中释放(whatEver.data)。这些函数都不支持 ARC,如果您要处理许多视频帧,您很快就会陷入停顿。 嗨,保罗,你能回答我的 SO 问题 (***.com/questions/60904676/…),它与这个答案有关,我不知道如何快速完成。

以上是关于如何从 vImage_Buffer 到 CVPixelBufferRef的主要内容,如果未能解决你的问题,请参考以下文章

swift vImage_Buffer包装器

如何使用加速框架(vImage)缩放灰度图像

iOS - 将 UIImage 转换为 vImage 内存处理

在 iOS 中使用 Swift 导出 16 位图像

如何将文件从网站保存到 SD 卡

如何将数据从 Ms 访问迁移到 Derby 数据库或如何将数据从 My sql 迁移到 Derby 数据库