Skip to content
This repository was archived by the owner on Nov 22, 2019. It is now read-only.

Commit 8e92c1f

Browse files
author
Marie
committed
Merge branch 'develop'
2 parents 82f832f + 0cb7599 commit 8e92c1f

File tree

6 files changed

+252
-84
lines changed

6 files changed

+252
-84
lines changed

lutLab/rgb_to_xyz_matrix.py renamed to lutLab/rgb_to_rgb_matrix.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55
.. moduleauthor:: `Marie FETIVEAU <github.com/mfe>`_
66
77
"""
8-
__version__ = "0.4"
9-
from utils.colors_helper import get_RGB_to_XYZ_matrix, get_primaries_matrix
8+
__version__ = "0.5"
9+
from utils.colors_helper import get_RGB_to_RGB_matrix, get_colorspace_matrix
1010
from utils.colorspaces import COLORSPACES
1111
from utils.private_colorspaces import PRIVATE_COLORSPACES
1212
from utils.matrix_helper import matrix_to_string, matrix_to_spimtx_string
1313
import argparse
1414
import sys
1515
from utils import debug_helper
1616

17+
XYZ_colorspace = 'XYZ'
18+
1719

1820
class RGBToXYZMatrixException(Exception):
1921
"""Module custom exception
@@ -25,7 +27,7 @@ class RGBToXYZMatrixException(Exception):
2527
pass
2628

2729

28-
def display_matrix(colorspace, matrix_format, primaries_only=False):
30+
def display_matrix(in_colorspace, out_colorspace, matrix_format, primaries_only=False):
2931
"""Display RGB to XYZ matrix corresponding to colorspace and formatting
3032
as format
3133
@@ -35,34 +37,28 @@ def display_matrix(colorspace, matrix_format, primaries_only=False):
3537
matrix_format (str): output format. simple, matrix, spimtx.
3638
3739
"""
38-
try:
39-
colorspace_obj = COLORSPACES[colorspace]
40-
except KeyError:
41-
colorspace_obj = PRIVATE_COLORSPACES[colorspace]
42-
if primaries_only:
43-
matrix = get_primaries_matrix(colorspace_obj.get_red_primaries(),
44-
colorspace_obj.get_green_primaries(),
45-
colorspace_obj.get_blue_primaries())
46-
matrix_type = "Primaries"
40+
if in_colorspace == XYZ_colorspace:
41+
if out_colorspace == XYZ_colorspace:
42+
raise AttributeError("In and out colorspaces can't be both XYZ !")
43+
matrix = get_colorspace_matrix(out_colorspace, primaries_only, inv=True)
44+
elif out_colorspace == XYZ_colorspace:
45+
matrix = get_colorspace_matrix(in_colorspace, primaries_only, inv=False)
4746
else:
48-
matrix = get_RGB_to_XYZ_matrix(colorspace_obj.get_red_primaries(),
49-
colorspace_obj.get_green_primaries(),
50-
colorspace_obj.get_blue_primaries(),
51-
colorspace_obj.get_white_point())
52-
matrix_type = "Primaries + white point"
47+
matrix = get_RGB_to_RGB_matrix(in_colorspace, out_colorspace, primaries_only)
48+
5349
if matrix_format == 'simple':
5450
matrix_dump = matrix_to_string(matrix)
55-
inv_matrix_dump = matrix_to_string(matrix.I)
5651
elif matrix_format == 'spimtx':
5752
matrix_dump = matrix_to_spimtx_string(matrix)
58-
inv_matrix_dump = matrix_to_spimtx_string(matrix.I)
5953
else:
6054
matrix_dump = "{0}".format(matrix)
61-
inv_matrix_dump = "{0}".format(matrix.I)
62-
print "{0} to XYZ matrix ({1}, {2} output):\n".format(colorspace, matrix_type, matrix_format)
55+
56+
print "{0} to {1} matrix ({2} {3} output):\n".format(in_colorspace,
57+
out_colorspace,
58+
primaries_only and "primaries" or
59+
"primaries + white point",
60+
matrix_format)
6361
print matrix_dump
64-
print "XYZ to {0} matrix ({1}, {2} output):\n".format(colorspace, matrix_type, matrix_format)
65-
print inv_matrix_dump
6662

6763

