Search code examples
iosswiftavfoundationaccelerate-framework

How does a histogram work for the different channels in vImageHistogramCalculation_Planar8


I am attempting to calculate a histogram for the Y channel in a kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange image buffer. When I use vImageHistogramCalculation_Planar8 I pass in a reference to only a single histogram.

How do I know which channel is being used to create the histogram? What would I do if I wanted to read all channels?

Also open to critiques of the code sample.

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))
    }
}

Solution

  • The kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange is a planar format with all the planes encoded into the buffer. And vImage planar functions only work on one plane at a time. The above code is computing an histogram on the three planes but treated as one big plane which is probably not what you want.

    It is possible to access the base address and the number of bytes per row for the Y plane with these functions:

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

    The plane index depends of the buffer format. The name usually gives you a hint. Here it's YpCbCr so the Y plane should be the first one, at index 0.