问题描述
如何在Swift中将两个字节(UInt8)转换为半精度(16位)浮点数,如使用kCIFormatRGBAh读取CIAreaHistogram的输出时所需要的,如下例所示:
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)
}
推荐答案
Swift中没有16位浮点类型,但是您可以将结果转换
转换为32位浮点数( Float
)。
此线程
There is no 16-bit floating point type in Swift, but you can convertthe results to 32-bit floating point numbers (Float
).This thread
- 32-bit to 16-bit Floating Point Conversion
包含有关$ b的很多信息$ b ,以及各种转换方法。但是,关键提示在中:
contains a lot of information about theHalf-precision floating-point format, and various conversion methods. The crucial hint however is in Ian Ollman's answer:
Ian没有提供任何代码,因此这是在Swift中可能的实现
:
Ian did provide no code however, so here is a possible implementationin 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)
}
备注:
- 您需要
导入加速
。 - 请注意,您的代码分配了
totalBytes * bpp
字节,而不是必需的totalBytes
中的
。 -
kCIFormatRGBAh
Simulator(从Xcode 7开始)不支持像素格式,因此您必须在真实设备上测试代码。
- You need to
import Accelerate
. - Note that your code allocates
totalBytes * bpp
bytes insteadof 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.
更新::Swift 5.3(当前处于beta版的Xcode 12)引入了新的 Float16
类型,该类型可在iOS 14中使用参见 o n Swift快速开发。
Update: Swift 5.3 (Xcode 12, currently in beta) introduces a new Float16
type which is available in iOS 14, see SE-0277 Float16 on Swift Evolution.
这简化了代码,因为不再需要转换为 Float
。我还删除了从iOS 12开始不推荐使用的OpenGL函数:
This simplifies the code because a conversion to Float
is no longer necessary. I have also removed the use of OpenGL functions which are deprecated as of iOS 12:
func areaHistogram(image: UIImage, bins: Int) -> [Float16] {
let comps = 4 // Number of components (RGBA)
let inputImage = CIImage(image: image)
var rgbaFloat = [Float16](repeating: 0, count: comps * bins)
let totalBytes = MemoryLayout<Float16>.size * comps * bins
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 options: [CIContextOption : Any] = [.workingColorSpace : NSNull()]
let context = CIContext(options: options)
rgbaFloat.withUnsafeMutableBytes {
context.render(filter.outputImage!, toBitmap: $0.baseAddress!, rowBytes: totalBytes,
bounds: filter.outputImage!.extent, format: CIFormat.RGBAh,
colorSpace: CGColorSpaceCreateDeviceRGB())
}
return rgbaFloat
}
这篇关于如何在Swift中将字节转换为半浮点数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!