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 🎉