Replace a particular color inside an image with another color
Asked Answered
S

4

9

I have one "room" image.

Now, user will click on any particular color suppose blue color in image.

And also user will select replaced color like "black" color.

So, all "blue" color inside the image will be replaced by "black" color.

Any one have idea for how to achieve this in iPhone?

Help is appreciated.

Stearn answered 24/8, 2011 at 7:6 Comment(1)
Yes, perfect color replace is possible, but it will not actually work in real world video data. See this question for more info and a real solution: #18453308Buttery
D
7
  1. You have to iterate through each pixel in the image and take its rgb values
  2. Check its rgb values matches your fromColor rgb values, if yes change the pixel value to your toColor rgb values.If not, just leave that pixel and go to next one..

Wrote a function from memory..errors possible..correct yourself

-(UIImage*)changeColor:(UIImage*)myImage fromColor:(UIColor*)fromColor toColor:(UIColor*)toColor{
    CGImageRef originalImage    = [myImage CGImage];
    CGColorSpaceRef colorSpace  = CGColorSpaceCreateDeviceRGB();
    CGContextRef bitmapContext  = 
CGBitmapContextCreate(NULL,CGImageGetWidth(originalImage),CGImageGetHeight(originalImage),
 8,CGImageGetWidth(originalImage)*4,colorSpace,kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(bitmapContext, CGRectMake(0, 0, 
CGBitmapContextGetWidth(bitmapContext),CGBitmapContextGetHeight(bitmapContext)), 
originalImage);
    UInt8 *data          = CGBitmapContextGetData(bitmapContext);
    int numComponents    = 4;
    int bytesInContext   = CGBitmapContextGetHeight(bitmapContext) *      
    CGBitmapContextGetBytesPerRow(bitmapContext);
    double redIn, greenIn, blueIn,alphaIn;
    CGFloat fromRed,fromGreen,fromBlue,fromAlpha;
    CGFloat toRed,toGreen,toBlue,toAlpha; 

    //Get RGB values of fromColor
    int fromCountComponents = CGColorGetNumberOfComponents([fromColor CGColor]);
    if (fromCountComponents == 4) {
     const CGFloat *_components = CGColorGetComponents([fromColor CGColor]);
     fromRed = _components[0];
     fromGreen = _components[1];
     fromBlue = _components[2];
     fromAlpha = _components[3];
    }


    //Get RGB values for toColor
    int toCountComponents = CGColorGetNumberOfComponents([toColor CGColor]);
    if (toCountComponents == 4) {
      const CGFloat *_components = CGColorGetComponents([toColor CGColor]);
      toRed   = _components[0];
      toGreen = _components[1];
      toBlue  = _components[2];
      toAlpha = _components[3];
    }


    //Now iterate through each pixel in the image..
    for (int i = 0; i < bytesInContext; i += numComponents) {
        //rgba value of current pixel..
        redIn    =   (double)data[i]/255.0;
        greenIn  =   (double)data[i+1]/255.0;
        blueIn   =   (double)data[i+2]/255.0;
        alphaIn  =   (double)data[i+3]/255.0;

        //now you got current pixel rgb values...check it curresponds with your fromColor
        if( redIn == fromRed && greenIn == fromGreen && blueIn == fromBlue ){
            //image color matches fromColor, then change current pixel color to toColor
            data[i]    =   toRed;
            data[i+1]  =   toGreen;
            data[i+2]  =   toBlue;
            data[i+3]  = toAlpha;       
        }
    }
    CGImageRef outImage =   CGBitmapContextCreateImage(bitmapContext);
    myImage             =   [UIImage imageWithCGImage:outImage];
    CGImageRelease(outImage);
    return myImage;
}

I hope you are not calling this function every time...It is a bit processor heavy..And if I was your processor, I wouldn't be happy with you..:)

Dachi answered 24/8, 2011 at 7:42 Comment(5)
Dint work for me. did not get any change in the colorAdalbert
@Shailesh, this code was written years ago when there was no native image filtering library available for iOS. iOS now has core image framework, and probably that is what you should be looking into.Dachi
Yep .. I am doing that. Btw .. here is the question i had posted. #27483008 I would appreciate if you could helpAdalbert
@Adalbert I would love to help. But I have not touched iOS development in last 3 years. I will have to relearn firstDachi
Okay.I think I will find a way around.. if possible will post the solution here. Since not much material is available about this concern on the webAdalbert
A
6

I stumbled in this answer searching for replacing a color with another. Well, since that I need for Swift code, I have had several try and I find a nice solution. Thanks @Rob for its answer.

extension UIImageView {

    @discardableResult func replace(color: UIColor, withColor replacingColor: UIColor) -> Bool {
        guard let inputCGImage = self.image?.cgImage else {
            return false
        }
        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 false
        }
        context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))

        guard let buffer = context.data else {
            return false
        }

        let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)

        let inColor = RGBA32(color: color)
        let outColor = RGBA32(color: replacingColor)
        for row in 0 ..< Int(height) {
            for column in 0 ..< Int(width) {
                let offset = row * width + column
                if pixelBuffer[offset] == inColor {
                    pixelBuffer[offset] = outColor
                }
            }
        }

        guard let outputCGImage = context.makeImage() else {
            return false
        }
        self.image = UIImage(cgImage: outputCGImage, scale: self.image!.scale, orientation: self.image!.imageOrientation)
        return true
    }

}

