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