Поскольку у вас есть доступ к вашему CVPixelBuffer, вы можете напрямую использовать фреймворк Accelerate, чтобы выполнить преобразование за вас.
Я не проверяю какие-либо ошибки, инструкции try / catch, никаких средств защиты и т. Д. c в этом коде. Вам нужно будет убедиться, что код защищен от ошибок.
Давайте сначала определим наш цветовой формат BGRA. Поскольку у нас 4 канала, нам нужно 32 бита на пиксель. Мы также определяем, что наш альфа-канал является последним битом.
var bgraSourceFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: Unmanaged.passRetained(CGColorSpaceCreateDeviceRGB()),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue),
version: 0,
decode: nil,
renderingIntent: .defaultIntent
)
Теперь мы можем определить формат BGR. Нам нужно 3 канала, поэтому 24 бита на пиксель вполне достаточно. Мы также определяем, что этот формат не будет иметь альфа-канала.
var bgrDestinationFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 24,
colorSpace: nil,
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
version: 0,
decode: nil,
renderingIntent: .defaultIntent
)
И создать конвертер ...
let bgraToRgbConverter = vImageConverter_CreateWithCGImageFormat(
&bgraSourceFormat,
&bgrDestinationFormat,
nil,
vImage_Flags(kvImagePrintDiagnosticsToConsole),
nil
)
let converter = bgraToRgbConverter!.takeRetainedValue()
Теперь нам нужно создать буфер чтения из нашего существующего пиксельные данные и буфер записи для копирования того, что нам нужно. Чтобы создать буфер чтения из CVPixelBuffer, мы можем сделать что-то вроде этого:
var bgraBuffer = vImage_Buffer()
let imageFormat = vImageCVImageFormat_CreateWithCVPixelBuffer(cvPixelBuffer).takeRetainedValue()
vImageCVImageFormat_SetColorSpace(imageFormat, CGColorSpaceCreateDeviceRGB())
vImageBuffer_InitWithCVPixelBuffer(
&bgraBuffer,
&bgraSourceFormat,
cvPixelBuffer,
imageFormat,
nil,
vImage_Flags(kvImageNoFlags)
)
И создать пустой буфер записи ...
var bgrBuffer = vImage_Buffer()
vImageBuffer_Init(
&bgrBuffer,
bgraBuffer.height,
bgraBuffer.width,
bgrDestinationFormat.bitsPerPixel,
vImage_Flags(kvImageNoFlags)
)
Мы готовы ... Давайте скажем фреймворк ускорения для преобразования из одного формата в другой
vImageConvert_AnyToAny(
converter,
&bgraBuffer,
&bgrBuffer,
nil,
vImage_Flags(kvImagePrintDiagnosticsToConsole)
)
И все. Ваш BGRA теперь преобразован в BGR как vImage_Buffer
. Мы можем проверить, выполнили ли мы то, что хотели, напрямую считывая данные пикселей. Во-первых, нам нужно получить доступ к данным:
let bgraData = bgraBuffer.data!.assumingMemoryBound(to: UInt8.self)
let bgrData = bgrBuffer.data!.assumingMemoryBound(to: UInt8.self)
Теперь мы можем распечатать первый и второй пиксели
print(bgraData[0], bgraData[1], bgraData[2], bgraData[3])
print(bgrData[0], bgrData[1], bgrData[2])
print(bgraData[4], bgraData[5], bgraData[6], bgraData[7])
print(bgrData[3], bgrData[4], bgrData[5])
Это результат, который я вижу из PNG image Я использовал для тестирования на игровых площадках:
249 244 234 255
249 244 234
251 242 233 255
251 242 233
Как видите, пиксели копируются без альфа-канала. Будьте осторожны с циклами for, если вы собираетесь их использовать, так как теперь у нас есть 3 канала.
Если вы разрабатываете игру + делаете это для каждого кадра, попробуйте сохранить ваши объекты живыми. Определения форматов Accelerate, буфер записи и конвертер никогда не меняются для одного и того же размера + формата изображения, поэтому их можно создать один раз и сохранить в памяти для использования в будущем.
Похоже, вы возвращаете объект данных. Вы можете преобразовать конструкцию UnsafeMutablePointer
во все, что вам нужно.
Или вы также можете преобразовать vImage_Buffer
обратно в CVPixelBuffer (если вам нужно) с помощью метода vImageBuffer_CopyToCVPixelBuffer
ускорения. В vImage_Buffer
много конвертеров, один точно вам подойдет. Проверьте эту ссылку для получения дополнительной информации о том, как использовать метод копирования в буфер пикселей. Эта ссылка содержит отличный пример использования.
Изменить: Ваш CVPixelBuffer может быть дополнен.
Из-за требований к оборудованию ваше изображение может иметь заполнение, чтобы убедиться, что ширина и высота буфера кратны 16. Это также приведет к заполнению ваших структур vImage_Buffer
. Если вам нужно l oop, но нужно только получить доступ / обновить отдельные пиксели, вы можете использовать функции Accelerate для увеличения скорости. Проверьте эту ссылку , чтобы узнать о возможных методах, и в конце страницы есть отличные примеры.
Чтобы полностью прочитать данные, вы можете написать что-то вроде этого:
var bgrData = bgrBuffer.data!.assumingMemoryBound(to: UInt8.self)
print(bgrBuffer.width, bgrBuffer.height, bgrBuffer.rowBytes)
for _ in 0 ..< Int(bgrBuffer.height) {
for x in 0 ..< Int(bgrBuffer.width) {
let b = (bgrData + x * 3 + 0).pointee
let g = (bgrData + x * 3 + 1).pointee
let r = (bgrData + x * 3 + 2).pointee
print(b, g, r)
}
bgrData = bgrData.advanced(by: bgrBuffer.rowBytes)
}
Это гарантирует, что вы читаете пиксели во всю ширину, но передаете заполнение в конце.