Change color of certain pixels in a UIImage Change color of certain pixels in a UIImage ios ios

Change color of certain pixels in a UIImage


You have to extract the pixel buffer of the image, at which point you can loop through, changing pixels as you see fit. At the end, create a new image from the buffer.

In Swift 3, this looks like:

func processPixels(in image: UIImage) -> UIImage? {    guard let inputCGImage = image.cgImage else {        print("unable to get cgImage")        return nil    }    let colorSpace       = CGColorSpaceCreateDeviceRGB()    let width            = inputCGImage.width    let height           = inputCGImage.height    let bytesPerPixel    = 4    let bitsPerComponent = 8    let bytesPerRow      = bytesPerPixel * width    let bitmapInfo       = RGBA32.bitmapInfo    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {        print("unable to create context")        return nil    }    context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))    guard let buffer = context.data else {        print("unable to get context data")        return nil    }    let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)    for row in 0 ..< Int(height) {        for column in 0 ..< Int(width) {            let offset = row * width + column            if pixelBuffer[offset] == .black {                pixelBuffer[offset] = .red            }        }    }    let outputCGImage = context.makeImage()!    let outputImage = UIImage(cgImage: outputCGImage, scale: image.scale, orientation: image.imageOrientation)    return outputImage}struct RGBA32: Equatable {    private var color: UInt32    var redComponent: UInt8 {        return UInt8((color >> 24) & 255)    }    var greenComponent: UInt8 {        return UInt8((color >> 16) & 255)    }    var blueComponent: UInt8 {        return UInt8((color >> 8) & 255)    }    var alphaComponent: UInt8 {        return UInt8((color >> 0) & 255)    }            init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {        let red   = UInt32(red)        let green = UInt32(green)        let blue  = UInt32(blue)        let alpha = UInt32(alpha)        color = (red << 24) | (green << 16) | (blue << 8) | (alpha << 0)    }    static let red     = RGBA32(red: 255, green: 0,   blue: 0,   alpha: 255)    static let green   = RGBA32(red: 0,   green: 255, blue: 0,   alpha: 255)    static let blue    = RGBA32(red: 0,   green: 0,   blue: 255, alpha: 255)    static let white   = RGBA32(red: 255, green: 255, blue: 255, alpha: 255)    static let black   = RGBA32(red: 0,   green: 0,   blue: 0,   alpha: 255)    static let magenta = RGBA32(red: 255, green: 0,   blue: 255, alpha: 255)    static let yellow  = RGBA32(red: 255, green: 255, blue: 0,   alpha: 255)    static let cyan    = RGBA32(red: 0,   green: 255, blue: 255, alpha: 255)    static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue    static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {        return lhs.color == rhs.color    }}

For Swift 2 rendition, see previous revision of this answer.


For getting better result we can search color range in image pixels, referring to @Rob answer I made update and now the result is better.

func processByPixel(in image: UIImage) -> UIImage? {    guard let inputCGImage = image.cgImage else { print("unable to get cgImage"); return nil }    let colorSpace       = CGColorSpaceCreateDeviceRGB()    let width            = inputCGImage.width    let height           = inputCGImage.height    let bytesPerPixel    = 4    let bitsPerComponent = 8    let bytesPerRow      = bytesPerPixel * width    let bitmapInfo       = RGBA32.bitmapInfo    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {        print("Cannot create context!"); return nil    }    context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))    guard let buffer = context.data else { print("Cannot get context data!"); return nil }    let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)    for row in 0 ..< Int(height) {        for column in 0 ..< Int(width) {            let offset = row * width + column           /*             * Here I'm looking for color : RGBA32(red: 231, green: 239, blue: 247, alpha: 255)             * and I will convert pixels color that in range of above color to transparent             * so comparetion can done like this (pixelColorRedComp >= ourColorRedComp - 1 && pixelColorRedComp <= ourColorRedComp + 1 && green && blue)             */            if pixelBuffer[offset].redComponent >=  230 && pixelBuffer[offset].redComponent <=  232 &&                pixelBuffer[offset].greenComponent >=  238 && pixelBuffer[offset].greenComponent <=  240 &&                pixelBuffer[offset].blueComponent >= 246 && pixelBuffer[offset].blueComponent <= 248 &&                pixelBuffer[offset].alphaComponent == 255 {                pixelBuffer[offset] = .transparent            }        }    }    let outputCGImage = context.makeImage()!    let outputImage = UIImage(cgImage: outputCGImage, scale: image.scale, orientation: image.imageOrientation)    return outputImage}

I hope this helps someone 🎉