I'm trying to use the vImageConvert_ARGB8888toPlanar8
API to extract RGB channels. Suppose the following inputImage
is a solid white image from: https://www.solidbackgrounds.com/images/1080x1920/1080x1920-white-solid-color-background.jpg
Here is my code:
let inputImage = #imageLiteral(resourceName: "1080x1920-white-solid-color-background.jpg")
guard let sourceCGImage = inputImage.cgImage,
let sourceImageFormat = vImage_CGImageFormat(cgImage: sourceCGImage),
var sourceImageBuffer = try? vImage_Buffer(cgImage: sourceCGImage,
format: sourceImageFormat) else {
fatalError()
}
let componentCount = sourceImageFormat.componentCount
var argbSourcePlanarBuffers: [vImage_Buffer] = (0..<componentCount).map { _ in
let bitsPerPixel = UInt32(8)
guard let buffer = try? vImage_Buffer(width: Int(sourceImageBuffer.width),
height: Int(sourceImageBuffer.height),
bitsPerPixel: bitsPerPixel) else {
fatalError("Error creating planar buffers.")
}
return buffer
}
vImageConvert_ARGB8888toPlanar8(&sourceImageBuffer,
&argbSourcePlanarBuffers[0], // R
&argbSourcePlanarBuffers[1], // G
&argbSourcePlanarBuffers[2], // B
&argbSourcePlanarBuffers[3], // A
vImage_Flags(kvImageNoFlags))
print("Image H: \(sourceCGImage.height), W: \(sourceCGImage.width)")
let capacity = sourceCGImage.width * sourceCGImage.height
print("capacity: \(capacity)")
// Repeat the following code for other 3 channels
let channelDataPtrR = argbSourcePlanarBuffers[0].data.bindMemory(to: Pixel_8.self,
capacity: capacity)
let channelRawDataR = UnsafeBufferPointer(start: channelDataPtrR, count: capacity)
let channelDataR = Array(channelRawDataR)
var histogramR = [Double](repeating: 0.0, count: 255 + 1)
channelDataR.map { histogramR[Int($0)] += 1.0 }
print("histogramR:")
for (i, px) in histogramR.enumerated() {
if px > 0.0 {
print("Pixel Val \(i): \(px)")
}
}
and here is what I got:
Image H: 1920, W: 1080
capacity: 2073600
histogramR:
Pixel Val 0: 129600.0
Pixel Val 255: 1944000.0
histogramG:
Pixel Val 0: 129600.0
Pixel Val 255: 1944000.0
histogramB:
Pixel Val 0: 129600.0
Pixel Val 255: 1944000.0
histogramA:
Pixel Val 0: 129600.0
Pixel Val 255: 1944000.0
Question: How come I'm getting pixel value 0
if the image is supposed to be solid white (i.e. all pixel value = 255?)
Also note that if I'm running the code above using the Xcode's simulator, I'm getting a different number of Pixel Val 0
, but the total capacity
is still 2073600
.
This may be because the actual row bytes of your buffer is wider than your image (see Create Floating Point Pixels to Use in vDSP in Finding the Sharpest Image in a Sequence of Captured Images for an explanation of how row bytes extend beyond an image's bounding box).
If you use the code from Specifying Histograms with vImage, you should get the value that you're expecting:
var histogramBinZero = [vImagePixelCount](repeating: 0, count: 256)
var histogramBinOne = [vImagePixelCount](repeating: 0, count: 256)
var histogramBinTwo = [vImagePixelCount](repeating: 0, count: 256)
var histogramBinThree = [vImagePixelCount](repeating: 0, count: 256)
histogramBinZero.withUnsafeMutableBufferPointer { zeroPtr in
histogramBinOne.withUnsafeMutableBufferPointer { onePtr in
histogramBinTwo.withUnsafeMutableBufferPointer { twoPtr in
histogramBinThree.withUnsafeMutableBufferPointer { threePtr in
var histogramBins = [zeroPtr.baseAddress, onePtr.baseAddress,
twoPtr.baseAddress, threePtr.baseAddress]
histogramBins.withUnsafeMutableBufferPointer { histogramBinsPtr in
let error = vImageHistogramCalculation_ARGB8888(&sourceImageBuffer,
histogramBinsPtr.baseAddress!,
vImage_Flags(kvImageNoFlags))
guard error == kvImageNoError else {
fatalError("Error calculating histogram: \(error)")
}
}
}
}
}
}
for (i, px) in histogramBinTwo.enumerated() {
if px > 0 {
print("Pixel Val \(i): \(px)")
}
}