Color Balance with Core Image

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-04 20:00:35

Core Image also allows to develop your own custom image filters. So called custom kernels (CIKernel) are small programs written in a dialect of the OpenGL Shading Language (GLSL) to run your custom filter on the GPU.

Photoshop is closed source and it is unclear what algorithm they are using, but GIMP has color balance functionality and it is open source.

The implementation can be found here: https://github.com/GNOME/gimp/blob/master/app/operations/gimpoperationcolorbalance.c

Now we would need to translate that into GLSL.

Fortunately an Australian developer did that already, see https://gist.github.com/liovch/3168961#file-gistfile1-m-L4

To quickly test whether this gives the expected result one can just use a cool utility from Apple called Quartz Composer. You can download it from Apple's developer site, it is hidden in the Additional Tools for Xcode 9.3.dmg.

There you choose 'Image Filter' as template, delete all nodes beside input and output image. From the library add 'Core Image Filter'. Then select the filter and press CMD-2. You should see now an inspector with a code editor that shows a simple general Core Image kernel routine (CIKernel).

Since we just need access to the corresponding source pixel and not to the whole image we can use a more specific CIColorKernel instead of a CIKernel function.

Additional parameters can be just added to the kernel function.

Given the code from the gist file above which itself is based on GIMP's color balance routines, one can copy the helper functions RGBToL, RGBToHSL, HueToRGB, HSLToRGB. Then there is the actual CIColorKernel function, where we just adapted the function signature and return value:

/* based on GIMP's color balance routine */
kernel vec4 balanceFilter(__sample textureColor,             
                             float redShift,
                             float greenShift,
                             float blueShift) {

    lowp float lightness = RGBToL(textureColor.rgb);
    lowp vec3 shift = vec3(redShift, greenShift, blueShift);

    const lowp float a = 0.25;
    const lowp float b = 0.333;
    const lowp float scale = 0.7;

    lowp vec3 midtones = (clamp((lightness - b) /  a + 0.5, 0.0, 1.0) * clamp ((lightness + b - 1.0) / -a + 0.5, 0.0, 1.0) * scale) * shift;

    lowp vec3 newColor = textureColor.rgb + midtones;
    newColor = clamp(newColor, 0.0, 1.0);

    lowp vec3 newHSL = RGBToHSL(newColor);
    lowp float oldLum = RGBToL(textureColor.rgb);
    textureColor.rgb = HSLToRGB(vec3(newHSL.x, newHSL.y, oldLum));

    return textureColor;
}

When the code is entered it is compiled automatically. You can then wire up the nodes in Composer, see screenshot. With the filter node selected you can use CMD-1 to switch to the input inspector. Composer automatically generates an UI with the redShift, greenShift and blueShift parameter, e.g. if you enter -0.64 for greenShift you would see this result:

Some additional notes:

  • the source code is from GIMP. If you want to use that, please read GIMP's license
  • the results of GIMP can be very different from Photoshop, I don't know, but might be still a good starting point for own experiments
  • obviously the implementation from the GIST is just for midtones, but with the link to GIMP's source code, it should be easy to extent for shadows and highlights
  • to use a kernel in your iOS app you have to wrap the kernel code in a string and feed it into CIColorKernel constructor
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!