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.

Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
Nishant B
  • 2,897
  • 1
  • 18
  • 25
  • 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: https://stackoverflow.com/questions/18452808/gpuimage-masking-transparency – MoDJ Sep 03 '17 at 22:29

4 Answers4

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..:)

Krishnabhadra
  • 34,169
  • 30
  • 118
  • 167
  • Dint work for me. did not get any change in the color – Shailesh Dec 16 '14 at 10:03
  • @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. – Krishnabhadra Dec 16 '14 at 10:44
  • Yep .. I am doing that. Btw .. here is the question i had posted. http://stackoverflow.com/questions/27482508/virtual-wall-painting-like-app-ios I would appreciate if you could help – Shailesh Dec 16 '14 at 10:50
  • @Shailesh I would love to help. But I have not touched iOS development in last 3 years. I will have to relearn first – Krishnabhadra Dec 16 '14 at 10:59
  • 1
    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 web – Shailesh Dec 16 '14 at 11:06
5

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.

Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
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;}
TylerC
  • 95
  • 1
  • 6
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.

Community
  • 1
  • 1
damian
  • 3,604
  • 1
  • 27
  • 46