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