15759cb78e15ac43b242579c33863b0b29895b7f
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / create_aces_config.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Defines objects creating the *ACES* configuration.
6 """
7
8 import math
9 import numpy
10 import os
11 import pprint
12 import shutil
13 import string
14 import sys
15
16 import PyOpenColorIO as ocio
17
18 import aces_ocio.create_arri_colorspaces as arri
19 import aces_ocio.create_canon_colorspaces as canon
20 import aces_ocio.create_red_colorspaces as red
21 import aces_ocio.create_sony_colorspaces as sony
22 from aces_ocio.generate_lut import (
23     generate_1d_LUT_from_CTL,
24     generate_3d_LUT_from_CTL,
25     write_SPI_1d)
26 from aces_ocio.process import Process
27 from aces_ocio.utilities import ColorSpace, mat44_from_mat33, sanitize_path, compact
28
29 __author__ = 'ACES Developers'
30 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
31 __license__ = ''
32 __maintainer__ = 'ACES Developers'
33 __email__ = 'aces@oscars.org'
34 __status__ = 'Production'
35
36 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
37            'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
38            'set_config_default_roles',
39            'write_config',
40            'generate_OCIO_transform',
41            'create_config',
42            'generate_LUTs',
43            'generate_baked_LUTs',
44            'create_config_dir',
45            'get_transform_info',
46            'get_ODT_info',
47            'get_LMT_info',
48            'create_ACES_config',
49            'main']
50
51 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
52 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
53
54
55 def set_config_default_roles(config,
56                              color_picking='',
57                              color_timing='',
58                              compositing_log='',
59                              data='',
60                              default='',
61                              matte_paint='',
62                              reference='',
63                              scene_linear='',
64                              texture_paint=''):
65     """
66     Sets given *OCIO* configuration default roles.
67
68     Parameters
69     ----------
70     config : config
71         *OCIO* configuration.
72     color_picking : str or unicode
73         Color picking role title.
74     color_timing : str or unicode
75         Color timing role title.
76     compositing_log : str or unicode
77         Compositing log role title.
78     data : str or unicode
79         Data role title.
80     default : str or unicode
81         Default role title.
82     matte_paint : str or unicode
83         Matte painting role title.
84     reference : str or unicode
85         Reference role title.
86     scene_linear : str or unicode
87         Scene linear role title.
88     texture_paint : str or unicode
89         Texture painting role title.
90
91     Returns
92     -------
93     bool
94          Definition success.
95     """
96
97     if color_picking:
98         config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
99     if color_timing:
100         config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
101     if compositing_log:
102         config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
103     if data:
104         config.setRole(ocio.Constants.ROLE_DATA, data)
105     if default:
106         config.setRole(ocio.Constants.ROLE_DEFAULT, default)
107     if matte_paint:
108         config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
109     if reference:
110         config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
111     if scene_linear:
112         config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
113     if texture_paint:
114         config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
115
116     return True
117
118
119 def write_config(config, config_path, sanity_check=True):
120     """
121     Writes the configuration to given path.
122
123     Parameters
124     ----------
125     parameter : type
126         Parameter description.
127
128     Returns
129     -------
130     type
131          Return value description.
132     """
133
134     if sanity_check:
135         try:
136             config.sanityCheck()
137         except Exception, e:
138             print e
139             print 'Configuration was not written due to a failed Sanity Check'
140             return
141
142     with open(config_path, mode='w') as fp:
143         fp.write(config.serialize())
144
145
146 def generate_OCIO_transform(transforms):
147     """
148     Object description.
149
150     Parameters
151     ----------
152     parameter : type
153         Parameter description.
154
155     Returns
156     -------
157     type
158          Return value description.
159     """
160
161     interpolation_options = {
162         'linear': ocio.Constants.INTERP_LINEAR,
163         'nearest': ocio.Constants.INTERP_NEAREST,
164         'tetrahedral': ocio.Constants.INTERP_TETRAHEDRAL
165     }
166     direction_options = {
167         'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
168         'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE
169     }
170
171     ocio_transforms = []
172
173     for transform in transforms:
174
175         # lutFile transform
176         if transform['type'] == 'lutFile':
177             ocio_transform = ocio.FileTransform(
178                 src=transform['path'],
179                 interpolation=interpolation_options[
180                     transform['interpolation']],
181                 direction=direction_options[transform['direction']])
182             ocio_transforms.append(ocio_transform)
183         
184         # matrix transform
185         elif transform['type'] == 'matrix':
186             ocio_transform = ocio.MatrixTransform()
187             # MatrixTransform member variables can't be initialized directly.
188             # Each must be set individually.
189             ocio_transform.setMatrix(transform['matrix'])
190
191             if 'offset' in transform:
192                 ocio_transform.setOffset(transform['offset'])
193
194             if 'direction' in transform:
195                 ocio_transform.setDirection(
196                     direction_options[transform['direction']])
197
198             ocio_transforms.append(ocio_transform)
199
200         # exponent transform
201         elif transform['type'] == 'exponent':
202             ocio_transform = ocio.ExponentTransform()
203             ocio_transform.setValue(transform['value'])
204             ocio_transforms.append(ocio_transform)
205
206         # log transform
207         elif transform['type'] == 'log':
208             ocio_transform = ocio.LogTransform(
209                 base=transform['base'],
210                 direction=direction_options[transform['direction']])
211
212             ocio_transforms.append(ocio_transform)
213
214         # color space transform
215         elif transform['type'] == 'colorspace':
216             ocio_transform = ocio.ColorSpaceTransform( src=transform['src'],
217                 dst=transform['dst'],
218                 direction=direction_options['forward'] )
219             ocio_transforms.append(ocio_transform)
220
221         # unknown type
222         else:
223             print("Ignoring unknown transform type : %s" % transform['type'])
224
225     if len(ocio_transforms) > 1:
226         group_transform = ocio.GroupTransform()
227         for transform in ocio_transforms:
228             group_transform.push_back(transform)
229         transform = group_transform
230     else:
231         transform = ocio_transforms[0]
232
233     return transform
234
235 def add_colorspace_alias(config, reference_colorspace, colorspace, colorspace_alias_names):
236     """
237     Object description.
238
239     Parameters
240     ----------
241     parameter : type
242         Parameter description.
243
244     Returns
245     -------
246     type
247          Return value description.
248     """
249
250     for alias_name in colorspace_alias_names:
251         if alias_name == colorspace.name.lower():
252             return
253
254         print( "Adding alias colorspace space %s, alias to %s" % (
255             alias_name, colorspace.name))
256
257         compact_family_name = "Aliases"
258
259         ocio_colorspace_alias = ocio.ColorSpace(
260             name=alias_name,
261             bitDepth=colorspace.bit_depth,
262             description=colorspace.description,
263             equalityGroup=colorspace.equality_group,
264             family=compact_family_name,
265             isData=colorspace.is_data,
266             allocation=colorspace.allocation_type,
267             allocationVars=colorspace.allocation_vars)
268
269         if colorspace.to_reference_transforms != []:
270             print("Generating To-Reference transforms")
271             ocio_transform = generate_OCIO_transform([{
272                 'type': 'colorspace',
273                 'src': colorspace.name,
274                 'dst': reference_colorspace.name,
275                 'direction': 'forward'
276                 }])
277             ocio_colorspace_alias.setTransform(
278                 ocio_transform,
279                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
280
281         if colorspace.from_reference_transforms != []:
282             print("Generating From-Reference transforms")
283             ocio_transform = generate_OCIO_transform([{
284                 'type': 'colorspace',
285                 'src': reference_colorspace.name,
286                 'dst': colorspace.name,
287                 'direction': 'forward'
288                 }])
289             ocio_colorspace_alias.setTransform(
290                 ocio_transform,
291                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
292
293         config.addColorSpace(ocio_colorspace_alias)
294
295
296 def create_config(config_data, nuke=False):
297     """
298     Object description.
299
300     Parameters
301     ----------
302     parameter : type
303         Parameter description.
304
305     Returns
306     -------
307     type
308          Return value description.
309     """
310
311     # Creating the *OCIO* configuration.
312     config = ocio.Config()
313
314     # Setting configuration overall values.
315     config.setDescription('An ACES config generated from python')
316     config.setSearchPath('luts')
317
318     # Defining the reference colorspace.
319     reference_data = config_data['referenceColorSpace']
320     print('Adding the reference color space : %s' % reference_data.name)
321
322     reference = ocio.ColorSpace(
323         name=reference_data.name,
324         bitDepth=reference_data.bit_depth,
325         description=reference_data.description,
326         equalityGroup=reference_data.equality_group,
327         family=reference_data.family,
328         isData=reference_data.is_data,
329         allocation=reference_data.allocation_type,
330         allocationVars=reference_data.allocation_vars)
331
332     config.addColorSpace(reference)
333
334     # Add alias
335     if reference_data.aliases != []:
336         add_colorspace_alias(config, reference_data,
337             reference_data, reference_data.aliases)
338
339     print("")
340
341     # Creating the remaining colorspaces.
342     for colorspace in sorted(config_data['colorSpaces']):
343         print('Creating new color space : %s' % colorspace.name)
344
345         ocio_colorspace = ocio.ColorSpace(
346             name=colorspace.name,
347             bitDepth=colorspace.bit_depth,
348             description=colorspace.description,
349             equalityGroup=colorspace.equality_group,
350             family=colorspace.family,
351             isData=colorspace.is_data,
352             allocation=colorspace.allocation_type,
353             allocationVars=colorspace.allocation_vars)
354
355         if colorspace.to_reference_transforms:
356             print('Generating To-Reference transforms')
357             ocio_transform = generate_OCIO_transform(
358                 colorspace.to_reference_transforms)
359             ocio_colorspace.setTransform(
360                 ocio_transform,
361                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
362
363         if colorspace.from_reference_transforms:
364             print('Generating From-Reference transforms')
365             ocio_transform = generate_OCIO_transform(
366                 colorspace.from_reference_transforms)
367             ocio_colorspace.setTransform(
368                 ocio_transform,
369                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
370
371         config.addColorSpace(ocio_colorspace)
372
373         #
374         # Add alias to normal colorspace, using compact name
375         #
376         if colorspace.aliases != []:
377             add_colorspace_alias(config, reference_data, 
378                 colorspace, colorspace.aliases)
379
380         print('')
381
382     # Defining the *views* and *displays*.
383     displays = []
384     views = []
385
386     # Defining a *generic* *display* and *view* setup.
387     if not nuke:
388         for display, view_list in config_data['displays'].iteritems():
389             for view_name, colorspace in view_list.iteritems():
390                 config.addDisplay(display, view_name, colorspace.name)
391                 if not (view_name in views):
392                     views.append(view_name)
393             displays.append(display)
394     # Defining the *Nuke* specific set of *views* and *displays*.
395     else:
396         for display, view_list in config_data['displays'].iteritems():
397             for view_name, colorspace in view_list.iteritems():
398                 if view_name == 'Output Transform':
399                     view_name = 'View'
400                     config.addDisplay(display, view_name, colorspace.name)
401                     if not (view_name in views):
402                         views.append(view_name)
403             displays.append(display)
404
405         config.addDisplay('linear', 'View', 'ACES2065-1')
406         displays.append('linear')
407         config.addDisplay('log', 'View', 'ACEScc')
408         displays.append('log')
409
410     # Setting the active *displays* and *views*.
411     config.setActiveDisplays(','.join(sorted(displays)))
412     config.setActiveViews(','.join(views))
413
414     set_config_default_roles(
415         config,
416         color_picking=reference.getName(),
417         color_timing=reference.getName(),
418         compositing_log=reference.getName(),
419         data=reference.getName(),
420         default=reference.getName(),
421         matte_paint=reference.getName(),
422         reference=reference.getName(),
423         scene_linear=reference.getName(),
424         texture_paint=reference.getName())
425
426     config.sanityCheck()
427
428     return config
429
430
431 def generate_LUTs(odt_info,
432                   lmt_info,
433                   shaper_name,
434                   aces_CTL_directory,
435                   lut_directory,
436                   lut_resolution_1d=4096,
437                   lut_resolution_3d=64,
438                   cleanup=True):
439     """
440     Object description.
441
442     Parameters
443     ----------
444     parameter : type
445         Parameter description.
446
447     Returns
448     -------
449     dict
450          Colorspaces and transforms converting between those colorspaces and
451          the reference colorspace, *ACES*.
452     """
453
454     print('generateLUTs - begin')
455     config_data = {}
456
457     # Defining the reference colorspace.
458     ACES = ColorSpace('ACES2065-1')
459     ACES.description = (
460         'The Academy Color Encoding System reference color space')
461     ACES.equality_group = ''
462     ACES.aliases = ["lin_ap0", "aces"]
463     ACES.family = 'ACES'
464     ACES.is_data = False
465     ACES.allocation_type = ocio.Constants.ALLOCATION_LG2
466     ACES.allocation_vars = [-15, 6]
467
468     config_data['referenceColorSpace'] = ACES
469
470     config_data['displays'] = {}
471     config_data['colorSpaces'] = []
472
473     # Matrix converting *ACES AP1* primaries to *AP0*.
474     ACES_AP1_to_AP0 = [0.6954522414, 0.1406786965, 0.1638690622,
475                        0.0447945634, 0.8596711185, 0.0955343182,
476                        -0.0055258826, 0.0040252103, 1.0015006723]
477
478     # Matrix converting *ACES AP0* primaries to *XYZ*.
479     ACES_AP0_to_XYZ = [0.9525523959, 0.0000000000, 0.0000936786,
480                        0.3439664498, 0.7281660966, -0.0721325464,
481                        0.0000000000, 0.0000000000, 1.0088251844]
482
483     # -------------------------------------------------------------------------
484     # *ACEScc*
485     # -------------------------------------------------------------------------
486     def create_ACEScc(name='ACEScc',
487                       min_value=0.0,
488                       max_value=1.0,
489                       input_scale=1.0):
490         cs = ColorSpace(name)
491         cs.description = 'The %s color space' % name
492         cs.aliases = ["acescc_ap1"]
493         cs.equality_group = ''
494         cs.family = 'ACES'
495         cs.is_data = False
496
497         ctls = [os.path.join(aces_CTL_directory,
498                              'ACEScc',
499                              'ACEScsc.ACEScc_to_ACES.a1.0.0.ctl'),
500                 # This transform gets back to the *AP1* primaries.
501                 # Useful as the 1d LUT is only covering the transfer function.
502                 # The primaries switch is covered by the matrix below:
503                 os.path.join(aces_CTL_directory,
504                              'ACEScg',
505                              'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
506         lut = '%s_to_ACES.spi1d' % name
507
508         lut = sanitize_path(lut)
509
510         generate_1d_LUT_from_CTL(
511             os.path.join(lut_directory, lut),
512             ctls,
513             lut_resolution_1d,
514             'float',
515             input_scale,
516             1.0,
517             {},
518             cleanup,
519             aces_CTL_directory,
520             min_value,
521             max_value)
522
523         cs.to_reference_transforms = []
524         cs.to_reference_transforms.append({
525             'type': 'lutFile',
526             'path': lut,
527             'interpolation': 'linear',
528             'direction': 'forward'})
529
530         # *AP1* primaries to *AP0* primaries.
531         cs.to_reference_transforms.append({
532             'type': 'matrix',
533             'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
534             'direction': 'forward'})
535
536         cs.from_reference_transforms = []
537         return cs
538
539     ACEScc = create_ACEScc()
540     config_data['colorSpaces'].append(ACEScc)
541
542     # -------------------------------------------------------------------------
543     # *ACESproxy*
544     # -------------------------------------------------------------------------
545     def create_ACESproxy(name='ACESproxy'):
546         cs = ColorSpace(name)
547         cs.description = 'The %s color space' % name
548         cs.aliases = ["acesproxy_ap1"]
549         cs.equality_group = ''
550         cs.family = 'ACES'
551         cs.is_data = False
552
553         ctls = [os.path.join(aces_CTL_directory,
554                              'ACESproxy',
555                              'ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl'),
556                 # This transform gets back to the *AP1* primaries.
557                 # Useful as the 1d LUT is only covering the transfer function.
558                 # The primaries switch is covered by the matrix below:
559                 os.path.join(aces_CTL_directory,
560                              'ACEScg',
561                              'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
562         lut = '%s_to_aces.spi1d' % name
563
564         lut = sanitize_path(lut)
565
566         generate_1d_LUT_from_CTL(
567             os.path.join(lut_directory, lut),
568             ctls,
569             lut_resolution_1d,
570             'uint16',
571             64.0,
572             1.0,
573             {},
574             cleanup,
575             aces_CTL_directory)
576
577         cs.to_reference_transforms = []
578         cs.to_reference_transforms.append({
579             'type': 'lutFile',
580             'path': lut,
581             'interpolation': 'linear',
582             'direction': 'forward'
583         })
584
585         # *AP1* primaries to *AP0* primaries.
586         cs.to_reference_transforms.append({
587             'type': 'matrix',
588             'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
589             'direction': 'forward'
590         })
591
592         cs.from_reference_transforms = []
593         return cs
594
595     ACESproxy = create_ACESproxy()
596     config_data['colorSpaces'].append(ACESproxy)
597
598     # -------------------------------------------------------------------------
599     # *ACEScg*
600     # -------------------------------------------------------------------------
601     def create_ACEScg(name='ACEScg'):
602         cs = ColorSpace(name)
603         cs.description = 'The %s color space' % name
604         cs.aliases = ["lin_ap1"]
605         cs.equality_group = ''
606         cs.family = 'ACES'
607         cs.is_data = False
608
609         cs.to_reference_transforms = []
610
611         # *AP1* primaries to *AP0* primaries.
612         cs.to_reference_transforms.append({
613             'type': 'matrix',
614             'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
615             'direction': 'forward'
616         })
617
618         cs.from_reference_transforms = []
619         return cs
620
621     ACEScg = create_ACEScg()
622     config_data['colorSpaces'].append(ACEScg)
623
624     # -------------------------------------------------------------------------
625     # *ADX*
626     # -------------------------------------------------------------------------
627     def create_ADX(bit_depth=10, name='ADX'):
628         name = '%s%s' % (name, bit_depth)
629         cs = ColorSpace(name)
630         cs.description = '%s color space - used for film scans' % name
631         cs.aliases = ["adx%s" % str(bit_depth)]
632         cs.equality_group = ''
633         cs.family = 'ADX'
634         cs.is_data = False
635
636         if bit_depth == 10:
637             cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT10
638             adx_to_cdd = [1023.0 / 500.0, 0.0, 0.0, 0.0,
639                           0.0, 1023.0 / 500.0, 0.0, 0.0,
640                           0.0, 0.0, 1023.0 / 500.0, 0.0,
641                           0.0, 0.0, 0.0, 1.0]
642             offset = [-95.0 / 500.0, -95.0 / 500.0, -95.0 / 500.0, 0.0]
643         elif bit_depth == 16:
644             cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT16
645             adx_to_cdd = [65535.0 / 8000.0, 0.0, 0.0, 0.0,
646                           0.0, 65535.0 / 8000.0, 0.0, 0.0,
647                           0.0, 0.0, 65535.0 / 8000.0, 0.0,
648                           0.0, 0.0, 0.0, 1.0]
649             offset = [-1520.0 / 8000.0, -1520.0 / 8000.0, -1520.0 / 8000.0,
650                       0.0]
651
652         cs.to_reference_transforms = []
653
654         # Converting from *ADX* to *Channel-Dependent Density*.
655         cs.to_reference_transforms.append({
656             'type': 'matrix',
657             'matrix': adx_to_cdd,
658             'offset': offset,
659             'direction': 'forward'})
660
661         # Convert from Channel-Dependent Density to Channel-Independent Density
662         cs.to_reference_transforms.append({
663             'type': 'matrix',
664             'matrix': [0.75573, 0.22197, 0.02230, 0,
665                        0.05901, 0.96928, -0.02829, 0,
666                        0.16134, 0.07406, 0.76460, 0,
667                        0.0, 0.0, 0.0, 1.0],
668             'direction': 'forward'})
669
670         # Copied from *Alex Fry*'s *adx_cid_to_rle.py*
671         def create_CID_to_RLE_LUT():
672
673             def interpolate_1D(x, xp, fp):
674                 return numpy.interp(x, xp, fp)
675
676             LUT_1D_xp = [-0.190000000000000,
677                          0.010000000000000,
678                          0.028000000000000,
679                          0.054000000000000,
680                          0.095000000000000,
681                          0.145000000000000,
682                          0.220000000000000,
683                          0.300000000000000,
684                          0.400000000000000,
685                          0.500000000000000,
686                          0.600000000000000]
687
688             LUT_1D_fp = [-6.000000000000000,
689                          -2.721718645000000,
690                          -2.521718645000000,
691                          -2.321718645000000,
692                          -2.121718645000000,
693                          -1.921718645000000,
694                          -1.721718645000000,
695                          -1.521718645000000,
696                          -1.321718645000000,
697                          -1.121718645000000,
698                          -0.926545676714876]
699
700             REF_PT = ((7120.0 - 1520.0) / 8000.0 * (100.0 / 55.0) -
701                       math.log(0.18, 10.0))
702
703             def cid_to_rle(x):
704                 if x <= 0.6:
705                     return interpolate_1D(x, LUT_1D_xp, LUT_1D_fp)
706                 return (100.0 / 55.0) * x - REF_PT
707
708             def fit(value, from_min, from_max, to_min, to_max):
709                 if from_min == from_max:
710                     raise ValueError('from_min == from_max')
711                 return (value - from_min) / (from_max - from_min) * (
712                     to_max - to_min) + to_min
713
714             NUM_SAMPLES = 2 ** 12
715             RANGE = (-0.19, 3.0)
716             data = []
717             for i in xrange(NUM_SAMPLES):
718                 x = i / (NUM_SAMPLES - 1.0)
719                 x = fit(x, 0.0, 1.0, RANGE[0], RANGE[1])
720                 data.append(cid_to_rle(x))
721
722             lut = 'ADX_CID_to_RLE.spi1d'
723             write_SPI_1d(os.path.join(lut_directory, lut),
724                          RANGE[0],
725                          RANGE[1],
726                          data,
727                          NUM_SAMPLES, 1)
728
729             return lut
730
731         # Converting *Channel Independent Density* values to
732         # *Relative Log Exposure* values.
733         lut = create_CID_to_RLE_LUT()
734         cs.to_reference_transforms.append({
735             'type': 'lutFile',
736             'path': lut,
737             'interpolation': 'linear',
738             'direction': 'forward'})
739
740         # Converting *Relative Log Exposure* values to
741         # *Relative Exposure* values.
742         cs.to_reference_transforms.append({
743             'type': 'log',
744             'base': 10,
745             'direction': 'inverse'})
746
747         # Convert *Relative Exposure* values to *ACES* values.
748         cs.to_reference_transforms.append({
749             'type': 'matrix',
750             'matrix': [0.72286, 0.12630, 0.15084, 0,
751                        0.11923, 0.76418, 0.11659, 0,
752                        0.01427, 0.08213, 0.90359, 0,
753                        0.0, 0.0, 0.0, 1.0],
754             'direction': 'forward'})
755
756         cs.from_reference_transforms = []
757         return cs
758
759     ADX10 = create_ADX(bit_depth=10)
760     config_data['colorSpaces'].append(ADX10)
761
762     ADX16 = create_ADX(bit_depth=16)
763     config_data['colorSpaces'].append(ADX16)
764
765     # -------------------------------------------------------------------------
766     # *Camera Input Transforms*
767     # -------------------------------------------------------------------------
768
769     # *RED* colorspaces to *ACES*.
770     red_colorspaces = red.create_colorspaces(lut_directory, lut_resolution_1d)
771     for cs in red_colorspaces:
772         config_data['colorSpaces'].append(cs)
773
774     # *Canon-Log* to *ACES*.
775     canon_colorspaces = canon.create_colorspaces(lut_directory,
776                                                  lut_resolution_1d)
777     for cs in canon_colorspaces:
778         config_data['colorSpaces'].append(cs)
779
780     # *S-Log* to *ACES*.
781     sony_colorSpaces = sony.create_colorspaces(lut_directory,
782                                                lut_resolution_1d)
783     for cs in sony_colorSpaces:
784         config_data['colorSpaces'].append(cs)
785
786     # *Log-C* to *ACES*.
787     arri_colorSpaces = arri.create_colorspaces(lut_directory,
788                                                lut_resolution_1d)
789     for cs in arri_colorSpaces:
790         config_data['colorSpaces'].append(cs)
791
792     # -------------------------------------------------------------------------
793     # *Generic Log Transform*
794     # -------------------------------------------------------------------------
795     def create_generic_log(name='log',
796                            aliases=[],
797                            min_value=0.0,
798                            max_value=1.0,
799                            input_scale=1.0,
800                            middle_grey=0.18,
801                            min_exposure=-6.0,
802                            max_exposure=6.5,
803                            lut_resolution_1d=lut_resolution_1d):
804         cs = ColorSpace(name)
805         cs.description = 'The %s color space' % name
806         cs.aliases = aliases
807         cs.equality_group = name
808         cs.family = 'Utility'
809         cs.is_data = False
810
811         ctls = [os.path.join(
812             aces_CTL_directory,
813             'utilities',
814             'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl')]
815         lut = '%s_to_aces.spi1d' % name
816
817         lut = sanitize_path(lut)
818
819         generate_1d_LUT_from_CTL(
820             os.path.join(lut_directory, lut),
821             ctls,
822             lut_resolution_1d,
823             'float',
824             input_scale,
825             1.0,
826             {'middleGrey': middle_grey,
827              'minExposure': min_exposure,
828              'maxExposure': max_exposure},
829             cleanup,
830             aces_CTL_directory,
831             min_value,
832             max_value)
833
834         cs.to_reference_transforms = []
835         cs.to_reference_transforms.append({
836             'type': 'lutFile',
837             'path': lut,
838             'interpolation': 'linear',
839             'direction': 'forward'})
840
841         cs.from_reference_transforms = []
842         return cs
843
844     # -------------------------------------------------------------------------
845     # *ACES LMTs*
846     # -------------------------------------------------------------------------
847     def create_ACES_LMT(lmt_name,
848                         lmt_values,
849                         shaper_info,
850                         lut_resolution_1d=1024,
851                         lut_resolution_3d=64,
852                         cleanup=True,
853                         aliases=[]):
854         cs = ColorSpace('%s' % lmt_name)
855         cs.description = 'The ACES Look Transform: %s' % lmt_name
856         cs.aliases = aliases
857         cs.equality_group = ''
858         cs.family = 'Look'
859         cs.is_data = False
860
861         pprint.pprint(lmt_values)
862
863         # Generating the *shaper* transform.
864         (shaper_name,
865          shaper_to_ACES_CTL,
866          shaper_from_ACES_CTL,
867          shaper_input_scale,
868          shaper_params) = shaper_info
869
870         shaper_lut = '%s_to_aces.spi1d' % shaper_name
871         if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
872             ctls = [shaper_to_ACES_CTL % aces_CTL_directory]
873
874             shaper_lut = sanitize_path(shaper_lut)
875
876             generate_1d_LUT_from_CTL(
877                 os.path.join(lut_directory, shaper_lut),
878                 ctls,
879                 lut_resolution_1d,
880                 'float',
881                 1.0 / shaper_input_scale,
882                 1.0,
883                 shaper_params,
884                 cleanup,
885                 aces_CTL_directory)
886
887         shaper_OCIO_transform = {
888             'type': 'lutFile',
889             'path': shaper_lut,
890             'interpolation': 'linear',
891             'direction': 'inverse'}
892
893         # Generating the forward transform.
894         cs.from_reference_transforms = []
895
896         if 'transformCTL' in lmt_values:
897             ctls = [shaper_to_ACES_CTL % aces_CTL_directory,
898                     os.path.join(aces_CTL_directory,
899                                  lmt_values['transformCTL'])]
900             lut = '%s.%s.spi3d' % (shaper_name, lmt_name)
901
902             lut = sanitize_path(lut)
903
904             generate_3d_LUT_from_CTL(
905                 os.path.join(lut_directory, lut),
906                 ctls,
907                 lut_resolution_3d,
908                 'float',
909                 1.0 / shaper_input_scale,
910                 1.0,
911                 shaper_params,
912                 cleanup,
913                 aces_CTL_directory)
914
915             cs.from_reference_transforms.append(shaper_OCIO_transform)
916             cs.from_reference_transforms.append({
917                 'type': 'lutFile',
918                 'path': lut,
919                 'interpolation': 'tetrahedral',
920                 'direction': 'forward'
921             })
922
923         # Generating the inverse transform.
924         cs.to_reference_transforms = []
925
926         if 'transformCTLInverse' in lmt_values:
927             ctls = [os.path.join(aces_CTL_directory,
928                                  odt_values['transformCTLInverse']),
929                     shaper_from_ACES_CTL % aces_CTL_directory]
930             lut = 'Inverse.%s.%s.spi3d' % (odt_name, shaper_name)
931
932             lut = sanitize_path(lut)
933
934             generate_3d_LUT_from_CTL(
935                 os.path.join(lut_directory, lut),
936                 ctls,
937                 lut_resolution_3d,
938                 'half',
939                 1.0,
940                 shaper_input_scale,
941                 shaper_params,
942                 cleanup,
943                 aces_CTL_directory)
944
945             cs.to_reference_transforms.append({
946                 'type': 'lutFile',
947                 'path': lut,
948                 'interpolation': 'tetrahedral',
949                 'direction': 'forward'})
950
951             shaper_inverse = shaper_OCIO_transform.copy()
952             shaper_inverse['direction'] = 'forward'
953             cs.to_reference_transforms.append(shaper_inverse)
954
955         return cs
956
957     # -------------------------------------------------------------------------
958     # *LMT Shaper*
959     # -------------------------------------------------------------------------
960
961     lmt_lut_resolution_1d = max(4096, lut_resolution_1d)
962     lmt_lut_resolution_3d = max(65, lut_resolution_3d)
963
964     # Defining the *Log 2* shaper.
965     lmt_shaper_name = 'LMT Shaper'
966     lmt_shaper_name_aliases = ['crv_lmtshaper']
967     lmt_params = {
968         'middleGrey': 0.18,
969         'minExposure': -10.0,
970         'maxExposure': 6.5}
971
972     lmt_shaper = create_generic_log(name=lmt_shaper_name,
973                                     middle_grey=lmt_params['middleGrey'],
974                                     min_exposure=lmt_params['minExposure'],
975                                     max_exposure=lmt_params['maxExposure'],
976                                     lut_resolution_1d=lmt_lut_resolution_1d,
977                                     aliases=lmt_shaper_name_aliases)
978     config_data['colorSpaces'].append(lmt_shaper)
979
980     shaper_input_scale_generic_log2 = 1.0
981
982     # *Log 2* shaper name and *CTL* transforms bundled up.
983     lmt_shaper_data = [
984         lmt_shaper_name,
985         os.path.join('%s',
986                      'utilities',
987                      'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl'),
988         os.path.join('%s',
989                      'utilities',
990                      'ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl'),
991         shaper_input_scale_generic_log2,
992         lmt_params]
993
994     sorted_LMTs = sorted(lmt_info.iteritems(), key=lambda x: x[1])
995     print(sorted_LMTs)
996     for lmt in sorted_LMTs:
997         (lmt_name, lmt_values) = lmt
998         lmt_aliases = ["look_%s" % compact(lmt_values['transformUserName'])]
999         cs = create_ACES_LMT(
1000             lmt_values['transformUserName'],
1001             lmt_values,
1002             lmt_shaper_data,
1003             lmt_lut_resolution_1d,
1004             lmt_lut_resolution_3d,
1005             cleanup,
1006             lmt_aliases)
1007         config_data['colorSpaces'].append(cs)
1008
1009     # -------------------------------------------------------------------------
1010     # *ACES RRT* with supplied *ODT*.
1011     # -------------------------------------------------------------------------
1012     def create_ACES_RRT_plus_ODT(odt_name,
1013                                  odt_values,
1014                                  shaper_info,
1015                                  lut_resolution_1d=1024,
1016                                  lut_resolution_3d=64,
1017                                  cleanup=True,
1018                                  aliases=[]):
1019         cs = ColorSpace('%s' % odt_name)
1020         cs.description = '%s - %s Output Transform' % (
1021             odt_values['transformUserNamePrefix'], odt_name)
1022         cs.aliases = aliases
1023         cs.equality_group = ''
1024         cs.family = 'Output'
1025         cs.is_data = False
1026
1027         pprint.pprint(odt_values)
1028
1029         # Generating the *shaper* transform.
1030         (shaper_name,
1031          shaper_to_ACES_CTL,
1032          shaper_from_ACES_CTL,
1033          shaper_input_scale,
1034          shaper_params) = shaper_info
1035
1036         if 'legalRange' in odt_values:
1037             shaper_params['legalRange'] = odt_values['legalRange']
1038         else:
1039             shaper_params['legalRange'] = 0
1040
1041         shaper_lut = '%s_to_aces.spi1d' % shaper_name
1042         if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
1043             ctls = [shaper_to_ACES_CTL % aces_CTL_directory]
1044
1045             shaper_lut = sanitize_path(shaper_lut)
1046
1047             generate_1d_LUT_from_CTL(
1048                 os.path.join(lut_directory, shaper_lut),
1049                 ctls,
1050                 lut_resolution_1d,
1051                 'float',
1052                 1.0 / shaper_input_scale,
1053                 1.0,
1054                 shaper_params,
1055                 cleanup,
1056                 aces_CTL_directory)
1057
1058         shaper_OCIO_transform = {
1059             'type': 'lutFile',
1060             'path': shaper_lut,
1061             'interpolation': 'linear',
1062             'direction': 'inverse'}
1063
1064         # Generating the *forward* transform.
1065         cs.from_reference_transforms = []
1066
1067         if 'transformLUT' in odt_values:
1068             transform_LUT_file_name = os.path.basename(
1069                 odt_values['transformLUT'])
1070             lut = os.path.join(lut_directory, transform_LUT_file_name)
1071             shutil.copy(odt_values['transformLUT'], lut)
1072
1073             cs.from_reference_transforms.append(shaper_OCIO_transform)
1074             cs.from_reference_transforms.append({
1075                 'type': 'lutFile',
1076                 'path': transform_LUT_file_name,
1077                 'interpolation': 'tetrahedral',
1078                 'direction': 'forward'})
1079         elif 'transformCTL' in odt_values:
1080             ctls = [
1081                 shaper_to_ACES_CTL % aces_CTL_directory,
1082                 os.path.join(aces_CTL_directory,
1083                              'rrt',
1084                              'RRT.a1.0.0.ctl'),
1085                 os.path.join(aces_CTL_directory,
1086                              'odt',
1087                              odt_values['transformCTL'])]
1088             lut = '%s.RRT.a1.0.0.%s.spi3d' % (shaper_name, odt_name)
1089
1090             lut = sanitize_path(lut)
1091
1092             generate_3d_LUT_from_CTL(
1093                 os.path.join(lut_directory, lut),
1094                 # shaperLUT,
1095                 ctls,
1096                 lut_resolution_3d,
1097                 'float',
1098                 1.0 / shaper_input_scale,
1099                 1.0,
1100                 shaper_params,
1101                 cleanup,
1102                 aces_CTL_directory)
1103
1104             cs.from_reference_transforms.append(shaper_OCIO_transform)
1105             cs.from_reference_transforms.append({
1106                 'type': 'lutFile',
1107                 'path': lut,
1108                 'interpolation': 'tetrahedral',
1109                 'direction': 'forward'})
1110
1111         # Generating the *inverse* transform.
1112         cs.to_reference_transforms = []
1113
1114         if 'transformLUTInverse' in odt_values:
1115             transform_LUT_inverse_file_name = os.path.basename(
1116                 odt_values['transformLUTInverse'])
1117             lut = os.path.join(lut_directory, transform_LUT_inverse_file_name)
1118             shutil.copy(odt_values['transformLUTInverse'], lut)
1119
1120             cs.to_reference_transforms.append({
1121                 'type': 'lutFile',
1122                 'path': transform_LUT_inverse_file_name,
1123                 'interpolation': 'tetrahedral',
1124                 'direction': 'forward'})
1125
1126             shaper_inverse = shaper_OCIO_transform.copy()
1127             shaper_inverse['direction'] = 'forward'
1128             cs.to_reference_transforms.append(shaper_inverse)
1129         elif 'transformCTLInverse' in odt_values:
1130             ctls = [os.path.join(aces_CTL_directory,
1131                                  'odt',
1132                                  odt_values['transformCTLInverse']),
1133                     os.path.join(aces_CTL_directory,
1134                                  'rrt',
1135                                  'InvRRT.a1.0.0.ctl'),
1136                     shaper_from_ACES_CTL % aces_CTL_directory]
1137             lut = 'InvRRT.a1.0.0.%s.%s.spi3d' % (odt_name, shaper_name)
1138
1139             lut = sanitize_path(lut)
1140
1141             generate_3d_LUT_from_CTL(
1142                 os.path.join(lut_directory, lut),
1143                 # None,
1144                 ctls,
1145                 lut_resolution_3d,
1146                 'half',
1147                 1.0,
1148                 shaper_input_scale,
1149                 shaper_params,
1150                 cleanup,
1151                 aces_CTL_directory)
1152
1153             cs.to_reference_transforms.append({
1154                 'type': 'lutFile',
1155                 'path': lut,
1156                 'interpolation': 'tetrahedral',
1157                 'direction': 'forward'})
1158
1159             shaper_inverse = shaper_OCIO_transform.copy()
1160             shaper_inverse['direction'] = 'forward'
1161             cs.to_reference_transforms.append(shaper_inverse)
1162
1163         return cs
1164
1165     # -------------------------------------------------------------------------
1166     # *RRT / ODT* Shaper Options
1167     # -------------------------------------------------------------------------
1168     shaper_data = {}
1169
1170     # Defining the *Log 2* shaper.
1171     log2_shaper_name = shaper_name
1172     log2_shaper_name_aliases = ["crv_%s" % compact(shaper_name)]
1173     log2_params = {
1174         'middleGrey': 0.18,
1175         'minExposure': -6.0,
1176         'maxExposure': 6.5}
1177
1178     log2_shaper = create_generic_log(
1179         name=log2_shaper_name,
1180         middle_grey=log2_params['middleGrey'],
1181         min_exposure=log2_params['minExposure'],
1182         max_exposure=log2_params['maxExposure'],
1183         aliases=log2_shaper_name_aliases)
1184     config_data['colorSpaces'].append(log2_shaper)
1185
1186     shaper_input_scale_generic_log2 = 1.0
1187
1188     # *Log 2* shaper name and *CTL* transforms bundled up.
1189     log2_shaper_data = [
1190         log2_shaper_name,
1191         os.path.join('%s',
1192                      'utilities',
1193                      'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl'),
1194         os.path.join('%s',
1195                      'utilities',
1196                      'ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl'),
1197         shaper_input_scale_generic_log2,
1198         log2_params]
1199
1200     shaper_data[log2_shaper_name] = log2_shaper_data
1201
1202     # Shaper that also includes the AP1 primaries.
1203     # Needed for some LUT baking steps.
1204     log2_shaper_api1_name_aliases = ["%s_ap1" % compact(shaper_name)]
1205     log2_shaper_AP1 = create_generic_log(
1206         name=log2_shaper_name,
1207         middle_grey=log2_params['middleGrey'],
1208         min_exposure=log2_params['minExposure'],
1209         max_exposure=log2_params['maxExposure'],
1210         aliases=log2_shaper_api1_name_aliases)
1211     log2_shaper_AP1.name = '%s - AP1' % log2_shaper_AP1.name
1212
1213     # *AP1* primaries to *AP0* primaries.
1214     log2_shaper_AP1.to_reference_transforms.append({
1215         'type': 'matrix',
1216         'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
1217         'direction': 'forward'
1218     })
1219     config_data['colorSpaces'].append(log2_shaper_AP1)
1220
1221     rrt_shaper = log2_shaper_data
1222
1223     # *RRT + ODT* combinations.
1224     sorted_odts = sorted(odt_info.iteritems(), key=lambda x: x[1])
1225     print(sorted_odts)
1226     for odt in sorted_odts:
1227         (odt_name, odt_values) = odt
1228
1229         # Handling *ODTs* that can generate either *legal* or *full* output.
1230         if odt_name in ['Academy.Rec2020_100nits_dim.a1.0.0',
1231                         'Academy.Rec709_100nits_dim.a1.0.0',
1232                         'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1233             odt_name_legal = '%s - Legal' % odt_values['transformUserName']
1234         else:
1235             odt_name_legal = odt_values['transformUserName']
1236
1237         odt_legal = odt_values.copy()
1238         odt_legal['legalRange'] = 1
1239
1240         odt_aliases = ["out_%s" % compact(odt_name_legal)]
1241
1242         cs = create_ACES_RRT_plus_ODT(
1243             odt_name_legal,
1244             odt_legal,
1245             rrt_shaper,
1246             lut_resolution_1d,
1247             lut_resolution_3d,
1248             cleanup,
1249             odt_aliases)
1250         config_data['colorSpaces'].append(cs)
1251
1252         config_data['displays'][odt_name_legal] = {
1253             'Linear': ACES,
1254             'Log': ACEScc,
1255             'Output Transform': cs}
1256
1257         if odt_name in ['Academy.Rec2020_100nits_dim.a1.0.0',
1258                         'Academy.Rec709_100nits_dim.a1.0.0',
1259                         'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1260             print('Generating full range ODT for %s' % odt_name)
1261
1262             odt_name_full = '%s - Full' % odt_values['transformUserName']
1263             odt_full = odt_values.copy()
1264             odt_full['legalRange'] = 0
1265
1266             odt_full_aliases = ["out_%s" % compact(odt_name_full)]
1267
1268             cs_full = create_ACES_RRT_plus_ODT(
1269                 odt_name_full,
1270                 odt_full,
1271                 rrt_shaper,
1272                 lut_resolution_1d,
1273                 lut_resolution_3d,
1274                 cleanup,
1275                 odt_full_aliases)
1276             config_data['colorSpaces'].append(cs_full)
1277
1278             config_data['displays'][odt_name_full] = {
1279                 'Linear': ACES,
1280                 'Log': ACEScc,
1281                 'Output Transform': cs_full}
1282
1283     # -------------------------------------------------------------------------
1284     # Generic Matrix transform
1285     # -------------------------------------------------------------------------
1286     def create_generic_matrix(name='matrix',
1287                               from_reference_values=None,
1288                               to_reference_values=None,
1289                               aliases=[]):
1290
1291         if from_reference_values is None:
1292              from_reference_values = []
1293         if to_reference_values is None:
1294              to_reference_values = []
1295
1296         cs = ColorSpace(name)
1297         cs.description = 'The %s color space' % name
1298         cs.aliases = []
1299         cs.equality_group = name
1300         cs.family = 'Utility'
1301         cs.is_data = False
1302
1303         cs.to_reference_transforms = []
1304         if to_reference_values:
1305             for matrix in to_reference_values:
1306                 cs.to_reference_transforms.append({
1307                     'type': 'matrix',
1308                     'matrix': mat44_from_mat33(matrix),
1309                     'direction': 'forward'})
1310
1311         cs.from_reference_transforms = []
1312         if from_reference_values:
1313             for matrix in from_reference_values:
1314                 cs.from_reference_transforms.append({
1315                     'type': 'matrix',
1316                     'matrix': mat44_from_mat33(matrix),
1317                     'direction': 'forward'})
1318
1319         return cs
1320
1321     cs = create_generic_matrix('XYZ', 
1322         from_reference_values=[ACES_AP0_to_XYZ], 
1323         aliases=["lin_xyz"])
1324     config_data['colorSpaces'].append(cs)
1325
1326     cs = create_generic_matrix(
1327         'Linear - AP1', 
1328         to_reference_values=[ACES_AP1_to_AP0],
1329         aliases=["lin_ap1"])
1330     config_data['colorSpaces'].append(cs)
1331
1332     # *ACES* to *Linear*, *P3D60* primaries.
1333     XYZ_to_P3D60 = [2.4027414142, -0.8974841639, -0.3880533700,
1334                     -0.8325796487, 1.7692317536, 0.0237127115,
1335                     0.0388233815, -0.0824996856, 1.0363685997]
1336
1337     cs = create_generic_matrix(
1338         'Linear - P3-D60',
1339         from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_P3D60],
1340         aliases=["lin_p3d60"])
1341     config_data['colorSpaces'].append(cs)
1342
1343     # *ACES* to *Linear*, *P3DCI* primaries.
1344     XYZ_to_P3DCI = [2.7253940305, -1.0180030062, -0.4401631952,
1345                     -0.7951680258, 1.6897320548, 0.0226471906,
1346                     0.0412418914, -0.0876390192, 1.1009293786]
1347
1348     cs = create_generic_matrix(
1349         'Linear - P3-DCI',
1350         from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_P3DCI],
1351         aliases=["lin_p3dci"])
1352     config_data['colorSpaces'].append(cs)
1353
1354     # *ACES* to *Linear*, *Rec. 709* primaries.
1355     XYZ_to_Rec709 = [3.2409699419, -1.5373831776, -0.4986107603,
1356                      -0.9692436363, 1.8759675015, 0.0415550574,
1357                      0.0556300797, -0.2039769589, 1.0569715142]
1358
1359     cs = create_generic_matrix(
1360         'Linear - Rec.709',
1361         from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_Rec709],
1362         aliases=["lin_rec709"])
1363     config_data['colorSpaces'].append(cs)
1364
1365     # *ACES* to *Linear*, *Rec. 2020* primaries.
1366     XYZ_to_Rec2020 = [1.7166511880, -0.3556707838, -0.2533662814,
1367                       -0.6666843518, 1.6164812366, 0.0157685458,
1368                       0.0176398574, -0.0427706133, 0.9421031212]
1369
1370     cs = create_generic_matrix(
1371         'Linear - Rec.2020',
1372         from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_Rec2020],
1373         aliases=["lin_rec2020"])
1374     config_data['colorSpaces'].append(cs)
1375
1376     print('generateLUTs - end')
1377     return config_data
1378
1379
1380 def generate_baked_LUTs(odt_info,
1381                         shaper_name,
1382                         baked_directory,
1383                         config_path,
1384                         lut_resolution_1d,
1385                         lut_resolution_3d,
1386                         lut_resolution_shaper=1024):
1387     """
1388     Object description.
1389
1390     Parameters
1391     ----------
1392     parameter : type
1393         Parameter description.
1394
1395     Returns
1396     -------
1397     type
1398          Return value description.
1399     """
1400
1401     odt_info_C = dict(odt_info)
1402     for odt_CTL_name, odt_values in odt_info.iteritems():
1403         if odt_CTL_name in ['Academy.Rec2020_100nits_dim.a1.0.0',
1404                             'Academy.Rec709_100nits_dim.a1.0.0',
1405                             'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1406             odt_name = odt_values['transformUserName']
1407
1408             odt_values_legal = dict(odt_values)
1409             odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1410             odt_info_C['%s - Legal' % odt_CTL_name] = odt_values_legal
1411
1412             odt_values_full = dict(odt_values)
1413             odt_values_full['transformUserName'] = '%s - Full' % odt_name
1414             odt_info_C['%s - Full' % odt_CTL_name] = odt_values_full
1415
1416             del (odt_info_C[odt_CTL_name])
1417
1418     for odt_CTL_name, odt_values in odt_info_C.iteritems():
1419         odt_prefix = odt_values['transformUserNamePrefix']
1420         odt_name = odt_values['transformUserName']
1421
1422         # *Photoshop*
1423         for input_space in ['ACEScc', 'ACESproxy']:
1424             args = ['--iconfig', config_path,
1425                     '-v',
1426                     '--inputspace', input_space]
1427             args += ['--outputspace', '%s' % odt_name]
1428             args += ['--description',
1429                      '%s - %s for %s data' % (odt_prefix,
1430                                               odt_name,
1431                                               input_space)]
1432             args += ['--shaperspace', shaper_name,
1433                      '--shapersize', str(lut_resolution_shaper)]
1434             args += ['--cubesize', str(lut_resolution_3d)]
1435             args += ['--format',
1436                      'icc',
1437                      os.path.join(baked_directory,
1438                                   'photoshop',
1439                                   '%s for %s.icc' % (odt_name, input_space))]
1440
1441             bake_LUT = Process(description='bake a LUT',
1442                                cmd='ociobakelut',
1443                                args=args)
1444             bake_LUT.execute()
1445
1446         # *Flame*, *Lustre*
1447         for input_space in ['ACEScc', 'ACESproxy']:
1448             args = ['--iconfig', config_path,
1449                     '-v',
1450                     '--inputspace', input_space]
1451             args += ['--outputspace', '%s' % odt_name]
1452             args += ['--description',
1453                      '%s - %s for %s data' % (
1454                          odt_prefix, odt_name, input_space)]
1455             args += ['--shaperspace', shaper_name,
1456                      '--shapersize', str(lut_resolution_shaper)]
1457             args += ['--cubesize', str(lut_resolution_3d)]
1458
1459             fargs = ['--format',
1460                      'flame',
1461                      os.path.join(
1462                          baked_directory,
1463                          'flame',
1464                          '%s for %s Flame.3dl' % (odt_name, input_space))]
1465             bake_LUT = Process(description='bake a LUT',
1466                                cmd='ociobakelut',
1467                                args=(args + fargs))
1468             bake_LUT.execute()
1469
1470             largs = ['--format',
1471                      'lustre',
1472                      os.path.join(
1473                          baked_directory,
1474                          'lustre',
1475                          '%s for %s Lustre.3dl' % (odt_name, input_space))]
1476             bake_LUT = Process(description='bake a LUT',
1477                                cmd='ociobakelut',
1478                                args=(args + largs))
1479             bake_LUT.execute()
1480
1481         # *Maya*, *Houdini*
1482         for input_space in ['ACEScg', 'ACES2065-1']:
1483             args = ['--iconfig', config_path,
1484                     '-v',
1485                     '--inputspace', input_space]
1486             args += ['--outputspace', '%s' % odt_name]
1487             args += ['--description',
1488                      '%s - %s for %s data' % (
1489                          odt_prefix, odt_name, input_space)]
1490             if input_space == 'ACEScg':
1491                 lin_shaper_name = '%s - AP1' % shaper_name
1492             else:
1493                 lin_shaper_name = shaper_name
1494             args += ['--shaperspace', lin_shaper_name,
1495                      '--shapersize', str(lut_resolution_shaper)]
1496
1497             args += ['--cubesize', str(lut_resolution_3d)]
1498
1499             margs = ['--format',
1500                      'cinespace',
1501                      os.path.join(
1502                          baked_directory,
1503                          'maya',
1504                          '%s for %s Maya.csp' % (odt_name, input_space))]
1505             bake_LUT = Process(description='bake a LUT',
1506                                cmd='ociobakelut',
1507                                args=(args + margs))
1508             bake_LUT.execute()
1509
1510             hargs = ['--format',
1511                      'houdini',
1512                      os.path.join(
1513                          baked_directory,
1514                          'houdini',
1515                          '%s for %s Houdini.lut' % (odt_name, input_space))]
1516             bake_LUT = Process(description='bake a LUT',
1517                                cmd='ociobakelut',
1518                                args=(args + hargs))
1519             bake_LUT.execute()
1520
1521
1522 def create_config_dir(config_directory, bake_secondary_LUTs):
1523     """
1524     Object description.
1525
1526     Parameters
1527     ----------
1528     parameter : type
1529         Parameter description.
1530
1531     Returns
1532     -------
1533     type
1534          Return value description.
1535     """
1536
1537     dirs = [config_directory, os.path.join(config_directory, 'luts')]
1538     if bake_secondary_LUTs:
1539         dirs.extend([os.path.join(config_directory, 'baked'),
1540                      os.path.join(config_directory, 'baked', 'flame'),
1541                      os.path.join(config_directory, 'baked', 'photoshop'),
1542                      os.path.join(config_directory, 'baked', 'houdini'),
1543                      os.path.join(config_directory, 'baked', 'lustre'),
1544                      os.path.join(config_directory, 'baked', 'maya')])
1545
1546     for d in dirs:
1547         not os.path.exists(d) and os.mkdir(d)
1548
1549
1550 def get_transform_info(ctl_transform):
1551     """
1552     Object description.
1553
1554     Parameters
1555     ----------
1556     parameter : type
1557         Parameter description.
1558
1559     Returns
1560     -------
1561     type
1562          Return value description.
1563     """
1564
1565     with open(ctl_transform, 'rb') as fp:
1566         lines = fp.readlines()
1567
1568     # Retrieving the *transform ID* and *User Name*.
1569     transform_id = lines[1][3:].split('<')[1].split('>')[1].strip()
1570     transform_user_name = '-'.join(
1571         lines[2][3:].split('<')[1].split('>')[1].split('-')[1:]).strip()
1572     transform_user_name_prefix = (
1573         lines[2][3:].split('<')[1].split('>')[1].split('-')[0].strip())
1574
1575     return transform_id, transform_user_name, transform_user_name_prefix
1576
1577
1578 def get_ODT_info(aces_CTL_directory):
1579     """
1580     Object description.
1581
1582     For versions after WGR9.
1583
1584     Parameters
1585     ----------
1586     parameter : type
1587         Parameter description.
1588
1589     Returns
1590     -------
1591     type
1592          Return value description.
1593     """
1594
1595     # TODO: Investigate usage of *files_walker* definition here.
1596     # Credit to *Alex Fry* for the original approach here.
1597     odt_dir = os.path.join(aces_CTL_directory, 'odt')
1598     all_odt = []
1599     for dir_name, subdir_list, file_list in os.walk(odt_dir):
1600         for fname in file_list:
1601             all_odt.append((os.path.join(dir_name, fname)))
1602
1603     odt_CTLs = [x for x in all_odt if
1604                 ('InvODT' not in x) and (os.path.split(x)[-1][0] != '.')]
1605
1606     odts = {}
1607
1608     for odt_CTL in odt_CTLs:
1609         odt_tokens = os.path.split(odt_CTL)
1610
1611         # Handling nested directories.
1612         odt_path_tokens = os.path.split(odt_tokens[-2])
1613         odt_dir = odt_path_tokens[-1]
1614         while odt_path_tokens[-2][-3:] != 'odt':
1615             odt_path_tokens = os.path.split(odt_path_tokens[-2])
1616             odt_dir = os.path.join(odt_path_tokens[-1], odt_dir)
1617
1618         # Building full name,
1619         transform_CTL = odt_tokens[-1]
1620         odt_name = string.join(transform_CTL.split('.')[1:-1], '.')
1621
1622         # Finding id, user name and user name prefix.
1623         (transform_ID,
1624          transform_user_name,
1625          transform_user_name_prefix) = get_transform_info(
1626             os.path.join(aces_CTL_directory, 'odt', odt_dir, transform_CTL))
1627
1628         # Finding inverse.
1629         transform_CTL_inverse = 'InvODT.%s.ctl' % odt_name
1630         if not os.path.exists(
1631                 os.path.join(odt_tokens[-2], transform_CTL_inverse)):
1632             transform_CTL_inverse = None
1633
1634         # Add to list of ODTs
1635         odts[odt_name] = {}
1636         odts[odt_name]['transformCTL'] = os.path.join(odt_dir, transform_CTL)
1637         if transform_CTL_inverse is not None:
1638             odts[odt_name]['transformCTLInverse'] = os.path.join(
1639                 odt_dir, transform_CTL_inverse)
1640
1641         odts[odt_name]['transformID'] = transform_ID
1642         odts[odt_name]['transformUserNamePrefix'] = transform_user_name_prefix
1643         odts[odt_name]['transformUserName'] = transform_user_name
1644
1645         forward_CTL = odts[odt_name]['transformCTL']
1646
1647         print('ODT : %s' % odt_name)
1648         print('\tTransform ID               : %s' % transform_ID)
1649         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
1650         print('\tTransform User Name        : %s' % transform_user_name)
1651         print('\tForward ctl                : %s' % forward_CTL)
1652         if 'transformCTLInverse' in odts[odt_name]:
1653             inverse_CTL = odts[odt_name]['transformCTLInverse']
1654             print('\tInverse ctl                : %s' % inverse_CTL)
1655         else:
1656             print('\tInverse ctl                : %s' % 'None')
1657
1658     print('\n')
1659
1660     return odts
1661
1662
1663 def get_LMT_info(aces_CTL_directory):
1664     """
1665     Object description.
1666
1667     For versions after WGR9.
1668
1669     Parameters
1670     ----------
1671     parameter : type
1672         Parameter description.
1673
1674     Returns
1675     -------
1676     type
1677          Return value description.
1678     """
1679
1680     # TODO: Investigate refactoring with previous definition.
1681
1682     # Credit to Alex Fry for the original approach here
1683     lmt_dir = os.path.join(aces_CTL_directory, 'lmt')
1684     all_lmt = []
1685     for dir_name, subdir_list, file_list in os.walk(lmt_dir):
1686         for fname in file_list:
1687             all_lmt.append((os.path.join(dir_name, fname)))
1688
1689     lmt_CTLs = [x for x in all_lmt if
1690                 ('InvLMT' not in x) and ('README' not in x) and (
1691                     os.path.split(x)[-1][0] != '.')]
1692
1693     lmts = {}
1694
1695     for lmt_CTL in lmt_CTLs:
1696         lmt_tokens = os.path.split(lmt_CTL)
1697
1698         # Handlimg nested directories.
1699         lmt_path_tokens = os.path.split(lmt_tokens[-2])
1700         lmt_dir = lmt_path_tokens[-1]
1701         while lmt_path_tokens[-2][-3:] != 'ctl':
1702             lmt_path_tokens = os.path.split(lmt_path_tokens[-2])
1703             lmt_dir = os.path.join(lmt_path_tokens[-1], lmt_dir)
1704
1705         # Building full name.
1706         transform_CTL = lmt_tokens[-1]
1707         lmt_name = string.join(transform_CTL.split('.')[1:-1], '.')
1708
1709         # Finding id, user name and user name prefix.
1710         (transform_ID,
1711          transform_user_name,
1712          transform_user_name_prefix) = get_transform_info(
1713             os.path.join(aces_CTL_directory, lmt_dir, transform_CTL))
1714
1715         # Finding inverse.
1716         transform_CTL_inverse = 'InvLMT.%s.ctl' % lmt_name
1717         if not os.path.exists(
1718                 os.path.join(lmt_tokens[-2], transform_CTL_inverse)):
1719             transform_CTL_inverse = None
1720
1721         lmts[lmt_name] = {}
1722         lmts[lmt_name]['transformCTL'] = os.path.join(lmt_dir, transform_CTL)
1723         if transform_CTL_inverse is not None:
1724             lmts[lmt_name]['transformCTLInverse'] = os.path.join(
1725                 lmt_dir, transform_CTL_inverse)
1726
1727         lmts[lmt_name]['transformID'] = transform_ID
1728         lmts[lmt_name]['transformUserNamePrefix'] = transform_user_name_prefix
1729         lmts[lmt_name]['transformUserName'] = transform_user_name
1730
1731         forward_CTL = lmts[lmt_name]['transformCTL']
1732
1733         print('LMT : %s' % lmt_name)
1734         print('\tTransform ID               : %s' % transform_ID)
1735         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
1736         print('\tTransform User Name        : %s' % transform_user_name)
1737         print('\t Forward ctl               : %s' % forward_CTL)
1738         if 'transformCTLInverse' in lmts[lmt_name]:
1739             inverse_CTL = lmts[lmt_name]['transformCTLInverse']
1740             print('\t Inverse ctl                : %s' % inverse_CTL)
1741         else:
1742             print('\t Inverse ctl                : %s' % 'None')
1743
1744     print('\n')
1745
1746     return lmts
1747
1748
1749 def create_ACES_config(aces_CTL_directory,
1750                        config_directory,
1751                        lut_resolution_1d=4096,
1752                        lut_resolution_3d=64,
1753                        bake_secondary_LUTs=True,
1754                        cleanup=True):
1755     """
1756     Creates the ACES configuration.
1757
1758     Parameters
1759     ----------
1760     parameter : type
1761         Parameter description.
1762
1763     Returns
1764     -------
1765     type
1766          Return value description.
1767     """
1768
1769     odt_info = get_ODT_info(aces_CTL_directory)
1770     lmt_info = get_LMT_info(aces_CTL_directory)
1771
1772     create_config_dir(config_directory, bake_secondary_LUTs)
1773
1774     lut_directory = os.path.join(config_directory, 'luts')
1775     shaper_name = 'Output Shaper'
1776     config_data = generate_LUTs(odt_info,
1777                                 lmt_info,
1778                                 shaper_name,
1779                                 aces_CTL_directory,
1780                                 lut_directory,
1781                                 lut_resolution_1d,
1782                                 lut_resolution_3d,
1783                                 cleanup)
1784
1785     print('Creating "generic" config')
1786     config = create_config(config_data)
1787     print('\n\n\n')
1788
1789     write_config(config,
1790                  os.path.join(config_directory, 'config.ocio'))
1791
1792     print('Creating "Nuke" config')
1793     nuke_config = create_config(config_data, nuke=True)
1794     print('\n\n\n')
1795
1796     write_config(nuke_config,
1797                  os.path.join(config_directory, 'nuke_config.ocio'))
1798
1799     if bake_secondary_LUTs:
1800         generate_baked_LUTs(odt_info,
1801                             shaper_name,
1802                             os.path.join(config_directory, 'baked'),
1803                             os.path.join(config_directory, 'config.ocio'),
1804                             lut_resolution_1d,
1805                             lut_resolution_3d,
1806                             lut_resolution_1d)
1807
1808     return True
1809
1810
1811 def main():
1812     """
1813     Object description.
1814
1815     Parameters
1816     ----------
1817     parameter : type
1818         Parameter description.
1819
1820     Returns
1821     -------
1822     type
1823          Return value description.
1824     """
1825
1826     import optparse
1827
1828     p = optparse.OptionParser(description='An OCIO config generation script',
1829                               prog='createACESConfig',
1830                               version='createACESConfig 0.1',
1831                               usage='%prog [options]')
1832     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1833         ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1834     p.add_option('--configDir', '-c', default=os.environ.get(
1835         ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1836     p.add_option('--lutResolution1d', default=4096)
1837     p.add_option('--lutResolution3d', default=64)
1838     p.add_option('--dontBakeSecondaryLUTs', action='store_true')
1839     p.add_option('--keepTempImages', action='store_true')
1840
1841     options, arguments = p.parse_args()
1842
1843     aces_CTL_directory = options.acesCTLDir
1844     config_directory = options.configDir
1845     lut_resolution_1d = int(options.lutResolution1d)
1846     lut_resolution_3d = int(options.lutResolution3d)
1847     bake_secondary_LUTs = not options.dontBakeSecondaryLUTs
1848     cleanup_temp_images = not options.keepTempImages
1849
1850     # TODO: Investigate the following statements.
1851     try:
1852         args_start = sys.argv.index('--') + 1
1853         args = sys.argv[args_start:]
1854     except:
1855         args_start = len(sys.argv) + 1
1856         args = []
1857
1858     print('command line : \n%s\n' % ' '.join(sys.argv))
1859
1860     assert aces_CTL_directory is not None, (
1861         'process: No "{0}" environment variable defined or no "ACES CTL" '
1862         'directory specified'.format(
1863             ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1864
1865     assert config_directory is not None, (
1866         'process: No "{0}" environment variable defined or no configuration '
1867         'directory specified'.format(
1868             ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1869
1870     return create_ACES_config(aces_CTL_directory,
1871                               config_directory,
1872                               lut_resolution_1d,
1873                               lut_resolution_3d,
1874                               bake_secondary_LUTs,
1875                               cleanup_temp_images)
1876
1877
1878 if __name__ == '__main__':
1879     main()