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