Added --createMultipleDisplays option and some usage instructions.
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / aces_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 os
11 import sys
12
13 import PyOpenColorIO as ocio
14 from aces_ocio.colorspaces import aces
15 from aces_ocio.colorspaces import arri
16 from aces_ocio.colorspaces import canon
17 from aces_ocio.colorspaces import general
18 from aces_ocio.colorspaces import gopro
19 from aces_ocio.colorspaces import panasonic
20 from aces_ocio.colorspaces import red
21 from aces_ocio.colorspaces import sony
22 from aces_ocio.process import Process
23
24 from aces_ocio.utilities import replace
25
26 __author__ = 'ACES Developers'
27 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
28 __license__ = ''
29 __maintainer__ = 'ACES Developers'
30 __email__ = 'aces@oscars.org'
31 __status__ = 'Production'
32
33 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
34            'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
35            'set_config_default_roles',
36            'write_config',
37            'generate_OCIO_transform',
38            'add_colorspace_alias',
39            'create_config',
40            'generate_LUTs',
41            'generate_baked_LUTs',
42            'create_config_dir',
43            'create_ACES_config',
44            'main']
45
46 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
47 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
48
49
50 def set_config_default_roles(config,
51                              color_picking='',
52                              color_timing='',
53                              compositing_log='',
54                              data='',
55                              default='',
56                              matte_paint='',
57                              reference='',
58                              scene_linear='',
59                              texture_paint='',
60                              rendering='',
61                              compositing_linear=''):
62     """
63     Sets given *OCIO* configuration default roles.
64
65     Parameters
66     ----------
67     config : config
68         *OCIO* configuration.
69     color_picking : str or unicode
70         Color picking role title.
71     color_timing : str or unicode
72         Color timing role title.
73     compositing_log : str or unicode
74         Compositing log role title.
75     data : str or unicode
76         Data role title.
77     default : str or unicode
78         Default role title.
79     matte_paint : str or unicode
80         Matte painting role title.
81     reference : str or unicode
82         Reference role title.
83     scene_linear : str or unicode
84         Scene linear role title.
85     texture_paint : str or unicode
86         Texture painting role title.
87
88     Returns
89     -------
90     bool
91          Definition success.
92     """
93
94     if color_picking:
95         config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
96     if color_timing:
97         config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
98     if compositing_log:
99         config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
100     if data:
101         config.setRole(ocio.Constants.ROLE_DATA, data)
102     if default:
103         config.setRole(ocio.Constants.ROLE_DEFAULT, default)
104     if matte_paint:
105         config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
106     if reference:
107         config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
108     if texture_paint:
109         config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
110
111     # 'rendering' and 'compositing_linear' roles default to the 'scene_linear'
112     # value if not set explicitly
113     if rendering:
114         config.setRole("rendering", rendering)
115     if compositing_linear:
116         config.setRole("compositing_linear", compositing_linear)
117     if scene_linear:
118         config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
119         if not rendering:
120             config.setRole("rendering", scene_linear)
121         if not compositing_linear:
122             config.setRole("compositing_linear", scene_linear)
123
124     return True
125
126
127 def write_config(config, config_path, sanity_check=True):
128     """
129     Writes the configuration to given path.
130
131     Parameters
132     ----------
133     parameter : type
134         Parameter description.
135
136     Returns
137     -------
138     type
139          Return value description.
140     """
141
142     if sanity_check:
143         try:
144             config.sanityCheck()
145         except Exception, e:
146             print e
147             print 'Configuration was not written due to a failed Sanity Check'
148             return
149
150     with open(config_path, mode='w') as fp:
151         fp.write(config.serialize())
152
153
154 def generate_OCIO_transform(transforms):
155     """
156     Object description.
157
158     Parameters
159     ----------
160     parameter : type
161         Parameter description.
162
163     Returns
164     -------
165     type
166          Return value description.
167     """
168
169     interpolation_options = {
170         'linear': ocio.Constants.INTERP_LINEAR,
171         'nearest': ocio.Constants.INTERP_NEAREST,
172         'tetrahedral': ocio.Constants.INTERP_TETRAHEDRAL}
173
174     direction_options = {
175         'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
176         'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
177
178     ocio_transforms = []
179
180     for transform in transforms:
181
182         # lutFile transform
183         if transform['type'] == 'lutFile':
184             ocio_transform = ocio.FileTransform(
185                 src=transform['path'],
186                 interpolation=interpolation_options[
187                     transform['interpolation']],
188                 direction=direction_options[transform['direction']])
189             ocio_transforms.append(ocio_transform)
190
191         # matrix transform
192         elif transform['type'] == 'matrix':
193             ocio_transform = ocio.MatrixTransform()
194             # MatrixTransform member variables can't be initialized directly.
195             # Each must be set individually.
196             ocio_transform.setMatrix(transform['matrix'])
197
198             if 'offset' in transform:
199                 ocio_transform.setOffset(transform['offset'])
200
201             if 'direction' in transform:
202                 ocio_transform.setDirection(
203                     direction_options[transform['direction']])
204
205             ocio_transforms.append(ocio_transform)
206
207         # exponent transform
208         elif transform['type'] == 'exponent':
209             ocio_transform = ocio.ExponentTransform()
210             ocio_transform.setValue(transform['value'])
211             ocio_transforms.append(ocio_transform)
212
213         # log transform
214         elif transform['type'] == 'log':
215             ocio_transform = ocio.LogTransform(
216                 base=transform['base'],
217                 direction=direction_options[transform['direction']])
218
219             ocio_transforms.append(ocio_transform)
220
221         # color space transform
222         elif transform['type'] == 'colorspace':
223             ocio_transform = ocio.ColorSpaceTransform(src=transform['src'],
224                                                       dst=transform['dst'],
225                                                       direction=
226                                                       direction_options[
227                                                           'forward'])
228             ocio_transforms.append(ocio_transform)
229         # unknown type
230         else:
231             print("Ignoring unknown transform type : %s" % transform['type'])
232
233     if len(ocio_transforms) > 1:
234         group_transform = ocio.GroupTransform()
235         for transform in ocio_transforms:
236             group_transform.push_back(transform)
237         transform = group_transform
238     else:
239         transform = ocio_transforms[0]
240
241     return transform
242
243
244 def add_colorspace_alias(config,
245                          reference_colorspace,
246                          colorspace,
247                          colorspace_alias_names):
248     """
249     Object description.
250
251     Parameters
252     ----------
253     parameter : type
254         Parameter description.
255
256     Returns
257     -------
258     type
259          Return value description.
260     """
261
262     for alias_name in colorspace_alias_names:
263         if alias_name.lower() == colorspace.name.lower():
264             print('Skipping alias creation for %s, alias %s, because lower cased names match' % (
265                 colorspace.name, alias_name) )
266             return
267
268         print('Adding alias colorspace space %s, alias to %s' % (
269             alias_name, colorspace.name))
270
271         compact_family_name = 'Aliases'
272
273         ocio_colorspace_alias = ocio.ColorSpace(
274             name=alias_name,
275             bitDepth=colorspace.bit_depth,
276             description=colorspace.description,
277             equalityGroup=colorspace.equality_group,
278             family=compact_family_name,
279             isData=colorspace.is_data,
280             allocation=colorspace.allocation_type,
281             allocationVars=colorspace.allocation_vars)
282
283         if colorspace.to_reference_transforms:
284             print('Generating To-Reference transforms')
285             ocio_transform = generate_OCIO_transform(
286                 [{'type': 'colorspace',
287                   'src': colorspace.name,
288                   'dst': reference_colorspace.name,
289                   'direction': 'forward'}])
290             ocio_colorspace_alias.setTransform(
291                 ocio_transform,
292                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
293
294         if colorspace.from_reference_transforms:
295             print('Generating From-Reference transforms')
296             ocio_transform = generate_OCIO_transform(
297                 [{'type': 'colorspace',
298                   'src': reference_colorspace.name,
299                   'dst': colorspace.name,
300                   'direction': 'forward'}])
301             ocio_colorspace_alias.setTransform(
302                 ocio_transform,
303                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
304
305         config.addColorSpace(ocio_colorspace_alias)
306
307 def colorspace_prefixed_name(colorspace):
308     prefix = colorspace.family.replace("/", " - ")
309     return "%s - %s" % (prefix, colorspace.name)
310
311 def create_config(config_data, 
312     aliases=False, 
313     prefix=False,
314     multiple_displays=False):
315     """
316     Object description.
317
318     Parameters
319     ----------
320     parameter : type
321         Parameter description.
322
323     Returns
324     -------
325     type
326          Return value description.
327     """
328
329     prefixed_names = {}
330     alias_colorspaces = []
331
332     # Creating the *OCIO* configuration.
333     config = ocio.Config()
334
335     # Setting configuration overall values.
336     config.setDescription('An ACES config generated from python')
337     config.setSearchPath('luts')
338
339     # Defining the reference colorspace.
340     reference_data = config_data['referenceColorSpace']
341
342     # Adding the color space Family into the name
343     # Helps with applications that present colorspaces as one long list
344     if prefix:
345         prefixed_name = colorspace_prefixed_name(reference_data)
346         prefixed_names[reference_data.name] = prefixed_name
347         reference_data.name = prefixed_name
348
349     print('Adding the reference color space : %s' % reference_data.name)
350
351     reference = ocio.ColorSpace(
352         name=reference_data.name,
353         bitDepth=reference_data.bit_depth,
354         description=reference_data.description,
355         equalityGroup=reference_data.equality_group,
356         family=reference_data.family,
357         isData=reference_data.is_data,
358         allocation=reference_data.allocation_type,
359         allocationVars=reference_data.allocation_vars)
360
361     config.addColorSpace(reference)
362
363     # Add alias
364     if aliases:
365         if reference_data.aliases != []:
366             #add_colorspace_alias(config, reference_data,
367             #                     reference_data, reference_data.aliases)
368             # defer adding alias colorspaces until end. Helps with some applications
369             alias_colorspaces.append([reference_data, reference_data, reference_data.aliases])
370
371
372     print("")
373
374     #print( "color spaces : %s" % [x.name for x in sorted(config_data['colorSpaces'])])
375
376     print('Adding the regular color spaces')
377
378     # Creating the remaining colorspaces.
379     for colorspace in sorted(config_data['colorSpaces']):
380         # Adding the color space Family into the name
381         # Helps with applications that present colorspaces as one long list
382         if prefix:
383             prefixed_name = colorspace_prefixed_name(colorspace)
384             prefixed_names[colorspace.name] = prefixed_name
385             colorspace.name = prefixed_name
386
387         print('Creating new color space : %s' % colorspace.name)
388
389         ocio_colorspace = ocio.ColorSpace(
390             name=colorspace.name,
391             bitDepth=colorspace.bit_depth,
392             description=colorspace.description,
393             equalityGroup=colorspace.equality_group,
394             family=colorspace.family,
395             isData=colorspace.is_data,
396             allocation=colorspace.allocation_type,
397             allocationVars=colorspace.allocation_vars)
398
399         if colorspace.to_reference_transforms:
400             print('Generating To-Reference transforms')
401             ocio_transform = generate_OCIO_transform(
402                 colorspace.to_reference_transforms)
403             ocio_colorspace.setTransform(
404                 ocio_transform,
405                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
406
407         if colorspace.from_reference_transforms:
408             print('Generating From-Reference transforms')
409             ocio_transform = generate_OCIO_transform(
410                 colorspace.from_reference_transforms)
411             ocio_colorspace.setTransform(
412                 ocio_transform,
413                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
414
415         config.addColorSpace(ocio_colorspace)
416
417         #
418         # Add alias to normal colorspace, using compact name
419         #
420         if aliases:
421             if colorspace.aliases != []:
422                 #add_colorspace_alias(config, reference_data,
423                 #                     colorspace, colorspace.aliases)
424                 # defer adding alias colorspaces until end. Helps with some applications
425                 alias_colorspaces.append([reference_data, colorspace, colorspace.aliases])
426
427         print('')
428
429     print("")
430
431     # We add these at the end as some applications use the order of the colorspaces
432     # definitions in the config to order the colorspaces in their selection lists.
433     # Other go alphabetically. This should keep the alias colorspaces out of the way
434     # for the apps that use the order of definition in the config.
435     print('Adding the alias colorspaces')
436     for reference, colorspace, aliases in alias_colorspaces:
437         add_colorspace_alias(config, reference, colorspace, aliases)
438
439     print("")
440
441     print('Adding the diplays and views')
442
443     # Defining the *views* and *displays*.
444     displays = []
445     views = []
446
447
448     # Defining a *generic* *display* and *view* setup.
449     if multiple_displays:
450         for display, view_list in config_data['displays'].iteritems():
451             for view_name, colorspace in view_list.iteritems():
452                 config.addDisplay(display, view_name, colorspace.name)
453                 if not (view_name in views):
454                     views.append(view_name)
455             displays.append(display)
456
457     else:
458         # Defining the set of *views* and *displays* useful in a *GUI* context.
459         #display_name = 'ACES'
460         display_name = config_data['roles']['scene_linear']
461         displays.append(display_name)
462
463         display_names = sorted(config_data['displays'])
464
465         # Make sure the default display is first
466         default_display = config_data['defaultDisplay']
467         display_names.insert(0, display_names.pop(display_names.index(default_display)))
468
469         for display in display_names:
470             view_list = config_data['displays'][display]
471             for view_name, colorspace in view_list.iteritems():
472                 if view_name == 'Output Transform':
473                     display_cleaned = replace(display, {')': '', '(': ''})
474                     config.addDisplay(display_name, display_cleaned, colorspace.name)
475                     if not (display_cleaned in views):
476                         views.append(display_cleaned)
477
478         # Works with Nuke Studio and Mari, but not Nuke
479         # display_name = 'Utility'
480         # displays.append(display_name)
481
482         linear_display_space_name = config_data['roles']['scene_linear']
483         log_display_space_name = config_data['roles']['compositing_log']
484
485         # Find the newly-prefixed colorspace names
486         if prefix:
487             #print( prefixed_names )
488             linear_display_space_name = prefixed_names[linear_display_space_name]
489             log_display_space_name = prefixed_names[log_display_space_name]
490
491         config.addDisplay(display_name, 'Linear', linear_display_space_name)
492         views.append('Linear')
493         config.addDisplay(display_name, 'Log', log_display_space_name)
494         views.append('Log')
495
496     # Setting the active *displays* and *views*.
497     config.setActiveDisplays(','.join(sorted(displays)))
498     config.setActiveViews(','.join(views))
499
500     print("")
501
502     print('Setting the roles')
503
504     if prefix:
505         set_config_default_roles(
506             config,
507             color_picking=prefixed_names[config_data['roles']['color_picking']],
508             color_timing=prefixed_names[config_data['roles']['color_timing']],
509             compositing_log=prefixed_names[config_data['roles']['compositing_log']],
510             data=prefixed_names[config_data['roles']['data']],
511             default=prefixed_names[config_data['roles']['default']],
512             matte_paint=prefixed_names[config_data['roles']['matte_paint']],
513             reference=prefixed_names[config_data['roles']['reference']],
514             scene_linear=prefixed_names[config_data['roles']['scene_linear']],
515             texture_paint=prefixed_names[config_data['roles']['texture_paint']])
516     else:
517         set_config_default_roles(
518             config,
519             color_picking=config_data['roles']['color_picking'],
520             color_timing=config_data['roles']['color_timing'],
521             compositing_log=config_data['roles']['compositing_log'],
522             data=config_data['roles']['data'],
523             default=config_data['roles']['default'],
524             matte_paint=config_data['roles']['matte_paint'],
525             reference=config_data['roles']['reference'],
526             scene_linear=config_data['roles']['scene_linear'],
527             texture_paint=config_data['roles']['texture_paint'])
528
529     print("")
530
531     # Make sure we didn't create a bad config
532     config.sanityCheck()
533
534     # Reset the colorspace names back to their non-prefixed versions
535     if prefix:
536         # Build the reverse lookup
537         prefixed_names_inverse = {}
538         for original, prefixed in prefixed_names.iteritems():
539             prefixed_names_inverse[prefixed] = original
540
541         # Reet the reference colorspace name
542         reference_data.name = prefixed_names_inverse[reference_data.name]
543
544         # Reset the rest of the colorspace names
545         for colorspace in config_data['colorSpaces']:
546             colorspace.name = prefixed_names_inverse[colorspace.name]
547
548     return config
549
550
551 def generate_LUTs(odt_info,
552                   lmt_info,
553                   shaper_name,
554                   aces_ctl_directory,
555                   lut_directory,
556                   lut_resolution_1d=4096,
557                   lut_resolution_3d=64,
558                   cleanup=True):
559     """
560     Object description.
561
562     Parameters
563     ----------
564     parameter : type
565         Parameter description.
566
567     Returns
568     -------
569     dict
570          Colorspaces and transforms converting between those colorspaces and
571          the reference colorspace, *ACES*.
572     """
573
574     print('generateLUTs - begin')
575     config_data = {}
576
577     # Initialize a few variables
578     config_data['displays'] = {}
579     config_data['colorSpaces'] = []
580
581     # -------------------------------------------------------------------------
582     # *ACES Color Spaces*
583     # -------------------------------------------------------------------------
584
585     # *ACES* colorspaces
586     (aces_reference,
587      aces_colorspaces,
588      aces_displays,
589      aces_log_display_space,
590      aces_roles,
591      aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
592                                                      lut_directory,
593                                                      lut_resolution_1d,
594                                                      lut_resolution_3d,
595                                                      lmt_info,
596                                                      odt_info,
597                                                      shaper_name,
598                                                      cleanup)
599
600     config_data['referenceColorSpace'] = aces_reference
601     config_data['roles'] = aces_roles
602
603     for cs in aces_colorspaces:
604         config_data['colorSpaces'].append(cs)
605
606     for name, data in aces_displays.iteritems():
607         config_data['displays'][name] = data
608
609     config_data['defaultDisplay'] = aces_default_display
610     config_data['linearDisplaySpace'] = aces_reference
611     config_data['logDisplaySpace'] = aces_log_display_space
612
613     # -------------------------------------------------------------------------
614     # *Camera Input Transforms*
615     # -------------------------------------------------------------------------
616
617     # *ARRI Log-C* to *ACES*.
618     arri_colorSpaces = arri.create_colorspaces(lut_directory,
619                                                lut_resolution_1d)
620     for cs in arri_colorSpaces:
621         config_data['colorSpaces'].append(cs)
622
623     # *Canon-Log* to *ACES*.
624     canon_colorspaces = canon.create_colorspaces(lut_directory,
625                                                  lut_resolution_1d)
626     for cs in canon_colorspaces:
627         config_data['colorSpaces'].append(cs)
628
629     # *GoPro Protune* to *ACES*.
630     gopro_colorspaces = gopro.create_colorspaces(lut_directory,
631                                                  lut_resolution_1d)
632     for cs in gopro_colorspaces:
633         config_data['colorSpaces'].append(cs)
634
635     # *Panasonic V-Log* to *ACES*.
636     panasonic_colorSpaces = panasonic.create_colorspaces(lut_directory,
637                                                          lut_resolution_1d)
638     for cs in panasonic_colorSpaces:
639         config_data['colorSpaces'].append(cs)
640
641     # *RED* colorspaces to *ACES*.
642     red_colorspaces = red.create_colorspaces(lut_directory,
643                                              lut_resolution_1d)
644     for cs in red_colorspaces:
645         config_data['colorSpaces'].append(cs)
646
647     # *S-Log* to *ACES*.
648     sony_colorSpaces = sony.create_colorspaces(lut_directory,
649                                                lut_resolution_1d)
650     for cs in sony_colorSpaces:
651         config_data['colorSpaces'].append(cs)
652
653     # -------------------------------------------------------------------------
654     # General Color Spaces
655     # -------------------------------------------------------------------------
656     general_colorSpaces = general.create_colorspaces(lut_directory,
657                                                      lut_resolution_1d,
658                                                      lut_resolution_3d)
659     for cs in general_colorSpaces:
660         config_data['colorSpaces'].append(cs)
661
662     # The *Raw* color space
663     raw = general.create_raw()
664     config_data['colorSpaces'].append(raw)
665
666     # Override certain roles, for now
667     config_data['roles']['data'] = raw.name
668     config_data['roles']['reference'] = raw.name
669     config_data['roles']['texture_paint'] = raw.name
670
671     print('generateLUTs - end')
672     return config_data
673
674
675 def generate_baked_LUTs(odt_info,
676                         shaper_name,
677                         baked_directory,
678                         config_path,
679                         lut_resolution_1d,
680                         lut_resolution_3d,
681                         lut_resolution_shaper=1024):
682     """
683     Object description.
684
685     Parameters
686     ----------
687     parameter : type
688         Parameter description.
689
690     Returns
691     -------
692     type
693          Return value description.
694     """
695
696     # Create two entries for ODTs that have full and legal range support
697     odt_info_C = dict(odt_info)
698     for odt_ctl_name, odt_values in odt_info.iteritems():
699         if odt_values['transformHasFullLegalSwitch']:
700             odt_name = odt_values['transformUserName']
701
702             odt_values_legal = dict(odt_values)
703             odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
704             odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
705
706             odt_values_full = dict(odt_values)
707             odt_values_full['transformUserName'] = '%s - Full' % odt_name
708             odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
709
710             del (odt_info_C[odt_ctl_name])
711
712     # Generate appropriate LUTs for each ODT
713     for odt_ctl_name, odt_values in odt_info_C.iteritems():
714         odt_prefix = odt_values['transformUserNamePrefix']
715         odt_name = odt_values['transformUserName']
716
717         # *Photoshop*
718         for input_space in ['ACEScc', 'ACESproxy']:
719             args = ['--iconfig', config_path,
720                     '-v',
721                     '--inputspace', input_space]
722             args += ['--outputspace', '%s' % odt_name]
723             args += ['--description',
724                      '%s - %s for %s data' % (odt_prefix,
725                                               odt_name,
726                                               input_space)]
727             args += ['--shaperspace', shaper_name,
728                      '--shapersize', str(lut_resolution_shaper)]
729             args += ['--cubesize', str(lut_resolution_3d)]
730             args += ['--format',
731                      'icc',
732                      os.path.join(baked_directory,
733                                   'photoshop',
734                                   '%s for %s.icc' % (odt_name, input_space))]
735
736             bake_lut = Process(description='bake a LUT',
737                                cmd='ociobakelut',
738                                args=args)
739             bake_lut.execute()
740
741         # *Flame*, *Lustre*
742         for input_space in ['ACEScc', 'ACESproxy']:
743             args = ['--iconfig', config_path,
744                     '-v',
745                     '--inputspace', input_space]
746             args += ['--outputspace', '%s' % odt_name]
747             args += ['--description',
748                      '%s - %s for %s data' % (
749                          odt_prefix, odt_name, input_space)]
750             args += ['--shaperspace', shaper_name,
751                      '--shapersize', str(lut_resolution_shaper)]
752             args += ['--cubesize', str(lut_resolution_3d)]
753
754             fargs = ['--format',
755                      'flame',
756                      os.path.join(
757                          baked_directory,
758                          'flame',
759                          '%s for %s Flame.3dl' % (odt_name, input_space))]
760             bake_lut = Process(description='bake a LUT',
761                                cmd='ociobakelut',
762                                args=(args + fargs))
763             bake_lut.execute()
764
765             largs = ['--format',
766                      'lustre',
767                      os.path.join(
768                          baked_directory,
769                          'lustre',
770                          '%s for %s Lustre.3dl' % (odt_name, input_space))]
771             bake_lut = Process(description='bake a LUT',
772                                cmd='ociobakelut',
773                                args=(args + largs))
774             bake_lut.execute()
775
776         # *Maya*, *Houdini*
777         for input_space in ['ACEScg', 'ACES2065-1']:
778             args = ['--iconfig', config_path,
779                     '-v',
780                     '--inputspace', input_space]
781             args += ['--outputspace', '%s' % odt_name]
782             args += ['--description',
783                      '%s - %s for %s data' % (
784                          odt_prefix, odt_name, input_space)]
785             if input_space == 'ACEScg':
786                 lin_shaper_name = '%s - AP1' % shaper_name
787             else:
788                 lin_shaper_name = shaper_name
789             args += ['--shaperspace', lin_shaper_name,
790                      '--shapersize', str(lut_resolution_shaper)]
791
792             args += ['--cubesize', str(lut_resolution_3d)]
793
794             margs = ['--format',
795                      'cinespace',
796                      os.path.join(
797                          baked_directory,
798                          'maya',
799                          '%s for %s Maya.csp' % (odt_name, input_space))]
800             bake_lut = Process(description='bake a LUT',
801                                cmd='ociobakelut',
802                                args=(args + margs))
803             bake_lut.execute()
804
805             hargs = ['--format',
806                      'houdini',
807                      os.path.join(
808                          baked_directory,
809                          'houdini',
810                          '%s for %s Houdini.lut' % (odt_name, input_space))]
811             bake_lut = Process(description='bake a LUT',
812                                cmd='ociobakelut',
813                                args=(args + hargs))
814             bake_lut.execute()
815
816
817 def create_config_dir(config_directory, bake_secondary_LUTs):
818     """
819     Object description.
820
821     Parameters
822     ----------
823     parameter : type
824         Parameter description.
825
826     Returns
827     -------
828     type
829          Return value description.
830     """
831
832     lut_directory = os.path.join(config_directory, 'luts')
833     dirs = [config_directory, lut_directory]
834     if bake_secondary_LUTs:
835         dirs.extend([os.path.join(config_directory, 'baked'),
836                      os.path.join(config_directory, 'baked', 'flame'),
837                      os.path.join(config_directory, 'baked', 'photoshop'),
838                      os.path.join(config_directory, 'baked', 'houdini'),
839                      os.path.join(config_directory, 'baked', 'lustre'),
840                      os.path.join(config_directory, 'baked', 'maya')])
841
842     for d in dirs:
843         not os.path.exists(d) and os.mkdir(d)
844
845     return lut_directory
846
847
848 def create_ACES_config(aces_ctl_directory,
849                        config_directory,
850                        lut_resolution_1d=4096,
851                        lut_resolution_3d=64,
852                        bake_secondary_LUTs=True,
853                        multiple_displays=False,
854                        cleanup=True):
855     """
856     Creates the ACES configuration.
857
858     Parameters
859     ----------
860     parameter : type
861         Parameter description.
862
863     Returns
864     -------
865     type
866          Return value description.
867     """
868
869     lut_directory = create_config_dir(config_directory, bake_secondary_LUTs)
870
871     odt_info = aces.get_ODTs_info(aces_ctl_directory)
872     lmt_info = aces.get_LMTs_info(aces_ctl_directory)
873
874     shaper_name = 'Output Shaper'
875     config_data = generate_LUTs(odt_info,
876                                 lmt_info,
877                                 shaper_name,
878                                 aces_ctl_directory,
879                                 lut_directory,
880                                 lut_resolution_1d,
881                                 lut_resolution_3d,
882                                 cleanup)
883
884     print('Creating config - with prefixes, with aliases')
885     config = create_config(config_data, 
886         prefix=True, aliases=True, multiple_displays=multiple_displays)
887     print('\n\n\n')
888
889     write_config(config,
890                  os.path.join(config_directory, 'config.ocio'))
891
892     if bake_secondary_LUTs:
893         generate_baked_LUTs(odt_info,
894                             shaper_name,
895                             os.path.join(config_directory, 'baked'),
896                             os.path.join(config_directory, 'config.ocio'),
897                             lut_resolution_1d,
898                             lut_resolution_3d,
899                             lut_resolution_1d)
900
901     return True
902
903
904 def main():
905     """
906     Object description.
907
908     Parameters
909     ----------
910     parameter : type
911         Parameter description.
912
913     Returns
914     -------
915     type
916          Return value description.
917     """
918
919     import optparse
920
921     usage  = '%prog [options]\n'
922     usage += '\n'
923     usage += 'An OCIO config generation script for ACES 1.0\n'
924     usage += '\n'
925     usage += 'Command line examples'
926     usage += '\n'
927     usage += 'Create a GUI-friendly ACES 1.0 config with no secondary, baked LUTs : \n'
928     usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --dontBakeSecondaryLUTs'
929     usage += '\n'
930     usage += 'Create a traditional ACES 1.0 config with secondary, baked LUTs : \n'
931     usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --createMultipleDisplays'
932     usage += '\n'
933
934     p = optparse.OptionParser(description='',
935                               prog='create_aces_config',
936                               version='create_aces_config 1.0',
937                               usage=usage)
938     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
939         ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
940     p.add_option('--configDir', '-c', default=os.environ.get(
941         ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
942     p.add_option('--lutResolution1d', default=4096)
943     p.add_option('--lutResolution3d', default=64)
944     p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
945     p.add_option('--keepTempImages', action='store_true', default=False)
946     p.add_option('--createMultipleDisplays', action='store_true', default=False)
947
948     options, arguments = p.parse_args()
949
950     aces_ctl_directory = options.acesCTLDir
951     config_directory = options.configDir
952     lut_resolution_1d = int(options.lutResolution1d)
953     lut_resolution_3d = int(options.lutResolution3d)
954     bake_secondary_luts = not options.dontBakeSecondaryLUTs
955     cleanup_temp_images = not options.keepTempImages
956     multiple_displays = options.createMultipleDisplays
957
958     # TODO: Investigate the following statements.
959     try:
960         args_start = sys.argv.index('--') + 1
961         args = sys.argv[args_start:]
962     except:
963         args_start = len(sys.argv) + 1
964         args = []
965
966     print('command line : \n%s\n' % ' '.join(sys.argv))
967
968     assert aces_ctl_directory is not None, (
969         'process: No "{0}" environment variable defined or no "ACES CTL" '
970         'directory specified'.format(
971             ACES_OCIO_CTL_DIRECTORY_ENVIRON))
972
973     assert config_directory is not None, (
974         'process: No "{0}" environment variable defined or no configuration '
975         'directory specified'.format(
976             ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
977
978     return create_ACES_config(aces_ctl_directory,
979                               config_directory,
980                               lut_resolution_1d,
981                               lut_resolution_3d,
982                               bake_secondary_luts,
983                               multiple_displays,
984                               cleanup_temp_images)
985
986 if __name__ == '__main__':
987     main()