Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Image CIColorControls brightness filter creates wrong effect. How do I change my image's luminance?

I'm creating a color picker for iOS. I would like to enable the user to select the brightness (luminance) and have the color wheel reflect this change. I'm using Core Image to modify the brightness with the CIColorControls filter. Here's my code:

-(CIImage *)oldPhoto:(CIImage *)img withBrightness:(float)intensity
{
    CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"];
    [lighten setValue:img forKey:kCIInputImageKey];
    [lighten setValue:@((intensity * 2.0) - 1.0) forKey:@"inputBrightness"];
    return lighten.outputImage;
}

Here's how the color wheel looks with intensity = 0.5 (inputBrightness = 0):

My image with inputBrightness = 0

The problem is that the color wheel looks wrong when intensity < 0.5. For example, here's how it looks with intensity = 0.3 (inputBrightness = -0.4):

My image with inputBrightness = -0.4

Notice that there's a black circle in the middle, and the rest of the image hasn't been darkened correctly either. This is supposed to be an HSL color wheel, so I guess that what I actually want to change is the luminance, not the brightness.

First, can anyone explain why the image looks like this? I'm not an expert on color; it seems odd that the center of the circle quickly clips to black while the edges of it don't darken much.

Second, how can I achieve the effect I want?

Here's how I actually WANT the image to look:

Image with luminance = 0.3, generated with HSL function on CPU (too slow)

This was created with a custom HSL function and luminance = 0.3. This runs on the CPU, so it's far too slow for my needs. I'd be happy to post the code for this HSL function, but I didn't include it because it didn't seem immediately relevant. If you want to see it, just ask.

Please let me know if you have any questions, or if anything seems unclear. Thanks!

like image 826
Elliot Avatar asked Nov 25 '12 00:11

Elliot


2 Answers

I also found the non-linearity of the kCIInputBrightnessKey of CIColorControls to be annoying. I employed a linear CIToneCurve:

/** Change luminosity of `CIImage`

 @param inputImage The `CIImage` of the image to have it's luminosity changed.
 @param luminosity The percent change of the luminosity, ranging from -1.0 to 1.0.

 @return `CIImage` of image with luminosity changed. If luminosity of 0.0 used, original `inputImage` is returned.
 */

- (CIImage *)changeLuminosityOfCIImage:(CIImage *)inputImage luminosity:(CGFloat)luminosity
{
    if (luminosity == 0)
        return inputImage;

    NSParameterAssert(luminosity >= -1.0 && luminosity <= 1.0);

    CIFilter *toneCurveFilter = [CIFilter filterWithName:@"CIToneCurve"];
    [toneCurveFilter setDefaults];
    [toneCurveFilter setValue:inputImage forKey:kCIInputImageKey];

    if (luminosity > 0)
    {
        [toneCurveFilter setValue:[CIVector vectorWithX:0.0  Y:luminosity]                           forKey:@"inputPoint0"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.25 Y:luminosity + 0.25 * (1 - luminosity)] forKey:@"inputPoint1"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.50 Y:luminosity + 0.50 * (1 - luminosity)] forKey:@"inputPoint2"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.75 Y:luminosity + 0.75 * (1 - luminosity)] forKey:@"inputPoint3"];
        [toneCurveFilter setValue:[CIVector vectorWithX:1.0  Y:1.0]                                  forKey:@"inputPoint4"];
    }
    else
    {
        [toneCurveFilter setValue:[CIVector vectorWithX:0.0  Y:0.0]                     forKey:@"inputPoint0"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.25 Y:0.25 * (1 + luminosity)] forKey:@"inputPoint1"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.50 Y:0.50 * (1 + luminosity)] forKey:@"inputPoint2"];
        [toneCurveFilter setValue:[CIVector vectorWithX:0.75 Y:0.75 * (1 + luminosity)] forKey:@"inputPoint3"];
        [toneCurveFilter setValue:[CIVector vectorWithX:1.0  Y:1 + luminosity]          forKey:@"inputPoint4"];
    }

    return [toneCurveFilter outputImage];
}

Here is your image, reducing the luminosity by 30% using the above routine:

reduced

It can be done with CIToneCurve. Whether it's faster than your routine, you'll have benchmark it.

like image 83
Rob Avatar answered Oct 09 '22 19:10

Rob


Your CIColorControls filter is working as designed. It simply adds it's brightness parameter to the red, green, and blue values of each pixel. If a negative brightness takes the pixel below 0, it clips to black. If a positive brightness pushes it over 1, it clips to white. (Actually, each channel clips separately...)

Another issue is that CIColorControls works in RGB color space. HSL is very different. This is why your base color wheel looks very different from the standard Apple color picker.

Useful reference

like image 42
Jerry B Avatar answered Oct 09 '22 20:10

Jerry B