Where RGBA32 struct is simply:

struct RGBA32: Equatable {
    private var color: UInt32

    var redComponent: UInt8 {
        return UInt8((self.color >> 24) & 255)
    }

    var greenComponent: UInt8 {
        return UInt8((self.color >> 16) & 255)
    }

    var blueComponent: UInt8 {
        return UInt8((self.color >> 8) & 255)
    }

    var alphaComponent: UInt8 {
        return UInt8((self.color >> 0) & 255)
    }

    init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
        self.color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
    }

    init(color: UIColor) {
        let components = color.cgColor.components ?? [0.0, 0.0, 0.0, 1.0]
        let colors = components.map { UInt8($0 * 255) }
        self.init(red: colors[0], green: colors[1], blue: colors[2], alpha: colors[3])
    }

    static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue

    static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
        return lhs.color == rhs.color
    }

}

In this way , wherever in your code:

let imageView = UIImageView()
let image = UIImage(color: .red) // (*)
imageView.image = image
imageView.replace(color: .red, withColor: .green)

(*) creating image with color is explained here.

Amazon answered 7/11, 2017 at 9:36 Comment(1)
It would be great if you would add tolerance as input parameter!Peltate
U
0

You'll need access to the raw pixel data somehow (see this answer or user Krishnabhadra's code). Search through all the pixels and eg for blue->black replacement, replace all blue pixels with black. Look at converting the colour to HSV which will make the colour replacement calculation easier.

Umbrageous answered 24/8, 2011 at 7:51 Comment(0)
I
0

I modified a little of Krishnabhadra's's code, since I'm using RGB for color input as well as output e.g:

[UIColor colorWithRed:191.0f/255.0f green:30.0f/255.0f blue:45.0f/255.0f alpha:1.0f]

Works for me.

-(UIImage*)changeColor:(UIImage*)myImage fromColor:(UIColor*)fromColor toColor:(UIColor*)toColor{
CGImageRef originalImage    = [myImage CGImage];
CGColorSpaceRef colorSpace  = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext  =
CGBitmapContextCreate(NULL,CGImageGetWidth(originalImage),CGImageGetHeight(originalImage),
                      8,CGImageGetWidth(originalImage)*4,colorSpace,kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(bitmapContext, CGRectMake(0, 0,
                                             CGBitmapContextGetWidth(bitmapContext),CGBitmapContextGetHeight(bitmapContext)),
                   originalImage);
UInt8 *data          = CGBitmapContextGetData(bitmapContext);
int numComponents    = 4;
int bytesInContext   = CGBitmapContextGetHeight(bitmapContext) *
CGBitmapContextGetBytesPerRow(bitmapContext);
double redIn, greenIn, blueIn,alphaIn;
CGFloat fromRed,fromGreen,fromBlue,fromAlpha;
CGFloat toRed,toGreen,toBlue,toAlpha;

//Get RGB values of fromColor
int fromCountComponents = CGColorGetNumberOfComponents([fromColor CGColor]);
if (fromCountComponents == 4) {
    const CGFloat *_components = CGColorGetComponents([fromColor CGColor]);
    fromRed = _components[0];
    fromGreen = _components[1];
    fromBlue = _components[2];
    fromAlpha = _components[3];
}


//Get RGB values for toColor
int toCountComponents = CGColorGetNumberOfComponents([toColor CGColor]);
if (toCountComponents == 4) {
    const CGFloat *_components = CGColorGetComponents([toColor CGColor]);
    toRed   = _components[0]*255;
    toGreen = _components[1]*255;
    toBlue  = _components[2]*255;
    toAlpha = _components[3]*255;
}


//Now iterate through each pixel in the image..
for (int i = 0; i < bytesInContext; i += numComponents) {
    //rgba value of current pixel..
    redIn    =   (double)data[i];
    greenIn  =   (double)data[i+1];
    blueIn   =   (double)data[i+2];
    alphaIn  =   (double)data[i+3];
    //now you got current pixel rgb values...check it curresponds with your fromColor
    if( redIn == fromRed && greenIn == fromGreen && blueIn == fromBlue && alphaIn != 0 ){
        //image color matches fromColor, then change current pixel color to toColor
        data[i]    =   toRed;
        data[i+1]  =   toGreen;
        data[i+2]  =   toBlue;
        data[i+3]  = toAlpha;
    }
}
CGImageRef outImage =   CGBitmapContextCreateImage(bitmapContext);
myImage             =   [UIImage imageWithCGImage:outImage];
CGImageRelease(outImage);
return myImage;}
Informed answered 18/8, 2015 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.