преобразование изображения в двоичный код в swift
Я хочу преобразовать изображение в двоичное черно-белое, в данный момент я перебираю пиксели (хранящиеся в UnsafeMutableBufferPointer), используя обычные вложенные циклы, сравнивая каждый RGB со средним и устанавливая его в черный или белый, однако.
Это кажется очень медленным, и я уверен, что есть встроенный способ, который использует gpu или хорошо оптимизирован. Если бы вы могли предоставить пример кода или ссылку, это было бы здорово.
for var y in 0..<height {
for var x in 0..<width{
//Pixel is small class i made for 8 bit access and comparison
if (Buffer[x+y*width] < AVRRGB) {
Buffer[x+y*width] = Pixel(RGB: 0x000000FF)
} else{
Buffer[x+y*width] = Pixel(RGB: 0xFFFFFFFF)
}
}
}
2 ответов:
Пара замечаний:
Убедитесь, что вы выполняете тест на устройстве со сборкой выпуска (или оптимизациями, отключенными). Уже одно это делает его намного быстрее. На iPhone 7+ он уменьшил преобразование цветного изображения 1920 x 1080 пикселей в оттенки серого с 1,7 секунды до менее чем 0,1 секунды.
Возможно, вы захотите использовать
DispatchQueue.concurrentPerformдля одновременной обработки пикселей. На моем iPhone 7+ это было примерно в два раза быстрее.В моем опыте основные фильтры изображений были не намного быстрее, но вы можете созерцать vImage или металл, если вам это нужно гораздо быстрее. Но если вы не имеете дело с чрезвычайно большими изображениями, время отклика с оптимизированным (и, возможно, параллельным) простым Swift-кодом может быть достаточным.
Несвязанное наблюдение:
- Кроме того, я не уверен, как работает ваше преобразование в черно-белое, но часто вы хотите вычислить относительную яркостьцветного пикселя (например, 0.2126 * красный + 0.7152 * зеленый + 0.0722 * синий). Конечно, при преобразовании цветного изображения в оттенки серого вы бы сделали что-то подобное, чтобы получить изображение, которое более точно отражает то, что может видеть человеческий глаз, и я лично сделал бы что-то подобное, если бы тоже преобразовал в черно-белое.
К вашему сведению, моя процедура Swift 3/4 color-to-grayscale выглядит следующим образом:
func blackAndWhite(image: UIImage, completion: @escaping (UIImage?) -> Void) { DispatchQueue.global(qos: .userInitiated).async { // get information about image let imageref = image.cgImage! let width = imageref.width let height = imageref.height // create new bitmap context let bitsPerComponent = 8 let bytesPerPixel = 4 let bytesPerRow = width * bytesPerPixel let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = Pixel.bitmapInfo let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)! // draw image to context let rect = CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height)) context.draw(imageref, in: rect) // manipulate binary data guard let buffer = context.data else { print("unable to get context data") completion(nil) return } let pixels = buffer.bindMemory(to: Pixel.self, capacity: width * height) DispatchQueue.concurrentPerform(iterations: height) { row in for col in 0 ..< width { let offset = Int(row * width + col) let red = Float(pixels[offset].red) let green = Float(pixels[offset].green) let blue = Float(pixels[offset].blue) let alpha = pixels[offset].alpha let luminance = UInt8(0.2126 * red + 0.7152 * green + 0.0722 * blue) pixels[offset] = Pixel(red: luminance, green: luminance, blue: luminance, alpha: alpha) } } // return the image let outputImage = context.makeImage()! completion(UIImage(cgImage: outputImage, scale: image.scale, orientation: image.imageOrientation)) } } struct Pixel: Equatable { private var rgba: UInt32 var red: UInt8 { return UInt8((rgba >> 24) & 255) } var green: UInt8 { return UInt8((rgba >> 16) & 255) } var blue: UInt8 { return UInt8((rgba >> 8) & 255) } var alpha: UInt8 { return UInt8((rgba >> 0) & 255) } init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) { rgba = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0) } static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue static func ==(lhs: Pixel, rhs: Pixel) -> Bool { return lhs.rgba == rhs.rgba } }Ясно, что если вы хотите преобразовать его в абсолютное черно-белое, то отрегулируйте алгоритм соответствующим образом, но это иллюстрирует параллельный буфер изображений рутинные манипуляции.
Преобразование vImage в 1 бит - это vImageConvert_Planar8ToPlanar1. Я рекомендую использовать один из вариантов dithered. Сначала вам нужно будет преобразовать изображение RGB в оттенки серого. В принципе, это vImageMatrixMultiply_ARGB8888ToPlanar8 (), хотя на самом деле это, вероятно, должно включать в себя более сложное преобразование цветового пространства, а не простую матрицу.
Если все это звучит слишком сложно, просто используйте vImageConvert_AnyToAny, и он должен делать правильные вещи.
Comments