I'm trying to convert the CVPixelBuffer
I get from the iPhone camera (using CMSampleBufferGetImageBuffer
) to a grayscale CVPixelBuffer
, for use in CoreML.
Code looks like this:
extension CVPixelBuffer {
enum Error: Swift.Error {
case greyscaleConversionFailure
}
func greyscalePixelBuffer() throws -> CVPixelBuffer {
// Create color vImage_Buffer
let baseAddress = CVPixelBufferGetBaseAddress(self)
let width = CVPixelBufferGetWidth(self)
let height = CVPixelBufferGetHeight(self)
let stride = CVPixelBufferGetBytesPerRow(self)
var colorVImageBuffer = vImage_Buffer(data: baseAddress, height: UInt(height), width: UInt(width), rowBytes: stride)
// Create greyscale vImage_Buffer
let bitmap = malloc(width * height)
var greyscaleVImageBuffer = vImage_Buffer(data: bitmap, height: UInt(height), width: UInt(width), rowBytes: width)
defer {
free(bitmap)
}
// Arbitrary divisor to scale coefficients to integer values
let divisor: Int32 = 0x1000
let fDivisor = Float(divisor)
// Rec.709 coefficients
var coefficientsMatrix = [
Int16(0.0722 * fDivisor), // blue
Int16(0.7152 * fDivisor), // green
Int16(0.2126 * fDivisor), // red
0 // alpha
]
// Convert to greyscale
guard kvImageNoError == vImageMatrixMultiply_ARGB8888ToPlanar8(&colorVImageBuffer, &greyscaleVImageBuffer, &coefficientsMatrix, divisor, nil, 0, vImage_Flags(kvImageNoFlags)) else {
throw Error.greyscaleConversionFailure
}
// Format of grayscale buffer needed for conversion
let greySpace = CGColorSpaceCreateDeviceGray()
guard var monoFormat = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 8, colorSpace: greySpace, bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), renderingIntent: .defaultIntent) else {
throw Error.greyscaleConversionFailure
}
// Create a CVPixelBuffer for result
var destinationBuffer: CVPixelBuffer?
let info = [kCVPixelBufferCGImageCompatibilityKey : kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey : kCFBooleanTrue] as CFDictionary
let status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_OneComponent8, info, &destinationBuffer)
guard status == kCVReturnSuccess, let destinationBuffer = destinationBuffer else {
throw Error.greyscaleConversionFailure
}
// Format of the CVPixelBuffer content
let vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(destinationBuffer).takeRetainedValue()
vImageCVImageFormat_SetColorSpace(vformat, greySpace)
// Copy greyscale data into CVPixelBuffer
let error = vImageBuffer_CopyToCVPixelBuffer(&greyscaleVImageBuffer, &monoFormat, destinationBuffer, vformat, nil, vImage_Flags(kvImagePrintDiagnosticsToConsole))
guard error == kvImageNoError else {
throw Error.greyscaleConversionFailure
}
return destinationBuffer
}
}
I see this message in the console when vImageBuffer_CopyToCVPixelBuffer
is executed.
vImageBuffer_CopyToCVPixelBuffer error: CVImageFormat_GetConversionMatrix returned unhandled matrix type. Defaulting to kCVImageBufferYCbCrMatrix_ITU_R_601_4. (0)
Seems I need to set a matrix on the destination format, but not sure what I need.
Anyone know what I am missing?