How can I convert two bytes (UInt8) to a half-precision (16-bit) Float in Swift, such as needed when reading the output of CIAreaHistogram with the kCIFormatRGBAh, as in the following example:
func areaHistogram(image : UIImage) {
let inputImage = CIImage(image: image)
let totalBytes : Int = bpp * BINS //8 * 64 for example
let bitmap : UnsafeMutablePointer<Void> = calloc(totalBytes, bpp)
let filter = CIFilter(name: "CIAreaHistogram")!
filter.setValue(inputImage, forKey: kCIInputImageKey)
filter.setValue(CIVector(x: 0, y: 0, z: image.size.width, w: image.size.height), forKey: kCIInputExtentKey)
filter.setValue(BINS, forKey: "inputCount")
filter.setValue(1, forKey: "inputScale")
let myEAGLContext = EAGLContext(API: .OpenGLES2)
let options = [kCIContextWorkingColorSpace : kCFNull]
let context : CIContext = CIContext(EAGLContext: myEAGLContext, options: options)
context.render(filter.outputImage!, toBitmap: bitmap, rowBytes: totalBytes, bounds: filter.outputImage!.extent, format: kCIFormatRGBAh, colorSpace: CGColorSpaceCreateDeviceRGB())
let bytes = UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(bitmap), count: bpp * BINS)
//HOW TO CONVERT TWO CONSECUTIVE BYTES AS 16-BIT FLOATS?
//THIS CODE DOES NOT WORK (I guess because Float in Swift is 32-bit):
for var i=0; i < self.bpp * self.BINS; i+=self.bpp {
let bitsR = UnsafePointer<Float._BitsType>(self.queryHist!)[i+0].bigEndian
let R = Float( Float._fromBitPattern(bitsR) )
let bitsG = UnsafePointer<Float._BitsType>(self.queryHist!)[i+2].bigEndian
let G = Float( Float._fromBitPattern(bitsG) )
let bitsB = UnsafePointer<Float._BitsType>(self.queryHist!)[i+4].bigEndian
let B = Float( Float._fromBitPattern(bitsB) )
print("R/G/B = \(R) \(G) \(B)")
}
free(bitmap)
}
There is no 16-bit floating point type in Swift, but you can convert
the results to 32-bit floating point numbers (Float
).
This thread
contains a lot of information about the Half-precision floating-point format, and various conversion methods. The crucial hint however is in Ian Ollman's answer:
On OS X / iOS, you can use
vImageConvert_PlanarFtoPlanar16F
andvImageConvert_Planar16FtoPlanarF
. See Accelerate.framework.
Ian did provide no code however, so here is a possible implementation in Swift:
func areaHistogram(image : UIImage) {
let inputImage = CIImage(image: image)
let totalBytes : Int = bpp * BINS //8 * 64 for example
let bitmap = calloc(1, totalBytes)
let filter = CIFilter(name: "CIAreaHistogram")!
filter.setValue(inputImage, forKey: kCIInputImageKey)
filter.setValue(CIVector(x: 0, y: 0, z: image.size.width, w: image.size.height), forKey: kCIInputExtentKey)
filter.setValue(BINS, forKey: "inputCount")
filter.setValue(1, forKey: "inputScale")
let myEAGLContext = EAGLContext(API: .OpenGLES2)
let options = [kCIContextWorkingColorSpace : kCFNull]
let context : CIContext = CIContext(EAGLContext: myEAGLContext, options: options)
context.render(filter.outputImage!, toBitmap: bitmap, rowBytes: totalBytes, bounds: filter.outputImage!.extent, format: kCIFormatRGBAh, colorSpace: CGColorSpaceCreateDeviceRGB())
// *** CONVERSION FROM 16-bit TO 32-bit FLOAT ARRAY STARTS HERE ***
let comps = 4 // Number of components (RGBA)
// Array for the RGBA values of the histogram:
var rgbaFloat = [Float](count: comps * BINS, repeatedValue: 0)
// Source and image buffer structure for vImage conversion function:
var srcBuffer = vImage_Buffer(data: bitmap, height: 1, width: UInt(comps * BINS), rowBytes: bpp * BINS)
var dstBuffer = vImage_Buffer(data: &rgbaFloat, height: 1, width: UInt(comps * BINS), rowBytes: comps * sizeof(Float) * BINS)
// Half-precision float to Float conversion of entire buffer:
if vImageConvert_Planar16FtoPlanarF(&srcBuffer, &dstBuffer, 0) == kvImageNoError {
for bin in 0 ..< BINS {
let R = rgbaFloat[comps * bin + 0]
let G = rgbaFloat[comps * bin + 1]
let B = rgbaFloat[comps * bin + 2]
print("R/G/B = \(R) \(G) \(B)")
}
}
free(bitmap)
}
Remarks:
- You need to
import Accelerate
. - Note that your code allocates
totalBytes * bpp
bytes instead of the necessarytotalBytes
. - The
kCIFormatRGBAh
pixel format is not supported on the Simulator (as of Xcode 7), so you have to test the code on a real device.
来源:https://stackoverflow.com/questions/32933771/how-to-convert-bytes-to-half-floats-in-swift