Help: How to achieve "Vibrance"?

After testing some more of Vibrance / Vibrance [YCH], and working on Vibrance [YCH] (Note that G’MIC can’t be updated during this week). It seems that Vibrance [YCH] actually preserves perceptual luminosity better than Vibrance. I actually have compared it with Ed’s Harvey Vibrance plugin for Paint.NET.

I ranked from best to worst:

  1. Ed Harvey’s Vibrance - This preserves luminosity better than Vibrance [YCH], but it isn’t that much better. And also preserves saturation better at 100%. Sometimes this performs worse than Vibrance[YCH]. Basically 52/48.
  2. Vibrance [YCH] - Performs a slightly worse than the above. However, extremely close to the above, so they’re interchangeable, and there are few cases where this is better than the above. Clearly need of a improvement.
  3. Vibrance - This one has glaring issue with really high chroma image on high vibrance.

What I think that may improve my Vibrance [YCH] is either switching to a different color space, or modify the YUV color space so that it matches one of the gmic rgb2lab illuminant. There’s another possible way to make it better, a modified version of rgb2srgb/srgb2rgb. Or even both. That way, we finally have a proper Vibrance filter as luminosity would be better perserved in some areas. However, improvement could mean trying to grasp it and then finding a better mathematical model.

Here’s the test results for all Vibrance filters.

1st image - No filter
2nd image - Ed’s Harvey’s Vibrance for Paint.NET
3rd image - G’MIC Vibrance [YCH]
4th image - G’MIC Vibrance

You can clearly see why I ranked it like this after you zoomed onto the rooster’s head.

2 Likes

Could you try the HSI color space ?

HSI isn’t based on human perception, so I cannot try that. The last image utilize HS*. The solution has to be based on modified yuv color space.

2 Likes

Yes but with a conceptually different algorithm.

Your Vibrance [YCH] could be improved with a color space where the most rgb saturated color (for example red [1,0,0]) is mapped to a value of 1.0 in the chroma/saturation channel, this is the only way to limit or not push at all colors out of gamut.

I checked, it does seem to be out-of-chroma issue. However, more because of the wrong value used. Now, it looks better than Ed’s Harvey method.

See commit here - Fix Value in Vibrance [YCH] · dtschump/gmic-community@fe55132 · GitHub

And these tests with before/after:

C:\Windows\System32>gmic 256,256,256,3,[x,y,z] rep_vibrance_ych 1
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input image at position 0, with values '[x,y,z]' (1 image 256x256x256x3).
[gmic]-3./rep_vibrance_ych/*foreach/ 0.63235455751419067
[gmic]-3./rep_vibrance_ych/*foreach/ 0.63235485553741455
[gmic]-1./ Display image [0] = '[image of '[x,y,z]']'.
[0] = '[image of '[x,y,z]']':
  size = (256,256,256,3) [192 Mio of float32].
  data = (0,1.44314,2.88601,4.32861,5.77094,7.213,8.6548,10.0963,11.4778,12.7596,13.9606,15.1212,(...),255,255,255,255,255,255,255,255,255,255,255,255).
  min = 0, max = 255, mean = 118.538, std = 89.8106, coords_min = (0,0,0,0), coords_max = (255,1,0,0).
[gmic]-1./ End G'MIC interpreter.

C:\Windows\System32>gmic 256,256,256,3,[x,y,z] rep_vibrance_ych 1
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input image at position 0, with values '[x,y,z]' (1 image 256x256x256x3).
[gmic]-3./rep_vibrance_ych/*foreach/ 0.63235455751419067
[gmic]-3./rep_vibrance_ych/*foreach/ 0.63235455751419067
[gmic]-1./ Display image [0] = '[image of '[x,y,z]']'.
[0] = '[image of '[x,y,z]']':
  size = (256,256,256,3) [192 Mio of float32].
  data = (0,1.44314,2.88601,4.32861,5.77094,7.213,8.6548,10.0963,11.4778,12.7596,13.9605,15.1212,(...),255,255,255,255,255,255,255,255,255,255,255,255).
  min = 0, max = 255, mean = 118.538, std = 89.8106, coords_min = (0,0,0,0), coords_max = (255,1,0,0).
[gmic]-1./ End G'MIC interpreter.

The value remaining the same is more ideal because maximum chroma doesn’t shift. New problem is color bleed rather than luminosity.

2 Likes

Some observations on the ycbcr formula:
https://en.wikipedia.org/wiki/YCbCr

The “(1/2)*” is there only to compress chroma in the -0.5,+0.5 => 0,1 range => 0,255 integer range

In theory you could multiply the chroma channel by 1.8 (to stay safe) before vibrance to have a better starting point (chroma channel similar to a saturation channel) , at the end divide chroma by 1.8 before the conversion to rgb

1 Like

The trouble with colour is that as a component it doesn’t scale the same for all hues and brightnesses. If we multiply the colour component by a factor for all colours, we are going to get some weirdness at places, especially when we are in the more colourful range. We get math, gamut and/or other unexpected oddities. Vibrance is a stopgap to the “saturation problem”.

2 Likes