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 727185 - Converting to GIMP built-in sRGB produces the wrong RGB values
Converting to GIMP built-in sRGB produces the wrong RGB values
Status: VERIFIED FIXED
Product: GIMP
Classification: Other
Component: libgimp
git master
Other All
: Normal normal
: 2.10
Assigned To: GIMP Bugs
GIMP Bugs
Depends on:
Blocks:
 
 
Reported: 2014-03-27 19:13 UTC by Elle Stone
Modified: 2014-04-10 15:32 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Elle Stone 2014-03-27 19:13:14 UTC
At 32-bit floating point precision (gamma), converting an image to GIMP's built-in sRGB produces slightly different RGB values than converting the image to the equivalent sRGB profile from disk. The values that result from converting to the profile on disk are correct.

For example, converting linear gamma WideGamutRGB saturated green (0.0, 1.0, 0.0) to GIMP's built-in sRGB ought to produce (-11.377826, 1.118669, -1.120080). Instead it produces (-11.378868, 1.118671, -1.120196), for a discrepancy of (0.001042, 0.000002, 0.000116).

A complete description is here:

http://ninedegreesbelow.com/gimpgit/icc-profile-conversion-to-built-in-srgb-error.html

16-bit integer precision conversions are also wrong.
Comment 1 Michael Natterer 2014-03-28 08:18:15 UTC
Could it be that this is the profile equality test via MD5 digest giving
the wrong result? I simply took the hardcoded digest values from the lcms
plugin and put them into gimp_lcms_create_srgb_profile(). However, this
digest was created in an unknown way and probably refers to the old sRGB
profile which we no longer use.

Since you have that same profile on disk, can you debug-print its digest in
lcms.c and put the values into gimp_lcms_create_srgb_profile()?
Comment 2 Elle Stone 2014-03-28 17:04:29 UTC
(In reply to comment #1)
> Could it be that this is the profile equality test via MD5 digest giving
> the wrong result? I simply took the hardcoded digest values from the lcms
> plugin and put them into gimp_lcms_create_srgb_profile(). However, this
> digest was created in an unknown way and probably refers to the old sRGB
> profile which we no longer use.

That digest does refer to the old sRGB profile. Anything is possible, but I can't imagine how the digest could be relevant. In either case an ICC profile conversion is done and would be done even if the digest were produced from the current built-in sRGB profile, as the conversion is from some other color space to the sRGB color space. Even a linear gamma sRGB color space is "some other color space" because the tone reproduction curve is different.
 
I don't think a check for whether two profiles are identical is needed when doing 32-bit floating point unbounded mode ICC profile conversions, as they are lossless, at least as shown by extensive round-trip testing I've done (which were done at the command line and also using GIMP and never involved higher than 32-bit floating point precision). Even at 16-bit integer precision in bounded mode, converting for example from sRGB to ProPhoto and back is lossless, so surely converting from sRGB to (the exact same) sRGB is a lossless operation. I don't know about 8-bit conversions.

> Since you have that same profile on disk, 

Here's a link to the profile: 
http://ninedegreesbelow.com/gimpgit/gimp-srgb/GIMP-built-in-sRGB.icc

> can you debug-print its digest in
> lcms.c and put the values into gimp_lcms_create_srgb_profile()?

My apologies, I don't know what this sentence means. In any event, the profile on disk has a different description to allow distinguishing it from the GIMP built-in sRGB profile, so wouldn't the digest be different?

I don't see any reference to "digest" in the lcms1 documentation. How was the first digest created? 

From the lcms2 documentation:

MD5 message digest: 

In cryptography, MD5 (Message-Digest algorithm 5) is a widely used cryptographic hash function with a 128‐bit hash value. . . . ICC profiles can use MD5 as a checksum, and as a unique identifier for a profile.

cmsBool	cmsMD5computeID(cmsHPROFILE hProfile);	
Computes a MD5 checksum and stores it as Profile ID in the profile header. 

void cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID);	
Retrieves the Profile ID stored in the profile header.
Comment 3 Michael Natterer 2014-03-28 17:57:19 UTC
You are right, it probably isn't the equality check. However we can get
rid of the entire profile digest code I moved to libgimpcolor by simply
using that API you pointed out. I must have missed that since I checked
the lcms API for such a function.

Skipping the conversion for identical profiles is simply an optimization,
I don't think there is any harm in keeping it around.

