【问题标题】:CMSampleBuffer frame converted to vImage has wrong colors转换为 vImage 的 CMSampleBuffer 帧颜色错误
【发布时间】:2018-02-18 19:33:31
【问题描述】:

我正在尝试将 CMSampleBuffer 从相机输出转换为 vImage 并稍后应用一些处理。不幸的是,即使没有任何进一步的编辑,我从缓冲区获得的帧颜色也不正确:

实现(不考虑内存管理和错误):

配置视频输出设备:

    videoDataOutput = AVCaptureVideoDataOutput()
    videoDataOutput.videoSettings = [String(kCVPixelBufferPixelFormatTypeKey): kCVPixelFormatType_32BGRA]
    videoDataOutput.alwaysDiscardsLateVideoFrames = true
    videoDataOutput.setSampleBufferDelegate(self, queue: captureQueue)
    videoConnection = videoDataOutput.connection(withMediaType:  AVMediaTypeVideo)

    captureSession.sessionPreset = AVCaptureSessionPreset1280x720

    let videoDevice = AVCaptureDevice.defaultDevice(withMediaType:  AVMediaTypeVideo)
    guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
        return
    }

从相机接收到的CASampleBuffer 创建vImage

   // Convert `CASampleBuffer` to `CVImageBuffer`
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    var buffer: vImage_Buffer = vImage_Buffer()
    buffer.data = CVPixelBufferGetBaseAddress(pixelBuffer)
    buffer.rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer)
    buffer.width = vImagePixelCount(CVPixelBufferGetWidth(pixelBuffer))
    buffer.height = vImagePixelCount(CVPixelBufferGetHeight(pixelBuffer))

    let vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(pixelBuffer)
    let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)

    var cgFormat = vImage_CGImageFormat(bitsPerComponent: 8,
                                        bitsPerPixel: 32,
                                        colorSpace: nil,
                                        bitmapInfo: bitmapInfo,
                                        version: 0,
                                        decode: nil,
                                        renderingIntent: .defaultIntent)

     // Create vImage
     vImageBuffer_InitWithCVPixelBuffer(&buffer, &cgFormat, pixelBuffer, vformat!.takeRetainedValue(), cgColor, vImage_Flags(kvImageNoFlags))

将缓冲区转换为 UIImage:

为了方便测试,CVPixelBuffer 被导出到 UIImage 中,但是添加到视频缓冲区中的结果是一样的。

var dstPixelBuffer: CVPixelBuffer?

    let status = CVPixelBufferCreateWithBytes(nil, Int(buffer.width), Int(buffer.height),
                                              kCVPixelFormatType_32BGRA, buffer.data,
                                              Int(buffer.rowBytes), releaseCallback,
                                              nil, nil, &dstPixelBuffer)

    let destCGImage = vImageCreateCGImageFromBuffer(&buffer, &cgFormat, nil, nil, numericCast(kvImageNoFlags), nil)?.takeRetainedValue()

    // create a UIImage
    let exportedImage = destCGImage.flatMap { UIImage(cgImage: $0, scale: 0.0, orientation: UIImageOrientation.right) }

    DispatchQueue.main.async {
        self.previewView.image = exportedImage
    }

【问题讨论】:

  • 删除| CGBitmapInfo.byteOrder32Little.rawValue 有帮助吗?您的黄色鸭子从 FFFF00 变为 00FF00,因此如果您包含零 alpha,可能是字节交换问题。
  • @RhythmicFistman 这很有趣。我尝试删除 byteOrder32Little 并更改为 CGBitmapInfo(rawValue: CGImageByteOrderInfo.orderMask.rawValue | CGImageAlphaInfo.last.rawValue) 以保持默认顺序,但结果似乎在所有情况下都完全相同。
  • 你能链接到一个可运行的样本吗?

标签: swift avfoundation accelerate-framework cvpixelbuffer vimage


【解决方案1】:

尝试在您的 CV 图像格式上设置色彩空间:

    let vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(pixelBuffer).takeRetainedValue()

    vImageCVImageFormat_SetColorSpace(vformat,
                                      CGColorSpaceCreateDeviceRGB())

...更新您对vImageBuffer_InitWithCVPixelBuffer 的调用以反映vformat 现在是托管引用这一事实:

    let error = vImageBuffer_InitWithCVPixelBuffer(&buffer, &cgFormat, pixelBuffer, vformat, nil, vImage_Flags(kvImageNoFlags))

最后,您可以删除以下行,vImageBuffer_InitWithCVPixelBuffer 正在为您完成这项工作:

//        buffer.data = CVPixelBufferGetBaseAddress(pixelBuffer)
//        buffer.rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer)
//        buffer.width = vImagePixelCount(CVPixelBufferGetWidth(pixelBuffer))
//        buffer.height = vImagePixelCount(CVPixelBufferGetHeight(pixelBuffer))

请注意,您不需要锁定Core Video像素缓冲区,如果您检查headerdoc,它会说“在调用此函数之前不需要锁定CVPixelBuffer”。

【讨论】:

  • 我回到这个问题,刚刚找到你的建议。设置色彩空间是一个缺失的难题!非常感谢您的所有提示!!!
【解决方案2】:

vImageBuffer_InitWithCVPixelBuffer 的调用正在修改您的vImage_BufferCVPixelBuffer 的内容,这有点调皮,因为在您的(链接的)代码中,您承诺不会在您说的时候修改像素

CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)

为 BGRA8888 初始化 CGBitmapInfo 的正确方法是 alpha first,32bit little endian,这并不明显,但在 vImage_Utilities.h 中 vImage_CGImageFormat 的头文件中有介绍:

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue | CGImageByteOrderInfo.order32Little.rawValue)

我不明白为什么vImageBuffer_InitWithCVPixelBuffer 正在修改你的缓冲区,因为cgFormat (desiredFormat) 应该匹配vformat,尽管它记录了修改缓冲区,所以也许你应该复制数据第一的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-14
    • 2014-05-16
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多