Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Commit

Permalink
Merge pull request #85 from colour-science/feature/bt2020
Browse files Browse the repository at this point in the history
Implement support for "ITU-R BT.2020" colorspace.
  • Loading branch information
KelSolaar committed Mar 12, 2018
2 parents 0f5e043 + 2fba3e5 commit 89862ba
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
25 changes: 24 additions & 1 deletion colormath/color_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from colormath import spectral_constants
from colormath.color_objects import ColorBase, XYZColor, sRGBColor, \
LCHabColor, LCHuvColor, LabColor, xyYColor, LuvColor, HSVColor, HSLColor, \
CMYColor, CMYKColor, BaseRGBColor, IPTColor, SpectralColor, AdobeRGBColor
CMYColor, CMYKColor, BaseRGBColor, IPTColor, SpectralColor, AdobeRGBColor, \
BT2020Color
from colormath.chromatic_adaptation import apply_chromatic_adaptation
from colormath.color_exceptions import InvalidIlluminantError, \
UndefinedConversionError
Expand Down Expand Up @@ -520,6 +521,17 @@ def XYZ_to_RGB(cobj, target_rgb, *args, **kwargs):
nonlinear_channels[channel] = v * 12.92
else:
nonlinear_channels[channel] = 1.055 * math.pow(v, 1 / 2.4) - 0.055
elif target_rgb == BT2020Color:
if kwargs.get('is_12_bits_system'):
a, b = 1.0993, 0.0181
else:
a, b = 1.099, 0.018
for channel in ['r', 'g', 'b']:
v = linear_channels[channel]
if v < b:
nonlinear_channels[channel] = v * 4.5
else:
nonlinear_channels[channel] = a * math.pow(v, 0.45) - (a - 1)
else:
# If it's not sRGB...
for channel in ['r', 'g', 'b']:
Expand Down Expand Up @@ -548,6 +560,17 @@ def RGB_to_XYZ(cobj, target_illuminant=None, *args, **kwargs):
linear_channels[channel] = V / 12.92
else:
linear_channels[channel] = math.pow((V + 0.055) / 1.055, 2.4)
elif isinstance(cobj, BT2020Color):
if kwargs.get('is_12_bits_system'):
a, b, c = 1.0993, 0.0181, 0.081697877417347
else:
a, b, c = 1.099, 0.018, 0.08124794403514049
for channel in ['r', 'g', 'b']:
V = getattr(cobj, 'rgb_' + channel)
if V <= c:
linear_channels[channel] = V / 4.5
else:
linear_channels[channel] = math.pow((V + (a - 1)) / a, 1 / 0.45)
else:
# If it's not sRGB...
gamma = cobj.rgb_gamma
Expand Down
33 changes: 33 additions & 0 deletions colormath/color_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,39 @@ class sRGBColor(BaseRGBColor):
}


class BT2020Color(BaseRGBColor):
"""
Represents a ITU-R BT.2020 color.
.. note:: If you pass in upscaled values, we automatically scale them
down to 0.0-1.0. If you need the old upscaled values, you can
retrieve them with :py:meth:`get_upscaled_value_tuple`.
:ivar float rgb_r: R coordinate
:ivar float rgb_g: G coordinate
:ivar float rgb_b: B coordinate
:ivar bool is_upscaled: If True, RGB values are between 1-255. If False,
0.0-1.0.
"""

#: RGB space's gamma constant.
rgb_gamma = 2.4
#: The RGB space's native illuminant. Important when converting to XYZ.
native_illuminant = "d65"
conversion_matrices = {
"xyz_to_rgb":
numpy.array((
(1.716651187971269, -0.355670783776393, -0.253366281373660),
(-0.666684351832489, 1.616481236634939, 0.015768545813911),
(0.017639857445311, -0.042770613257809, 0.942103121235474))),
"rgb_to_xyz":
numpy.array((
(0.636958048301291, 0.144616903586208, 0.168880975164172),
(0.262700212011267, 0.677998071518871, 0.059301716469862),
(0.000000000000000, 0.028072693049087, 1.060985057710791))),
}


class AdobeRGBColor(BaseRGBColor):
"""
Represents an Adobe RGB color.
Expand Down
5 changes: 5 additions & 0 deletions doc_src/color_objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ sRGBColor

.. autoclass:: colormath.color_objects.sRGBColor

BT2020Color
-----------

.. autoclass:: colormath.color_objects.BT2020Color

AdobeRGBColor
-------------

Expand Down
21 changes: 19 additions & 2 deletions tests/test_color_conversion.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
import itertools
import numpy as np
import unittest
from colormath import color_conversions
from colormath.color_conversions import GraphConversionManager, XYZ_to_RGB, HSV_to_RGB
from colormath.color_conversions import GraphConversionManager, XYZ_to_RGB, HSV_to_RGB, \
RGB_to_XYZ
from colormath.color_exceptions import UndefinedConversionError
from colormath.color_objects import XYZColor, BaseRGBColor, HSVColor, HSLColor
from colormath.color_objects import XYZColor, BaseRGBColor, HSVColor, HSLColor, \
AdobeRGBColor, BT2020Color, sRGBColor


class GraphConversionManagerTestCase(unittest.TestCase):
Expand Down Expand Up @@ -52,3 +55,17 @@ def test_conversion_validity(self):
# Otherwise check that all the conversion functions math up
for a, b in zip(path[:-1], path[1:]):
self.assertEqual(a.target_type, b.start_type)

def test_transfer_functions(self):
"""
Tests the transfer functions of the various RGB colorspaces.
"""

for colorspace in (AdobeRGBColor, BT2020Color, sRGBColor):
for a in (0.0, 0.01, 0.18, 1.0):
RGB = [a] * 3
np.testing.assert_allclose(
XYZ_to_RGB(RGB_to_XYZ(colorspace(*RGB)), colorspace).get_value_tuple(),
RGB,
rtol=1e-5,
atol=1e-5)

0 comments on commit 89862ba

Please sign in to comment.