преобразование изображения в двоичный код в 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)
}
}
}
772   2  

2 ответов:

Пара замечаний:

  1. Убедитесь, что вы выполняете тест на устройстве со сборкой выпуска (или оптимизациями, отключенными). Уже одно это делает его намного быстрее. На iPhone 7+ он уменьшил преобразование цветного изображения 1920 x 1080 пикселей в оттенки серого с 1,7 секунды до менее чем 0,1 секунды.

  2. Возможно, вы захотите использовать DispatchQueue.concurrentPerform для одновременной обработки пикселей. На моем iPhone 7+ это было примерно в два раза быстрее.

В моем опыте основные фильтры изображений были не намного быстрее, но вы можете созерцать vImage или металл, если вам это нужно гораздо быстрее. Но если вы не имеете дело с чрезвычайно большими изображениями, время отклика с оптимизированным (и, возможно, параллельным) простым Swift-кодом может быть достаточным.

Несвязанное наблюдение:

  1. Кроме того, я не уверен, как работает ваше преобразование в черно-белое, но часто вы хотите вычислить относительную яркостьцветного пикселя (например, 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

    Ничего не найдено.