How to apply HSB color filters to UIImage

2019-05-25 06:02发布

I've been struggling for a few days for a project on UIImage colorization. The idea is that the app will embark a set of images that I will have to colorize with values retrieved from a webservice. Some sort of themes if you wish.

The designer I work with gave me a background image on all of his Photoshop values.

The first problem is that Photoshop uses HSL and iOS uses HSB. So the first challenge was to translate the values from Photoshop.

Photoshop HSL: -28 (range -180 => +180), 100 (range -100 => +100), 25 (range -100 => +100).

Luckily I found some code online, here it is.

//adapted from https://gist.github.com/peteroupc/4085710
- (void)convertLightnessToBrightness:(CGFloat)lightness withSaturation:(CGFloat)saturation completion:(void (^)(CGFloat, CGFloat))completion
{
    if (!completion)
        return; //What's the point of calling this method without a completion block!

    CGFloat brightness = 0.0f;
    CGFloat saturationOut = 0.0f;

    if (lightness > 0.0f)
    {
        CGFloat lumScale = (1.0f - MAX((lightness - 0.5f), 0.0f) * 2.0f);
        lumScale = ((lumScale == 0) ? 0 : (1.0f / lumScale));

        CGFloat lumStart = MAX(0.0f, (lumScale - 0.5f));

        CGFloat lumDiv = (lumScale - lumStart);
        lumDiv = (lumStart + (saturation * lumDiv));

        saturationOut = ((lumDiv == 0) ? 0.0f : (saturation / lumDiv));
        brightness = (lightness + (1.0f - lightness) * saturation);
    }

    NSLog(@"saturation: %0.2f - brightness: %0.2f", saturationOut, brightness);

    completion(saturationOut, brightness);
}

Using an online converter I verified that this method returns the good values.
I needed to change the ranges (H: 0->360, S: 0->100, L: 0->100)

color converter

So HSL 152, 100, 62 gives HSB 152, 76, 100. And the method returns 75 for saturation and 100 for brightness so we are good.

Next I needed to apply those values to the image, so here is the code to change...

The HUE:

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0f * M_PI)

- (void)colorize:(UIImage *)input hue:(CGFloat)hueDegrees completion:(void(^)(UIImage *outputHue))completion
{
    if (!completion)
        return; //What's the point of calling this method without a completion block!

    CGFloat hue = DEGREES_TO_RADIANS(hueDegrees);

    NSLog(@"degress: %0.2f | radian: %0.2f", hueDegrees, hue);

    CIImage *inputImage = [CIImage imageWithCGImage:input.CGImage];

    //---
    CIFilter *hueFilter = [CIFilter filterWithName:@"CIHueAdjust" keysAndValues:kCIInputImageKey, inputImage, nil];
    [hueFilter setDefaults];
    [hueFilter setValue:[NSNumber numberWithFloat:hue] forKey:kCIInputAngleKey];
    //---

    CIImage *outputImage = [hueFilter outputImage];
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *outputUIImage = [UIImage imageWithCGImage:cgimg];
    CGImageRelease(cgimg);

    completion(outputUIImage);
}

The SATURATION:

- (void)colorize:(UIImage *)input saturation:(CGFloat)saturation completion:(void(^)(UIImage *outputSaturation))completion
{
    if (!completion)
        return; //What's the point of calling this method without a completion block!

    NSLog(@"saturation: %0.2f", saturation);

    CIImage *inputImage = [CIImage imageWithCGImage:input.CGImage];

    //---
    CIFilter *saturationFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, inputImage, nil];
    [saturationFilter setDefaults];
    [saturationFilter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];
    //---

    CIImage *outputImage = [saturationFilter outputImage];
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *outputUIImage = [UIImage imageWithCGImage:cgimg];
    CGImageRelease(cgimg);

    completion(outputUIImage);
}

The BRIGHTNESS:

- (void)colorize:(UIImage *)input brightness:(CGFloat)brightness completion:(void(^)(UIImage *outputBrightness))completion
{
    if (!completion)
        return; //What's the point of calling this method without a completion block!

    NSLog(@"brightness: %0.2f", brightness);

    CIImage *inputImage = [CIImage imageWithCGImage:input.CGImage];

    //---
    CIFilter *brightnessFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, inputImage, nil];
    [brightnessFilter setDefaults];
    [brightnessFilter setValue:[NSNumber numberWithFloat:brightness] forKey:@"inputBrightness"];
    //---

    CIImage *outputImage = [brightnessFilter outputImage];
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *outputUIImage = [UIImage imageWithCGImage:cgimg];
    CGImageRelease(cgimg);

    completion(outputUIImage);
}

And everything put together:

CGFloat hue = -28.0f; //152 in 360° range (180 - 28 = 152)
CGFloat saturation = 1.0f; //((100 + 100.0f) / 200.0f)
CGFloat lightness = 0.625f; //((25 + 100.0f) / 200.0f)

[self convertLightnessToBrightness:ligthness withSaturation:saturation completion:^(CGFloat saturationOut, CGFloat brightness) {

    //saturarationOut = 0.75f and brigthness = 1.0f
    [self colorize:input hue:hue completion:^(UIImage *outputHue) {

        [self colorize:outputHue saturation:saturationOut completion:^(UIImage *outputSaturation) {

            [self colorize:outputSaturation brightness:brightness completion:completion];

        }];

    }];

}];

The last completion block simply applies the output image to the image view. Now here are the results:

Base image

base image

Colorize (hue only)

hue only

Colorize (hue and saturation)

hue and saturation

Colorize (hue, saturation and brightness)

hue, saturation and brightness

Expected result

expected result

As you can see, the final image is completely white (brigthness is 100%).

I'm completely lost here, I've tried many combination (applying H, S and B in every order), I've tried others libs such as iOS-Image-Filters, without any success. I've also read a lot of question here on Stack Overflow.

Links:

As anyone succeeded to apply HSL/HSB value to UIImages?

1条回答
对你真心纯属浪费
2楼-- · 2019-05-25 06:19

Well finally, we decided to change techniques. I'm now using a semi-transparent image to wich I apply a blend mode with the desired color.

Following this post I made a category on UIImage.

- (UIImage *)tintedBackgroundImageWithColor:(UIColor *)tintColor
{
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
    [tintColor setFill];
    CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
    UIRectFill(bounds);

    [self drawInRect:bounds blendMode:kCGBlendModeSourceAtop alpha:1.0f];

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return tintedImage;
}
查看更多
登录 后发表回答