After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 755270 - Decompose/Compose LAB: scaling code produces odd L, a, and b values
Decompose/Compose LAB: scaling code produces odd L, a, and b values
Status: RESOLVED FIXED
Product: GIMP
Classification: Other
Component: Plugins
git master
Other All
: Normal normal
: 2.10
Assigned To: GIMP Bugs
GIMP Bugs
Depends on:
Blocks:
 
 
Reported: 2015-09-19 19:06 UTC by Elle Stone
Modified: 2016-01-06 11:45 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Interim patch to make the AB channels for RGB gray be equal to 0.5 (1.88 KB, patch)
2015-09-20 13:38 UTC, Elle Stone
none Details | Review
Change LAB range, remove CLAMP, add decompose to LCH (3.34 KB, patch)
2015-09-21 14:17 UTC, Elle Stone
none Details | Review
Adds decompose/compose to LCH, removes CLAMP, changes LAB AB ranges (3.26 KB, patch)
2015-09-21 18:49 UTC, Elle Stone
none Details | Review
patch to make gray profile with lab companding curve (4.09 KB, patch)
2015-12-15 18:52 UTC, Elle Stone
none Details | Review
updated patch to add gray profile with lab companding curve (3.98 KB, patch)
2015-12-15 23:46 UTC, Elle Stone
none Details | Review

Description Elle Stone 2015-09-19 19:06:34 UTC
GIMP 2.9's decompose and compose plug-ins use babl/extensions/CIE.c equations to convert between RGB/XYZ and LAB. But then the decompose and compose plug-ins "rescale" and also "clip" the LAB a and b channel ranges:

#define CPN_LAB_L {"CIE L", N_("L"), 0.0, 100.0, TRUE}
#define CPN_LAB_A {"CIE a", N_("A"), -128.0, 127.0, TRUE}
#define CPN_LAB_B {"CIE b", N_("B"), -128.0, 127.0, TRUE}

When all processing is done internally using 32-bit floating point, what is the purpose of the (apparently integer-based?) GIMP rescaling code? 

Can the decompose/compose plug-ins be rewritten to output A and B layers that are in the range "-infinity to +infinity centered on 0"?

The current "rescaling code" produces some odd results when using GIMP's "decompose to LAB" plugin:

First, the decompose to LAB L channel values are scaled from 0.0 to 1.0, and also clipped to the range 0.0 to 1. This means the GIMP plug-in can't handle specular and HDR highlights. But at least the scale is easy to understand - just multiply by 100 to get the more usual LAB L scaling of 0 to 100.

Second, the decompose to LAB "A" and "B" channel results are centered around approximately 0.5 and clipped to the range 0.0 to 1.0, whereas the usual code for converting from XYZ to LAB (for example the code in babl's CIE.c that's used by the GIMP decompose plug-in) results in a and b channel values that range from -infinity to +infinity, centering around 0.0. 

The GIMP decomposed and rescaled LAB A and B channel values preset several user problems:

    1. The GIMP decompose to LAB plug-in clips any colors who's A or B channels fall outside the specified range, which means recompose produces clipped colors where previously the colors weren't clipped. So (after doing a floating point conversion to GIMP's built-in sRGB, or else recompiling a version of GIMP to use Rec.2020 primaries), Rec.2020's greenest green, for example, is clipped during a round-trip conversion from RGB to LAB to RGB. 

See this article for a nice graphic of real colors that are clipped using the "-128 to 127" scale (sadly, the page requires java and flash): http://brucelindbloom.com/index.html?LabGamutDisplay.html

    2. The user must resort to a spreadsheet to decipher the eye-droppered "decompose to LAB" A and B channels. The appropriate calculations look something like this, but I'm not sure whether the exact values really should be "255" and "128":

       "(GIMP_A_or_B times 255) minus 128" equals results fairly close to ArgyllCMS's xicclu results.

    3. One would expect that the "decompose to LAB" A and B channel results for neutral gray (R=G=B) would be "a=b=0.500000", which should correspond to "a=b=0" as expressed on the normal "0-centered" scale running from neg to pos infinity. But the actual decompose to LAB result is a=b=0.501961.
    
    4. If the CPN_LAB ranges are changed to "-127.5, 127.5", you get a=b=0.500000 for RGB colors where R=G=B. And other decomposed values seem closer to corresponding xicclu values, after converting to "0-centered" values using this equation: "(GIMP_A_or_B times 255) minus 127.5". But again, I'm not entirely sure how the conversion equation should go - maybe 255 and 127.5 aren't the exactly right numbers to use. And it would be much nicer if the rescaling code were simply eliminated.

As an aside, there is similar scaling code in babl/extensions/CIE.c and in gegl/operations/workshop/component-extract.c, but the babl and GEGL scaling code isn't used by the GIMP decompose/compose plug-ins. In fact, the babl CIE.c scaling code, and all the CIE.c code associated with it, can be removed from CIE.c, seemingly with no effect on how GIMP functions. So I'm not sure where the babl scaling code is actually used. Maybe it's legacy code?
Comment 1 Michael Natterer 2015-09-19 19:20:11 UTC
Pasting the 3 #defines would have been sufficient to convince me
that this is broken. There is no purpose, it's just half-ported legacy
stuff :)
Comment 2 Michael Natterer 2015-09-19 19:27:54 UTC
The code in Babl is apparently for the fast path to u8, so -127/+127
are fine here.
Comment 3 Elle Stone 2015-09-20 13:38:09 UTC
Created attachment 311699 [details] [review]
Interim patch to make the AB channels for RGB gray be equal to 0.5
Comment 4 Elle Stone 2015-09-21 14:17:31 UTC
Created attachment 311761 [details] [review]
Change LAB range, remove CLAMP, add decompose to LCH

