c69fcac17a836c66455a6a6f3b6969622d3eaae4
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / create_aces_colorspaces.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Implements support for *ACES* colorspaces conversions and transfer functions.
6 """
7
8 from __future__ import division
9
10 import math
11 import numpy
12 import os
13 import pprint
14 import string
15 import shutil
16
17 import PyOpenColorIO as ocio
18
19 from aces_ocio.generate_lut import (
20     generate_1d_LUT_from_CTL,
21     generate_3d_LUT_from_CTL,
22     write_SPI_1d)
23 from aces_ocio.utilities import (
24     ColorSpace,
25     mat44_from_mat33,
26     sanitize,
27     compact)
28
29
30 __author__ = 'ACES Developers'
31 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
32 __license__ = ''
33 __maintainer__ = 'ACES Developers'
34 __email__ = 'aces@oscars.org'
35 __status__ = 'Production'
36
37 __all__ = ['ACES_AP1_TO_AP0',
38            'ACES_AP0_TO_XYZ',
39            'create_ACES',
40            'create_ACEScc',
41            'create_ACESproxy',
42            'create_ACEScg',
43            'create_ADX',
44            'create_ACES_LMT',
45            'create_ACES_RRT_plus_ODT',
46            'create_generic_log',
47            'create_LMTs',
48            'create_ODTs',
49            'get_transform_info',
50            'get_ODTs_info',
51            'get_LMTs_info',
52            'create_colorspaces']
53
54 # Matrix converting *ACES AP1* primaries to *AP0*.
55 ACES_AP1_TO_AP0 = [0.6954522414, 0.1406786965, 0.1638690622,
56                    0.0447945634, 0.8596711185, 0.0955343182,
57                    -0.0055258826, 0.0040252103, 1.0015006723]
58
59 # Matrix converting *ACES AP0* primaries to *XYZ*.
60 ACES_AP0_TO_XYZ = [0.9525523959, 0.0000000000, 0.0000936786,
61                    0.3439664498, 0.7281660966, -0.0721325464,
62                    0.0000000000, 0.0000000000, 1.0088251844]
63
64
65 def create_ACES():
66     """
67     Object description.
68
69     Parameters
70     ----------
71     parameter : type
72         Parameter description.
73
74     Returns
75     -------
76     type
77          Return value description.
78     """
79
80     # Defining the reference colorspace.
81     aces2065_1 = ColorSpace('ACES2065-1')
82     aces2065_1.description = (
83         'The Academy Color Encoding System reference color space')
84     aces2065_1.equality_group = ''
85     aces2065_1.aliases = ["lin_ap0", "aces"]
86     aces2065_1.family = 'ACES'
87     aces2065_1.is_data = False
88     aces2065_1.allocation_type = ocio.Constants.ALLOCATION_LG2
89     aces2065_1.allocation_vars = [-15, 6]
90
91     return aces2065_1
92
93
94 def create_ACEScc(aces_ctl_directory,
95                   lut_directory,
96                   lut_resolution_1d,
97                   cleanup,
98                   name='ACEScc',
99                   min_value=0,
100                   max_value=1,
101                   input_scale=1):
102     """
103     Creates the *ACEScc* colorspace.
104
105     Parameters
106     ----------
107     parameter : type
108         Parameter description.
109
110     Returns
111     -------
112     Colorspace
113          *ACEScc* colorspace.
114     """
115
116     cs = ColorSpace(name)
117     cs.description = 'The %s color space' % name
118     cs.aliases = ["acescc_ap1"]
119     cs.equality_group = ''
120     cs.family = 'ACES'
121     cs.is_data = False
122
123     ctls = [os.path.join(aces_ctl_directory,
124                          'ACEScc',
125                          'ACEScsc.ACEScc_to_ACES.a1.0.0.ctl'),
126             # This transform gets back to the *AP1* primaries.
127             # Useful as the 1d LUT is only covering the transfer function.
128             # The primaries switch is covered by the matrix below:
129             os.path.join(aces_ctl_directory,
130                          'ACEScg',
131                          'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
132     lut = '%s_to_ACES.spi1d' % name
133
134     lut = sanitize(lut)
135
136     generate_1d_LUT_from_CTL(
137         os.path.join(lut_directory, lut),
138         ctls,
139         lut_resolution_1d,
140         'float',
141         input_scale,
142         1,
143         {},
144         cleanup,
145         aces_ctl_directory,
146         min_value,
147         max_value)
148
149     cs.to_reference_transforms = []
150     cs.to_reference_transforms.append({
151         'type': 'lutFile',
152         'path': lut,
153         'interpolation': 'linear',
154         'direction': 'forward'})
155
156     # *AP1* primaries to *AP0* primaries.
157     cs.to_reference_transforms.append({
158         'type': 'matrix',
159         'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
160         'direction': 'forward'})
161
162     cs.from_reference_transforms = []
163     return cs
164
165
166 def create_ACESproxy(aces_ctl_directory,
167                      lut_directory,
168                      lut_resolution_1d,
169                      cleanup,
170                      name='ACESproxy'):
171     """
172     Creates the *ACESproxy* colorspace.
173
174     Parameters
175     ----------
176     parameter : type
177         Parameter description.
178
179     Returns
180     -------
181     Colorspace
182          *ACESproxy* colorspace.
183     """
184
185     cs = ColorSpace(name)
186     cs.description = 'The %s color space' % name
187     cs.aliases = ["acesproxy_ap1"]
188     cs.equality_group = ''
189     cs.family = 'ACES'
190     cs.is_data = False
191
192     ctls = [os.path.join(aces_ctl_directory,
193                          'ACESproxy',
194                          'ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl'),
195             # This transform gets back to the *AP1* primaries.
196             # Useful as the 1d LUT is only covering the transfer function.
197             # The primaries switch is covered by the matrix below:
198             os.path.join(aces_ctl_directory,
199                          'ACEScg',
200                          'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
201     lut = '%s_to_aces.spi1d' % name
202
203     lut = sanitize(lut)
204
205     generate_1d_LUT_from_CTL(
206         os.path.join(lut_directory, lut),
207         ctls,
208         lut_resolution_1d,
209         'uint16',
210         64,
211         1,
212         {},
213         cleanup,
214         aces_ctl_directory)
215
216     cs.to_reference_transforms = []
217     cs.to_reference_transforms.append({
218         'type': 'lutFile',
219         'path': lut,
220         'interpolation': 'linear',
221         'direction': 'forward'})
222
223     # *AP1* primaries to *AP0* primaries.
224     cs.to_reference_transforms.append({
225         'type': 'matrix',
226         'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
227         'direction': 'forward'})
228
229     cs.from_reference_transforms = []
230     return cs
231
232
233 # -------------------------------------------------------------------------
234 # *ACEScg*
235 # -------------------------------------------------------------------------
236 def create_ACEScg(aces_ctl_directory,
237                   lut_directory,
238                   lut_resolution_1d,
239                   cleanup,
240                   name='ACEScg'):
241     """
242     Creates the *ACEScg* colorspace.
243
244     Parameters
245     ----------
246     parameter : type
247         Parameter description.
248
249     Returns
250     -------
251     Colorspace
252          *ACEScg* colorspace.
253     """
254
255     cs = ColorSpace(name)
256     cs.description = 'The %s color space' % name
257     cs.aliases = ["lin_ap1"]
258     cs.equality_group = ''
259     cs.family = 'ACES'
260     cs.is_data = False
261
262     cs.to_reference_transforms = []
263
264     # *AP1* primaries to *AP0* primaries.
265     cs.to_reference_transforms.append({
266         'type': 'matrix',
267         'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
268         'direction': 'forward'})
269
270     cs.from_reference_transforms = []
271     return cs
272
273
274 # -------------------------------------------------------------------------
275 # *ADX*
276 # -------------------------------------------------------------------------
277 def create_ADX(lut_directory,
278                lut_resolution_1d,
279                bit_depth=10,
280                name='ADX'):
281     """
282     Creates the *ADX* colorspace.
283
284     Parameters
285     ----------
286     parameter : type
287         Parameter description.
288
289     Returns
290     -------
291     Colorspace
292          *ADX* colorspace.
293     """
294
295     name = '%s%s' % (name, bit_depth)
296     cs = ColorSpace(name)
297     cs.description = '%s color space - used for film scans' % name
298     cs.aliases = ["adx%s" % str(bit_depth)]
299     cs.equality_group = ''
300     cs.family = 'ADX'
301     cs.is_data = False
302
303     if bit_depth == 10:
304         cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT10
305         ADX_to_CDD = [1023 / 500, 0, 0, 0,
306                       0, 1023 / 500, 0, 0,
307                       0, 0, 1023 / 500, 0,
308                       0, 0, 0, 1]
309         offset = [-95 / 500, -95 / 500, -95 / 500, 0]
310     elif bit_depth == 16:
311         cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT16
312         ADX_to_CDD = [65535 / 8000, 0, 0, 0,
313                       0, 65535 / 8000, 0, 0,
314                       0, 0, 65535 / 8000, 0,
315                       0, 0, 0, 1]
316         offset = [-1520 / 8000, -1520 / 8000, -1520 / 8000, 0]
317
318     cs.to_reference_transforms = []
319
320     # Converting from *ADX* to *Channel-Dependent Density*.
321     cs.to_reference_transforms.append({
322         'type': 'matrix',
323         'matrix': ADX_to_CDD,
324         'offset': offset,
325         'direction': 'forward'})
326
327     # Convert from Channel-Dependent Density to Channel-Independent Density
328     cs.to_reference_transforms.append({
329         'type': 'matrix',
330         'matrix': [0.75573, 0.22197, 0.02230, 0,
331                    0.05901, 0.96928, -0.02829, 0,
332                    0.16134, 0.07406, 0.76460, 0,
333                    0, 0, 0, 1],
334         'direction': 'forward'})
335
336     # Copied from *Alex Fry*'s *adx_cid_to_rle.py*
337     def create_CID_to_RLE_LUT():
338
339         def interpolate_1D(x, xp, fp):
340             return numpy.interp(x, xp, fp)
341
342         LUT_1D_xp = [-0.190000000000000,
343                      0.010000000000000,
344                      0.028000000000000,
345                      0.054000000000000,
346                      0.095000000000000,
347                      0.145000000000000,
348                      0.220000000000000,
349                      0.300000000000000,
350                      0.400000000000000,
351                      0.500000000000000,
352                      0.600000000000000]
353
354         LUT_1D_fp = [-6.000000000000000,
355                      -2.721718645000000,
356                      -2.521718645000000,
357                      -2.321718645000000,
358                      -2.121718645000000,
359                      -1.921718645000000,
360                      -1.721718645000000,
361                      -1.521718645000000,
362                      -1.321718645000000,
363                      -1.121718645000000,
364                      -0.926545676714876]
365
366         REF_PT = ((7120 - 1520) / 8000 * (100 / 55) -
367                   math.log(0.18, 10))
368
369         def cid_to_rle(x):
370             if x <= 0.6:
371                 return interpolate_1D(x, LUT_1D_xp, LUT_1D_fp)
372             return (100 / 55) * x - REF_PT
373
374         def fit(value, from_min, from_max, to_min, to_max):
375             if from_min == from_max:
376                 raise ValueError('from_min == from_max')
377             return (value - from_min) / (from_max - from_min) * (
378                 to_max - to_min) + to_min
379
380         num_samples = 2 ** 12
381         domain = (-0.19, 3)
382         data = []
383         for i in xrange(num_samples):
384             x = i / (num_samples - 1)
385             x = fit(x, 0, 1, domain[0], domain[1])
386             data.append(cid_to_rle(x))
387
388         lut = 'ADX_CID_to_RLE.spi1d'
389         write_SPI_1d(os.path.join(lut_directory, lut),
390                      domain[0],
391                      domain[1],
392                      data,
393                      num_samples, 1)
394
395         return lut
396
397     # Converting *Channel Independent Density* values to
398     # *Relative Log Exposure* values.
399     lut = create_CID_to_RLE_LUT()
400     cs.to_reference_transforms.append({
401         'type': 'lutFile',
402         'path': lut,
403         'interpolation': 'linear',
404         'direction': 'forward'})
405
406     # Converting *Relative Log Exposure* values to
407     # *Relative Exposure* values.
408     cs.to_reference_transforms.append({
409         'type': 'log',
410         'base': 10,
411         'direction': 'inverse'})
412
413     # Convert *Relative Exposure* values to *ACES* values.
414     cs.to_reference_transforms.append({
415         'type': 'matrix',
416         'matrix': [0.72286, 0.12630, 0.15084, 0,
417                    0.11923, 0.76418, 0.11659, 0,
418                    0.01427, 0.08213, 0.90359, 0,
419                    0, 0, 0, 1],
420         'direction': 'forward'})
421
422     cs.from_reference_transforms = []
423     return cs
424
425
426 def create_ACES_LMT(lmt_name,
427                     lmt_values,
428                     shaper_info,
429                     aces_ctl_directory,
430                     lut_directory,
431                     lut_resolution_1d=1024,
432                     lut_resolution_3d=64,
433                     cleanup=True,
434                     aliases=None):
435     """
436     Creates the *ACES LMT* colorspace.
437
438     Parameters
439     ----------
440     parameter : type
441         Parameter description.
442
443     Returns
444     -------
445     Colorspace
446          *ACES LMT* colorspace.
447     """
448
449     if aliases is None:
450         aliases = []
451
452     cs = ColorSpace('%s' % lmt_name)
453     cs.description = 'The ACES Look Transform: %s' % lmt_name
454     cs.aliases = aliases
455     cs.equality_group = ''
456     cs.family = 'Look'
457     cs.is_data = False
458
459     pprint.pprint(lmt_values)
460
461     # Generating the *shaper* transform.
462     (shaper_name,
463      shaper_to_ACES_CTL,
464      shaper_from_ACES_CTL,
465      shaper_input_scale,
466      shaper_params) = shaper_info
467
468     shaper_lut = '%s_to_aces.spi1d' % shaper_name
469     if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
470         ctls = [shaper_to_ACES_CTL % aces_ctl_directory]
471
472         shaper_lut = sanitize(shaper_lut)
473
474         generate_1d_LUT_from_CTL(
475             os.path.join(lut_directory, shaper_lut),
476             ctls,
477             lut_resolution_1d,
478             'float',
479             1 / shaper_input_scale,
480             1,
481             shaper_params,
482             cleanup,
483             aces_ctl_directory)
484
485     shaper_OCIO_transform = {
486         'type': 'lutFile',
487         'path': shaper_lut,
488         'interpolation': 'linear',
489         'direction': 'inverse'}
490
491     # Generating the forward transform.
492     cs.from_reference_transforms = []
493
494     if 'transformCTL' in lmt_values:
495         ctls = [shaper_to_ACES_CTL % aces_ctl_directory,
496                 os.path.join(aces_ctl_directory,
497                              lmt_values['transformCTL'])]
498         lut = '%s.%s.spi3d' % (shaper_name, lmt_name)
499
500         lut = sanitize(lut)
501
502         generate_3d_LUT_from_CTL(
503             os.path.join(lut_directory, lut),
504             ctls,
505             lut_resolution_3d,
506             'float',
507             1 / shaper_input_scale,
508             1,
509             shaper_params,
510             cleanup,
511             aces_ctl_directory)
512
513         cs.from_reference_transforms.append(shaper_OCIO_transform)
514         cs.from_reference_transforms.append({
515             'type': 'lutFile',
516             'path': lut,
517             'interpolation': 'tetrahedral',
518             'direction': 'forward'})
519
520     # Generating the inverse transform.
521     cs.to_reference_transforms = []
522
523     if 'transformCTLInverse' in lmt_values:
524         ctls = [os.path.join(aces_ctl_directory,
525                              # TODO: Investigate "odt_values" undeclared
526                              # variable.
527                              odt_values['transformCTLInverse']),
528                 shaper_from_ACES_CTL % aces_ctl_directory]
529         lut = 'Inverse.%s.%s.spi3d' % (odt_name, shaper_name)
530
531         lut = sanitize(lut)
532
533         generate_3d_LUT_from_CTL(
534             os.path.join(lut_directory, lut),
535             ctls,
536             lut_resolution_3d,
537             'half',
538             1,
539             shaper_input_scale,
540             shaper_params,
541             cleanup,
542             aces_ctl_directory)
543
544         cs.to_reference_transforms.append({
545             'type': 'lutFile',
546             'path': lut,
547             'interpolation': 'tetrahedral',
548             'direction': 'forward'})
549
550         shaper_inverse = shaper_OCIO_transform.copy()
551         shaper_inverse['direction'] = 'forward'
552         cs.to_reference_transforms.append(shaper_inverse)
553
554     return cs
555
556
557 def create_ACES_RRT_plus_ODT(odt_name,
558                              odt_values,
559                              shaper_info,
560                              aces_ctl_directory,
561                              lut_directory,
562                              lut_resolution_1d=1024,
563                              lut_resolution_3d=64,
564                              cleanup=True,
565                              aliases=None):
566     """
567     Object description.
568
569     Parameters
570     ----------
571     parameter : type
572         Parameter description.
573
574     Returns
575     -------
576     type
577          Return value description.
578     """
579
580     if aliases is None:
581         aliases = []
582
583     cs = ColorSpace('%s' % odt_name)
584     cs.description = '%s - %s Output Transform' % (
585         odt_values['transformUserNamePrefix'], odt_name)
586     cs.aliases = aliases
587     cs.equality_group = ''
588     cs.family = 'Output'
589     cs.is_data = False
590
591     pprint.pprint(odt_values)
592
593     # Generating the *shaper* transform.
594     (shaper_name,
595      shaper_to_ACES_CTL,
596      shaper_from_ACES_CTL,
597      shaper_input_scale,
598      shaper_params) = shaper_info
599
600     if 'legalRange' in odt_values:
601         shaper_params['legalRange'] = odt_values['legalRange']
602     else:
603         shaper_params['legalRange'] = 0
604
605     shaper_lut = '%s_to_aces.spi1d' % shaper_name
606     if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
607         ctls = [shaper_to_ACES_CTL % aces_ctl_directory]
608
609         shaper_lut = sanitize(shaper_lut)
610
611         generate_1d_LUT_from_CTL(
612             os.path.join(lut_directory, shaper_lut),
613             ctls,
614             lut_resolution_1d,
615             'float',
616             1 / shaper_input_scale,
617             1,
618             shaper_params,
619             cleanup,
620             aces_ctl_directory)
621
622     shaper_OCIO_transform = {
623         'type': 'lutFile',
624         'path': shaper_lut,
625         'interpolation': 'linear',
626         'direction': 'inverse'}
627
628     # Generating the *forward* transform.
629     cs.from_reference_transforms = []
630
631     if 'transformLUT' in odt_values:
632         transform_LUT_file_name = os.path.basename(
633             odt_values['transformLUT'])
634         lut = os.path.join(lut_directory, transform_LUT_file_name)
635         shutil.copy(odt_values['transformLUT'], lut)
636
637         cs.from_reference_transforms.append(shaper_OCIO_transform)
638         cs.from_reference_transforms.append({
639             'type': 'lutFile',
640             'path': transform_LUT_file_name,
641             'interpolation': 'tetrahedral',
642             'direction': 'forward'})
643     elif 'transformCTL' in odt_values:
644         ctls = [
645             shaper_to_ACES_CTL % aces_ctl_directory,
646             os.path.join(aces_ctl_directory,
647                          'rrt',
648                          'RRT.a1.0.0.ctl'),
649             os.path.join(aces_ctl_directory,
650                          'odt',
651                          odt_values['transformCTL'])]
652         lut = '%s.RRT.a1.0.0.%s.spi3d' % (shaper_name, odt_name)
653
654         lut = sanitize(lut)
655
656         generate_3d_LUT_from_CTL(
657             os.path.join(lut_directory, lut),
658             # shaperLUT,
659             ctls,
660             lut_resolution_3d,
661             'float',
662             1 / shaper_input_scale,
663             1,
664             shaper_params,
665             cleanup,
666             aces_ctl_directory)
667
668         cs.from_reference_transforms.append(shaper_OCIO_transform)
669         cs.from_reference_transforms.append({
670             'type': 'lutFile',
671             'path': lut,
672             'interpolation': 'tetrahedral',
673             'direction': 'forward'})
674
675     # Generating the *inverse* transform.
676     cs.to_reference_transforms = []
677
678     if 'transformLUTInverse' in odt_values:
679         transform_LUT_inverse_file_name = os.path.basename(
680             odt_values['transformLUTInverse'])
681         lut = os.path.join(lut_directory, transform_LUT_inverse_file_name)
682         shutil.copy(odt_values['transformLUTInverse'], lut)
683
684         cs.to_reference_transforms.append({
685             'type': 'lutFile',
686             'path': transform_LUT_inverse_file_name,
687             'interpolation': 'tetrahedral',
688             'direction': 'forward'})
689
690         shaper_inverse = shaper_OCIO_transform.copy()
691         shaper_inverse['direction'] = 'forward'
692         cs.to_reference_transforms.append(shaper_inverse)
693     elif 'transformCTLInverse' in odt_values:
694         ctls = [os.path.join(aces_ctl_directory,
695                              'odt',
696                              odt_values['transformCTLInverse']),
697                 os.path.join(aces_ctl_directory,
698                              'rrt',
699                              'InvRRT.a1.0.0.ctl'),
700                 shaper_from_ACES_CTL % aces_ctl_directory]
701         lut = 'InvRRT.a1.0.0.%s.%s.spi3d' % (odt_name, shaper_name)
702
703         lut = sanitize(lut)
704
705         generate_3d_LUT_from_CTL(
706             os.path.join(lut_directory, lut),
707             # None,
708             ctls,
709             lut_resolution_3d,
710             'half',
711             1,
712             shaper_input_scale,
713             shaper_params,
714             cleanup,
715             aces_ctl_directory)
716
717         cs.to_reference_transforms.append({
718             'type': 'lutFile',
719             'path': lut,
720             'interpolation': 'tetrahedral',
721             'direction': 'forward'})
722
723         shaper_inverse = shaper_OCIO_transform.copy()
724         shaper_inverse['direction'] = 'forward'
725         cs.to_reference_transforms.append(shaper_inverse)
726
727     return cs
728
729
730 def create_generic_log(aces_ctl_directory,
731                        lut_directory,
732                        lut_resolution_1d,
733                        cleanup,
734                        name='log',
735                        aliases=[],
736                        min_value=0,
737                        max_value=1,
738                        input_scale=1,
739                        middle_grey=0.18,
740                        min_exposure=-6,
741                        max_exposure=6.5):
742     """
743     Creates the *Generic Log* colorspace.
744
745     Parameters
746     ----------
747     parameter : type
748         Parameter description.
749
750     Returns
751     -------
752     Colorspace
753          *Generic Log* colorspace.
754     """
755
756     cs = ColorSpace(name)
757     cs.description = 'The %s color space' % name
758     cs.aliases = aliases
759     cs.equality_group = name
760     cs.family = 'Utility'
761     cs.is_data = False
762
763     ctls = [os.path.join(
764         aces_ctl_directory,
765         'utilities',
766         'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl')]
767     lut = '%s_to_aces.spi1d' % name
768
769     lut = sanitize(lut)
770
771     generate_1d_LUT_from_CTL(
772         os.path.join(lut_directory, lut),
773         ctls,
774         lut_resolution_1d,
775         'float',
776         input_scale,
777         1,
778         {'middleGrey': middle_grey,
779          'minExposure': min_exposure,
780          'maxExposure': max_exposure},
781         cleanup,
782         aces_ctl_directory,
783         min_value,
784         max_value)
785
786     cs.to_reference_transforms = []
787     cs.to_reference_transforms.append({
788         'type': 'lutFile',
789         'path': lut,
790         'interpolation': 'linear',
791         'direction': 'forward'})
792
793     cs.from_reference_transforms = []
794     return cs
795
796
797 def create_LMTs(aces_ctl_directory,
798                 lut_directory,
799                 lut_resolution_1d,
800                 lut_resolution_3d,
801                 lmt_info,
802                 shaper_name,
803                 cleanup):
804     """
805     Object description.
806
807     Parameters
808     ----------
809     parameter : type
810         Parameter description.
811
812     Returns
813     -------
814     type
815          Return value description.
816     """
817
818     colorspaces = []
819
820     # -------------------------------------------------------------------------
821     # *LMT Shaper*
822     # -------------------------------------------------------------------------
823     lmt_lut_resolution_1d = max(4096, lut_resolution_1d)
824     lmt_lut_resolution_3d = max(65, lut_resolution_3d)
825
826     # Defining the *Log 2* shaper.
827     lmt_shaper_name = 'LMT Shaper'
828     lmt_shaper_name_aliases = ['crv_lmtshaper']
829     lmt_params = {
830         'middleGrey': 0.18,
831         'minExposure': -10,
832         'maxExposure': 6.5}
833
834     lmt_shaper = create_generic_log(aces_ctl_directory,
835                                     lut_directory,
836                                     lmt_lut_resolution_1d,
837                                     cleanup,
838                                     name=lmt_shaper_name,
839                                     middle_grey=lmt_params['middleGrey'],
840                                     min_exposure=lmt_params['minExposure'],
841                                     max_exposure=lmt_params['maxExposure'],
842                                     aliases=lmt_shaper_name_aliases)
843     colorspaces.append(lmt_shaper)
844
845     shaper_input_scale_generic_log2 = 1
846
847     # *Log 2* shaper name and *CTL* transforms bundled up.
848     lmt_shaper_data = [
849         lmt_shaper_name,
850         os.path.join('%s',
851                      'utilities',
852                      'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl'),
853         os.path.join('%s',
854                      'utilities',
855                      'ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl'),
856         shaper_input_scale_generic_log2,
857         lmt_params]
858
859     sorted_LMTs = sorted(lmt_info.iteritems(), key=lambda x: x[1])
860     print(sorted_LMTs)
861     for lmt in sorted_LMTs:
862         lmt_name, lmt_values = lmt
863         lmt_aliases = ["look_%s" % compact(lmt_values['transformUserName'])]
864         cs = create_ACES_LMT(
865             lmt_values['transformUserName'],
866             lmt_values,
867             lmt_shaper_data,
868             aces_ctl_directory,
869             lut_directory,
870             lmt_lut_resolution_1d,
871             lmt_lut_resolution_3d,
872             cleanup,
873             lmt_aliases)
874         colorspaces.append(cs)
875
876     return colorspaces
877
878
879 def create_ODTs(aces_ctl_directory,
880                 lut_directory,
881                 lut_resolution_1d,
882                 lut_resolution_3d,
883                 odt_info,
884                 shaper_name,
885                 cleanup,
886                 linear_display_space,
887                 log_display_space):
888     """
889     Object description.
890
891     Parameters
892     ----------
893     parameter : type
894         Parameter description.
895
896     Returns
897     -------
898     type
899          Return value description.
900     """
901
902     colorspaces = []
903     displays = {}
904
905     # -------------------------------------------------------------------------
906     # *RRT / ODT* Shaper Options
907     # -------------------------------------------------------------------------
908     shaper_data = {}
909
910     # Defining the *Log 2* shaper.
911     log2_shaper_name = shaper_name
912     log2_shaper_name_aliases = ["crv_%s" % compact(shaper_name)]
913     log2_params = {
914         'middleGrey': 0.18,
915         'minExposure': -6,
916         'maxExposure': 6.5}
917
918     log2_shaper = create_generic_log(
919         aces_ctl_directory,
920         lut_directory,
921         lut_resolution_1d,
922         cleanup,
923         name=log2_shaper_name,
924         middle_grey=log2_params['middleGrey'],
925         min_exposure=log2_params['minExposure'],
926         max_exposure=log2_params['maxExposure'],
927         aliases=log2_shaper_name_aliases)
928     colorspaces.append(log2_shaper)
929
930     shaper_input_scale_generic_log2 = 1
931
932     # *Log 2* shaper name and *CTL* transforms bundled up.
933     log2_shaper_data = [
934         log2_shaper_name,
935         os.path.join('%s',
936                      'utilities',
937                      'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl'),
938         os.path.join('%s',
939                      'utilities',
940                      'ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl'),
941         shaper_input_scale_generic_log2,
942         log2_params]
943
944     shaper_data[log2_shaper_name] = log2_shaper_data
945
946     # Shaper that also includes the AP1 primaries.
947     # Needed for some LUT baking steps.
948     log2_shaper_api1_name_aliases = ["%s_ap1" % compact(shaper_name)]
949     log2_shaper_ap1 = create_generic_log(
950         aces_ctl_directory,
951         lut_directory,
952         lut_resolution_1d,
953         cleanup,
954         name=log2_shaper_name,
955         middle_grey=log2_params['middleGrey'],
956         min_exposure=log2_params['minExposure'],
957         max_exposure=log2_params['maxExposure'],
958         aliases=log2_shaper_api1_name_aliases)
959     log2_shaper_ap1.name = '%s - AP1' % log2_shaper_ap1.name
960
961     # *AP1* primaries to *AP0* primaries.
962     log2_shaper_ap1.to_reference_transforms.append({
963         'type': 'matrix',
964         'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
965         'direction': 'forward'
966     })
967     colorspaces.append(log2_shaper_ap1)
968
969     rrt_shaper = log2_shaper_data
970
971     # *RRT + ODT* combinations.
972     sorted_odts = sorted(odt_info.iteritems(), key=lambda x: x[1])
973     print(sorted_odts)
974     for odt in sorted_odts:
975         (odt_name, odt_values) = odt
976
977         # Generating legal range transform for *ODTs* that can generate 
978         # either *legal* or *full* output.
979         if odt_values['transformHasFullLegalSwitch']:
980             odt_name_legal = '%s - Legal' % odt_values['transformUserName']
981         else:
982             odt_name_legal = odt_values['transformUserName']
983
984         odt_legal = odt_values.copy()
985         odt_legal['legalRange'] = 1
986
987         odt_aliases = ["out_%s" % compact(odt_name_legal)]
988
989         cs = create_ACES_RRT_plus_ODT(
990             odt_name_legal,
991             odt_legal,
992             rrt_shaper,
993             aces_ctl_directory,
994             lut_directory,
995             lut_resolution_1d,
996             lut_resolution_3d,
997             cleanup,
998             odt_aliases)
999         colorspaces.append(cs)
1000
1001         displays[odt_name_legal] = {
1002             'Linear': linear_display_space,
1003             'Log': log_display_space,
1004             'Output Transform': cs}
1005
1006
1007         # Generating full range transform for *ODTs* that can generate 
1008         # either *legal* or *full* output.
1009         if odt_values['transformHasFullLegalSwitch']:
1010             print('Generating full range ODT for %s' % odt_name)
1011
1012             odt_name_full = '%s - Full' % odt_values['transformUserName']
1013             odt_full = odt_values.copy()
1014             odt_full['legalRange'] = 0
1015
1016             odt_full_aliases = ["out_%s" % compact(odt_name_full)]
1017
1018             cs_full = create_ACES_RRT_plus_ODT(
1019                 odt_name_full,
1020                 odt_full,
1021                 rrt_shaper,
1022                 aces_ctl_directory,
1023                 lut_directory,
1024                 lut_resolution_1d,
1025                 lut_resolution_3d,
1026                 cleanup,
1027                 odt_full_aliases)
1028             colorspaces.append(cs_full)
1029
1030             displays[odt_name_full] = {
1031                 'Linear': linear_display_space,
1032                 'Log': log_display_space,
1033                 'Output Transform': cs_full}
1034
1035     return (colorspaces, displays)
1036
1037
1038 def get_transform_info(ctl_transform):
1039     """
1040     Object description.
1041
1042     Parameters
1043     ----------
1044     parameter : type
1045         Parameter description.
1046
1047     Returns
1048     -------
1049     type
1050          Return value description.
1051     """
1052
1053     with open(ctl_transform, 'rb') as fp:
1054         lines = fp.readlines()
1055
1056     # Retrieving the *transform ID* and *User Name*.
1057     transform_id = lines[1][3:].split('<')[1].split('>')[1].strip()
1058     transform_user_name = '-'.join(
1059         lines[2][3:].split('<')[1].split('>')[1].split('-')[1:]).strip()
1060     transform_user_name_prefix = (
1061         lines[2][3:].split('<')[1].split('>')[1].split('-')[0].strip())
1062
1063     # Figuring out if this transform has options for processing full and legal range
1064     transform_full_legal_switch = False
1065     for line in lines:
1066         if line.strip() == "input varying int legalRange = 0":
1067             # print( "%s has legal range flag" % transform_user_name)
1068             transform_full_legal_switch = True
1069             break
1070
1071     return (transform_id, transform_user_name, transform_user_name_prefix,
1072             transform_full_legal_switch)
1073
1074
1075 def get_ODTs_info(aces_ctl_directory):
1076     """
1077     Object description.
1078
1079     For versions after WGR9.
1080
1081     Parameters
1082     ----------
1083     parameter : type
1084         Parameter description.
1085
1086     Returns
1087     -------
1088     type
1089          Return value description.
1090     """
1091
1092     # TODO: Investigate usage of *files_walker* definition here.
1093     # Credit to *Alex Fry* for the original approach here.
1094     odt_dir = os.path.join(aces_ctl_directory, 'odt')
1095     all_odt = []
1096     for dir_name, subdir_list, file_list in os.walk(odt_dir):
1097         for fname in file_list:
1098             all_odt.append((os.path.join(dir_name, fname)))
1099
1100     odt_CTLs = [x for x in all_odt if
1101                 ('InvODT' not in x) and (os.path.split(x)[-1][0] != '.')]
1102
1103     odts = {}
1104
1105     for odt_CTL in odt_CTLs:
1106         odt_tokens = os.path.split(odt_CTL)
1107
1108         # Handling nested directories.
1109         odt_path_tokens = os.path.split(odt_tokens[-2])
1110         odt_dir = odt_path_tokens[-1]
1111         while odt_path_tokens[-2][-3:] != 'odt':
1112             odt_path_tokens = os.path.split(odt_path_tokens[-2])
1113             odt_dir = os.path.join(odt_path_tokens[-1], odt_dir)
1114
1115         # Building full name,
1116         transform_CTL = odt_tokens[-1]
1117         odt_name = string.join(transform_CTL.split('.')[1:-1], '.')
1118
1119         # Finding id, user name and user name prefix.
1120         (transform_ID,
1121          transform_user_name,
1122          transform_user_name_prefix,
1123          transform_full_legal_switch) = get_transform_info(
1124             os.path.join(aces_ctl_directory, 'odt', odt_dir, transform_CTL))
1125
1126         # Finding inverse.
1127         transform_CTL_inverse = 'InvODT.%s.ctl' % odt_name
1128         if not os.path.exists(
1129                 os.path.join(odt_tokens[-2], transform_CTL_inverse)):
1130             transform_CTL_inverse = None
1131
1132         # Add to list of ODTs
1133         odts[odt_name] = {}
1134         odts[odt_name]['transformCTL'] = os.path.join(odt_dir, transform_CTL)
1135         if transform_CTL_inverse is not None:
1136             odts[odt_name]['transformCTLInverse'] = os.path.join(
1137                 odt_dir, transform_CTL_inverse)
1138
1139         odts[odt_name]['transformID'] = transform_ID
1140         odts[odt_name]['transformUserNamePrefix'] = transform_user_name_prefix
1141         odts[odt_name]['transformUserName'] = transform_user_name
1142         odts[odt_name][
1143             'transformHasFullLegalSwitch'] = transform_full_legal_switch
1144
1145         forward_CTL = odts[odt_name]['transformCTL']
1146
1147         print('ODT : %s' % odt_name)
1148         print('\tTransform ID               : %s' % transform_ID)
1149         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
1150         print('\tTransform User Name        : %s' % transform_user_name)
1151         print(
1152             '\tHas Full / Legal Switch    : %s' % transform_full_legal_switch)
1153         print('\tForward ctl                : %s' % forward_CTL)
1154         if 'transformCTLInverse' in odts[odt_name]:
1155             inverse_CTL = odts[odt_name]['transformCTLInverse']
1156             print('\tInverse ctl                : %s' % inverse_CTL)
1157         else:
1158             print('\tInverse ctl                : %s' % 'None')
1159
1160     print('\n')
1161
1162     return odts
1163
1164
1165 def get_LMTs_info(aces_ctl_directory):
1166     """
1167     Object description.
1168
1169     For versions after WGR9.
1170
1171     Parameters
1172     ----------
1173     parameter : type
1174         Parameter description.
1175
1176     Returns
1177     -------
1178     type
1179          Return value description.
1180     """
1181
1182     # TODO: Investigate refactoring with previous definition.
1183
1184     # Credit to Alex Fry for the original approach here
1185     lmt_dir = os.path.join(aces_ctl_directory, 'lmt')
1186     all_lmt = []
1187     for dir_name, subdir_list, file_list in os.walk(lmt_dir):
1188         for fname in file_list:
1189             all_lmt.append((os.path.join(dir_name, fname)))
1190
1191     lmt_CTLs = [x for x in all_lmt if
1192                 ('InvLMT' not in x) and ('README' not in x) and (
1193                     os.path.split(x)[-1][0] != '.')]
1194
1195     lmts = {}
1196
1197     for lmt_CTL in lmt_CTLs:
1198         lmt_tokens = os.path.split(lmt_CTL)
1199
1200         # Handlimg nested directories.
1201         lmt_path_tokens = os.path.split(lmt_tokens[-2])
1202         lmt_dir = lmt_path_tokens[-1]
1203         while lmt_path_tokens[-2][-3:] != 'ctl':
1204             lmt_path_tokens = os.path.split(lmt_path_tokens[-2])
1205             lmt_dir = os.path.join(lmt_path_tokens[-1], lmt_dir)
1206
1207         # Building full name.
1208         transform_CTL = lmt_tokens[-1]
1209         lmt_name = string.join(transform_CTL.split('.')[1:-1], '.')
1210
1211         # Finding id, user name and user name prefix.
1212         (transform_ID,
1213          transform_user_name,
1214          transform_user_name_prefix,
1215          transform_full_legal_switch) = get_transform_info(
1216             os.path.join(aces_ctl_directory, lmt_dir, transform_CTL))
1217
1218         # Finding inverse.
1219         transform_CTL_inverse = 'InvLMT.%s.ctl' % lmt_name
1220         if not os.path.exists(
1221                 os.path.join(lmt_tokens[-2], transform_CTL_inverse)):
1222             transform_CTL_inverse = None
1223
1224         lmts[lmt_name] = {}
1225         lmts[lmt_name]['transformCTL'] = os.path.join(lmt_dir, transform_CTL)
1226         if transform_CTL_inverse is not None:
1227             lmts[lmt_name]['transformCTLInverse'] = os.path.join(
1228                 lmt_dir, transform_CTL_inverse)
1229
1230         lmts[lmt_name]['transformID'] = transform_ID
1231         lmts[lmt_name]['transformUserNamePrefix'] = transform_user_name_prefix
1232         lmts[lmt_name]['transformUserName'] = transform_user_name
1233
1234         forward_CTL = lmts[lmt_name]['transformCTL']
1235
1236         print('LMT : %s' % lmt_name)
1237         print('\tTransform ID               : %s' % transform_ID)
1238         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
1239         print('\tTransform User Name        : %s' % transform_user_name)
1240         print('\t Forward ctl               : %s' % forward_CTL)
1241         if 'transformCTLInverse' in lmts[lmt_name]:
1242             inverse_CTL = lmts[lmt_name]['transformCTLInverse']
1243             print('\t Inverse ctl                : %s' % inverse_CTL)
1244         else:
1245             print('\t Inverse ctl                : %s' % 'None')
1246
1247     print('\n')
1248
1249     return lmts
1250
1251
1252 def create_colorspaces(aces_ctl_directory,
1253                        lut_directory,
1254                        lut_resolution_1d,
1255                        lut_resolution_3d,
1256                        lmt_info,
1257                        odt_info,
1258                        shaper_name,
1259                        cleanup):
1260     """
1261     Generates the colorspace conversions.
1262
1263     Parameters
1264     ----------
1265     parameter : type
1266         Parameter description.
1267
1268     Returns
1269     -------
1270     type
1271          Return value description.
1272     """
1273
1274     colorspaces = []
1275
1276     ACES = create_ACES()
1277
1278     ACEScc = create_ACEScc(aces_ctl_directory, lut_directory,
1279                            lut_resolution_1d, cleanup)
1280     colorspaces.append(ACEScc)
1281
1282     ACESproxy = create_ACESproxy(aces_ctl_directory, lut_directory,
1283                                  lut_resolution_1d, cleanup)
1284     colorspaces.append(ACESproxy)
1285
1286     ACEScg = create_ACEScg(aces_ctl_directory, lut_directory,
1287                            lut_resolution_1d, cleanup)
1288     colorspaces.append(ACEScg)
1289
1290     ADX10 = create_ADX(lut_directory, lut_resolution_1d, bit_depth=10)
1291     colorspaces.append(ADX10)
1292
1293     ADX16 = create_ADX(lut_directory, lut_resolution_1d, bit_depth=16)
1294     colorspaces.append(ADX16)
1295
1296     lmts = create_LMTs(aces_ctl_directory,
1297                        lut_directory,
1298                        lut_resolution_1d,
1299                        lut_resolution_3d,
1300                        lmt_info,
1301                        shaper_name,
1302                        cleanup)
1303     colorspaces.extend(lmts)
1304
1305     odts, displays = create_ODTs(aces_ctl_directory,
1306                                  lut_directory,
1307                                  lut_resolution_1d,
1308                                  lut_resolution_3d,
1309                                  odt_info,
1310                                  shaper_name,
1311                                  cleanup,
1312                                  ACES,
1313                                  ACEScc)
1314     colorspaces.extend(odts)
1315
1316     return ACES, colorspaces, displays, ACEScc