从缓冲区创建的NSData创建UIImage返回nil?

时间:2014-11-01 01:59:45

标签: ios image swift uiimage core-graphics

我试图通过抓取UIImage,获取每个像素并从中减去CGImage来使0xa变暗,然后将每个像素保存到新缓冲区。但是当我尝试将该缓冲区作为图像加载时,函数[创建CGImage ]返回 nil 。这意味着我必须在我的代码中做错了(我不会感到惊讶)。我希望它与缓冲区格式不正确或其他东西有关。熟悉核心图形的人可以帮助我发现错误吗?

var provider = CGImageGetDataProvider(imageArray[imageNumber]?.CGImage) //Get data provider for image in an array at index No. imageNumber
    let data = CGDataProviderCopyData(provider)
    var buffer = [Byte](count: CFDataGetLength(data), repeatedValue: 0) //create buffer for image data
    CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), &buffer) //load the image's bytes into buffer
    var newBuffer = [Byte](count:buffer.count, repeatedValue: 0) //Going to make some changes, need a place to save new image
    var index = 0
    for aByte in buffer {
        if aByte > 0xa && aByte != 0xff {
       newBuffer[index] = (aByte - 0xa) //subtract 0xa from buffer, where possible
        }
        else{
            newBuffer[index] = (0xff) //I *think* there is no alpha channel, but every fourth byte in buffer is 0xff
        }
        index += 1
    }
    var coreGraphicsImage = CGImageCreateWithJPEGDataProvider(CGDataProviderCreateWithCFData( CFDataCreate(kCFAllocatorDefault, newBuffer, newBuffer.count)), nil, true, kCGRenderingIntentDefault) //create CGimage from newBuffer.RETURNS NIL!
    let myImage = UIImage(CGImage: coreGraphicsImage) //also nil
    imageView.image = myImage

1 个答案:

答案 0 :(得分:4)

有几点想法:

  1. 在编写自己的图像处理例程之前,您可以考虑应用Core Image filters之一。它可能会让您的生活更轻松,也可能为您带来更精致的结果。只需将每个通道减少一些固定数字就会引入您不期望的失真(例如色彩偏移,饱和度变化等)。

  2. 如果你打算这样做,我会对抓住数据提供者并按原样操作它保持警惕。您可能希望为图像提供者的性质(每通道位数,ARGB与RGBA等)引入各种条件逻辑。如果查看Apple's example in Q&A #1509,您可以检索预定格式的像素缓冲区(例如ARGB,每个组件8位,每像素4个字节。

    此示例已过时,但它显示了如何创建预定格式的上下文,然后将图像绘制到该上下文中。然后,您可以使用您自己的提供程序而不是JPEG数据提供程序来操作该数据,并使用此预定格式创建新图像。

  3. 代码示例中最重要的问题是您正在尝试使用CGImageCreateWithJPEGDataProvider,它需要“数据提供程序提供JPEG编码数据”。但是你的提供者可能不是JPEG编码的,所以它会失败。如果您要使用原始图像提供程序格式的数据,则必须使用CGImageCreate创建新图像(手动提供width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorSpace,bitmapInfo,等)。

  4. 您的日常工作存在一些不太严重的问题:

    • 您注意到您看到每个第四个字节都是0xff。在您的代码注释中回答您的问题时,这无疑是alpha通道。 (您可以通过检查原始CGBitmapInfo的{​​{1}}来确认这一点。)您可能没有使用Alpha通道,但它显然在那里。

    • 如果频道的值小于CGImageRef,您的例程会将其设置为0x0a。这显然不是你的意图(例如,如果像素是黑色的,你会把它变成白色!)。你应该检查一下这个逻辑。

    • 在我的测试中,这种迭代/操作0xff数组的方法非常慢。我不完全确定为什么会这样,但是如果直接操作字节缓冲区,它会快得多。

  5. 所以,下面,请找到创建预定格式(RGBA,每个组件8位等)的上下文的例程,操作它(我转换为B& W虽然你可以做你想做的任何事情),并创建新的那个图像。所以,在Swift 2中:

    Byte

    其中

    func blackAndWhiteImage(image: UIImage) -> UIImage? {
        // get information about image
    
        let imageref = image.CGImage
        let width = CGImageGetWidth(imageref)
        let height = CGImageGetHeight(imageref)
    
        // create new bitmap context
    
        let bitsPerComponent = 8
        let bytesPerPixel = 4
        let bytesPerRow = width * bytesPerPixel
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = Pixel.bitmapInfo
        let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo)
    
        // draw image to context
    
        let rect = CGRectMake(0, 0, CGFloat(width), CGFloat(height))
        CGContextDrawImage(context, rect, imageref)
    
        // manipulate binary data
    
        let pixels = UnsafeMutablePointer<Pixel>(CGBitmapContextGetData(context))
    
        for row in 0 ..< height {
            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 = CGBitmapContextCreateImage(context)!
        return UIImage(CGImage: outputImage, scale: image.scale, orientation: image.imageOrientation)
    }
    

    或者,在Swift 3中:

    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
    }
    
    func ==(lhs: Pixel, rhs: Pixel) -> Bool {
        return lhs.rgba == rhs.rgba
    }