2014-11-01 59 views
3

我試圖通過抓住CGImage來暗化UIImage,獲取每個像素並從中減去0xa,然後將每個像素保存到新的緩衝區。但是,當我嘗試將該緩衝區加載回圖像時,函數[以創建CGImage]返回。這意味着我必須在我的代碼中做錯了一些事情(我不會感到驚訝)。我希望這與緩衝區格式不正確或者某些事情有關。有人能熟悉核心圖形幫我找到錯誤?從緩衝區創建的NSData創建UIImage返回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 
+0

而不是由像素打算像素,創建充滿色彩的圖像(是0xA) ,並從你的原始圖像中減去它。您可以使用UIRectFill使用顏色填充圖像。在UIColor上使用setFill()設置填充顏色。然後,減去 - http://stackoverflow.com/questions/9690576/subtract-one-image-from-another-ios – 2014-11-01 03:48:09

回答

4

一對夫婦的想法:

  1. 你去寫你自己的圖像處理程序之前,你可能會考慮應用Core Image filters之一。它可能會讓你的生活變得更加輕鬆,並且可能會給你更精緻的結果。只是將每個通道縮小一些固定的數字會導致您不期待的失真(例如,色彩偏移,飽和度變化等)。

  2. 如果你打算這樣做,我會警惕只抓住數據提供者並按原樣操縱它。您可能想要爲圖像提供者的性質介紹各種條件邏輯(每個通道的位數,ARGB與RGBA等)。如果您查看Apple's example in Q&A #1509,則可以取而代之檢索預定格式的像素緩衝區(在其示例ARGB中,每個組件8位,每個像素4個字節)

    此示例已過期,但它顯示瞭如何創建上下文然後將圖像繪製到該上下文中,然後您可以操作該數據,並使用此預定格式使用您自己的提供程序而不是JPEG數據提供程序創建新圖像

  3. 最重要的問題你的代碼示例是你試圖使用CGImageCreateWithJPEGDataProvider,它需要一個「提供JPEG編碼數據的數據提供者」。但是你的提供者可能不是JPEG編碼的,所以它會失敗。如果你打算使用f中的數據ormat),那麼你必須使用CGImageCreate(手動提供寬度,高度,bitsPerComponent,bitsPerPixel,bytesPerRow,colorSpace,bitmapInfo等)創建一個新圖像。

  4. 還有一些不太嚴重的問題,你的日常:

    • 你注意,你所看到的每一個第四個字節是0xff。在你的代碼評論中回答你的問題,這無疑是alpha通道。 (您可以通過檢查原始CGImageRefCGBitmapInfo來確認。)您可能沒有使用Alpha通道,但顯然存在。

    • 如果通道的值小於0x0a,則您的例程將其設置爲0xff。這顯然不是你的意圖(例如,如果像素是黑色的,你會使它變白!)。你應該檢查這個邏輯。

    • 在我的測試中,這種迭代/操作Byte數組的方法非常慢。我不完全確定這是爲什麼,但如果你直接操縱字節緩衝區,速度會更快。

所以,下面,請找創建預定格式的情況下(RGBA,每個組件8位等)例程,操作它(我轉換到B &W¯¯雖然你可以做任何你想要的),並從中創建新的圖像。因此,在斯威夫特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 
} 

或者,斯威夫特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 
    } 
} 
+0

你的答案是偉大的和讚賞,但我想指出,這是更多的學術練習操作緩衝區,十六進制值和像素 - 我知道實際結果會被扭曲,並且該功能效率低下 - 在我的2012 iMac上運行需要兩分鐘! ;)這就是說,你的答案非常有用,特別是當我將這個實驗轉化爲實際產品時。現在雖然我想知道爲什麼CGImage的創建返回零?有什麼方法我應該將緩衝區標記爲圖像數據,或者我的控制流程導致無效的十六進制值? – PopKernel 2014-11-01 04:30:27

+2

1.您最可能得到'nil',因爲您創建的提供者不包含「JPEG編碼數據」。 2.由於'Array '中發生了一些事情,速度非常緩慢:當我使用'UnsafeMutablePointer '時,速度提高了兩個數量級。看修改後的答案。 – Rob 2014-11-01 18:27:49

+0

謝謝,這是完美的! – PopKernel 2014-11-01 18:33:26