Now that GIMP has the LCH blend modes, it would be nice to be able to decompose to LCH. "L" of course is the same as LAB "L". But the "C" layer allows the user to use "chroma" as a mask. 

Chroma masks are very useful when adding or removing saturation/colorfulness/chroma/etc, because they allow the user to target the more or less saturated areas in the image. This type of mask is the "chroma" equivalent to a Luminosity mask, which is used when the user wants to target various ranges of tonality in an image (http://www.gimp.org/tutorials/Luminosity_Masks/)

I believe darktable provides a "chroma" mask (https://www.darktable.org/usermanual/ch03s02s06.html.php). This article shows differences between a true chroma mask and an HSV/HSL saturation mask: http://www.russellcottrell.com/photo/saturationMask.asp.

I realize the decompose/compose plugins need to be fully ported to GEGL. But in the meantime it would be nice to patch the existing plugin to be a bit more accurate, and to not clip when decomposing to LAB, and to allow decomposing to LCH.

I tried to write code to "compose from LCH", but what I wrote doesn't actually work. So I left it in the patch but commented it out. Can someone maybe look at it and tell me what I did wrong?
Comment 5 Massimo 2015-09-21 18:14:48 UTC
(In reply to Elle Stone from comment #4)
> 
> I tried to write code to "compose from LCH", but what I wrote doesn't
> actually work. So I left it in the patch but commented it out. Can someone
> maybe look at it and tell me what I did wrong?

-#define CPN_LAB_B {"CIE b", N_("_B:"), NULL, -128.0, 127.0, TRUE}
+#define CPN_LAB_A {"CIE a", N_("_A:"), NULL, -127.5, 127.5, TRUE}
+#define CPN_LAB_B {"CIE b", N_("_B:"), NULL, -127.5, 127.5, TRUE}
+
+/* This probably isn't right:  */
+//#define CPN_LCH_L {"CIE L", N_("_L"), 0.0, 100.0, TRUE}
+//#define CPN_LCH_C {"CIE C", N_("_C"), 0.0, 200.0, TRUE}
+//#define CPN_LCH_H {"CIE H", N_("_H"), 0.0, 360.0, TRUE}

if you compare CPN_LAB_B  with CPN_LCH_L you can see that it is missing a NULL
Comment 6 Elle Stone 2015-09-21 18:49:51 UTC
Created attachment 311797 [details] [review]
Adds decompose/compose to LCH, removes CLAMP, changes LAB AB ranges

Massimo, thanks! That, plus a change in the requested babl format, makes compose to LCH work. I compared those LAB and LCH lines of code for a long time, and completely failed to see the missing NULL.
Comment 7 Elle Stone 2015-09-21 18:53:13 UTC
Here's a better link for seeing why clipping the LAB A and B channels is a bad idea: http://brucelindbloom.com/index.html?LabGamutDisplayHelp.html#IntegerLab

About putting the decomposed AB ranges to be centered on 0 instead of 0.5, that would require offsetting what is sent to the screen for display. Otherwise all negative A and B channel values display as black.

A way to allow display of "easy to interpret" A and B values, for users who are color-picking/eye-droppering values in the A and B channels, might be to change the offset and range for the color picker. The C and H ranges could be similarly modified for sending to the color picker.
Comment 8 Michael Natterer 2015-11-24 11:47:32 UTC
Pushed a slightly modified patch because the attached one didn't build,
and it removed CLAMP() unconditionally, but the components know whether
or not they want to be clamped. Please check if it's doing the right thing.

commit f78dec232a643c4b44ac849f1889ba80a4db470e
Author: Elle Stone <ellestone@ninedegreesbelow.com>
Date:   Mon Sep 21 14:40:04 2015 -0400

    Bug 755270 - Decompose/Compose LAB: scaling code produces odd L, a, and b values
    
    Decompose/compose to/from LCH: remove unconditional CLAMP (only clamp
    if the component really needs clamping), change LAB AB range.

 plug-ins/common/compose.c   | 15 +++++++++++++--
 plug-ins/common/decompose.c | 45 ++++++++++++++++++++++++++++++++++-----------
 2 files changed, 47 insertions(+), 13 deletions(-)
Comment 9 Elle Stone 2015-11-24 14:33:46 UTC
Testing in default updated GIMP, everything seems just right. Thanks!
Comment 10 Elle Stone 2015-11-24 15:13:03 UTC
If the user drags a layer from the decomposed to LAB/LCH grayscale layer stack back to the RGB layer stack, to get correct results without invoking full grayscale ICC support (https://bugzilla.gnome.org/show_bug.cgi?id=756389), the "behind the scenes" drag-drop procedure would:

1. Make a profile with the sRGB primaries and the Lab-L TRC.
2. If the dragged layer's precision is "Linear light", convert the dragged layer's precision to "Perceptual gamma (sRGB)".
3. Convert the dragged layer to RGB mode.
3. Assign the profile with the sRGB primaries and the Lab-L TRC to the dragged layer.
4. Drop the layer to the RGB layer stack, changing the precision TRC if necessary.
Comment 11 Elle Stone 2015-11-25 21:22:54 UTC
I posted some patches here: https://bugzilla.gnome.org/show_bug.cgi?id=755376
in an attempt to get component-extract.c and GIMP's decompose/compose to produce the same correct results.
Comment 12 Elle Stone 2015-12-15 18:52:23 UTC
Created attachment 317453 [details] [review]
patch to make gray profile with lab companding curve

Currently the decomposed to LAB/LCH layer stack will be at Linear light precision if the RGB layer stack was at Linear light precision. However, the decomposed to LAB/LCH grayscale layer stacks should decompose to "Perceptual gamma (sRGB)" precision, regardless of the precision of the layer that was decomposed:

* The Linear Light precision linearizes the sRGB TRC, and the LAB color space has the LAB companding curve, not the sRGB companding curve.

* It doesn't seem to make any sense to present the user with a linearized decomposed to LAB/LCH layer stack because these color spaces are intended to be perceptually uniform. 

Now grayscale images can be color-managed. However, even when decomposed to Perceptual gamma precision the resulting grayscale layer stack isn't correct because it's assigned a gray profile with the sRGB TRC instead of the LAB TRC:

cmsFloat64Number labl_parameters[5] = 
   { 3.0, 1.0 / 1.16,  0.16 / 1.16, 2700.0 / 24389.0, 0.08000 };

The gray profile for LAB/LCH preferably should have the D50 white point:

/* calculated from D50 illuminant XYZ values in ICC specs */
cmsCIExyY d50_illuminant_specs = {0.345702915, 0.358538597, 1.0};

If the correct gray profile is assigned to a "Perceptual precision" decomposed to LAB/LCH layer stack, then dragging the layers from the LAB/LCH layer stack back over to the RGB layer stack produces correct results. Otherwise results are wrong by the amount that the sRGB TRC differs from the LAB TRC (https://pixls.us/articles/users-guide-to-high-bit-depth-gimp-2-9-2-part-1/#decomposing-from-srgb-to-lab).

The attached patch adds code to libgimpcolor/gimpcolorprofile.c to make a gray profile with the LAB companding curve and the ICC spec D50 white point, and also fixes a couple of typos. The patch doesn't have code that actually assigns the gray profile to RGB layers that are decomposed to LAB/LCH.
Comment 13 Michael Natterer 2015-12-15 19:48:09 UTC
Thanks. One question: we just switched back to D65 for the other GRAY
profiles for consistency with the built-in RGB profiles. Should this
profile be D65 too or is there a special reason it should be D50?

Also, this bug is actually closed :)
Comment 14 Elle Stone 2015-12-15 20:26:33 UTC
(In reply to Michael Natterer from comment #13)
> Thanks. One question: we just switched back to D65 for the other GRAY
> profiles for consistency with the built-in RGB profiles. 

My understanding is that we switched to D65 for gray profiles that are applied to sRGB images that have been converted to grayscale images (as per color to grayscale), which isn't the same as decomposing to LAB/LCH.

When converting between display profiles such as GIMP's matrix gray and sRGB profiles it actually makes no difference at all what white point is used to make the gray profile because in a V4 CMM absolute colorimetric conversions aren't done between display profiles. Though I'm not sure what happens if the source or destination profile is a printer LUT profile with a full set of tables.

> Should this
> profile be D65 too or is there a special reason it should be D50?

In practice, when applied to a decomposed to LAB/LCH layer stack, a gray profile with the LAB trc and the D65 white point produces exactly the same result as a gray profile with the LAB trc and the D50 white point. So if you want to use D65, that's fine, but it seems a bit odd given that the LAB PCS is defined as having the D50 white point (http://color.org/specification/ICC1v43_2010-12.pdf):

0.3 Colour management architecture and profile connection space "So, in summary, the PCS is based on CIEXYZ (or CIELAB) determined for a specific observer (CIE Standard 1931 Colorimetric Observer, often known colloquially as the 2 degree observer), relative to a specific illuminant chromaticity (that of CIE D50) . . . ."

> 
> Also, this bug is actually closed :)

Oh! Sorry! Should I open another bug report?
Comment 15 Michael Natterer 2015-12-15 23:18:48 UTC
Let's handle this here :) My head is already confused by too many
bugs...

So if I understand this correctly, we use D65 on our little sRGB island
because that's right for sRGB, but on the other little island called LAB,
they picked D50 and that's what we should right?
Comment 16 Michael Natterer 2015-12-15 23:19:45 UTC
"...should use, right?"
Comment 17 Elle Stone 2015-12-15 23:45:08 UTC
(In reply to Michael Natterer from comment #15)
> Let's handle this here :) My head is already confused by too many
> bugs...
> 
> So if I understand this correctly, we use D65 on our little sRGB island
> because that's right for sRGB, but on the other little island called LAB,
> they picked D50 and that's what we should right?

The "they" who picked D50 for LAB (and XYZ) is the ICC, and they did so with a print-oriented workflow in mind. D50 is the white point of the LAB/XYZ Profile Connection Spaces, the "illuminant" for all V2 and V4 profiles.

The more I think about this question of "which white point to use when making gray profiles for various purposes", the more confused I am. I'm going to ask for enlightenment on the LCMS mailing list. In the meantime:

* D65 for "decompose to LAB" seem just as odd as D50 for "from color to grayscale" for sRGB. 

* From a "what happens" point of view it doesn't seem to make a whit of difference. For purposes of allowing GIMP users to see correctly decomposed LAB/LCH results and to use "drag and drop" to pull layers back to the RGB layer stack, either white point will work just fine. 

In case it's useful I'm attaching an updated patch (to the latest git and to remove a line of code that was for testing) that still uses D50, but please change it to D65 if you want to. It can always be changed later.
Comment 18 Elle Stone 2015-12-15 23:46:08 UTC
Created attachment 317463 [details] [review]
updated patch to add gray profile with lab companding curve
Comment 19 Michael Natterer 2016-01-06 02:04:37 UTC
I forgot about this (the bug is closed so I don't see it any longer).
What was it that's missing here?
Comment 20 Elle Stone 2016-01-06 11:45:14 UTC
(In reply to Michael Natterer from comment #19)
> I forgot about this (the bug is closed so I don't see it any longer).
> What was it that's missing here?

When an image layer is decomposed to LAB or LCH, currently the resulting grayscale layer stack is not assigned the correct grayscale profile. Instead:

* If the RGB layer stack is at "Linear light" precision, the GIMP built-in Linear RGB grayscale color space is assigned.

* If the RGB layer stack is at "Perceptual gamma" precision, the GIMP built-in sRGB-Gamma grayscale color space is assigned.

Regardless of the precision of the source RGB layer stack, when decomposed to LAB or LCH, the resulting grayscale layer stack should be decomposed to "Perceptual gamma" precision and should have a grayscale profile with the LAB companding curve assigned. Otherwise results of dragging and dropping the decomposed grayscale layers back to the RGB layer stack will be incorrect (http://ninedegreesbelow.com/photography/lab/gimp29/correct-vs-gimp29-decompose-drag-drop.jpg).

Composing/recomposing from the LAB layer stack back to RGB seems to work just fine regardless of what grayscale ICC profile is assigned to the LAB layer stack.