【问题标题】:Perform normalization using Accelerate framework使用 Accelerate 框架执行规范化
【发布时间】:2020-11-22 21:50:18
【问题描述】:

我需要对包含 RGB 像素数据的 Data 执行简单的数学运算。目前我这样做是这样的:

let imageMean: Float = 127.5
let imageStd: Float = 127.5
let rgbData: Data // Some data containing RGB pixels 
let floats = (0..<rgbData.count).map {
    (Float(rgbData[$0]) - imageMean) / imageStd
}
return Data(bytes: floats, count: floats.count * MemoryLayout<Float>.size)

这行得通,但是太慢了。我希望我可以使用Accelerate 框架更快地计算这个,但不知道如何做到这一点。我保留了一些空间,以便每次启动此函数时都不会分配它,如下所示:

inputBufferDataNormalized = malloc(width * height * 3) // 3 channels RGB

我尝试了一些功能,例如vDSP_vasm,但我无法使其工作。有人可以指导我如何使用它吗?基本上我需要更换这个地图功能,因为它需要的时间太长。并且可能一直使用预先分配的空间会很棒。

【问题讨论】:

    标签: swift accelerate-framework


    【解决方案1】:

    我找到了一种使用Accelerate 的方法。首先,我像这样为转换后的缓冲区保留空间

    var inputBufferDataRawFloat = [Float](repeating: 0, count: width * height * 3)
    

    然后我可以这样使用它:

    let rawBytes = [UInt8](rgbData)
    vDSP_vfltu8(rawBytes, 1, &inputBufferDataRawFloat, 1, vDSP_Length(rawBytes.count))
                
    vDSP.add(inputBufferDataRawScalars.mean, inputBufferDataRawFloat, result: &inputBufferDataRawFloat)
    vDSP.multiply(inputBufferDataRawScalars.std, inputBufferDataRawFloat, result: &inputBufferDataRawFloat)
    
    return Data(bytes: inputBufferDataRawFloat, count: inputBufferDataRawFloat.count * MemoryLayout<Float>.size)
    

    运行速度非常快。 Accelerate 可能有更好的功能,如果有人知道,请告诉我。它需要执行函数(A[n] + B) * C(或者确切地说是(A[n] - B) / C,但第一个可以转换为此)。

    【讨论】:

    • add(multiplication:_:result:)developer.apple.com/documentation/accelerate/vdsp/3240840-add 有多种加乘组合可能比迭代两次更好。此外,您可以在不初始化 UnsafeMutableRawPointer 的情况下将内存分配给它,然后用Data.init(bytesNoCopy:) 接管它以避免最后的额外副本。
    • 我试过 vDSP.add(multiplication: (a: inputBufferDataRawFloat, b: inputBufferDataRawScalars.mean), inputBufferDataRawScalars.std, result: &inputBufferDataRawFloat) 但这不能正常工作。这会产生 a[n]*b + c,这不完全是我需要的公式( (A[n] - B) / C )。至于使用 UnsafeMutableRawPointer,我如何将它与这些功能一起使用?编译器给我一个错误:Static method 'add(multiplication:_:result:)' requires that 'UnsafeMutableRawPointer' conform to 'AccelerateBuffer'
    【解决方案2】:

    跟进我对您的其他相关问题的评论。您可以使用 SIMD 来并行化操作,但您需要将原始数组拆分为块。

    这是一个简化的例子,假设数组可以被 64 整除,例如一个有 1024 个元素的数组:

    let arr: [Float] = (0 ..< 1024).map { _ in Float.random(in: 0...1) }
    
    let imageMean: Float = 127.5
    let imageStd: Float = 127.5
    
    var chunks = [SIMD64<Float>]()
    chunks.reserveCapacity(arr.count / 64)
    
    for i in stride(from: 0, to: arr.count, by: 64) {
       let v = SIMD64.init(arr[i ..< i+64])
    
       chunks.append((v - imageMean) / imageStd) // same calculation using SIMD
    
    }
    

    您现在可以使用下标访问每个 chunk

    var results: [Float] = []
    results.reserveCapacity(arr.count)
    
    for chunk in chunks {
       for i in chunk.indices {
          results.append(chunk[i])
       }
    }
    

    当然,如果数组不能被 64 整除,则需要处理余数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-11
      • 1970-01-01
      • 2015-03-18
      • 1970-01-01
      相关资源
      最近更新 更多