变换图像为二进制中迅速

问题描述:

我想将图像转换为二进制黑白,在我通过像素(存储在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) 
     } 
    } 
} 
+0

我同意你最后的陈述,应该有一些现有的lib来做这个操作,最好是使用GPU,但是“思考” - 注释:你目前以非顺序方式访问像素。在处理性能关键应用程序(如图像处理)时,务必确保按照存储顺序依次访问存储器。由于你的缓冲区(看起来)只是一个很长的数组,你应该依次读写它的元素。 – dfri

+0

查看[这些搜索结果](https://*.com/search?tab=newest&q=%5bios%5d%20uiimage%20convert%20black%20white)。 – rmaddy

+1

不要不必要地使用标签。这个问题与Xcode完全无关。使用前请阅读标签说明。 – rmaddy

一对夫妇的意见:

  1. 确保您有一个发布版本的设备上做了测试(或优化关闭)。仅此而已,这使得它更快。在iPhone 7+上,它将1920 x 1080像素彩色图像的灰度从1.7秒降低到了不到0.1秒。

  2. 您可能需要使用DispatchQueue.concurrentPerform同时处理象素。在我的iPhone 7+上,这使它快了一倍。

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

一个无关的观察:

  1. 另外,我不知道如何转换为黑白作品,但往往你要计算的颜色relative luminance像素(例如,0.2126 *红色+ 0.7152 *绿色+ 0.0722 *蓝色)。当然,转换时的彩色图像灰度你会做这样的事情来获得更紧密地代表着什么人眼可以看到的图像,以及我个人做这样的事情,如果转换成黑色和白色,太。

  2. 仅供参考,我的雨燕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 
        } 
    } 
    

    显然,如果你想将它转换为绝对黑色和白色,然后调整算法相应地,但是这说明了并发图像缓冲器操作例程。

开始=“3”>
+0

yup发布构建伎俩,即使是我的整个例程,现在花费的时间可以忽略不计,谢谢小号 – temo

的VIMAGE转换为1位为vImageConvert_Planar8ToPlanar1。我建议使用抖动选项之一。您需要先将RGB图像转换为灰度图像。原则上,这是vImageMatrixMultiply_ARGB8888ToPlanar8(),但实际上它可能要涉及一些更复杂的色彩空间转换,而不是一个简单的矩阵。

如果这听起来太复杂了,只需使用vImageConvert_AnyToAny,它应该做正确的事情。