[英]How to convert bytes to half-floats in Swift?
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:如何在 Swift 中将两个字节 (UInt8) 转换为半精度(16 位)浮点数,例如在使用 kCIFormatRGBAh 读取 CIAreaHistogram 的输出时需要,如下例所示:
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
). Swift 中没有 16 位浮点类型,但您可以将结果转换为 32 位浮点数 (
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 :
然而,关键的提示是在Ian Ollman 的回答中:
On OS X / iOS, you can use
vImageConvert_PlanarFtoPlanar16F
andvImageConvert_Planar16FtoPlanarF
.在 OS X / iOS 上,您可以使用
vImageConvert_PlanarFtoPlanar16F
和vImageConvert_Planar16FtoPlanarF
。 See Accelerate.framework.请参阅 Accelerate.framework。
Ian did provide no code however, so here is a possible implementation in Swift:然而,Ian 没有提供任何代码,所以这里是一个可能的 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:评论:
import Accelerate
.import Accelerate
。totalBytes * bpp
bytes instead of the necessary totalBytes
.totalBytes * bpp
字节而不是必要的totalBytes
。kCIFormatRGBAh
pixel format is not supported on the Simulator (as of Xcode 7), so you have to test the code on a real device.kCIFormatRGBAh
像素格式(从 Xcode 7 开始),因此您必须在真实设备上测试代码。 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.更新: Swift 5.3(Xcode 12,目前处于测试阶段)引入了一种新的
Float16
类型,可在 iOS 14 中使用,请参阅 Swift Evolution 上的SE-0277 Float16 。
This simplifies the code because a conversion to Float
is no longer necessary.这简化了代码,因为不再需要转换为
Float
。 I have also removed the use of OpenGL functions which are deprecated as of iOS 12:我还删除了自 iOS 12 起已弃用的 OpenGL 函数的使用:
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
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.