Выполните нормализацию с помощью Accelerate framework - PullRequest
0 голосов
/ 02 августа 2020

Мне нужно выполнить простую математическую операцию с Data, содержащим данные пикселей RGB. Сейчас я делаю это так:

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, но не смог заставить их работать. Может ли кто-нибудь посоветовать мне, как его использовать? В основном мне нужно заменить эту функцию карты, потому что это занимает слишком много времени. И, наверное, было бы здорово использовать все время заранее выделенное пространство.

Ответы [ 2 ]

0 голосов
/ 02 августа 2020

Я нашел способ сделать это, используя 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, но первый может быть преобразован в это).

0 голосов
/ 02 августа 2020

В продолжение моего комментария по вашему другому связанному вопросу. Вы можете использовать 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...