如何在Swift中将字节转换为半浮点数? [英] How to convert bytes to half-floats in Swift?

查看:86
本文介绍了如何在Swift中将字节转换为半浮点数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在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 convert the results to 32-bit floating point numbers (Float). This thread

  • 32-bit to 16-bit Floating Point Conversion

包含有关$ b的很多信息$ b 半精度浮点格式,以及各种转换方法。但是,关键提示在 Ian Ollman的答案中:

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:


在OS X / iOS上,您可以使用 vImageConvert_PlanarFtoPlanar16F
vImageConvert_Planar16FtoPlanarF 。请参阅Accelerate.framework。

On OS X / iOS, you can use vImageConvert_PlanarFtoPlanar16F and vImageConvert_Planar16FtoPlanarF. See Accelerate.framework.

Ian没有提供任何代码,因此这是在Swift中可能的实现

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)
}

备注:


  • 您需要导入加速

  • 请注意,您的代码分配了 totalBytes * bpp 字节,而不是必需的 totalBytes 中的

  • kCIFormatRGBAh Simulator(从Xcode 7开始)不支持像素格式,因此您必须在真实设备上测试代码。

  • You need to import Accelerate.
  • Note that your code allocates totalBytes * bpp bytes instead of the necessary totalBytes.
  • 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中使用参见 SE-0277 Float16 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中将字节转换为半浮点数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