AcademySoftwareFoundation / AcademySoftwareFoundation/OpenColorIO

Add display gamut mapping as a fixed function

Offen
#1,618 8 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Needs Discussion
Vorherrschende Sprache
C++
Sterne
2.1k
Forks
503
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

As part of my efforts to improve my personal ACES OCIO configuration, I developed a simple gamut mapping method meant for display transforms. It's applied after the matrix transform and brings all values within the gamut. Since it's after the image has already been converted to another gamut, it can be gamut agnostic. Right now, I have it implemented as a LUT, but I think it would be great if this could be added to OpenColorIO as a fixed function. I don't know enough about the internals of OpenColorIO to feel comfortable adding this myself, so that's why I am opening an issue, in the hope that someone else can add this.

Now, I am aware that OpenColorIO already has a gamut mapping algorithm, the ACES gamut compress fixed function. However, this algorithm is not appropriate for display transforms and it does not attempt to preserve luminance. So, I still believe that there is a place for this algorithm.

Here's an image without the gamut mapping:
![Color Checkers BSP RRT AP1 No_Display_Gamut_Mapping](https://user-images.githubusercontent.com/8073547/158604378-c95c494e-41ea-44c0-bc0b-caeabc086571.png)
And with:
![Color Checkers BSP RRT AP1 Saturation_Luminance_Hue_Display_Gamut_Mapping](https://user-images.githubusercontent.com/8073547/158604428-e86712e9-dd7b-4278-85cf-01f8e09ed3f4.png)

The code for the gamut mapping is quite simple. The technique is basically that the RGB colour gets converted to HSV. Then the saturation gets remapped and the hue gets fixed. Then it's converted back to RGB and lastly the luminance gets corrected (since changing the saturation also changes the luminance). The code in Python looks like this:
```python
def luminance(R, G, B):
return 0.2126 * R + 0.7152 * G + 0.0722 * B

def gamutMap(R, G, B):
Lo = luminance(R, G, B)

# Convert to HSV
hsv = toHSV(R, G, B)
H = hsv[0]
S = hsv[1]
V = hsv[2]

# saturation mapping
if S > 0.98:
S = (S - 0.98) / 0.02
S = S / (S + 1.0)
S = S * (0.02 / 0.91) + 0.98
S = min(S, 1.0)

# Fix the hue in order to make the reds and blues
# show in a more predictable manner.
if H >= 354.0 and (B < 0.00001 or G < 0.00001):
H = 0.0
if H >= 240.0 and R < 0.00001:
H = 240.0

# Convert back to RGB
rgb = toRGB(H, S, V)
R = rgb[0]
G = rgb[1]
B = rgb[2]

# Make sure that the luminance is still the same
L = luminance(R, G, B)
Lscale = Lo / L
R *= Lscale
G *= Lscale
B *= Lscale

# If one of the RGB values is still above 1.0,
# then scale all of the values so that the values
# do stay within 0.0 and 1.0.
maxC = max(max(R, G), B)
if maxC > 1.0:
R /= maxC
G /= maxC
B /= maxC

return [R, G, B]
```

The toHSV and toRGB conversions are just the standard HSV conversions. The RGB_TO_HSV and HSV_TO_RGB fixed functions currently in OpenColorIO have modifications made to them, which means that they will not work with this!

The luminance function is based on the sRGB/Rec.709 gamut, but it could be anything. It could be a parameter that the configuration author can define, but it could also be some other function. Maybe just a sum, or by getting the cube root first, then a sum, and then cubed. What matters is that it's at least roughly representative of the luminance.

The hue correction might seem strange at first, but it's definitely needed. Here's an image without it:
![Color Checkers BSP RRT AP1 Saturation_Luminance_Display_Gamut_Mapping](https://user-images.githubusercontent.com/8073547/158607070-7b6b51a5-09e3-4418-9ba0-8a2e959a0565.png)
It's not the easiest to see, but take a look at the red gradient. It's slightly purple. Here's a chromaticity diagram showing what's happening:
image
The image gets converted from the ACEScg gamut to the sRGB gamut, but when using a simple hue-preserving saturation mapping, then you get what the grey dotted lines show. You can see that the red primary will show up as purple, yet there isn't supposed to be any blue light. The hue correction attempts to fix this.

The algorithm above is specifically for display transforms, but here is a modified version that can be used as a general gamut mapping method:
```python
def luminance(R, G, B):
return 0.2126 * R + 0.7152 * G + 0.0722 * B

def gamutMap(R, G, B):
Lo = luminance(R, G, B)

# Convert to HSV
hsv = toHSV(R, G, B)
H = hsv[0]
S = hsv[1]
V = hsv[2]

# saturation mapping
if S > 0.98:
S = (S - 0.98) / 0.02
S = S / (S + 1.0)
S = S * (0.02 / 0.91) + 0.98
S = min(S, 1.0)

# Fix the hue in order to make the reds and blues
# show in a more predictable manner.
if H >= 240.0 and B < 0.00001:
H = 0.0
if H >= 240.0 and R < 0.00001:
H = 240.0

# Convert back to RGB
rgb = toRGB(H, S, V)
R = rgb[0]
G = rgb[1]
B = rgb[2]

# Make sure that the luminance is still the same
L = luminance(R, G, B)
Lscale = Lo / L
R *= Lscale
G *= Lscale
B *= Lscale

return [R, G, B]
```
It uses a slightly different hue correction and it doesn't clamp the value to a maximum of 1.0. So, this version supports any RGB values and, assuming that the HSV conversion algorithms support negative values, it also supports negative values. Again, the luminance function could be whatever.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne mit der Überprüfung der bestehenden Fixed Functions RGB_TO_HSV und HSV_TO_RGB von OpenColorIO und vergleiche sie mit dem Python-Referenzalgorithmus im Issue. Kläre den Umfang der Display-Transformation, das Luminanzverhalten, die Farbtonkorrektur und die Parametrisierung; abgeschlossen ist die Arbeit, wenn eine definierte Fixed Function mit geeigneter Validierung für das angeforderte Gamut-Mapping-Verhalten integriert ist.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
cpp, python
Bereich
computer-graphics
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
28/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.