About the actual bug, might we see a bug in lcms? The only reason I can
come up with is that the profile stored on disk is actually different
from the one created from scratch in memory. I did read the lcms.c
plug-in code like 5 times last night, and it all looks right, emphasis
on *looks* of course :)
Comment 4 Elle Stone 2014-03-28 18:50:23 UTC
(In reply to comment #3)

> Skipping the conversion for identical profiles is simply an optimization,
> I don't think there is any harm in keeping it around.
No, there shouldn't be

> About the actual bug, might we see a bug in lcms? The only reason I can
> come up with is that the profile stored on disk is actually different
> from the one created from scratch in memory. 

I'll do some checks that I hadn't thought of before. Maybe it really is an lcms bug.
Comment 5 Elle Stone 2014-03-28 20:49:05 UTC
It does look like an lcms bug. I rolled back babl/gegl/gimp to before the new built-in sRGB and then modified lcms.c to save the lcms built-in sRGB to disk. Using the saved-to-disk lcms built-in sRGB, I can replicate the behavior with these two lcms tificc commands:

Using the lcms tificc built-in sRGB (tificc defaults to the lcms built-in sRGB as the output color space):
$ tificc -t 1 -b -w16 -e -i sRGB-elle-V2-g100.icc color-blocks.tif color-blocks2tificc-built-in.tif

Eyedroppering "color-blocks2tificc-built-in.tif" gives values that match the result of converting from the linear gamma sRGB to GIMP's old (lcms) built-in sRGB

Using the built-in lcms sRGB that was saved to disk, which ought to be the same as the built-in sRGB:
$ tificc -t 1 -b -w16 -e -i sRGB-elle-V2-g100.icc -o old-built-in-sRGB.icc color-blocks.tif color-blocks2oldgimp-built-in.tif

Eyedroppering color-blocks2oldgimp-built-in.tif gives the same results as converting from the linear gamma sRGB to the copy of the old built-in sRGB that was saved to disk.

I'll ask about this on the lcms mailing list. Probably this should be closed as not a GIMP bug.
Comment 6 Elle Stone 2014-04-09 22:30:17 UTC
I asked about this issue on the lcms mailing list. Here's the thread:

http://sourceforge.net/p/lcms/mailman/message/32178367/

Short version: the profile in memory will never be the same as the profile on disk because the profile in memory uses greater precision to do the profile conversions.

Which sounds like a good thing until you:

1. Make a solid white high precision image.

2. Assign a disk copy of "the same" profile as the built-in sRGB profile.

3. Use the color picker to confirm R=G=B=1.000000.

4. Convert the image from the disk copy to the GIMP built-in profile in memory.

5. Check the RGB values again. The RGB values have changed because the embedded profile uses a lower precision for the matrix values than does the profile in memory.

Make sure you color pick a different color from some other image between steps 4 and 5 to clear the color picker's memory.

I'm still puzzling over this idea of using a higher precision for profiles in memory. But I can't see how it can possibly be a good thing that converting a solid white image from sRGB on disk to sRGB in memory *changes* the RGB values such that white isn't exactly white any more. 

Maybe it would be better if GIMP made the built-in sRGB profile upon startup, saved it to disk, and used the disk copy instead of the copy from memory.
Comment 7 Michael Natterer 2014-04-10 11:51:36 UTC
Intersting, and pretty disturbing, to be honest :)

I think we should save the profile to memory when the function is
first called, and after that simply return a profile opened from that
memory blob.

Can you confirm that this "saving" gives the same results as if we
really saved to disk and loaded back from there?
Comment 8 Elle Stone 2014-04-10 12:43:16 UTC
(In reply to comment #7)
> Intersting, and pretty disturbing, to be honest :)

> I think we should save the profile to memory when the function is
> first called, and after that simply return a profile opened from that
> memory blob.
> 
> Can you confirm that this "saving" gives the same results as if we
> really saved to disk and loaded back from there?

If saving the profile as a memory blob "fools" LCMS into thinking the profile is being saved to disk, then yes, the extra precision should be removed because the ICC profile format is very specific. But to know for sure I'd have to actually check and see. Do you have a patch you can send?
Comment 9 Michael Natterer 2014-04-10 14:17:40 UTC
I'm pretty confident it will work because we also open on-disk profiles
using the "load from memory" function via mmap(). Will push to git right
away.
Comment 10 Michael Natterer 2014-04-10 14:34:19 UTC
Fixed in git, please reopen if it doesn't work as expected.

commit ba85bb78ba5d4dca5a956cfbfe7c670f6ec4a164
Author: Michael Natterer <mitch@gimp.org>
Date:   Thu Apr 10 16:30:04 2014 +0200

    Bug 727185 - Converting to GIMP built-in sRGB produces the wrong RGB values
    
    Profiles from disk have a lower precision than those created from
    scratch via lcms API. Ensure identical profiles by returning a
    "loaded" profile from gimp_lcms_create_srgb_profile() (simply do a
    save/load roundtrip in memory).

 libgimpcolor/gimpcolor.def |   1 +
 libgimpcolor/gimplcms.c    | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
 libgimpcolor/gimplcms.h    |   3 ++
 3 files changed, 89 insertions(+), 33 deletions(-)
Comment 11 Elle Stone 2014-04-10 15:23:20 UTC
When converting from sRGB on disk to regular built in sRGB, for 100% saturated
green (0.0, 1.0, 0.0) and cyan (0.0, 1.0, 1.0), there is still a small residual
deviation of -0.000001 in the red channel. 

I don't think a deviation of 0.000001 is anything to worry about. I've seen
this small residual deviation before. It's most likely from LCMS and likely
can't/won't be fixed. I *think* it has to do with the switch from V2 point
curves to V4 parametric curves, but I don't know why that would make a
difference.

Previously the deviation for the conversion to the built-in GIMP sRGB was much
greater. For example for saturated green the deviation was 0.000051 in the red
channel, 0.000001 in the green channel, and 0.000021 in the blue channel.

So the patch worked. Yeah!
Comment 12 Michael Natterer 2014-04-10 15:32:12 UTC
Yeah :) Thanks for checking.