Swift,数组,图像处理。使用 array.map() 是处理数组中所有数据的最快方法吗?
Swift, array, image processing. Is using array.map() the fastest way to process all data in an array?
我有一个包含数百万个元素(7201 x 7201 个数据点)的数组,我正在将数据转换为灰度图像。
var imagePixels = heights.map { terrainToColorScale([=12=].height, gradient: .Linear) }
let _ = writeImageToFile(imagePixels, height: latStrides, width: lonStrides, imgColorSpace: .BW, to: imageURL, as: .png)
这段代码大约需要 11 秒才能完成(CPU=2.3Ghz 8 核 i9),但我希望尽可能获得更好的性能。
代码目前 运行 在单线程中。
简单地将我的 heights
数组分成块(比如 100 个块)和 运行 每个块都有一个任务的 TaskGroup 是否会得到不错的改进?
还是我正在考虑进入 Metal 和着色器(我对 Metal 了解为零!!)以获得更好的结果?
出于兴趣,生成的典型图像是...
(图像已缩减采样,因为太大而无法在此处上传。)
更新:添加与 terrainToColorScale
关联的代码
基本上,对于线性转换,它将采用地形高度(通常为 0...9000)并将其缩放到 return 0...255 之间的值
我还有非线性实现(下面未显示),它将显示主要是 low/high 地形高程的数据集的更多细节。
let terrainLowerCutOff: Double = 0.0 // Mean Sea Level
let terrainUpperCutOff: Double = 9000.0 // Value in meters, just higher that Everest
func terrainToColorScale(_ elev: Double, lowerCutOff: Double = terrainLowerCutOff, upperCutOff: Double = terrainUpperCutOff, gradient: ImageColorGradient = .Linear) -> UInt8 {
switch gradient {
case .Linear:
return linearColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .LinearInverse:
return linearInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseLows:
return reciprocalPowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseLowsInverse:
return reciprocalInversePowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseHighs:
return powerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseHighsInverse:
return powerInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
}
}
fileprivate func linearColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> UInt8 {
return UInt8( 255 * normaliseColorScale(value, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) )
}
fileprivate func normaliseColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> Double {
switch value {
case _ where value <= lowerCutOff :
return 0.0
case _ where value >= upperCutOff :
return 1.0
default :
return (value - lowerCutOff) / (upperCutOff - lowerCutOff)
}
}
这不是您问题的完整答案,但我认为它应该能让您了解下一步该怎么做。 vDSP 是 Accelerate
的一部分,它旨在加速数组的数学运算。此代码使用多个步骤,因此可能会更加优化,并且它没有考虑除线性之外的任何其他过滤器,但我没有足够的知识来使这些步骤更有效。然而,在我的机器上,vDSP 在以下处理中比 map 快 4 倍:
import Foundation
import Accelerate
let count = 7200 * 7200
var date = Date()
print("Generating")
let test: [CGPoint] = (0..<count).map {
CGPoint(x: [=10=], y: Int.random(in: -2000...10000))
}
print("Generating took \(Date().timeIntervalSince(date))")
date = Date()
print("Mapping")
let heights: [Float] = test.map { Float([=10=].y) }
print("Mapping took \(Date().timeIntervalSince(date))")
date = Date()
print("Converting via vDSP")
let clipped = vDSP.clip(heights, to: 0...9000)
let scaled = vDSP.divide(clipped, 9000)
let multiplied = vDSP.multiply(255, scaled)
let integers = vDSP.floatingPointToInteger(multiplied, integerType: UInt8.self, rounding: .towardNearestInteger)
print("Converting via DSP took \(Date().timeIntervalSince(date))")
date = Date()
print("Converting via map")
let mappedIntegers = heights.map { height -> UInt8 in
let clipped: Float
if height < 0 {
clipped = 0
} else if height > 9000 {
clipped = 9000
} else {
clipped = height
}
let scaled = clipped / 9000
let multiplied = scaled * 255
return UInt8(multiplied.rounded())
}
print("Converting via map took \(Date().timeIntervalSince(date))")
我有一个包含数百万个元素(7201 x 7201 个数据点)的数组,我正在将数据转换为灰度图像。
var imagePixels = heights.map { terrainToColorScale([=12=].height, gradient: .Linear) }
let _ = writeImageToFile(imagePixels, height: latStrides, width: lonStrides, imgColorSpace: .BW, to: imageURL, as: .png)
这段代码大约需要 11 秒才能完成(CPU=2.3Ghz 8 核 i9),但我希望尽可能获得更好的性能。 代码目前 运行 在单线程中。
简单地将我的 heights
数组分成块(比如 100 个块)和 运行 每个块都有一个任务的 TaskGroup 是否会得到不错的改进?
还是我正在考虑进入 Metal 和着色器(我对 Metal 了解为零!!)以获得更好的结果?
出于兴趣,生成的典型图像是...
(图像已缩减采样,因为太大而无法在此处上传。)
更新:添加与 terrainToColorScale
关联的代码
基本上,对于线性转换,它将采用地形高度(通常为 0...9000)并将其缩放到 return 0...255 之间的值
我还有非线性实现(下面未显示),它将显示主要是 low/high 地形高程的数据集的更多细节。
let terrainLowerCutOff: Double = 0.0 // Mean Sea Level
let terrainUpperCutOff: Double = 9000.0 // Value in meters, just higher that Everest
func terrainToColorScale(_ elev: Double, lowerCutOff: Double = terrainLowerCutOff, upperCutOff: Double = terrainUpperCutOff, gradient: ImageColorGradient = .Linear) -> UInt8 {
switch gradient {
case .Linear:
return linearColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .LinearInverse:
return linearInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseLows:
return reciprocalPowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseLowsInverse:
return reciprocalInversePowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseHighs:
return powerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
case .CurveEmphasiseHighsInverse:
return powerInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff)
}
}
fileprivate func linearColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> UInt8 {
return UInt8( 255 * normaliseColorScale(value, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) )
}
fileprivate func normaliseColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> Double {
switch value {
case _ where value <= lowerCutOff :
return 0.0
case _ where value >= upperCutOff :
return 1.0
default :
return (value - lowerCutOff) / (upperCutOff - lowerCutOff)
}
}
这不是您问题的完整答案,但我认为它应该能让您了解下一步该怎么做。 vDSP 是 Accelerate
的一部分,它旨在加速数组的数学运算。此代码使用多个步骤,因此可能会更加优化,并且它没有考虑除线性之外的任何其他过滤器,但我没有足够的知识来使这些步骤更有效。然而,在我的机器上,vDSP 在以下处理中比 map 快 4 倍:
import Foundation
import Accelerate
let count = 7200 * 7200
var date = Date()
print("Generating")
let test: [CGPoint] = (0..<count).map {
CGPoint(x: [=10=], y: Int.random(in: -2000...10000))
}
print("Generating took \(Date().timeIntervalSince(date))")
date = Date()
print("Mapping")
let heights: [Float] = test.map { Float([=10=].y) }
print("Mapping took \(Date().timeIntervalSince(date))")
date = Date()
print("Converting via vDSP")
let clipped = vDSP.clip(heights, to: 0...9000)
let scaled = vDSP.divide(clipped, 9000)
let multiplied = vDSP.multiply(255, scaled)
let integers = vDSP.floatingPointToInteger(multiplied, integerType: UInt8.self, rounding: .towardNearestInteger)
print("Converting via DSP took \(Date().timeIntervalSince(date))")
date = Date()
print("Converting via map")
let mappedIntegers = heights.map { height -> UInt8 in
let clipped: Float
if height < 0 {
clipped = 0
} else if height > 9000 {
clipped = 9000
} else {
clipped = height
}
let scaled = clipped / 9000
let multiplied = scaled * 255
return UInt8(multiplied.rounded())
}
print("Converting via map took \(Date().timeIntervalSince(date))")