Replace hard-coded sRGB parameters to allow editing in other RGB working spaces
Submitted by Elle Stone
Created attachment 287578 Patch to retrieve the user-chosen RGB working space parameters.
Currently some GIMP editing operations are hard-coded to use parameters specific to sRGB. To allow correct editing in other RGB working spaces, the hard-coded sRGB parameters must be replaced with parameters retrieved from the image's actual RGB working space.
Problems with forcing a conversion to unbounded sRGB and keeping the hard-coded sRGB parameters include:
- Multiply produces different results in different RGB working spaces (http://nbviewer.ipython.org/github/colour-science/colour-website/blob/master/ipython/about_rendering_engines_colourspaces_agnosticism.ipynb).
- Adding and removing color casts (changing the image white balance) involves multiply, so color casts that were created in some other RGB working space cannot be easily corrected in the sRGB working space (http://ninedegreesbelow.com/photography/unbounded-srgb-color-correction-fails.html).
- Using sRGB colorants to encode colors that are outside the very small bounded sRGB color gamut requires using negative channel values. Multiplying and dividing colors that are encoded using negative channel values produces physically meaningless results (http://ninedegreesbelow.com/photography/unbounded-srgb-multiply-produces-meaningless-results.html).
- Over 50% of the core GIMP editing operations that I checked are chromaticity dependent, giving different results in different RGB working spaces (http://ninedegreesbelow.com/photography/unbounded-srgb-as-universal-working-space.html#chromaticities-matter). Any editing operation that involves multiply, divide, or raising RGB values to a power produces different results in different RGB working spaces. Only the artist can say which working space gives the best results. Therefore a forced conversion to sRGB severely compromises artistic freedom.
- Converting an image from one RGB working space to another rearranges channel data, sometimes making it unuseable. Therefore a forced converstion to unbounded sRGB severely limits the artist's ability to make creative use of channel data.
- Standard display-referred image editing requires using RGB channel values that are encoded using RGB channel values that are clipped to the RGB floating point range 0.0 to 1.0, inclusively. Trying to perform display-referred edits on colors that are encoded using negative channel values and/or channel values that are greater than 1.0 produces meaningless results. Consequently display-referred image editing is flatly broken in the unbounded sRGB color space (http://ninedegreesbelow.com/photography/display-referred-scene-referred.html#unbounded-sRGB-broken-model). Examples of affected editing operations are making a Levels gamma slider adjustment, dividing to flat-field an image, and using any layer Blend mode that involve multiply or divide.
A previously suggested way to fix problems created by a forced conversion to unbounded sRGB is to write new code that uses "BABL formats" to:
- Use LCMS to retrieve the profile colorants of the RGB working space that the user would otherwise choose to use.
- Create an new ICC profile RGB working space that uses: a. The profile colorants of the RGB working space that the user would otherwise choose to use. b. The sRGB TRC as the BABL-designated adequate substitute for "perceptually uniform" RGB.
- Add new code to every editing operation that gives the wrong results in the unbounded sRGB color space. This added code will activate the following sequence of events: a. Convert the affected image buffer(s) from unbounded sRGB to the new ICC profile RGB working space with the colorants of the RGB working space that the user would otherwise choose to use: i. For some editing operations, the affected image buffer is just the one layer. ii.For layer Blend mode operations, the entire layer stack that uses any layer Blend modes that give the wrong results in the unbounded sRGB color space (which in fact is most layer blend modes) would have to be converted to the new ICC profile RGB working space with the colorants of the RGB working space the user would otherwise choose to use. b. Perform the affected editing operation. If the affected editing operation involves a layer Blend mode, the blended results would need to be made into a "New from Visible" layer. The convert/blend/"New from Visible" sequence would need to be repeated every time the user makes any modification to the affected layer stack. c. Convert the image(s) from the new ICC profile with the colorants of the RGB working space that the user would otherwise choose to use, back to unbounded sRGB.
Problems with this previously suggested solution to problems created by a forced conversion to unbounded sRGB include:
- This solution requires adding repetitive code to many editing operations. Developer time will be required not only to write the basic conversion code, but also to identify and modify all the requisite editing operations so the conversion code is activated as required.
- For correct results, over 50% of the core editing operations that the GIMP UI makes available to the user will require converting image buffer(s) from unbounded sRGB to an LCMS-made ICC profile that uses the colorants of the RGB working space that the user would prefer to be using, and then back to unbounded sRGB.
- This solution to the problems created by forced conversion of images to unbounded sRGB will require constantly converting image buffers back and forth between two RGB working spaces. This constant churning raises the possibility that many editing operations that are already somewhat slow, will be made even slower, chewing through CPU cycles to make the required color space conversions.
- This solution puts the user's artistic intentions entirely at the mercy of developer decisions as to which RGB editing operations really do require a conversion to an RGB working space that uses the colorants of the user's chosen RGB working space.
Instead of keeping all of the hard-coded sRGB parameters, forcing a conversion to unbounded sRGB, and fixing the resulting problems, a better solution is as follows:
- All hard-coded sRGB colorant-related and reference white parameters should be replaced with RGB working-space-specific parameters retrieved by LCMS from the user's chosen RGB working space.
- The BABL code that switches between linear light and perceptually uniform RGB using the sRGB TRC (tone reproduction curve) should be kept.
- To accomodate the BABL linear-to-perceptual sRGB-TRC-specific code, users must edit their images only in RGB working spaces that use the sRGB TRC. This can be accomplished in two ways: i. Either inform the user that the user needs to convert their images to a version of their chosen RGB working space that already has the sRGB TRC, or else they will get wrong results. Appropriate ICC RGB working space profiles can be distributed with GIMP. ii. Or else convert the image to a version of the user-chosen image profile that has the sRGB TRC: a. Upon opening an image, and also when the user converts the image to a new RGB working space, use LCMS to make a new ICC profile that uses the user-chosen RGB working space's profile colorants and the sRGB TRC, and then convert the image to this new profile. b. Retain the user-chosen image RGB working space information in case the user wants to convert the image back to the user-chosen RGB working space when exporting an image to a non-XCF file format. This solution eliminates all the problems associated with a forced conversion to unbounded sRGB, and retains all the benefits of the currently coded BABL conversions between "linear light" and "perceptually uniform" RGB.
Below is a list of (hopefully all) the files that have hard-coded device and sRGB parameters, that need to be modified to use parameters pulled from the image RGB working space:
Files that use hard-coded sRGB unadapted Y values: BABL babl/extensions/ycbcr.c GEGL operations/common/svg-luminancetoalpha.c operations/common/svg-saturate.c operations/workshop/box-percentile.c operations/workshop/disc-percentile.c GIMP libgimpcolor/gimprgb.h gives RGB_LUMINANCE_ #defines:
#define GIMP_RGB_LUMINANCE_RED (0.2126) #define GIMP_RGB_LUMINANCE_GREEN (0.7152) #define GIMP_RGB_LUMINANCE_BLUE (0.0722) #define GIMP_RGB_LUMINANCE(r,g,b) ((r) * GIMP_RGB_LUMINANCE_RED + \ (g) * GIMP_RGB_LUMINANCE_GREEN + \ (b) * GIMP_RGB_LUMINANCE_BLUE) libgimpcolor/gimprgb.c uses these #defines to write two more functions that calculate Luminance: gimp_rgb_luminance gimp_rgb_luminance_uchar These files use #define GIMP_RGB_LUMINANCE(r,g,b) to calculate Luminance: app/core/gimpimage-convert-type.c app/gui/splash.c (splash.c is a UI that probably isn't and can't be color-managed by LCMS) app/operations/gimplevelsconfig.c app/operations/gimpoperationcolorize.c app/operations/gimpoperationdesaturate.c app/widgets/gimpgradienteditor.c plug-ins/common/bump-map.c plug-ins/common/colorify.c plug-ins/common/displace.c plug-ins/common/despeckle.c plug-ins/common/engrave.c plug-ins/common/file-aa.c plug-ins/common/file-pnm.c plug-ins/common/newsprint.c plug-ins/common/oilify.c plug-ins/file-fli/fli-gimp.c plug-ins/fractal-explorer plug-ins/gimpressionist/gimp.c plug-ins/gradient-flare.c plug-ins/pagecurl.c plug-ins/pygimp/gimpcolormodule.c This file uses gimp_rgb_luminance to calculate luminance: plug-ins/pygimp/pygimp-colors.c These files use gimp_rgb_luminance_uchar to calculate luminance: libgimp/gimpdrawable.c libgimp/gimppixelfetcher.c plug-ins/common/file-mng.c plug-ins/common/file-png.c plug-ins/common/grid.c plug-ins/gradient-flare.c plug-ins/maze/maze-utils.c
Files that use hard-coded sRGB D50-adapted Y values BABL babl/base/rgb-constants.h (#defines for RGB_LUMINANCE_) babl/base/model-gray.c (uses the RGB_LUMINANCE_ #defines) extensions/grey.c (uses the RGB_LUMINANCE_ #defines) GEGL operations/workshop/snn-percentile.c code in the opencl folder that repeats BABL code GIMP code never uses D50-adapted sRGB Y values.
Fixing editing operations that currently use hard-coded sRGB Y values to calculate Luminance is straightfoward: Write a function that uses LCMS to retrieve the image's ICC profile's actual red, green, and blue colorant Y values and ferry the information to where it's needed. Modify the relevant files to use the RGB working space Y values instead of hard-coded Y values.
The BABL "extensions/CIE.c" file uses D65 sRGB color space xy values and the D65 sRGB reference white to convert to XYZ and then to LAB and LCH(ab). As currently programmed, the conversion to XYZ uses unadapted sRGB color space values, and so gives wrong results even for color managed sRGB images. The following files use the BABL conversion to XYZ and then to LAB and LCH(ab): BABL babl/tests/srgb_to_lab_u8.c GEGL gegl/operations/common/image-compare.c gegl/operations/common/noise-cie-lch.c GIMP gimp/plug-ins/common/compose.c gimp/plug-ins/common/decompose.c gimp/app/operations/tests/test-operations.c gimp/app/core/gimpimage-convert-type.c To fix the code that converts from RGB to XYZ and then to LAB, write a function that uses LCMS to retrieve the image's ICC profile's actual red, green, and blue colorant X, Y, and Z values, plus the ICC profile's illuminant X, Y, and Z values (currently always D50), and ferry the information to where it's needed. The BABL file "extensions/CIE.c" should use the retrieved XYZ values to convert from RGB to XYZ. Once in the XYZ reference color space, the conversion to LAB should be done.
Code files in BABL/GEGL/GIMP that refer to YCbCr (not including jpeg/jp2-related and BABL format-defining code): BABL babl/base/model-ycbcr.c (NTSC) extensions/ycbcr.c (BT.709) tests/rgb_to_ycbcr.c (NTSC) GEGL opencl/colors.cl.h (NTSC?) gegl/opencl/gegl-cl-color.c (NTSC?) operations/common/cartoon.c (NTSC?) GIMP plug-ins/common/decompose.c (BT.709 and NTSC) plug-ins/common/compose.c (BT.709 and NTSC) The way to correct the YCbCr calculations to make them useable for all RGB working spaces is to use the generalized equations for converting from RGB to YCbCr, along with the LCMS-retrieved Y values for the image's RGB working space profile. The existing device-color-space-specific code gives the wrong results even for color-managed sRGB and NTSC images.
Other hard-coded NTSC parameters: GEGL opencl/colors.cl.h (used by operations/common/oilify.c) GEGL's "operations/common/oilify.c" should use Luminance calculations based on the Y values retrieved from the actual image ICC profile. GIMP libgimpcolor/gimprgb.h (in the deprecated GIMP_RGB_INTENSITY #defines, which don't seem to be used in any other GIMP code and so can be removed altogether)
libgimpcolor/gimprgb.c (in the deprecated functions gimp_rgb_intensity and gimp_rgb_intensity_uchar, which don't seem to be used in any other GIMP code and so can be removed altogether) modules/gimpcolorwheel.c If the file "gimp/modules/gimpcolorwheel.c" is part of the color picker UI (or other UI that isn't color-managed), the code should use hard-coded sRGB values (not NTSC values) until such time as the color picker (or other) UI is color-managed. plug-ins/common/hot.c GIMP's hot pixel repair code ("plug-ins/common/hot.c") has a precalculated NTSC-to-YIQ matrix. The current code gives wrong results even for sRGB images. It seems unlikely that YIQ is an especially felicitous RGB color space transform for identifying hot pixels in an ICC profile color managed image editing application.
NTSC broadcasting has ceased. So all NTSC-based code should be removed from BABL, GEGL, and GIMP.
The following BABL/GEGL/GIMP code files mention YUV: BABL extensions/gggl.c GEGL operations/external/ff-load.c operations/external/v4l.c operations/workshop/external/ff-save.c GIMP plug-ins/twain/twain.h Probably none of the above-listed files except BABL's "extensions/gggl.c" file have anything to do with color-space-specific transforms. Whatever "extensions/gggl.c" does (possibly fast conversions between 8-bit integer and floating point for display on a BT.709 device?), there shouldn't be any device-color-space-specific transforms in an ICC profile color managed image editing application. In addition, there are some sRGB-specific tables in the BABL code, possibly also for fast conversions for 8-bit images. Not all 8-bit images are sRGB images. Not even all legacy XCF files produced using 8-bit GIMP are sRGB XCF files. I'm not sure how these tables are intended to be used with high bit depth GIMP (possibly for working with legacy 8-bit XCF files?). So I can't give advice on how to deal with them. I did compile a version of BABL with all such tables removed and never had a problem running GIMP. But I don't have any legacy 8-bit XCF files.
The BABL/GEGL/GIMP code base also contains files with hard-coded matrices, tables, and coefficients that are pre-calculated on the assumption that the image is in a specific color space. Such precalculated, hard-coded, color space-specific values need to be identified and dealt with on a case by case basis. Below are two examples of pre-baked, color-space-specific transforms:
GEGL uses hard-coded sRGB-based coefficients in the function "gfloat rgb_r55", in the file "operations/common/color-temperature.c" Functions in this file are called upon when you use GIMP's "Colors/Color Temperature" to raise or lower an image's color temperature. As currently coded, the color temperature changing code produces pleasing though colorimetrically inaccurate results for sRGB images. It produces very wrong results for non-sRGB images. One method for calculation color temperature changes, for which the required equations are readily available and easy to code, is to: Convert the image from RGB to XYZ using the image RGB working space colorants retrieved by LCMS. Perform a Bradford or CAT02 chromatic adaptation from TEMP1 to TEMP2. Convert the image from XYZ to RGB.
The GIMP file "modules/display-filter-color-blind.c" simulates? enhances? what a colorblind person sees. This code uses RGB/LMS transforms that are hard-coded to use unadapted sRGB values, applicable to a "modern CRT", the likes of which haven't been in wide use for quite a few years now. This code is not valid for anyone using ICC profile color management, even for sRGB images. Nor is it valid for anyone viewing an sRGB image without color management, except on a monitor that has been calibrated to match sRGB. A way to generalize this code to work in a color-managed environment might be to: Convert the image from the image's D50-adapted RGB working space to the user's monitor profile (which LCMS does anyway as the image is sent to the monitor) Do the required RGB/XYZ/LMS transforms using the XYZ parameters retrieved from the monitor profile, with the sRGB profile as the usual "no monitor profile" fallback. XYZ/LMS equations can be found on Bruce Lindbloom's website. The alternative to generalizing the code to work with all monitor profiles in an ICC profile color-managed workflow is to include a UI notification that this display filter only produces valid results if: Color management is disabled. The image is an sRGB image. The monitor is a CRT or other display device that's been calibrated to match sRGB, or else the user could try whatever sRGB simulation mode their LCD might provide.
Further examination of the code is likely to reveal a few other instances of hard-coded device-based and sRGB-specific matrices, tables, and coefficients. Such code has no place in an ICC profile color managed editing application.
Additional details on how to replace the hard-coded sRGB colorant-related and device-based parameters can be found here: http://ninedegreesbelow.com/gimpgit/gimp-hard-coded-sRGB.html
Patch 287578, "Patch to retrieve the user-chosen RGB working space parameters.":
Version: git master