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