How to deal with negative values when manipulating CIE xyY or XYZ colors?

373 views Asked by At

I'm writing some image processing code to do some creative color gamut limiting that I want to use for color grading applicationgs. I'm building to operate in the Davinci Wide Gamut color space, typically using the DaVinci Intermediate logarithmic encoding, since that is my typical working space in a color managed workflow. To do this, I want to convert the input to xyY, limit the value of xy to fit within my desired gamut and then convert back to DaVinci Wide Gamut/Intermediate.

These are the steps I'm following:

  1. Convert DaVinci Intermediate RGB to linear RGB
  2. Multiply by DWG to XYZ conversion matrix.
  3. Convert XYZ to xyY.
  4. Modify x and y to fall within gamut.
  5. Convert xyY to XYZ.
  6. Multiply by XYZ to DWG conversion matrix.
  7. Convert linear RGB to DaVinci Intermediate.

This works fine for most cases, I have also verified that roundtripping without modification of x and y works as intended. The problem occurs for for extremely saturated blues. Take the following RGB values in Davinci Wide Gamut/Intermediate as an example: [0, 0, 1.0] Let's follow along:

  1. Convert to linear: [0, 0, 100]
  2. Convert to XYZ: [10.11,-14.78, 132.59]
  3. Convert to xyY: [0.08,-0.12,-14.78]
  4. Modifying x and y to fit in my gamut: [0.18, 0.07, -14.78]
  5. Convert back to XYZ: [-35.64,-14.78,-149.08]
  6. Convert back to DWG linear: [-27.99,-27.99,-117.44]
  7. Convert back to DaVinci Intermediate: [-292.34, -292.34, -1226.52]

As you can see, the result is nonsensical, but only for these extreme blue values. To me, it seems pretty clear that this is caused by the blue primary in DaVinci Wide Gamut which has a negative y coordinate ([0.0790, -0.1155]). This causes negative luminance (Y = -14.78) and when messing with the xy values without dealing with the luminance at the same time, things go haywire.

How do I deal with negative values in xyY and XYZ when doing gamut limiting in this way?

1

There are 1 answers

0
denis On

Step 2 XYZ < 0 ?? Afaik CIELAB XYZ should always be >= 0. Are any other of your XYZ s < 0 ?
There are quite a few RGB <-> XYZ formulas and code snippets on the web, for example

# https://python-colormath.readthedocs.io/en/latest/conversions.html

from colormath.color_objects import sRGBColor, LabColor, XYZColor
from colormath.color_conversions import convert_color

print( 80 * "▄" )

c = [ 0., 0., 1. ]
rgb = sRGBColor( *c )
lab = convert_color( rgb, LabColor )  # .lab_l .lab_a .lab_b
xyz = convert_color( rgb, XYZColor )  # .xyz_x .xyz_y .xyz_z
print( "rgb:", c )
print( lab )
print( xyz )

# rgb: [0.0, 0.0, 1.0]
# LabColor (lab_l:32.2994 lab_a:79.1914 lab_b:-107.8655)
# XYZColor (xyz_x:0.1805 xyz_y:0.0722 xyz_z:0.9504)  # 0 - 1, not XYZ100