从缓冲区创build的NSData创buildUIImage返回nil?
我试图通过抓住CGImage
,获取每个像素,并从中减去0xa
,然后将每个像素保存到一个新的缓冲区来使UIImage
变暗。 但是当我尝试加载缓冲区作为图像,函数[ 创build一个CGImage ]返回零 。 这意味着我必须在代码中做错了一些事情(我不会感到惊讶)。 我希望这与缓冲区格式不正确有关。 熟悉Core Graphics的人能帮我发现错误吗?
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
一些想法:
-
在编写自己的image processing例程之前,可以考虑应用Core Image滤镜之一 。 这可能会让你的生活变得更容易,而且可能会给你更精致的结果。 只是将每个通道减less一些固定的数字就会引起失真,这是你不期望的(例如,色彩偏移,饱和度变化等)。
-
如果你打算这样做的话,我会很谨慎,只是抓住数据提供者并按原样操纵它。 你可能会想要介绍各种条件逻辑的图像的提供者的性质(比特每通道,ARGB与RGBA等)。 如果你看一下苹果公司在Q&A#1509中的例子 ,你也可以检索一个预定格式的像素缓冲区(在他们的ARGB例子中,每个组件8位,每个像素4个字节)。
这个例子是过时的,但它显示了如何创build一个预定格式的上下文,然后将图像绘制到该上下文中。 然后,您可以使用自己的提供程序而不是JPEG数据提供程序来处理该数据,并使用此预定格式创build新图像。
-
代码示例中最重要的问题是您正在尝试使用
CGImageCreateWithJPEGDataProvider
,它期望“数据提供程序提供JPEG编码的数据”。 但是你的提供者可能不是JPEG编码的,所以它会失败。 如果要使用原始图像提供者格式的数据,则必须使用CGImageCreate
(手动提供width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorSpace,bitmapInfo等)创build一个新图像。 -
你的例程有一些不太严重的问题:
-
你注意到你看到每个第四个字节是一个
0xff
。 在你的代码评论中回答你的问题,这无疑是alpha通道。 (你可以通过检查原CGImageRef
的CGBitmapInfo
来确认。)你可能没有使用alpha通道,但是很明显。 -
你的例程,如果通道的值小于
0x0a
,将其设置为0xff
。 这显然不是你的意图(例如,如果像素是黑色的,你会使它变白!)。 你应该检查这个逻辑。 -
在我的testing中,迭代/操作
Byte
数组的方法非常慢。 我不完全确定这是为什么,但如果你直接操纵字节缓冲区,速度会更快。
-
因此,下面请find创build预定格式(RGBA,每个组件8位等)的上下文的例程,操纵它(尽pipe您可以随心所欲地转换为B&W),并从中创build新的图像。 所以,在Swift 2:
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) }
哪里
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 }
或者,在Swift 3:
func blackAndWhite(image: UIImage) -> UIImage? { // 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") return nil } let pixels = buffer.bindMemory(to: Pixel.self, capacity: width * height) 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 = context.makeImage()! return 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 } }