6864
def __get_options():
@@ -73,15 +69,20 @@ def __get_options():
7369
7470
"""
7571
# Define parser
76-
description = 'Print RGB -> XYZ matrix'
72+
description = 'Print RGB -> RGB matrix'
7773
parser = argparse.ArgumentParser(description=description)
7874
# RGB colorspace
79-
parser.add_argument("-c", "--colorspace",
75+
colorspaces = sorted(COLORSPACES.keys() + PRIVATE_COLORSPACES.keys() + [XYZ_colorspace])
76+
parser.add_argument("-in", "--in-colorspace",
8077
help=("Input RGB Colorspace."),
8178
type=str,
82-
choices=sorted(COLORSPACES.keys() +
83-
PRIVATE_COLORSPACES.keys()),
84-
default='Rec709')
79+
choices=colorspaces,
80+
required=True)
81+
parser.add_argument("-out", "--out-colorspace",
82+
help=("Output RGB Colorspace."),
83+
type=str,
84+
choices=colorspaces,
85+
required=True)
8586
# Get primarie matrix only
8687
parser.add_argument("-po", "--primaries-only",
8788
help="Primaries matrix only, doesn't include white point.",
@@ -109,4 +110,4 @@ def __get_options():
109110

110111
if __name__ == '__main__':
111112
ARGS = __get_options()
112-
display_matrix(ARGS.colorspace, ARGS.format, ARGS.primaries_only)
113+
display_matrix(ARGS.in_colorspace, ARGS.out_colorspace, ARGS.format, ARGS.primaries_only)

test/colorspaces_test.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
33
"""
44
import unittest
5+
import numpy
56
from utils.colorspaces import (REC709, ALEXALOGCV3, WIDEGAMUT, REC2020_12B,
67
ACESLOG_32f, sRGB, SGAMUTSLOG, SGAMUTSLOG2,
7-
SGAMUTSLOG3,
8+
SGAMUTSLOG3, ACESCC, ACESPROXY_10i
89
)
10+
from utils.colors_helper import apply_matrix, get_RGB_to_RGB_matrix, get_colorspace_matrix
911

1012

1113
class ColorspaceTest(unittest.TestCase):
@@ -16,27 +18,77 @@ def test_gradation(self):
1618
""" Test encode + decode
1719
1820
"""
21+
1922
colorspace_to_test = [REC709,
2023
ALEXALOGCV3,
2124
WIDEGAMUT,
2225
REC2020_12B,
2326
ACESLOG_32f,
27+
ACESCC,
2428
sRGB,
2529
SGAMUTSLOG,
2630
SGAMUTSLOG2,
2731
SGAMUTSLOG3,
2832
]
29-
delta = 0.000000000000001
3033
for space in colorspace_to_test:
3134
name = space.__class__.__name__
3235
for value in [0.0, 1.0, 0.5]:
3336
res = space.decode_gradation(space.encode_gradation(value))
34-
diff = abs(res - value)
3537
message = ("{0} gradations not transparent ! "
3638
"in: {1:8f} out: {2:8f}").format(name,
3739
value,
3840
res)
39-
self.assert_(diff < delta, message)
41+
self.assertTrue(numpy.isclose(res, value, atol=0.00000000000001), message)
42+
43+
def test_aces_proxy(self):
44+
"""Test ACES proxy (matrix + encoding)
45+
46+
"""
47+
ref_colors = [[0.001184464, 64.0, 0.001185417],
48+
[222.875, 940.0, 222.860944204]
49+
]
50+
ACES_to_proxy_matrix = get_RGB_to_RGB_matrix('ACES', 'ACESproxy_10')
51+
proxy_to_ACES_matrix = get_RGB_to_RGB_matrix('ACESproxy_10', 'ACES')
52+
for color in ref_colors:
53+
aces_proxy_lin = apply_matrix(ACES_to_proxy_matrix, [color[0]]*3)[2]
54+
aces_proxy = ACESPROXY_10i._encode_gradation(aces_proxy_lin)
55+
self.assertEqual(aces_proxy, color[1])
56+
aces_proxy_lin = ACESPROXY_10i._decode_gradation(aces_proxy)
57+
aces = apply_matrix(proxy_to_ACES_matrix, [aces_proxy_lin]*3)[0]
58+
message = ("ACESproxy not valid ! "
59+
"in: {0} out: {1}").format(aces, color[2])
60+
self.assertTrue(numpy.isclose(aces, color[2], atol=0.000001), message)
61+
62+
def test_colorspace_matrices(self):
63+
"""Test matrix conversions
64+
65+
"""
66+
ACES_to_XYZ = [[0.95255239593818575, 0.0, 9.3678631660468553e-05],
67+
[0.34396644976507507, 0.72816609661348575, -0.072132546378560786],
68+
[0.0, 0.0, 1.0088251843515859]]
69+
70+
XYZ_to_ACES = [[1.0498110174979742, 0.0, -9.7484540579252874e-05],
71+
[-0.49590302307731976, 1.3733130458157063, 0.098240036057309993],
72+
[0.0, 0.0, 0.99125201820049902]]
73+
self.assertEqual(ACES_to_XYZ, get_colorspace_matrix("ACES").tolist())
74+
self.assertEqual(XYZ_to_ACES, get_colorspace_matrix("ACES", inv=True).tolist())
75+
76+
def test_rgb_to_rgb_matrix(self):
77+
"""Test rgb to rgb matrix
78+
79+
"""
80+
ACES_to_proxy_matrix = get_RGB_to_RGB_matrix('ACES', 'ACEScc')
81+
ref_value = numpy.matrix([[1.4514393161, -0.2365107469, -0.2149285693],
82+
[-0.0765537734, 1.1762296998, -0.0996759264],
83+
[0.0083161484, -0.0060324498, 0.9977163014]])
84+
self.assertTrue(numpy.allclose(ACES_to_proxy_matrix, ref_value),
85+
"Processed ACES to ACEScc matrix is different from reference ! ")
86+
ACES_to_Rec2020_matrix = get_RGB_to_RGB_matrix("ACES", 'Rec2020_12bits')
87+
ref_value = [[1.5128613853114372, -0.2589874063019148, -0.22978603267468098],
88+
[-0.079036464595355627, 1.1770668323294038, -0.10075565571179679],
89+
[0.0020912324769753847, -0.03114411050570343, 0.95350416068074784]]
90+
self.assertTrue(numpy.allclose(ACES_to_Rec2020_matrix, ref_value),
91+
"Processed ACES to Rec2020 matrix is different from reference ! ")
4092

