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