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