在swift中将图像转换为二进制

tem*_*emo 4 cocoa-touch core-graphics uiimage ios 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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 6

几点意见:

  1. 确保您在具有发布版本(或关闭优化)的设备上进行测试.仅这一点就可以让它更快.在iPhone 7+上,它将1920 x 1080像素彩色图像的转换率从1.7秒降低到不到0.1秒.

  2. 您可能希望DispatchQueue.concurrentPerform同时处理像素.在我的iPhone 7+上,它的速度提高了一倍.

根据我的经验,Core Image滤镜的速度并不快,但如果您需要更快的速度,可以考虑使用vImage或Metal.但除非您处理的是非常大的图像,否则使用优化(可能是并发)的简单Swift代码的响应时间可能就足够了.

一个不相关的观察:

  1. 另外,我不确定你的黑白转换是如何工作的,但通常你想要计算彩色像素的相对亮度(例如0.2126*红+ 0.7152*绿+ 0.0722*蓝).当然,在将彩色图像转换为灰度时,你会做类似的事情,以获得更接近人眼可以看到的图像,如果转换为黑白,我会亲自做类似的事情.

仅供参考,我的Swift 3/4颜色到灰度的例程看起来像:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,如果要将其转换为绝对黑白,则相应地调整算法,但这说明了并发图像缓冲区操作例程.