cce8ae9d3efec10c893205ce0929f73025221e37
[OpenColorIO-Configs.git] / aces_1.0.1 / python / aces_ocio / generate_config.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Defines objects creating the *ACES* configuration.
6 """
7
8 from __future__ import division
9
10 import copy
11 import os
12 import shutil
13 import sys
14
15 import PyOpenColorIO as ocio
16 from aces_ocio.colorspaces import aces
17 from aces_ocio.colorspaces import arri
18 from aces_ocio.colorspaces import canon
19 from aces_ocio.colorspaces import general
20 from aces_ocio.colorspaces import gopro
21 from aces_ocio.colorspaces import panasonic
22 from aces_ocio.colorspaces import red
23 from aces_ocio.colorspaces import sony
24 from aces_ocio.process import Process
25
26 from aces_ocio.utilities import (
27     ColorSpace,
28     colorspace_prefixed_name,
29     compact,
30     replace,
31     unpack_default)
32
33 __author__ = 'ACES Developers'
34 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
35 __license__ = ''
36 __maintainer__ = 'ACES Developers'
37 __email__ = 'aces@oscars.org'
38 __status__ = 'Production'
39
40 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
41            'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
42            'set_config_roles',
43            'create_ocio_transform',
44            'add_colorspace_aliases',
45            'add_look',
46            'add_looks_to_views',
47            'create_config',
48            'create_config_data',
49            'write_config',
50            'generate_baked_LUTs',
51            'generate_config_directory',
52            'generate_config',
53            'main']
54
55 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
56 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
57
58
59 def set_config_roles(config,
60                      color_picking=None,
61                      color_timing=None,
62                      compositing_log=None,
63                      data=None,
64                      default=None,
65                      matte_paint=None,
66                      reference=None,
67                      scene_linear=None,
68                      texture_paint=None,
69                      rendering=None,
70                      compositing_linear=None):
71     """
72     Sets given *OCIO* configuration roles to the config.
73     Parameters
74     ----------
75     config : Config
76         *OCIO* configuration.
77     color_picking : str or unicode, optional
78         Color Picking role title.
79     color_timing : str or unicode, optional
80         Color Timing role title.
81     compositing_log : str or unicode, optional
82         Compositing Log role title.
83     data : str or unicode, optional
84         Data role title.
85     default : str or unicode, optional
86         Default role title.
87     matte_paint : str or unicode, optional
88         Matte Painting role title.
89     reference : str or unicode, optional
90         Reference role title.
91     scene_linear : str or unicode, optional
92         Scene Linear role title.
93     texture_paint : str or unicode, optional
94         Texture Painting role title.
95     rendering : str or unicode, optional
96         Rendering role title.
97     compositing_linear : str or unicode, optional
98         Compositing Linear role title.
99     Returns
100     -------
101     bool
102          Definition success.
103     """
104
105     if color_picking is not None:
106         config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
107     if color_timing is not None:
108         config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
109     if compositing_log is not None:
110         config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
111     if data is not None:
112         config.setRole(ocio.Constants.ROLE_DATA, data)
113     if default is not None:
114         config.setRole(ocio.Constants.ROLE_DEFAULT, default)
115     if matte_paint is not None:
116         config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
117     if reference is not None:
118         config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
119     if texture_paint is not None:
120         config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
121
122     # *rendering* and *compositing_linear* roles default to the *scene_linear*
123     # value if not set explicitly.
124     if rendering is not None:
125         config.setRole('rendering', rendering)
126     if compositing_linear is not None:
127         config.setRole('compositing_linear', compositing_linear)
128     if scene_linear is not None:
129         config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
130         if rendering is None:
131             config.setRole('rendering', scene_linear)
132         if compositing_linear is None:
133             config.setRole('compositing_linear', scene_linear)
134
135     return True
136
137
138 def create_ocio_transform(transforms):
139     """
140     Returns an *OCIO* transform from given array of transform descriptions.
141
142     Parameters
143     ----------
144     transforms : array_like
145         Transform descriptions as an array_like of dicts:
146         {'type', 'src', 'dst', 'direction'}
147
148     Returns
149     -------
150     Transform
151          *OCIO* transform.
152     """
153
154     direction_options = {
155         'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
156         'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
157
158     ocio_transforms = []
159
160     for transform in transforms:
161
162         # *lutFile* transform
163         if transform['type'] == 'lutFile':
164             ocio_transform = ocio.FileTransform()
165
166             if 'path' in transform:
167                 ocio_transform.setSrc(transform['path'])
168
169             if 'cccid' in transform:
170                 ocio_transform.setCCCId(transform['cccid'])
171
172             if 'interpolation' in transform:
173                 ocio_transform.setInterpolation(transform['interpolation'])
174             else:
175                 ocio_transform.setInterpolation(ocio.Constants.INTERP_BEST)
176
177             if 'direction' in transform:
178                 ocio_transform.setDirection(
179                     direction_options[transform['direction']])
180
181             ocio_transforms.append(ocio_transform)
182
183         # *matrix* transform
184         elif transform['type'] == 'matrix':
185             ocio_transform = ocio.MatrixTransform()
186             # `MatrixTransform` member variables can't be initialized directly,
187             # each must be set individually.
188             ocio_transform.setMatrix(transform['matrix'])
189
190             if 'offset' in transform:
191                 ocio_transform.setOffset(transform['offset'])
192
193             if 'direction' in transform:
194                 ocio_transform.setDirection(
195                     direction_options[transform['direction']])
196
197             ocio_transforms.append(ocio_transform)
198
199         # *exponent* transform
200         elif transform['type'] == 'exponent':
201             ocio_transform = ocio.ExponentTransform()
202
203             if 'value' in transform:
204                 ocio_transform.setValue(transform['value'])
205
206             ocio_transforms.append(ocio_transform)
207
208         # *log* transform
209         elif transform['type'] == 'log':
210             ocio_transform = ocio.LogTransform()
211
212             if 'base' in transform:
213                 ocio_transform.setBase(transform['base'])
214
215             if 'direction' in transform:
216                 ocio_transform.setDirection(
217                     direction_options[transform['direction']])
218
219             ocio_transforms.append(ocio_transform)
220
221         # *colorspace* transform
222         elif transform['type'] == 'colorspace':
223             ocio_transform = ocio.ColorSpaceTransform()
224
225             if 'src' in transform:
226                 ocio_transform.setSrc(transform['src'])
227
228             if 'dst' in transform:
229                 ocio_transform.setDst(transform['dst'])
230
231             if 'direction' in transform:
232                 ocio_transform.setDirection(
233                     direction_options[transform['direction']])
234
235             ocio_transforms.append(ocio_transform)
236
237         # *look* transform
238         elif transform['type'] == 'look':
239             ocio_transform = ocio.LookTransform()
240             if 'look' in transform:
241                 ocio_transform.setLooks(transform['look'])
242
243             if 'src' in transform:
244                 ocio_transform.setSrc(transform['src'])
245
246             if 'dst' in transform:
247                 ocio_transform.setDst(transform['dst'])
248
249             if 'direction' in transform:
250                 ocio_transform.setDirection(
251                     direction_options[transform['direction']])
252
253             ocio_transforms.append(ocio_transform)
254
255         # *unknown* type
256         else:
257             print('Ignoring unknown transform type : %s' % transform['type'])
258
259     if len(ocio_transforms) > 1:
260         group_transform = ocio.GroupTransform()
261         for transform in ocio_transforms:
262             group_transform.push_back(transform)
263         transform = group_transform
264     else:
265         transform = ocio_transforms[0]
266
267     return transform
268
269
270 def add_colorspace_aliases(config,
271                            reference_colorspace,
272                            colorspace,
273                            colorspace_alias_names,
274                            family='Aliases'):
275     """
276     Adds given colorspace aliases to the *OCIO* config.
277
278     Parameters
279     ----------
280     config : Config
281         *OCIO* configuration.
282     reference_colorspace : Colorspace
283         Reference colorspace.
284     colorspace : Colorspace
285         Colorspace to set the aliases into the *OCIO* config.
286     family : unicode
287         Family.
288
289     Returns
290     -------
291     bool
292         Definition success.
293     """
294
295     for alias_name in colorspace_alias_names:
296         if alias_name.lower() == colorspace.name.lower():
297             print('Skipping alias creation for %s, alias %s, '
298                   'because lower cased names match' % (
299                       colorspace.name, alias_name))
300             continue
301
302         print('Adding alias colorspace space %s, alias to %s' % (
303             alias_name, colorspace.name))
304
305         compact_family_name = family
306
307         description = colorspace.description
308         if colorspace.aces_transform_id:
309             description += (
310                 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
311
312         ocio_colorspace_alias = ocio.ColorSpace(
313             name=alias_name,
314             bitDepth=colorspace.bit_depth,
315             description=description,
316             equalityGroup=colorspace.equality_group,
317             family=compact_family_name,
318             isData=colorspace.is_data,
319             allocation=colorspace.allocation_type,
320             allocationVars=colorspace.allocation_vars)
321
322         if colorspace.to_reference_transforms:
323             print('\tGenerating To-Reference transforms')
324             ocio_transform = create_ocio_transform(
325                 [{'type': 'colorspace',
326                   'src': colorspace.name,
327                   'dst': reference_colorspace.name,
328                   'direction': 'forward'}])
329             ocio_colorspace_alias.setTransform(
330                 ocio_transform,
331                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
332
333         if colorspace.from_reference_transforms:
334             print('\tGenerating From-Reference transforms')
335             ocio_transform = create_ocio_transform(
336                 [{'type': 'colorspace',
337                   'src': reference_colorspace.name,
338                   'dst': colorspace.name,
339                   'direction': 'forward'}])
340             ocio_colorspace_alias.setTransform(
341                 ocio_transform,
342                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
343
344         config.addColorSpace(ocio_colorspace_alias)
345
346
347 def add_look(config,
348              look,
349              custom_lut_dir,
350              reference_name,
351              config_data):
352     """
353     Adds given look to the *OCIO* config.
354
355     Parameters
356     ----------
357     config : Config
358         *OCIO* configuration.
359     look : array_like
360         Look description: {'name', 'colorspace', 'lut', 'cccid'}
361     custom_lut_dir : str or unicode
362         Directory to copy the look lut into.
363     reference_name : str or unicode
364         Reference name.
365     config_data : dict
366         Colorspaces and transforms converting between those colorspaces and
367         the reference colorspace, *ACES*.
368
369     Returns
370     -------
371     bool
372         Definition success.
373     """
374
375     look_name, look_colorspace, look_lut, look_cccid = unpack_default(look, 4)
376
377     print('Adding look %s - %s' % (look_name, ', '.join(look)))
378
379     # Copy *look LUT* if `custom_lut_dir` is provided.
380     if custom_lut_dir:
381         if '$' not in look_lut:
382             print('Getting ready to copy look lut : %s' % look_lut)
383             shutil.copy2(look_lut, custom_lut_dir)
384             look_lut = os.path.split(look_lut)[1]
385         else:
386             print('Skipping LUT copy because path contains a context variable')
387
388     print('Adding look to config')
389     ocio_look = ocio.Look()
390     ocio_look.setName(look_name)
391     ocio_look.setProcessSpace(look_colorspace)
392
393     keys = {'type': 'lutFile',
394             'path': look_lut,
395             'direction': 'forward'}
396     if look_cccid:
397         keys['cccid'] = look_cccid
398
399     ocio_transform = create_ocio_transform([keys])
400     ocio_look.setTransform(ocio_transform)
401
402     config.addLook(ocio_look)
403
404     print('Creating aliased colorspace')
405
406     # Creating *OCIO* colorspace referencing the look:
407     # - Needed for implementations that don't process looks properly.
408     # - Needed for implementations that don't expose looks properly.
409     look_aliases = ['look_%s' % compact(look_name)]
410     colorspace = ColorSpace(look_name,
411                             aliases=look_aliases,
412                             description='The %s Look colorspace' % look_name,
413                             family='Look')
414
415     colorspace.from_reference_transforms = [{'type': 'look',
416                                              'look': look_name,
417                                              'src': reference_name,
418                                              'dst': reference_name,
419                                              'direction': 'forward'}]
420
421     print('Adding colorspace %s, alias to look %s to config data' % (
422         look_name, look_name))
423
424     config_data['colorSpaces'].append(colorspace)
425
426     print('')
427
428
429 def add_looks_to_views(looks,
430                        reference_name,
431                        config_data,
432                        multiple_displays=False):
433     """
434     Integrates a set of looks into the *OCIO* config's Displays and Views
435
436     Parameters
437     ----------
438     looks : array of str or unicode
439         Names of looks
440     reference_name : str or unicode
441         The name of the *OCIO* reference colorspace
442     config_data : dict
443         Colorspaces and transforms converting between those colorspaces and
444         the reference colorspace, *ACES*.
445     multiple_displays : bool
446         If true, looks are added to the config_data looks list
447         If false, looks are integrated directly into the list of displays and 
448         views. This may be necessary due to limitations of some applications' 
449         currently implementation of OCIO, ex. Maya 2016.
450
451     Returns
452     -------
453     None
454     """
455     look_names = [look[0] for look in looks]
456
457     # Option 1
458     # - Adding a *look* per *Display*.
459     # - Assuming there is a *Display* for each *ACES* *Output Transform*.
460     if multiple_displays:
461         for look_name in look_names:
462             config_data['looks'].append(look_name)
463
464     # Option 2
465     # - Copy each *Output Transform* colorspace.
466     # - For each copy, add a *LookTransform* to the head of the
467     # `from_reference` transform list.
468     # - Add these the copy colorspaces for the *Displays* / *Views*.
469     else:
470         for display, view_list in config_data['displays'].iteritems():
471             colorspace_c = None
472             look_names_string = ''
473             for view_name, output_colorspace in view_list.iteritems():
474                 if view_name == 'Output Transform':
475
476                     print('Adding new View that incorporates looks')
477
478                     colorspace_c = copy.deepcopy(output_colorspace)
479
480                     for i, look_name in enumerate(look_names):
481                         look_name = look_names[i]
482
483                         # Add the `LookTransform` to the head of the
484                         # `from_reference` transform list.
485                         if colorspace_c.from_reference_transforms:
486                             colorspace_c.from_reference_transforms.insert(
487                                 i,
488                                 {'type': 'look',
489                                  'look': look_name,
490                                  'src': reference_name,
491                                  'dst': reference_name,
492                                  'direction': 'forward'})
493
494                         # Add the `LookTransform` to the end of
495                         # the `to_reference` transform list.
496                         if colorspace_c.to_reference_transforms:
497                             inverse_look_name = look_names[
498                                 len(look_names) - 1 - i]
499
500                             colorspace_c.to_reference_transforms.append(
501                                 {'type': 'look',
502                                  'look': inverse_look_name,
503                                  'src': reference_name,
504                                  'dst': reference_name,
505                                  'direction': 'inverse'})
506
507                         if look_name not in config_data['looks']:
508                             config_data['looks'].append(look_name)
509
510                     look_names_string = ', '.join(look_names)
511                     colorspace_c.name = '%s with %s' % (
512                         output_colorspace.name, look_names_string)
513                     colorspace_c.aliases = [
514                         'out_%s' % compact(colorspace_c.name)]
515
516                     print('Colorspace that incorporates looks '
517                           'created : %s' % colorspace_c.name)
518
519                     config_data['colorSpaces'].append(colorspace_c)
520
521             if colorspace_c:
522                 print('Adding colorspace that incorporates looks '
523                       'into view list')
524
525                 # Updating the *View* name.
526                 view_list['Output Transform with %s' % look_names_string] = (
527                     colorspace_c)
528                 config_data['displays'][display] = view_list
529
530
531 def create_config(config_data,
532                   aliases=False,
533                   prefix=False,
534                   multiple_displays=False,
535                   look_info=None,
536                   custom_lut_dir=None):
537     """
538     Create the *OCIO* config based on the configuration data
539
540     Parameters
541     ----------
542     config_data : dict
543         Colorspaces and transforms converting between those colorspaces and
544         the reference colorspace, *ACES*, along with other data needed to 
545         generate a complete *OCIO* configuration
546     aliases : bool, optional
547         Whether or not to include Alias colorspaces 
548     prefix : bool, optional
549         Whether or not to prefix the colorspace names with their Family names
550     multiple_displays : bool, optional
551         Whether to create a single display named *ACES* with Views for each
552         Output Transform or multiple displays, one for each Output Transform
553     look_info : array of str or unicode, optional
554         Paths and names for look data
555     custom_lut_dir : str or unicode, optional
556         Directory to use for storing custom look files
557
558     Returns
559     -------
560     *OCIO* config
561          The constructed OCIO configuration
562     """
563
564     if look_info is None:
565         look_info = []
566
567     prefixed_names = {}
568     alias_colorspaces = []
569
570     config = ocio.Config()
571
572     config.setDescription('An ACES config generated from python')
573
574     search_path = ['luts']
575     if custom_lut_dir:
576         search_path.append('custom')
577     config.setSearchPath(':'.join(search_path))
578
579     reference_data = config_data['referenceColorSpace']
580
581     # Adding the colorspace *Family* into the name which helps with
582     # applications that presenting colorspaces as one a flat list.
583     if prefix:
584         prefixed_name = colorspace_prefixed_name(reference_data)
585         prefixed_names[reference_data.name] = prefixed_name
586         reference_data.name = prefixed_name
587
588     print('Adding the reference color space : %s' % reference_data.name)
589
590     reference = ocio.ColorSpace(
591         name=reference_data.name,
592         bitDepth=reference_data.bit_depth,
593         description=reference_data.description,
594         equalityGroup=reference_data.equality_group,
595         family=reference_data.family,
596         isData=reference_data.is_data,
597         allocation=reference_data.allocation_type,
598         allocationVars=reference_data.allocation_vars)
599
600     config.addColorSpace(reference)
601
602     if aliases:
603         if reference_data.aliases:
604             # Deferring adding alias colorspaces until end, which helps with
605             # applications listing the colorspaces in the order that they were
606             # defined in the configuration: alias colorspaces are usually named
607             # lower case with spaces but normal colorspaces names are longer
608             # and more verbose, thus it becomes harder for user to visually
609             # parse the list of colorspaces when there are names such as
610             # "crv_canonlog" interspersed with names like
611             # "Input - Canon - Curve - Canon-Log".
612             # Moving the alias colorspace definitions to the end of the
613             # configuration avoids the above problem.
614             alias_colorspaces.append(
615                 [reference_data, reference_data, reference_data.aliases])
616
617     print('')
618
619     if look_info:
620         print('Adding looks')
621
622         config_data['looks'] = []
623
624         for look in look_info:
625             add_look(config,
626                      look,
627                      custom_lut_dir,
628                      reference_data.name,
629                      config_data)
630
631         add_looks_to_views(look_info,
632                            reference_data.name,
633                            config_data,
634                            multiple_displays)
635
636         print('')
637
638     print('Adding regular colorspaces')
639
640     for colorspace in sorted(config_data['colorSpaces'],
641         cmp=lambda x,y: cmp(x.family.lower(), y.family.lower())):
642         # Adding the colorspace *Family* into the name which helps with
643         # applications that presenting colorspaces as one a flat list.
644         if prefix:
645             prefixed_name = colorspace_prefixed_name(colorspace)
646             prefixed_names[colorspace.name] = prefixed_name
647             colorspace.name = prefixed_name
648
649         print('Creating new color space : %s' % colorspace.name)
650
651         description = colorspace.description
652         if colorspace.aces_transform_id:
653             description += (
654                 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
655
656         ocio_colorspace = ocio.ColorSpace(
657             name=colorspace.name,
658             bitDepth=colorspace.bit_depth,
659             description=description,
660             equalityGroup=colorspace.equality_group,
661             family=colorspace.family,
662             isData=colorspace.is_data,
663             allocation=colorspace.allocation_type,
664             allocationVars=colorspace.allocation_vars)
665
666         if colorspace.to_reference_transforms:
667             print('\tGenerating To-Reference transforms')
668             ocio_transform = create_ocio_transform(
669                 colorspace.to_reference_transforms)
670             ocio_colorspace.setTransform(
671                 ocio_transform,
672                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
673
674         if colorspace.from_reference_transforms:
675             print('\tGenerating From-Reference transforms')
676             ocio_transform = create_ocio_transform(
677                 colorspace.from_reference_transforms)
678             ocio_colorspace.setTransform(
679                 ocio_transform,
680                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
681
682         config.addColorSpace(ocio_colorspace)
683
684         if aliases:
685             if colorspace.aliases:
686                 # Deferring adding alias colorspaces until end, which helps
687                 # with applications listing the colorspaces in the order that
688                 # they were defined in the configuration.
689                 alias_colorspaces.append(
690                     [reference_data, colorspace, colorspace.aliases])
691
692         print('')
693
694     print('')
695
696     # Adding roles early so that alias colorspaces can be created
697     # with roles names before remaining colorspace aliases are added
698     # to the configuration.
699     print('Setting the roles')
700
701     if prefix:
702         set_config_roles(
703             config,
704             color_picking=prefixed_names[
705                 config_data['roles']['color_picking']],
706             color_timing=prefixed_names[config_data['roles']['color_timing']],
707             compositing_log=prefixed_names[
708                 config_data['roles']['compositing_log']],
709             data=prefixed_names[config_data['roles']['data']],
710             default=prefixed_names[config_data['roles']['default']],
711             matte_paint=prefixed_names[config_data['roles']['matte_paint']],
712             reference=prefixed_names[config_data['roles']['reference']],
713             scene_linear=prefixed_names[config_data['roles']['scene_linear']],
714             compositing_linear=prefixed_names[config_data['roles']['scene_linear']],
715             rendering=prefixed_names[config_data['roles']['scene_linear']],
716             texture_paint=prefixed_names[
717                 config_data['roles']['texture_paint']])
718
719         # Add the aliased colorspaces for each role
720         for role_name, role_colorspace_name in config_data['roles'].iteritems():
721             role_colorspace_prefixed_name = prefixed_names[role_colorspace_name]
722
723             #print( 'Finding colorspace : %s' % role_colorspace_prefixed_name )
724             # Find the colorspace pointed to by the role
725             role_colorspaces = [colorspace
726                 for colorspace in config_data['colorSpaces']
727                 if colorspace.name == role_colorspace_prefixed_name]
728             role_colorspace = None
729             if len(role_colorspaces) > 0:
730                 role_colorspace = role_colorspaces[0]
731             else:
732                 if reference_data.name == role_colorspace_prefixed_name:
733                     role_colorspace = reference_data
734
735             if role_colorspace:
736                 # The alias colorspace shouldn't match the role name exactly
737                 role_name_alias1 = "role_%s" % role_name
738                 role_name_alias2 = "Role - %s" % role_name
739
740                 print( 'Adding a role colorspace named %s, pointing to %s' % (
741                     role_name_alias2, role_colorspace.name))
742
743                 alias_colorspaces.append(
744                 (reference_data, role_colorspace, [role_name_alias1]))
745
746                 add_colorspace_aliases(
747                 config, reference_data, role_colorspace, [role_name_alias2], 'Roles')
748
749     else:
750         set_config_roles(
751             config,
752             color_picking=config_data['roles']['color_picking'],
753             color_timing=config_data['roles']['color_timing'],
754             compositing_log=config_data['roles']['compositing_log'],
755             data=config_data['roles']['data'],
756             default=config_data['roles']['default'],
757             matte_paint=config_data['roles']['matte_paint'],
758             reference=config_data['roles']['reference'],
759             scene_linear=config_data['roles']['scene_linear'],
760             compositing_linear=config_data['roles']['scene_linear'],
761             rendering=config_data['roles']['scene_linear'],
762             texture_paint=config_data['roles']['texture_paint'])
763
764         # Add the aliased colorspaces for each role
765         for role_name, role_colorspace_name in config_data['roles'].iteritems():
766             # Find the colorspace pointed to by the role
767             role_colorspaces = [colorspace
768             for colorspace in config_data['colorSpaces']
769             if colorspace.name == role_colorspace_name]
770             role_colorspace = None
771             if len(role_colorspaces) > 0:
772                 role_colorspace = role_colorspaces[0]
773             else:
774                 if reference_data.name == role_colorspace_name:
775                     role_colorspace = reference_data
776
777             if role_colorspace:
778                 # The alias colorspace shouldn't match the role name exactly
779                 role_name_alias1 = "role_%s" % role_name
780                 role_name_alias2 = "Role - %s" % role_name
781
782                 print('Adding a role colorspace named %s, pointing to %s' % (
783                     role_name_alias2, role_colorspace.name))
784
785                 alias_colorspaces.append(
786                 (reference_data, role_colorspace, [role_name_alias1]))
787
788                 add_colorspace_aliases(
789                 config, reference_data, role_colorspace, [role_name_alias2], 'Roles')
790
791     print('')
792
793     # Adding alias colorspaces at the end as some applications use
794     # colorspaces definitions order of the configuration to order
795     # the colorspaces in their selection lists, some applications
796     # use alphabetical ordering.
797     # This should keep the alias colorspaces out of the way for applications
798     # using the configuration order.
799     print('Adding the alias colorspaces')
800     for reference, colorspace, aliases in alias_colorspaces:
801         add_colorspace_aliases(config, reference, colorspace, aliases)
802
803     print('')
804
805     print('Adding the diplays and views')
806
807     # Setting the *color_picking* role to be the first *Display*'s
808     # *Output Transform* *View*.
809     default_display_name = config_data['defaultDisplay']
810     default_display_views = config_data['displays'][default_display_name]
811     default_display_colorspace = default_display_views['Output Transform']
812
813     # Defining *Displays* and *Views*.
814     displays, views = [], []
815
816     # Defining a generic *Display* and *View* setup.
817     if multiple_displays:
818         looks = config_data['looks'] if ('looks' in config_data) else []
819         looks = ', '.join(looks)
820         print('Creating multiple displays, with looks : %s' % looks)
821
822         # *Displays* are not reordered to put the *defaultDisplay* first
823         # because *OCIO* will order them alphabetically when the configuration
824         # is written to disk.
825         for display, view_list in config_data['displays'].iteritems():
826             for view_name, colorspace in view_list.iteritems():
827                 config.addDisplay(display, view_name, colorspace.name, looks)
828                 if 'Output Transform' in view_name and looks != '':
829                     # *Views* without *Looks*.
830                     config.addDisplay(display, view_name, colorspace.name)
831
832                     # *Views* with *Looks*.
833                     view_name_with_looks = '%s with %s' % (view_name, looks)
834                     config.addDisplay(display, view_name_with_looks,
835                                       colorspace.name, looks)
836                 else:
837                     config.addDisplay(display, view_name, colorspace.name)
838                 if not (view_name in views):
839                     views.append(view_name)
840             displays.append(display)
841
842     # *Displays* and *Views* useful in a *GUI* context.
843     else:
844         single_display_name = 'ACES'
845         displays.append(single_display_name)
846
847         # Ensuring the *defaultDisplay* is first.
848         display_names = sorted(config_data['displays'])
849         display_names.insert(0, display_names.pop(
850             display_names.index(default_display_name)))
851
852         looks = config_data['looks'] if ('looks' in config_data) else []
853         look_names = ', '.join(looks)
854
855         displays_views_colorspaces = []
856
857         for display in display_names:
858             view_list = config_data['displays'][display]
859             for view_name, colorspace in view_list.iteritems():
860                 if 'Output Transform' in view_name:
861
862                     # We use the *Display* names as the *View* names in this
863                     # case as there is a single *Display* containing all the
864                     # *Views*.
865                     # This works for more applications than not,as of the time
866                     # of this implementation.
867
868                     # Autodesk Maya 2016 doesn't support parentheses in
869                     # *View* names.
870                     sanitised_display = replace(display, {')': '', '(': ''})
871
872                     # *View* with *Looks*.
873                     if 'with' in view_name:
874                         sanitised_display = '%s with %s' % (
875                             sanitised_display, look_names)
876
877                         views_with_looks_at_end = False
878                         # Storing combo of *Display*, *View* and *Colorspace*
879                         # name so they can be added to the end of the list.
880                         if views_with_looks_at_end:
881                             displays_views_colorspaces.append(
882                                 [single_display_name, sanitised_display,
883                                  colorspace.name])
884                         else:
885                             config.addDisplay(single_display_name,
886                                               sanitised_display,
887                                               colorspace.name)
888
889                             if not (sanitised_display in views):
890                                 views.append(sanitised_display)
891
892                     # *View* without *Looks*.
893                     else:
894                         config.addDisplay(single_display_name,
895                                           sanitised_display,
896                                           colorspace.name)
897
898                         if not (sanitised_display in views):
899                             views.append(sanitised_display)
900
901         # Adding to the configuration any *Display*, *View* combinations that
902         # were saved for later.
903         # This list should be empty unless `views_with_looks_at_end` is
904         # set `True` above.
905         for display_view_colorspace in displays_views_colorspaces:
906             single_display_name, sanitised_display, colorspace_name = (
907                 display_view_colorspace)
908
909             config.addDisplay(single_display_name,
910                               sanitised_display,
911                               colorspace_name)
912
913             if not (sanitised_display in views):
914                 views.append(sanitised_display)
915
916         raw_display_space_name = config_data['roles']['data']
917         log_display_space_name = config_data['roles']['compositing_log']
918
919         if prefix:
920             raw_display_space_name = prefixed_names[raw_display_space_name]
921             log_display_space_name = prefixed_names[log_display_space_name]
922
923         config.addDisplay(single_display_name, 'Raw', raw_display_space_name)
924         views.append('Raw')
925         config.addDisplay(single_display_name, 'Log', log_display_space_name)
926         views.append('Log')
927
928     config.setActiveDisplays(','.join(sorted(displays)))
929     config.setActiveViews(','.join(views))
930
931     print('')
932
933     # Ensuring the configuration is valid.
934     config.sanityCheck()
935
936     # Resetting colorspace names to their non-prefixed versions.
937     if prefix:
938         prefixed_names_inverse = {}
939         for original, prefixed in prefixed_names.iteritems():
940             prefixed_names_inverse[prefixed] = original
941
942         reference_data.name = prefixed_names_inverse[reference_data.name]
943
944         try:
945             for colorspace in config_data['colorSpaces']:
946                 colorspace.name = prefixed_names_inverse[colorspace.name]
947         except:
948             print('Error with Prefixed names')
949             for original, prefixed in prefixed_names.iteritems():
950                 print('%s, %s' % (original, prefixed))
951
952             print('\n')
953
954             print('Inverse Lookup of Prefixed names')
955             for prefixed, original in prefixed_names_inverse.iteritems():
956                 print('%s, %s' % (prefixed, original))
957             raise
958
959     return config
960
961
962 def create_config_data(odt_info,
963                        lmt_info,
964                        shaper_name,
965                        aces_ctl_directory,
966                        lut_directory,
967                        lut_resolution_1d=4096,
968                        lut_resolution_3d=64,
969                        cleanup=True):
970     """
971     Create the *ACES* LUTs and data structures needed for later *OCIO* 
972     configuration generation
973
974     Parameters
975     ----------
976     odt_info : array of dicts of str or unicode
977         Descriptions of the *ACES* Output Transforms
978     lmt_info : array of dicts of str or unicode
979         Descriptions of the *ACES* Look Transforms
980     shaper_name : str or unicode
981         The name of the Shaper function to use when generating LUTs. 
982         Options: Log2, DolbyPQ
983     aces_ctl_directory : str or unicode
984         The path to the aces 'transforms/ctl/utilities'
985     lut_directory : str or unicode
986         The path to use when writing LUTs
987     lut_resolution_1d : int, optional
988         The resolution of generated 1D LUTs
989     lut_resolution_3d : int, optional
990         The resolution of generated 3D LUTs
991     cleanup : bool
992         Whether or not to clean up the intermediate images 
993
994     Returns
995     -------
996     dict
997          Colorspaces, LUT paths and transforms converting between those 
998          colorspaces and the reference colorspace, *ACES*.
999     """
1000
1001     print('create_config_data - begin')
1002     config_data = {}
1003
1004     config_data['displays'] = {}
1005     config_data['colorSpaces'] = []
1006
1007     # -------------------------------------------------------------------------
1008     # *ACES Color Spaces*
1009     # -------------------------------------------------------------------------
1010
1011     # *ACES* colorspaces
1012     (aces_reference,
1013      aces_colorspaces,
1014      aces_displays,
1015      aces_log_display_space,
1016      aces_roles,
1017      aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
1018                                                      lut_directory,
1019                                                      lut_resolution_1d,
1020                                                      lut_resolution_3d,
1021                                                      lmt_info,
1022                                                      odt_info,
1023                                                      shaper_name,
1024                                                      cleanup)
1025
1026     config_data['referenceColorSpace'] = aces_reference
1027     config_data['roles'] = aces_roles
1028
1029     for cs in aces_colorspaces:
1030         config_data['colorSpaces'].append(cs)
1031
1032     for name, data in aces_displays.iteritems():
1033         config_data['displays'][name] = data
1034
1035     config_data['defaultDisplay'] = aces_default_display
1036     config_data['linearDisplaySpace'] = aces_reference
1037     config_data['logDisplaySpace'] = aces_log_display_space
1038
1039     # -------------------------------------------------------------------------
1040     # *Camera Input Transforms*
1041     # -------------------------------------------------------------------------
1042
1043     # *ARRI Log-C* to *ACES*
1044     arri_colorspaces = arri.create_colorspaces(lut_directory,
1045                                                lut_resolution_1d)
1046     for cs in arri_colorspaces:
1047         config_data['colorSpaces'].append(cs)
1048
1049     # *Canon-Log* to *ACES*
1050     canon_colorspaces = canon.create_colorspaces(lut_directory,
1051                                                  lut_resolution_1d)
1052     for cs in canon_colorspaces:
1053         config_data['colorSpaces'].append(cs)
1054
1055     # *GoPro Protune* to *ACES*
1056     gopro_colorspaces = gopro.create_colorspaces(lut_directory,
1057                                                  lut_resolution_1d)
1058     for cs in gopro_colorspaces:
1059         config_data['colorSpaces'].append(cs)
1060
1061     # *Panasonic V-Log* to *ACES*
1062     panasonic_colorspaces = panasonic.create_colorspaces(lut_directory,
1063                                                          lut_resolution_1d)
1064     for cs in panasonic_colorspaces:
1065         config_data['colorSpaces'].append(cs)
1066
1067     # *RED* colorspaces to *ACES*
1068     red_colorspaces = red.create_colorspaces(lut_directory,
1069                                              lut_resolution_1d)
1070     for cs in red_colorspaces:
1071         config_data['colorSpaces'].append(cs)
1072
1073     # *S-Log* to *ACES*
1074     sony_colorspaces = sony.create_colorspaces(lut_directory,
1075                                                lut_resolution_1d)
1076     for cs in sony_colorspaces:
1077         config_data['colorSpaces'].append(cs)
1078
1079     # -------------------------------------------------------------------------
1080     # General Colorspaces
1081     # -------------------------------------------------------------------------
1082     general_colorspaces = general.create_colorspaces(lut_directory,
1083                                                      lut_resolution_1d)
1084     for cs in general_colorspaces:
1085         config_data['colorSpaces'].append(cs)
1086
1087     # The *Raw* colorspace
1088     raw = general.create_raw()
1089     config_data['colorSpaces'].append(raw)
1090
1091     # Overriding various roles
1092     config_data['roles']['data'] = raw.name
1093     config_data['roles']['reference'] = raw.name
1094     config_data['roles']['texture_paint'] = raw.name
1095
1096     print('create_config_data - end')
1097
1098     return config_data
1099
1100
1101 def write_config(config, config_path, sanity_check=True):
1102     """
1103     Writes the configuration to given path.
1104
1105     Parameters
1106     ----------
1107     config : Config
1108         *OCIO* configuration.
1109     config_path : str or unicode
1110         Path to write the configuration path.
1111     sanity_check : bool
1112         Performs configuration sanity checking prior to writing it on disk.
1113
1114     Returns
1115     -------
1116     bool
1117          Definition success.
1118     """
1119
1120     if sanity_check:
1121         try:
1122             config.sanityCheck()
1123         except Exception, e:
1124             print e
1125             print 'Configuration was not written due to a failed Sanity Check'
1126             return
1127
1128     with open(config_path, mode='w') as fp:
1129         fp.write(config.serialize())
1130
1131
1132 def generate_baked_LUTs(odt_info,
1133                         shaper_name,
1134                         baked_directory,
1135                         config_path,
1136                         lut_resolution_3d,
1137                         lut_resolution_shaper=1024,
1138                         prefix=False):
1139     """
1140     Generate baked representations of the transforms from the *ACES* *OCIO*
1141     configuration
1142
1143     Parameters
1144     ----------
1145     odt_info : array of dicts of str or unicode
1146         Descriptions of the *ACES* Output Transforms
1147     shaper_name : str or unicode
1148         The name of the Shaper function to use when generating LUTs. 
1149         Options: Log2, DolbyPQ
1150     baked_directory : str or unicode
1151         The path to use when writing baked LUTs
1152     config_path : str or unicode
1153         The path to the *OCIO* configuration
1154     lut_resolution_3d : int, optional
1155         The resolution of generated 3D LUTs
1156     lut_resolution_shaper : int, optional
1157         The resolution of shaper used as part of some 3D LUTs
1158     prefix : bool, optional
1159         Whether or not colorspace names will use their Family names as prefixes
1160         in the *OCIO* config
1161
1162     Returns
1163     -------
1164     None
1165     """
1166
1167     odt_info_C = dict(odt_info)
1168
1169     # Older behavior for *ODTs* that have support for full and legal ranges,
1170     # generating a LUT for both ranges.
1171     """
1172     # Create two entries for ODTs that have full and legal range support
1173     for odt_ctl_name, odt_values in odt_info.iteritems():
1174         if odt_values['transformHasFullLegalSwitch']:
1175             odt_name = odt_values['transformUserName']
1176
1177             odt_values_legal = dict(odt_values)
1178             odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1179             odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
1180
1181             odt_values_full = dict(odt_values)
1182             odt_values_full['transformUserName'] = '%s - Full' % odt_name
1183             odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
1184
1185             del (odt_info_C[odt_ctl_name])
1186     """
1187
1188     for odt_ctl_name, odt_values in odt_info_C.iteritems():
1189         odt_prefix = odt_values['transformUserNamePrefix']
1190         odt_name = odt_values['transformUserName']
1191
1192         if odt_name in ['P3-D60 ST2048 (1000 nits)', 'Rec.2020 ST2048 (1000 nits)']:
1193             odt_shaper = shaper_name.replace("48 nits", "1000 nits")
1194         elif odt_name in ['P3-D60 ST2048 (2000 nits)']:
1195             odt_shaper = shaper_name.replace("48 nits", "2000 nits")
1196         elif odt_name in ['P3-D60 ST2048 (4000 nits)']:
1197             odt_shaper = shaper_name.replace("48 nits", "4000 nits")
1198         else:
1199             odt_shaper = shaper_name
1200
1201         # *Photoshop*
1202         for input_space in ['ACEScc', 'ACESproxy']:
1203             args = ['--iconfig', config_path,
1204                     '-v']
1205             if prefix:
1206                 args += ['--inputspace', 'ACES - %s' % input_space]
1207                 args += ['--outputspace', 'Output - %s' % odt_name]
1208             else:
1209                 args += ['--inputspace', input_space]
1210                 args += ['--outputspace', odt_name]
1211
1212             args += ['--description',
1213                      '%s - %s for %s data' % (odt_prefix,
1214                                               odt_name,
1215                                               input_space)]
1216             if prefix:
1217                 args += ['--shaperspace', 'Utility - %s' % odt_shaper,
1218                          '--shapersize', str(lut_resolution_shaper)]
1219             else:
1220                 args += ['--shaperspace', odt_shaper,
1221                          '--shapersize', str(lut_resolution_shaper)]
1222             args += ['--cubesize', str(lut_resolution_3d)]
1223             args += ['--format',
1224                      'icc',
1225                      os.path.join(baked_directory,
1226                                   'photoshop',
1227                                   '%s for %s.icc' % (odt_name, input_space))]
1228
1229             bake_lut = Process(description='bake a LUT',
1230                                cmd='ociobakelut',
1231                                args=args)
1232             bake_lut.execute()
1233
1234         # *Flame*, *Lustre*
1235         for input_space in ['ACEScc', 'ACESproxy']:
1236             args = ['--iconfig', config_path,
1237                     '-v']
1238             if prefix:
1239                 args += ['--inputspace', 'ACES - %s' % input_space]
1240                 args += ['--outputspace', 'Output - %s' % odt_name]
1241             else:
1242                 args += ['--inputspace', input_space]
1243                 args += ['--outputspace', odt_name]
1244             args += ['--description',
1245                      '%s - %s for %s data' % (
1246                          odt_prefix, odt_name, input_space)]
1247             if prefix:
1248                 args += ['--shaperspace', 'Utility - %s' % odt_shaper,
1249                          '--shapersize', str(lut_resolution_shaper)]
1250             else:
1251                 args += ['--shaperspace', odt_shaper,
1252                          '--shapersize', str(lut_resolution_shaper)]
1253             args += ['--cubesize', str(lut_resolution_3d)]
1254
1255             fargs = ['--format',
1256                      'flame',
1257                      os.path.join(
1258                          baked_directory,
1259                          'flame',
1260                          '%s for %s Flame.3dl' % (odt_name, input_space))]
1261             bake_lut = Process(description='bake a LUT',
1262                                cmd='ociobakelut',
1263                                args=(args + fargs))
1264             bake_lut.execute()
1265
1266             largs = ['--format',
1267                      'lustre',
1268                      os.path.join(
1269                          baked_directory,
1270                          'lustre',
1271                          '%s for %s Lustre.3dl' % (odt_name, input_space))]
1272             bake_lut = Process(description='bake a LUT',
1273                                cmd='ociobakelut',
1274                                args=(args + largs))
1275             bake_lut.execute()
1276
1277         # *Maya*, *Houdini*
1278         for input_space in ['ACEScg', 'ACES2065-1']:
1279             args = ['--iconfig', config_path,
1280                     '-v']
1281             if prefix:
1282                 args += ['--inputspace', 'ACES - %s' % input_space]
1283                 args += ['--outputspace', 'Output - %s' % odt_name]
1284             else:
1285                 args += ['--inputspace', input_space]
1286                 args += ['--outputspace', odt_name]
1287             args += ['--description',
1288                      '%s - %s for %s data' % (
1289                          odt_prefix, odt_name, input_space)]
1290             if input_space == 'ACEScg':
1291                 lin_shaper_name = '%s - AP1' % odt_shaper
1292             else:
1293                 lin_shaper_name = odt_shaper
1294             if prefix:
1295                 lin_shaper_name = 'Utility - %s' % lin_shaper_name
1296             args += ['--shaperspace', lin_shaper_name,
1297                      '--shapersize', str(lut_resolution_shaper)]
1298
1299             args += ['--cubesize', str(lut_resolution_3d)]
1300
1301             margs = ['--format',
1302                      'cinespace',
1303                      os.path.join(
1304                          baked_directory,
1305                          'maya',
1306                          '%s for %s Maya.csp' % (odt_name, input_space))]
1307             bake_lut = Process(description='bake a LUT',
1308                                cmd='ociobakelut',
1309                                args=(args + margs))
1310             bake_lut.execute()
1311
1312             hargs = ['--format',
1313                      'houdini',
1314                      os.path.join(
1315                          baked_directory,
1316                          'houdini',
1317                          '%s for %s Houdini.lut' % (odt_name, input_space))]
1318             bake_lut = Process(description='bake a LUT',
1319                                cmd='ociobakelut',
1320                                args=(args + hargs))
1321             bake_lut.execute()
1322
1323
1324 def generate_config_directory(config_directory,
1325                               bake_secondary_luts=False,
1326                               custom_lut_dir=None):
1327     """
1328     Create the directories needed for configuration generation
1329
1330     Parameters
1331     ----------
1332     config_directory : str or unicode
1333         The base config directory
1334     bake_secondary_luts : bool, optional
1335         Whether or not to create directories for baked LUTs
1336     custom_lut_dir : bool, optional
1337         Whether or not to create directories for custom Look LUTs
1338
1339     Returns
1340     -------
1341     None
1342     """
1343
1344     lut_directory = os.path.join(config_directory, 'luts')
1345     dirs = [config_directory, lut_directory]
1346
1347     if bake_secondary_luts:
1348         dirs.extend([os.path.join(config_directory, 'baked'),
1349                      os.path.join(config_directory, 'baked', 'flame'),
1350                      os.path.join(config_directory, 'baked', 'photoshop'),
1351                      os.path.join(config_directory, 'baked', 'houdini'),
1352                      os.path.join(config_directory, 'baked', 'lustre'),
1353                      os.path.join(config_directory, 'baked', 'maya')])
1354
1355     if custom_lut_dir:
1356         dirs.append(os.path.join(config_directory, 'custom'))
1357
1358     for d in dirs:
1359         not os.path.exists(d) and os.mkdir(d)
1360
1361     return lut_directory
1362
1363
1364 def generate_config(aces_ctl_directory,
1365                     config_directory,
1366                     lut_resolution_1d=4096,
1367                     lut_resolution_3d=64,
1368                     bake_secondary_luts=True,
1369                     multiple_displays=False,
1370                     look_info=None,
1371                     copy_custom_luts=True,
1372                     cleanup=True,
1373                     prefix_colorspaces_with_family_names=True,
1374                     shaper_base_name='Log2'):
1375     """
1376     Generates LUTs, matrices and configuration data and then creates the 
1377     *ACES* configuration.
1378
1379     Parameters
1380     ----------
1381     aces_ctl_directory : str or unicode
1382         The path to the aces 'transforms/ctl/utilities'
1383     config_directory : str or unicode
1384         The directory that will hold the generated configuration and LUTs
1385     lut_resolution_1d : int, optional
1386         The resolution of generated 1D LUTs
1387     lut_resolution_3d : int, optional
1388         The resolution of generated 3D LUTs
1389     bake_secondary_luts : bool, optional
1390         Whether or not to create directories for baked LUTs
1391     multiple_displays : bool, optional
1392         Whether to create a single display named *ACES* with Views for each
1393         Output Transform or multiple displays, one for each Output Transform
1394     look_info : array of str or unicode, optional
1395         Paths and names for look data
1396     copy_custom_luts : bool, optional
1397         Whether to reference custom look LUTs directly or to copy them into a 
1398         directory within the generated configuration
1399     cleanup : bool, optional
1400         Whether or not to clean up the intermediate images 
1401     prefix_colorspaces_with_family_names : bool, optional
1402         Whether or not colorspace names will use their Family names as prefixes
1403         in the *OCIO* config
1404     shaper_base_name : str or unicode
1405         The name of the Shaper function to use when generating LUTs. 
1406         Options: Log2, DolbyPQ
1407
1408     Returns
1409     -------
1410     bool
1411          Success or failure of configuration generation process
1412     """
1413
1414     if look_info is None:
1415         look_info = []
1416
1417     custom_lut_dir = None
1418     if copy_custom_luts:
1419         custom_lut_dir = os.path.join(config_directory, 'custom')
1420
1421     lut_directory = generate_config_directory(config_directory,
1422                                               bake_secondary_luts,
1423                                               custom_lut_dir)
1424     odt_info = aces.get_ODTs_info(aces_ctl_directory)
1425     lmt_info = aces.get_LMTs_info(aces_ctl_directory)
1426
1427     if shaper_base_name == 'DolbyPQ':
1428         shaper_name = 'Dolby PQ 48 nits Shaper'
1429     else:
1430         shaper_name = 'Log2 48 nits Shaper'
1431
1432     config_data = create_config_data(odt_info,
1433                                      lmt_info,
1434                                      shaper_name,
1435                                      aces_ctl_directory,
1436                                      lut_directory,
1437                                      lut_resolution_1d,
1438                                      lut_resolution_3d,
1439                                      cleanup)
1440
1441     print('Creating config - with prefixes, with aliases')
1442     config = create_config(config_data,
1443                            prefix=prefix_colorspaces_with_family_names,
1444                            aliases=True,
1445                            multiple_displays=multiple_displays,
1446                            look_info=look_info,
1447                            custom_lut_dir=custom_lut_dir)
1448     print('\n\n\n')
1449
1450     write_config(config,
1451                  os.path.join(config_directory, 'config.ocio'))
1452
1453     if bake_secondary_luts:
1454         generate_baked_LUTs(odt_info,
1455                             shaper_name,
1456                             os.path.join(config_directory, 'baked'),
1457                             os.path.join(config_directory, 'config.ocio'),
1458                             lut_resolution_3d,
1459                             lut_resolution_1d,
1460                             prefix=prefix_colorspaces_with_family_names)
1461
1462     return True
1463
1464
1465 def main():
1466     """
1467     A simple main that allows the user to exercise the various functions
1468     defined in this file
1469
1470     Parameters
1471     ----------
1472     None
1473
1474     Returns
1475     -------
1476     None
1477     """
1478
1479     import optparse
1480
1481     usage = '%prog [options]\n'
1482     usage += '\n'
1483     usage += 'An OCIO config generation script for ACES 1.0.1\n'
1484     usage += '\n'
1485     usage += 'Command-line examples'
1486     usage += '\n'
1487     usage += ('Create a GUI-friendly ACES 1.0.1 config with no secondary, '
1488               'baked LUTs: \n')
1489     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1490               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1491               '--dontBakeSecondaryLUTs')
1492     usage += '\n'
1493     usage += 'Create a more OCIO-compliant ACES 1.0.1 config: \n'
1494     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1495               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1496               '--createMultipleDisplays')
1497     usage += '\n'
1498     usage += '\n'
1499     usage += 'Adding custom looks'
1500     usage += '\n'
1501     usage += ('Create a GUI-friendly ACES 1.0.1 config with an ACES-style CDL '
1502               '(will be applied in the ACEScc colorspace): \n')
1503     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1504               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1505               '\n\t\t--addACESLookCDL ACESCDLName '
1506               '/path/to/SampleCDL.ccc cc03345')
1507     usage += '\n'
1508     usage += 'Create a GUI-friendly ACES 1.0.1 config with a general CDL: \n'
1509     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1510               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1511               '\n\t\t--addCustomLookCDL CustomCDLName "ACES - ACEScc" '
1512               '/path/to/SampleCDL.ccc cc03345')
1513     usage += '\n'
1514     usage += ('\tIn this example, the CDL will be applied in the '
1515               'ACEScc colorspace, but the user could choose other spaces '
1516               'by changing the argument after the name of the look. \n')
1517     usage += '\n'
1518     usage += ('Create a GUI-friendly ACES 1.0.1 config with an ACES-style LUT '
1519               '(will be applied in the ACEScc colorspace): \n')
1520     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1521               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1522               '\n\t\t--addACESLookLUT ACESLUTName '
1523               '/path/to/SampleCDL.ccc cc03345')
1524     usage += '\n'
1525     usage += 'Create a GUI-friendly ACES 1.0.1 config with a general LUT: \n'
1526     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1527               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1528               '\n\t\t--addCustomLookLUT CustomLUTName "ACES - ACEScc" '
1529               '/path/to/SampleCDL.ccc cc03345')
1530     usage += '\n'
1531     usage += ('\tIn this example, the LUT will be applied in the '
1532               'ACEScc colorspace, but the user could choose other spaces '
1533               'by changing the argument after the name of the look. \n')
1534     usage += '\n'
1535     usage += ('Create a GUI-friendly ACES 1.0.1 config using the Dolby PQ '
1536               'transfer function as the shaper: \n')
1537     usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1538               '--lutResolution1d 4096 --lutResolution3d 65 -c aces_1.0.1 '
1539               '--shaper DolbyPQ')
1540     usage += '\n'
1541  
1542     look_info = []
1543
1544     def look_info_callback(option, opt_str, value, parser):
1545         print('look_info_callback')
1546         print(option, opt_str, value, parser)
1547         if opt_str == '--addCustomLookCDL':
1548             look_info.append(value)
1549         elif opt_str == '--addCustomLookLUT':
1550             look_info.append(value)
1551         elif opt_str == '--addACESLookCDL':
1552             look_info.append([value[0], 'ACES - ACEScc', value[1], value[2]])
1553         elif opt_str == '--addACESLookLUT':
1554             look_info.append([value[0], 'ACES - ACEScc', value[1]])
1555
1556     p = optparse.OptionParser(description='',
1557                               prog='create_aces_config',
1558                               version='create_aces_config 1.0',
1559                               usage=usage)
1560     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1561         ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1562     p.add_option('--configDir', '-c', default=os.environ.get(
1563         ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1564     p.add_option('--lutResolution1d', default=4096)
1565     p.add_option('--lutResolution3d', default=64)
1566     p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
1567     p.add_option('--keepTempImages', action='store_true', default=False)
1568
1569     p.add_option('--createMultipleDisplays', action='store_true',
1570                  default=False)
1571
1572     p.add_option('--addCustomLookLUT', '', type='string', nargs=3,
1573                  action='callback', callback=look_info_callback)
1574     p.add_option('--addCustomLookCDL', '', type='string', nargs=4,
1575                  action='callback', callback=look_info_callback)
1576     p.add_option('--addACESLookLUT', '', type='string', nargs=2,
1577                  action='callback', callback=look_info_callback)
1578     p.add_option('--addACESLookCDL', '', type='string', nargs=3,
1579                  action='callback', callback=look_info_callback)
1580     p.add_option('--copyCustomLUTs', action='store_true', default=False)
1581
1582     p.add_option('--shaper', '-s', default='Log2')
1583
1584     options, arguments = p.parse_args()
1585
1586     aces_ctl_directory = options.acesCTLDir
1587     config_directory = options.configDir
1588     lut_resolution_1d = int(options.lutResolution1d)
1589     lut_resolution_3d = int(options.lutResolution3d)
1590     bake_secondary_luts = not options.dontBakeSecondaryLUTs
1591     cleanup_temp_images = not options.keepTempImages
1592     multiple_displays = options.createMultipleDisplays
1593     copy_custom_luts = options.copyCustomLUTs
1594     shaper_base_name = options.shaper
1595     prefix = True
1596
1597     print(look_info)
1598
1599     print('command line : \n%s\n' % ' '.join(sys.argv))
1600
1601     assert aces_ctl_directory is not None, (
1602         'process: No "{0}" environment variable defined or no "ACES CTL" '
1603         'directory specified'.format(
1604             ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1605
1606     assert config_directory is not None, (
1607         'process: No "{0}" environment variable defined or no configuration '
1608         'directory specified'.format(
1609             ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1610
1611     return generate_config(aces_ctl_directory,
1612                            config_directory,
1613                            lut_resolution_1d,
1614                            lut_resolution_3d,
1615                            bake_secondary_luts,
1616                            multiple_displays,
1617                            look_info,
1618                            copy_custom_luts,
1619                            cleanup_temp_images,
1620                            prefix,
1621                            shaper_base_name)
1622
1623
1624 if __name__ == '__main__':
1625     main()