4193

4294
if __name__ == "__main__":

test/general_test.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import tempfile
88
import shutil
9-
from lutLab import rgb_to_xyz_matrix
9+
from utils.colors_helper import get_colorspace_matrix, get_RGB_to_RGB_matrix
1010

1111

1212
DISPLAY = False
@@ -45,13 +45,6 @@ def test_lut_3d(self):
4545
# TODO
4646
pass
4747

48-
def test_rgb_to_matrix(self):
49-
"""Display rgb matrix
50-
51-
"""
52-
rgb_to_xyz_matrix.display_matrix('Rec709', 'spimtx')
53-
# TODO test values
54-
5548
def tearDown(self):
5649
# Remove test directory
5750
shutil.rmtree(self.tmp_dir)

utils/colors_helper.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
.. moduleauthor:: `Marie FETIVEAU <github.com/mfe>`_
44
55
"""
6-
__version__ = "0.3"
6+
__version__ = "0.4"
77
import math
88
import numpy
99

@@ -121,11 +121,8 @@ def apply_matrix(matrix, triplet):
121121
.[float, float, float]
122122
123123
"""
124-
a, b, c = triplet
125-
ap = matrix.item(0, 0) * a + matrix.item(0, 1) * b + matrix.item(0, 2) * c
126-
bp = matrix.item(1, 0) * a + matrix.item(1, 1) * b + matrix.item(1, 2) * c
127-
cp = matrix.item(2, 0) * a + matrix.item(2, 1) * b + matrix.item(2, 2) * c
128-
return [ap, bp, cp]
124+
values = numpy.matrix(triplet)
125+
return numpy.dot(matrix, values.T).T.tolist()[0]
129126

130127

131128
def clamp_value(value, max_value=1.0, min_value=0.0):
@@ -307,3 +304,60 @@ def get_XYZ_to_RGB_matrix(xy_red, xy_green, xy_blue, xy_white):
307304
308305
"""
309306
return get_RGB_to_XYZ_matrix(xy_red, xy_green, xy_blue, xy_white).I
307+
308+
309+
def get_colorspace_matrix(colorspace, primaries_only=False, inv=False):
310+
"""Return a colorspace RGB to XYZ matrix.
311+
312+
Args:
313+
colorspace (str): input colorspace.
314+
315+
Kwargs:
316+
primaries_only (bool): primaries matrix only, doesn't include white point.
317+
inv (bool): return XYZ to RGB matrix.
318+
319+
Returns:
320+
.numpy.matrix (3x3)
321+
322+
"""
323+
from utils.colorspaces import COLORSPACES
324+
from utils.private_colorspaces import PRIVATE_COLORSPACES
325+
colorspace_obj = COLORSPACES.get(colorspace) or PRIVATE_COLORSPACES.get(colorspace)
326+
327+
if not colorspace_obj:
328+
raise NotImplementedError("Could not find {0} colorspace".format(colorspace))
329+
330+
if primaries_only:
331+
matrix = get_primaries_matrix(colorspace_obj.get_red_primaries(),
332+
colorspace_obj.get_green_primaries(),
333+
colorspace_obj.get_blue_primaries())
334+
else:
335+
matrix = get_RGB_to_XYZ_matrix(colorspace_obj.get_red_primaries(),
336+
colorspace_obj.get_green_primaries(),
337+
colorspace_obj.get_blue_primaries(),
338+
colorspace_obj.get_white_point())
339+
if inv:
340+
return matrix.I
341+
return matrix
342+
343+
344+
def get_RGB_to_RGB_matrix(in_colorspace, out_colorspace, primaries_only=False):
345+
"""Return RGB to RGB conversion matrix.
346+
347+
Args:
348+
in_colorspace (str): input colorspace.
349+
out_colorspace (str): output colorspace.
350+
351+
Kwargs:
352+
primaries_only (bool): primaries matrix only, doesn't include white point.
353+
354+
Returns:
355+
.numpy.matrix (3x3)
356+
357+
"""
358+
# Get colorspace in to XYZ matrix
359+
in_matrix = get_colorspace_matrix(in_colorspace, primaries_only)
360+
# Get XYZ to colorspace out matrix
361+
out_matrix = get_colorspace_matrix(out_colorspace, primaries_only, inv=True)
362+
# Return scalar product of the 2 matrices
363+
return numpy.dot(out_matrix, in_matrix)

0 commit comments

Comments
 (0)