2 # -*- coding: utf-8 -*-
5 Implements support for general colorspaces conversions and transfer functions.
8 from __future__ import division
13 import PyOpenColorIO as ocio
15 import aces_ocio.generate_lut as genlut
16 from aces_ocio.colorspaces import aces
17 from aces_ocio.utilities import ColorSpace, mat44_from_mat33
19 __author__ = 'ACES Developers'
20 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
22 __maintainer__ = 'ACES Developers'
23 __email__ = 'aces@oscars.org'
24 __status__ = 'Production'
26 __all__ = ['create_matrix_colorspace',
27 'create_transfer_colorspace',
28 'create_matrix_plus_transfer_colorspace',
29 'transfer_function_sRGB_to_linear',
30 'transfer_function_Rec709_to_linear',
31 'transfer_function_Rec2020_10bit_to_linear',
32 'transfer_function_Rec2020_12bit_to_linear',
33 'transfer_function_Rec1886_to_linear',
38 # -------------------------------------------------------------------------
40 # -------------------------------------------------------------------------
41 def create_matrix_colorspace(name='matrix',
42 from_reference_values=None,
43 to_reference_values=None,
46 Creates a ColorSpace that only uses *Matrix Transforms*
51 Aliases for this colorspace
52 from_reference_values : list of matrices
53 List of matrices to convert from the reference colorspace to this space
54 to_reference_values : list of matrices
55 List of matrices to convert to the reference colorspace from this space
56 aliases : list of str, optional
57 Aliases for this colorspace
62 A *Matrix Transform*-based ColorSpace
65 if from_reference_values is None:
66 from_reference_values = []
68 if to_reference_values is None:
69 to_reference_values = []
75 cs.description = 'The %s color space' % name
77 cs.equality_group = name
81 # A linear space needs allocation variables.
82 cs.allocation_type = ocio.Constants.ALLOCATION_LG2
83 cs.allocation_vars = [-8, 5, 0.00390625]
85 cs.to_reference_transforms = []
86 if to_reference_values:
87 for matrix in to_reference_values:
88 cs.to_reference_transforms.append({
90 'matrix': mat44_from_mat33(matrix),
91 'direction': 'forward'})
93 cs.from_reference_transforms = []
94 if from_reference_values:
95 for matrix in from_reference_values:
96 cs.from_reference_transforms.append({
98 'matrix': mat44_from_mat33(matrix),
99 'direction': 'forward'})
104 # -------------------------------------------------------------------------
105 # *Transfer Function Transform*
106 # -------------------------------------------------------------------------
107 def create_transfer_colorspace(name='transfer',
108 transfer_function_name='transfer_function',
109 transfer_function=lambda x: x,
110 lut_directory='/tmp',
111 lut_resolution_1d=1024,
114 Creates a ColorSpace that only uses transfer functions encoded as 1D LUTs
119 Aliases for this colorspace
120 transfer_function_name : str, optional
121 The name of the transfer function
122 transfer_function : function, optional
123 The transfer function to be evaluated
124 lut_directory : str or unicode
125 The directory to use when generating LUTs
126 lut_resolution_1d : int
127 The resolution of generated 1D LUTs
128 aliases : list of str
129 Aliases for this colorspace
134 A *LUT1D Transform*-based ColorSpace representing a transfer function
140 cs = ColorSpace(name)
141 cs.description = 'The %s color space' % name
143 cs.equality_group = name
144 cs.family = 'Utility'
147 # A linear space needs allocation variables.
148 cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM
149 cs.allocation_vars = [0, 1]
151 # Sampling the transfer function.
152 data = array.array('f', '\0' * lut_resolution_1d * 4)
153 for c in range(lut_resolution_1d):
154 data[c] = transfer_function(c / (lut_resolution_1d - 1))
156 # Writing the sampled data to a *LUT*.
157 lut = '%s_to_linear.spi1d' % transfer_function_name
159 os.path.join(lut_directory, lut),
166 # Creating the *to_reference* transforms.
167 cs.to_reference_transforms = []
168 cs.to_reference_transforms.append({
171 'interpolation': 'linear',
172 'direction': 'forward'})
174 # Creating the *from_reference* transforms.
175 cs.from_reference_transforms = []
180 # -------------------------------------------------------------------------
181 # *Transfer Function + Matrix Transform*
182 # -------------------------------------------------------------------------
183 def create_matrix_plus_transfer_colorspace(
184 name='matrix_plus_transfer',
185 transfer_function_name='transfer_function',
186 transfer_function=lambda x: x,
187 lut_directory='/tmp',
188 lut_resolution_1d=1024,
189 from_reference_values=None,
190 to_reference_values=None,
193 Creates a ColorSpace that uses transfer functions encoded as 1D LUTs and
199 Aliases for this colorspace
200 transfer_function_name : str, optional
201 The name of the transfer function
202 transfer_function : function, optional
203 The transfer function to be evaluated
204 lut_directory : str or unicode
205 The directory to use when generating LUTs
206 lut_resolution_1d : int
207 The resolution of generated 1D LUTs
208 from_reference_values : list of matrices
209 List of matrices to convert from the reference colorspace to this space
210 to_reference_values : list of matrices
211 List of matrices to convert to the reference colorspace from this space
212 aliases : list of str
213 Aliases for this colorspace
218 A *Matrx and LUT1D Transform*-based ColorSpace representing a transfer
222 if from_reference_values is None:
223 from_reference_values = []
225 if to_reference_values is None:
226 to_reference_values = []
231 cs = ColorSpace(name)
232 cs.description = 'The %s color space' % name
234 cs.equality_group = name
235 cs.family = 'Utility'
238 # A linear space needs allocation variables.
239 cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM
240 cs.allocation_vars = [0, 1]
242 # Sampling the transfer function.
243 data = array.array('f', '\0' * lut_resolution_1d * 4)
244 for c in range(lut_resolution_1d):
245 data[c] = transfer_function(c / (lut_resolution_1d - 1))
247 # Writing the sampled data to a *LUT*.
248 lut = '%s_to_linear.spi1d' % transfer_function_name
250 os.path.join(lut_directory, lut),
257 # Creating the *to_reference* transforms.
258 cs.to_reference_transforms = []
259 if to_reference_values:
260 cs.to_reference_transforms.append({
263 'interpolation': 'linear',
264 'direction': 'forward'})
266 for matrix in to_reference_values:
267 cs.to_reference_transforms.append({
269 'matrix': mat44_from_mat33(matrix),
270 'direction': 'forward'})
272 # Creating the *from_reference* transforms.
273 cs.from_reference_transforms = []
274 if from_reference_values:
275 for matrix in from_reference_values:
276 cs.from_reference_transforms.append({
278 'matrix': mat44_from_mat33(matrix),
279 'direction': 'forward'})
281 cs.from_reference_transforms.append({
284 'interpolation': 'linear',
285 'direction': 'inverse'})
290 # Transfer functions for standard colorspaces.
291 def transfer_function_sRGB_to_linear(v):
293 The sRGB (IEC 61966-2-1) transfer function
298 The normalized value to pass through the function
312 return pow(((v + (a - 1)) / a), g)
315 def transfer_function_Rec709_to_linear(v):
317 The Rec.709 transfer function
322 The normalized value to pass through the function
337 return pow(((v + (a - 1)) / a), g)
340 def transfer_function_Rec2020_10bit_to_linear(v):
342 The Rec.2020 10-bit transfer function
347 The normalized value to pass through the function
362 return pow(((v + (a - 1)) / a), g)
365 def transfer_function_Rec2020_12bit_to_linear(v):
367 The Rec.2020 12-bit transfer function
372 The normalized value to pass through the function
387 return pow(((v + (a - 1)) / a), g)
390 def transfer_function_Rec1886_to_linear(v):
392 The Rec.1886 transfer function
397 The normalized value to pass through the function
408 # Ignoring legal to full scaling for now.
409 # v = (1023.0*v - 64.0)/876.0
411 t = pow(Lw, 1.0 / g) - pow(Lb, 1.0 / g)
413 b = pow(Lb, 1.0 / g) / t
415 return a * pow(max((v + b), 0.0), g)
418 def create_colorspaces(lut_directory,
421 Generates the colorspace conversions.
425 lut_directory : str or unicode
426 The directory to use when generating LUTs
427 lut_resolution_1d : int
428 The resolution of generated 1D LUTs
433 A list of colorspaces for general colorspaces and encodings
438 # -------------------------------------------------------------------------
440 # -------------------------------------------------------------------------
441 cs = create_matrix_colorspace('XYZ - D60',
442 to_reference_values=[aces.ACES_XYZ_TO_AP0],
443 from_reference_values=[aces.ACES_AP0_TO_XYZ],
444 aliases=['lin_xyz_d60'])
445 colorspaces.append(cs)
447 # -------------------------------------------------------------------------
449 # -------------------------------------------------------------------------
450 # *ACES* to *Linear*, *P3D60* primaries
451 XYZ_to_P3D60 = [2.4027414142, -0.8974841639, -0.3880533700,
452 -0.8325796487, 1.7692317536, 0.0237127115,
453 0.0388233815, -0.0824996856, 1.0363685997]
455 cs = create_matrix_colorspace(
457 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_P3D60],
458 aliases=['lin_p3d60'])
459 colorspaces.append(cs)
461 # -------------------------------------------------------------------------
463 # -------------------------------------------------------------------------
464 # *ACES* to *Linear*, *P3DCI* primaries, using Bradford chromatic
466 XYZ_to_P3DCI = [2.66286135, -1.11031783, -0.42271635,
467 -0.82282376, 1.75861704, 0.02502194,
468 0.03932561, -0.08383448, 1.0372175]
470 cs = create_matrix_colorspace(
472 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_P3DCI],
473 aliases=['lin_p3dci'])
474 colorspaces.append(cs)
476 # -------------------------------------------------------------------------
478 # -------------------------------------------------------------------------
479 # *sRGB* and *Rec 709* use the same gamut.
481 # *ACES* to *Linear*, *Rec. 709* primaries, D65 white point, using
482 # Bradford chromatic adaptation
483 XYZ_to_Rec709 = [3.20959735, -1.55742955, -0.49580497,
484 -0.97098887, 1.88517118, 0.03948941,
485 0.05971934, -0.21010444, 1.14312482]
487 cs = create_matrix_colorspace(
489 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
490 aliases=['lin_srgb'])
491 colorspaces.append(cs)
493 # *Linear* to *sRGB* Transfer Function*
494 cs = create_transfer_colorspace(
497 transfer_function_sRGB_to_linear,
500 aliases=['crv_srgb'])
501 colorspaces.append(cs)
503 # *ACES* to *sRGB* Primaries + Transfer Function*
504 cs = create_matrix_plus_transfer_colorspace(
507 transfer_function_sRGB_to_linear,
510 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
511 aliases=['srgb_texture'])
512 colorspaces.append(cs)
514 # -------------------------------------------------------------------------
516 # -------------------------------------------------------------------------
517 # *sRGB* and *Rec 709* use the same gamut.
518 cs = create_matrix_colorspace(
520 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
521 aliases=['lin_rec709'])
522 colorspaces.append(cs)
524 # *Linear* to *Rec. 709* Transfer Function*
525 cs = create_transfer_colorspace(
528 transfer_function_Rec709_to_linear,
531 aliases=['crv_rec709'])
532 colorspaces.append(cs)
534 # *ACES* to *Rec. 709* Primaries + Transfer Function*
535 cs = create_matrix_plus_transfer_colorspace(
538 transfer_function_Rec709_to_linear,
541 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
542 aliases=['rec709_camera'])
543 colorspaces.append(cs)
545 # -------------------------------------------------------------------------
547 # -------------------------------------------------------------------------
548 # *ACES* to *Linear*, *Rec. 2020* primaries, D65 white point, using
549 # Bradford chromatic adaptation
550 XYZ_to_Rec2020 = [1.69662619, -0.36551982, -0.24857099,
551 -0.67039877, 1.62348187, 0.01503821,
552 0.02063163, -0.04775634, 1.01910818]
554 cs = create_matrix_colorspace(
556 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
557 aliases=['lin_rec2020'])
558 colorspaces.append(cs)
560 # *Linear* to *Rec. 2020 10 bit* Transfer Function*
561 cs = create_transfer_colorspace(
564 transfer_function_Rec2020_10bit_to_linear,
567 aliases=['crv_rec2020'])
568 colorspaces.append(cs)
570 # *ACES* to *Rec. 2020 10 bit* Primaries + Transfer Function*
571 cs = create_matrix_plus_transfer_colorspace(
574 transfer_function_Rec2020_10bit_to_linear,
577 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
578 aliases=['rec2020_camera'])
579 colorspaces.append(cs)
581 # -------------------------------------------------------------------------
583 # -------------------------------------------------------------------------
584 # *Linear* to *Rec.1886* Transfer Function*
585 cs = create_transfer_colorspace(
588 transfer_function_Rec1886_to_linear,
591 aliases=['crv_rec1886'])
592 colorspaces.append(cs)
594 # *ACES* to *Rec. 709* Primaries + Transfer Function*
595 cs = create_matrix_plus_transfer_colorspace(
598 transfer_function_Rec1886_to_linear,
601 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
602 aliases=['rec709_display'])
603 colorspaces.append(cs)
605 # *ACES* to *Rec. 2020* Primaries + Transfer Function*
606 cs = create_matrix_plus_transfer_colorspace(
607 'Rec.2020 - Display',
609 transfer_function_Rec1886_to_linear,
612 from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
613 aliases=['rec2020_display'])
614 colorspaces.append(cs)
616 # -------------------------------------------------------------------------
618 # -------------------------------------------------------------------------
619 # *ACES* to *Linear*, *Pro Photo* primaries, D50 white point, using
620 # Bradford chromatic adaptation
621 AP0_to_RIMM = [1.2412367771, -0.1685692287, -0.0726675484,
622 0.0061203066, 1.083151174, -0.0892714806,
623 -0.0032853314, 0.0099796402, 0.9933056912]
625 cs = create_matrix_colorspace(
626 'Linear - RIMM ROMM (ProPhoto)',
627 from_reference_values=[AP0_to_RIMM],
628 aliases=['lin_prophoto', 'lin_rimm'])
629 colorspaces.append(cs)
631 # -------------------------------------------------------------------------
633 # -------------------------------------------------------------------------
634 # *ACES* to *Linear*, *Adobe RGB* primaries, D65 white point, using
635 # Bradford chromatic adaptation
636 AP0_to_ADOBERGB = [1.7245603168, -0.4199935942, -0.3045667227,
637 -0.2764799142, 1.3727190877, -0.0962391734,
638 -0.0261255258, -0.0901747807, 1.1163003065]
640 cs = create_matrix_colorspace(
641 'Linear - Adobe RGB',
642 from_reference_values=[AP0_to_ADOBERGB],
643 aliases=['lin_adobergb'])
644 colorspaces.append(cs)
646 # -------------------------------------------------------------------------
647 # Adobe Wide Gamut RGB
648 # -------------------------------------------------------------------------
649 # *ACES* to *Linear*, *Adobe Wide Gamut RGB* primaries, D50 white point,
650 # using Bradford chromatic adaptation
651 AP0_to_ADOBEWIDEGAMUT = [1.3809814778, -0.1158594573, -0.2651220205,
652 0.0057015535, 1.0402949043, -0.0459964578,
653 -0.0038908746, -0.0597091815, 1.0636000561]
655 cs = create_matrix_colorspace(
656 'Linear - Adobe Wide Gamut RGB',
657 from_reference_values=[AP0_to_ADOBEWIDEGAMUT],
658 aliases=['lin_adobewidegamutrgb'])
659 colorspaces.append(cs)
666 Creates the *raw* color space
675 *raw* and all its identifying information
677 # *Raw* utility space
679 raw = ColorSpace(name)
680 raw.description = 'The %s color space' % name
681 raw.aliases = ['raw']
682 raw.equality_group = name
683 raw.family = 'Utility'