How to modify chroma with fill in LCH depending on hue

Hi,

I’m trying to implement a kind of vibrance algorithm, i.e. enhancing the colorfulness of an image by multiplying the chroma channel with respect to color and degree of saturation. Most vibrance functions like the one in Lightroom reduce the amount of enhancement for skin tones to avoid overprocessed results. Skin tones usually are on the yellow, orange,red spectrum, i.e. somewhere between 345 and 75 degrees on hue. Of course this needs more tweaking, but for a start to get a working function it is o.k.

I converted the image to LCH and tried to implement it with fill, but it is not working. The colors are completely off…

Pseudocode:
fill[0] if((i2>345)||(i2<75),MULTIPLIER=1.1,MULTIPLIER=1.5);i1*MULTIPLIER

From the G’Mic reference I got this information: iN: N-th channel value of current processed pixel
The example there shows it for RGB, where i0 is R, i1 is G and i2 is B.
But in LCH colorspace it is not working. It seems that i0 is here G and not L, i1 is R and not C and i2 is still B and not H. I’ve tested a little bit around with functions like -fill[0] [i0*0,i1*1,i2*0] and it seems to proof my assumption.

Does i0, i1 and i2 only work in RGB?

If so - how can I implement in G’Mic otherwise?

TIA,
Guenther

A few remarks:

  • WIth rgb2lch, the h is specified in radians, not in degree, so the range [75,345] must be compared with the equivalent amount in degrees.
  • If the expression provided to fill returns a scalar value (as it is the case in your expression), then it will be evaluated for all channels of the image, which is not want here. Here, you want the expression to be evaluated only for each point (x,y,z). So your expression should return a RGB (i.e. vector3()) instead.

Thus, I propose this:

  rgb2lch. f. "[ i0,(inrange(i2*180/pi,75,345)?1.5:1.1)*i1,i2 ]" lch2rgb.

which should do what you want (if I’ve understood it correctly).

Some notes to take:

  1. G’MIC has no concept of color space.
  2. You’re on the right direction with the observation of how math parser treat the last value as the value to insert on the image, and vector is one way to do it.

Also, I would consider using sh. 0,2 rgb2lch. rm. because rgb2lch erase alpha channel which is exactly why my code uses shared when converting to a color space model.

Which is not expected, and which will be fixed soon.

rgb2lab too.

No, it preserves the alpha channel for me.

I checked. Yes, it’s just rgb2lch. I don’t know if there are others that need to be looked at, but if I do run into another one. I’d let you know.

Hi David,

Thank you so much for helping me!

Yes, your solution is working as expected. I still need to tweak the parameters, but the functionality is there.

I was on the right track, but I didn’t know about the radians for LCH hue and the use of square brackets for a vector. There is still a long way to learn G’Mic, but it’s a great tool!

@Reptorian: Thanks for the hint with the alpha channel, but fortunately my images have none.

BR Guenther

1 Like

If you are using fill, the 3 channel image vector is [ i0, i1, i2 ] or [ R G B ]. It could be RGB or L*C*h°: i0 or R would represent the first channel and so on. There is a lot you can do within fill (the math interpreter). E.g.,

fill "
  L = i0/2;      # pre-calculate
  [ L, i1, i2 ]
"

fill "
  [ B, G, R ]    # reorder or duplicate channels
"

Hi afre,

Thanks!

If you do pre-calculations or the formula gets longer it is very convenient to use double quotes to arrange the lines for more readability.

But in this case I cannot use any variables or constant created outside of the fill command - correct?

BR Guenther

This is still possible, with things like:

foo=1
bar=1,2,3,4

eval "
  foo = "$foo";
  bar = [ "$bar" ];

  foo2 = get('foo');
  bar2 = get('bar',4);

  print(foo,foo2,bar,bar2);
"

Would it be possible to get() a certain vector range such as to make bar2 = 2,3? Or what is the best way to do it currently?

If your vector-valued variable is not too large :

bar_sub = get('bar',4)[1,2];

If it’s really large, just don’t use vectors, but preferably store your values in an image, and use crop() instead.