vImageHistogramCalculation_Planar8 中的不同通道如何使用直方图

How does a histogram work for the different channels in vImageHistogramCalculation_Planar8

我正在尝试计算 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 图像缓冲区中 Y 通道的直方图。当我使用 vImageHistogramCalculation_Planar8 时,我只传递对单个直方图的引用。

我如何知道正在使用哪个通道创建直方图?如果我想阅读所有频道,我该怎么办?

也欢迎对代码示例的批评。

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ captureOutput: AVCaptureOutput!,
                       didOutputSampleBuffer sampleBuffer: CMSampleBuffer!,
                       from connection: AVCaptureConnection!) {

        let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
        CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))

        let height = CVPixelBufferGetHeight(imageBuffer)
        let width = CVPixelBufferGetWidth(imageBuffer)
        let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
        let pixelBuffer = CVPixelBufferGetBaseAddress(imageBuffer)

//        let format = CVPixelBufferGetPixelFormatType(imageBuffer)
//        print("format: \(format)")

        ///kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v'

        var vBuffer = vImage_Buffer()
        vBuffer.data = pixelBuffer
        vBuffer.rowBytes = bytesPerRow
        vBuffer.width = vImagePixelCount(width)
        vBuffer.height = vImagePixelCount(height)

        let luma = [UInt](repeating: 0, count: 256)

        let lumaHist = UnsafeMutablePointer<vImagePixelCount>(mutating: luma)

        vImageHistogramCalculation_Planar8(&vBuffer, lumaHist, UInt32(kvImageNoFlags))

        CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
    }
}

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 是平面格式,所有平面都编码到缓冲区中。 vImage 平面函数一次只能在一个平面上工作。 上面的代码正在计算三个平面上的直方图,但被视为一个大平面,这可能不是您想要的。

可以使用这些函数访问 Y 平面的基地址和每行字节数:

let bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0)
let pixelBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)

平面索引取决于缓冲区格式。这个名字通常会给你一个提示。这是 YpCbCr 所以 Y 平面应该是第一个,在索引 0.

根据header,CVPixelBufferGetBaseAddress将return:

    For chunky buffers, this will return a pointer to the pixel at 
      0,0 in the buffer. 
    For planar buffers this will return a pointer to a PlanarComponentInfo struct 
       (defined in QuickTime). 

因此,如果为真,则它不会同时计算所有三个通道的直方图。它正在计算 PlanarComponentInfo 结构的更没用的直方图,并且可能会崩溃。

要读取所有通道,您可以使用上面 Sparga 的回答中描述的接口(CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,1) 和 CVPixelBufferGetBaseAddressOfPlane(imageBuffer,1)) 获取第二个平面,然后做一半的 ARGB 直方图宽度色度图像并将偶数直方图和奇数直方图相加。请注意,因为这是 420,所以色度平面的高度和宽度与亮度平面不同。

我还会向 apple 提交错误报告,要求 vImageHistogramCalculation_RG88 处理双平面色度数据。