9272908e5b87d94a48c78f5a625d8bfcef4a0a3f
[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         description = colorspace.description
323         if colorspace.aces_transform_id:
324             description += "\n\nACES Transform ID : %s" % colorspace.aces_transform_id
325
326         ocio_colorspace_alias = ocio.ColorSpace(
327             name=alias_name,
328             bitDepth=colorspace.bit_depth,
329             description=description,
330             equalityGroup=colorspace.equality_group,
331             family=compact_family_name,
332             isData=colorspace.is_data,
333             allocation=colorspace.allocation_type,
334             allocationVars=colorspace.allocation_vars)
335
336         if colorspace.to_reference_transforms:
337             print('\tGenerating To-Reference transforms')
338             ocio_transform = generate_OCIO_transform(
339                 [{'type': 'colorspace',
340                   'src': colorspace.name,
341                   'dst': reference_colorspace.name,
342                   'direction': 'forward'}])
343             ocio_colorspace_alias.setTransform(
344                 ocio_transform,
345                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
346
347         if colorspace.from_reference_transforms:
348             print('\tGenerating From-Reference transforms')
349             ocio_transform = generate_OCIO_transform(
350                 [{'type': 'colorspace',
351                   'src': reference_colorspace.name,
352                   'dst': colorspace.name,
353                   'direction': 'forward'}])
354             ocio_colorspace_alias.setTransform(
355                 ocio_transform,
356                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
357
358         config.addColorSpace(ocio_colorspace_alias)
359
360 def colorspace_prefixed_name(colorspace):
361     prefix = colorspace.family.replace("/", " - ")
362     return "%s - %s" % (prefix, colorspace.name)
363
364 def add_look(config,
365              look,
366              prefix,
367              custom_lut_dir,
368              reference_name,
369              config_data,
370              multiple_displays=False):
371     """
372     Object description.
373
374     Parameters
375     ----------
376     parameter : type
377         Parameter description.
378
379     Returns
380     -------
381     type
382          Return value description.
383     """
384
385     look_name = look[0]
386     look_colorspace = look[1]
387     look_lut = look[2]
388     look_cccid = None
389     if len(look) == 4:
390         look_cccid = look[3]
391
392     print('Adding look %s - %s' % (look_name, ", ".join(look)) )
393
394     #
395     # Copy look lut
396     #
397     if custom_lut_dir:
398         if not '$' in look_lut:
399             print( "Getting ready to copy look lut : %s" % look_lut )
400             shutil.copy2(look_lut, custom_lut_dir)
401             look_lut = os.path.split(look_lut)[1]
402         else:
403             print( "Skipping LUT copy because path contains a context variable" )
404
405     #
406     # Create OCIO Look
407     #
408     # Look 1
409     print('Adding look to config' )
410     lk1 = ocio.Look()
411     lk1.setName( look_name )
412     lk1.setProcessSpace( look_colorspace )
413
414     keys = {'type': 'lutFile',
415             'path': look_lut,
416             'direction': 'forward'}
417     if look_cccid:
418         keys['cccid'] = look_cccid
419
420     ocio_transform = generate_OCIO_transform([keys])
421     lk1.setTransform( ocio_transform )
422
423     # add to config
424     config.addLook( lk1 )
425
426     print( "Creating aliased colorspace")
427
428     #
429     # Create OCIO colorspace that references that look
430     # - Needed for some implementations that don't process looks well
431     # - Also needed for some implementations that don't expose looks well
432     #
433     look_aliases = ["look_%s" % compact(look_name)]
434     colorspace = ColorSpace(look_name,
435         aliases=look_aliases,
436         description="The %s Look colorspace" % look_name,
437         family='Look')
438
439     colorspace.from_reference_transforms = [{'type': 'look',
440         'look': look_name,
441         'src': reference_name,
442         'dst': reference_name,
443         'direction': 'forward'}]
444
445     print('Adding colorspace %s, alias to look %s to config data' % (
446         look_name, look_name))
447
448     # Add this colorspace into the main list of colorspaces
449     config_data['colorSpaces'].append(colorspace)
450
451     print("")
452
453 def integrate_looks_into_views(config,
454                                looks,
455                                reference_name,
456                                config_data,
457                                multiple_displays=False):
458     """
459     Object description.
460
461     Parameters
462     ----------
463     parameter : type
464         Parameter description.
465
466     Returns
467     -------
468     type
469          Return value description.
470     """
471     look_names = [look[0] for look in looks] 
472
473     # Option 1 - Add a 'look' to each Display
474     # - Assumes there is a Display for each ACES Output Transform
475     if multiple_displays:
476         for look_name in look_names:
477             config_data['looks'].append(look_name)
478
479     # Option 2
480     # - Copy each Output Transform colorspace
481     # - For each copy, add a LookTransform at the head of the from_reference
482     #     transform list
483     # - Add these new copied colorspaces for the Displays / Views 
484     else:
485         for display, view_list in config_data['displays'].iteritems():
486             output_colorspace_copy = None
487             look_names_string = ""
488             for view_name, output_colorspace in view_list.iteritems():
489                 if view_name == "Output Transform":
490
491                     print( "Adding new View that incorporates looks" )
492
493                     # Make a copy of the output colorspace
494                     output_colorspace_copy = copy.deepcopy(output_colorspace)
495
496                     #for look_name in look_names:
497                     for i in range(len(look_names)):
498                         look_name = look_names[i]
499
500                         # Add the LookTransform to the head of the from_reference transform list
501                         if output_colorspace_copy.from_reference_transforms:
502                             output_colorspace_copy.from_reference_transforms.insert(i, {'type': 'look',
503                                 'look': look_name,
504                                 'src': reference_name,
505                                 'dst': reference_name,
506                                 'direction': 'forward'})
507
508                         # Add the LookTransform to the end of the to_reference transform list
509                         if output_colorspace_copy.to_reference_transforms:
510                             inverse_look_name = look_names[len(look_names) -1 -i]
511
512                             output_colorspace_copy.to_reference_transforms.append({'type': 'look',
513                                 'look': inverse_look_name,
514                                 'src': reference_name,
515                                 'dst': reference_name,
516                                 'direction': 'inverse'})
517
518                         if not look_name in config_data['looks']:
519                             config_data['looks'].append(look_name)
520
521                     look_names_string = ", ".join(look_names)
522                     output_colorspace_copy.name = "%s with %s" % (output_colorspace.name, look_names_string)
523                     output_colorspace_copy.aliases = ["out_%s" % compact(output_colorspace_copy.name)]
524
525                     print( "Colorspace that incorporates looks created : %s" % output_colorspace_copy.name )
526
527                     config_data['colorSpaces'].append(output_colorspace_copy)
528
529             if output_colorspace_copy:
530                 print( "Adding colorspace that incorporates looks into view list" )
531
532                 # Change the name of the View
533                 view_list["Output Transform with %s" % look_names_string] = output_colorspace_copy
534                 config_data['displays'][display] = view_list
535
536                 #print( "Display : %s, View List : %s" % (display, ", ".join(view_list)) )
537
538 def create_config(config_data, 
539     aliases=False, 
540     prefix=False,
541     multiple_displays=False,
542     look_info=[],
543     custom_lut_dir=None):
544     """
545     Object description.
546
547     Parameters
548     ----------
549     parameter : type
550         Parameter description.
551
552     Returns
553     -------
554     type
555          Return value description.
556     """
557
558     prefixed_names = {}
559     alias_colorspaces = []
560
561     # Creating the *OCIO* configuration.
562     config = ocio.Config()
563
564     # Setting configuration description.
565     config.setDescription('An ACES config generated from python')
566
567     # Setting configuration search path.
568     searchPath = ['luts']
569     if custom_lut_dir:
570         searchPath.append('custom')
571     config.setSearchPath(':'.join(searchPath))
572
573     # Defining the reference colorspace.
574     reference_data = config_data['referenceColorSpace']
575
576     # Adding the color space Family into the name
577     # Helps with applications that present colorspaces as one long list
578     if prefix:
579         prefixed_name = colorspace_prefixed_name(reference_data)
580         prefixed_names[reference_data.name] = prefixed_name
581         reference_data.name = prefixed_name
582
583     print('Adding the reference color space : %s' % reference_data.name)
584
585     reference = ocio.ColorSpace(
586         name=reference_data.name,
587         bitDepth=reference_data.bit_depth,
588         description=reference_data.description,
589         equalityGroup=reference_data.equality_group,
590         family=reference_data.family,
591         isData=reference_data.is_data,
592         allocation=reference_data.allocation_type,
593         allocationVars=reference_data.allocation_vars)
594
595     config.addColorSpace(reference)
596
597     # Add alias
598     if aliases:
599         if reference_data.aliases != []:
600             #add_colorspace_alias(config, reference_data,
601             #                     reference_data, reference_data.aliases)
602             # defer adding alias colorspaces until end. Helps with some applications
603             alias_colorspaces.append([reference_data, reference_data, reference_data.aliases])
604
605
606     print("")
607
608     #print( "color spaces : %s" % [x.name for x in sorted(config_data['colorSpaces'])])
609
610     #
611     # Add Looks and Look colorspaces
612     #
613     if look_info != []:
614         print('Adding looks')
615
616         config_data['looks'] = []
617
618         # Add looks and colorspaces
619         for look in look_info:
620             add_look(config, 
621                 look, 
622                 prefix, 
623                 custom_lut_dir, 
624                 reference_data.name,
625                 config_data)
626
627         # Integrate looks with displays, views
628         integrate_looks_into_views(config, 
629             look_info,
630             reference_data.name,
631             config_data,
632             multiple_displays)
633
634         print("")
635
636     print('Adding the regular color spaces')
637
638     # Creating the remaining colorspaces.
639     for colorspace in sorted(config_data['colorSpaces']):
640         # Adding the color space Family into the name
641         # Helps with applications that present colorspaces as one long list
642         if prefix:
643             prefixed_name = colorspace_prefixed_name(colorspace)
644             prefixed_names[colorspace.name] = prefixed_name
645             colorspace.name = prefixed_name
646
647         print('Creating new color space : %s' % colorspace.name)
648
649         description = colorspace.description
650         if colorspace.aces_transform_id:
651             description += "\n\nACES Transform ID : %s" % colorspace.aces_transform_id
652
653         ocio_colorspace = ocio.ColorSpace(
654             name=colorspace.name,
655             bitDepth=colorspace.bit_depth,
656             description=description,
657             equalityGroup=colorspace.equality_group,
658             family=colorspace.family,
659             isData=colorspace.is_data,
660             allocation=colorspace.allocation_type,
661             allocationVars=colorspace.allocation_vars)
662
663         if colorspace.to_reference_transforms:
664             print('\tGenerating To-Reference transforms')
665             ocio_transform = generate_OCIO_transform(
666                 colorspace.to_reference_transforms)
667             ocio_colorspace.setTransform(
668                 ocio_transform,
669                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
670
671         if colorspace.from_reference_transforms:
672             print('\tGenerating From-Reference transforms')
673             ocio_transform = generate_OCIO_transform(
674                 colorspace.from_reference_transforms)
675             ocio_colorspace.setTransform(
676                 ocio_transform,
677                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
678
679         config.addColorSpace(ocio_colorspace)
680
681         #
682         # Add alias to normal colorspace, using compact name
683         #
684         if aliases:
685             if colorspace.aliases != []:
686                 #add_colorspace_alias(config, reference_data,
687                 #                     colorspace, colorspace.aliases)
688                 # defer adding alias colorspaces until end. Helps with some applications
689                 alias_colorspaces.append([reference_data, colorspace, colorspace.aliases])
690
691         print('')
692
693     print("")
694
695     # We add these at the end as some applications use the order of the colorspaces
696     # definitions in the config to order the colorspaces in their selection lists.
697     # Other go alphabetically. This should keep the alias colorspaces out of the way
698     # for the apps that use the order of definition in the config.
699     print('Adding the alias colorspaces')
700     for reference, colorspace, aliases in alias_colorspaces:
701         add_colorspace_alias(config, reference, colorspace, aliases)
702
703     print("")
704
705     print('Adding the diplays and views')
706
707     # Defining the *views* and *displays*.
708     displays = []
709     views = []
710
711
712     # Defining a *generic* *display* and *view* setup.
713     if multiple_displays:
714         # Built list of looks to add to Displays
715         looks = config_data['looks'] if ('looks' in config_data) else []
716         looks = ", ".join(looks)
717         print( "Creating multiple displays, with looks : %s" % looks)
718
719         # Create Displays, Views
720         for display, view_list in config_data['displays'].iteritems():
721             for view_name, colorspace in view_list.iteritems():
722                 config.addDisplay(display, view_name, colorspace.name, looks)
723                 if 'Output Transform' in view_name and looks != "":
724                     # Add normal View, without looks
725                     config.addDisplay(display, view_name, colorspace.name)
726
727                     # Add View with looks
728                     view_name_with_looks = "%s with %s" % (view_name, looks)
729                     config.addDisplay(display, view_name_with_looks, colorspace.name, looks)
730                 else:
731                     config.addDisplay(display, view_name, colorspace.name)
732                 if not (view_name in views):
733                     views.append(view_name)
734             displays.append(display)
735
736     else:
737         # Defining the set of *views* and *displays* useful in a *GUI* context.
738         #display_name = 'ACES'
739         single_display_name = config_data['roles']['scene_linear']
740         displays.append(single_display_name)
741
742         display_names = sorted(config_data['displays'])
743
744         # Make sure the default display is first
745         default_display = config_data['defaultDisplay']
746         display_names.insert(0, display_names.pop(display_names.index(default_display)))
747
748         # Built list of looks to add to Displays
749         looks = config_data['looks'] if ('looks' in config_data) else []
750         look_names = ", ".join(looks)
751
752         displays_views_colorspaces = []
753
754         # Create Displays, Views
755         for display in display_names:
756             view_list = config_data['displays'][display]
757             for view_name, colorspace in view_list.iteritems():
758                 if 'Output Transform' in view_name:
759                     #print( "Adding view for %s" % view_name )
760
761                     # Maya 2016 doesn't like parentheses in View names
762                     display_cleaned = replace(display, {')': '', '(': ''})
763
764                     # We use the Display names as the View names in this case
765                     # as there is a single Display that contains all views.
766                     # This works for more applications than not, as of the time of this implementation.
767
768                     # If View includes looks
769                     if 'with' in view_name:
770                         # Integrate looks into view name
771                         display_cleaned = "%s with %s" % (display_cleaned, look_names)
772
773                         viewsWithLooksAtEnd = False
774                         # Storing combo of display, view and colorspace name in a list so we can
775                         # add them to the end of the list
776                         if viewsWithLooksAtEnd:
777                             displays_views_colorspaces.append([single_display_name, display_cleaned, colorspace.name])
778
779                         # Or add as normal
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                     # A normal View
788                     else:
789                         config.addDisplay(single_display_name, display_cleaned, colorspace.name)
790
791                         # Add to views list
792                         if not (display_cleaned in views):
793                             views.append(display_cleaned)
794
795         # Add to config any display, view combinations that were saved for later
796         for display_view_colorspace in displays_views_colorspaces:
797             single_display_name, display_cleaned, colorspace_name = display_view_colorspace
798
799             # Add to config
800             config.addDisplay(single_display_name, display_cleaned, colorspace_name)
801
802             # Add to views list
803             if not (display_cleaned in views):
804                 views.append(display_cleaned)
805
806
807         # Works with Nuke Studio and Mari, but not Nuke
808         # single_display_name = 'Utility'
809         # displays.append(single_display_name)
810
811         raw_display_space_name = config_data['roles']['data']        
812         log_display_space_name = config_data['roles']['compositing_log']
813
814         # Find the newly-prefixed colorspace names
815         if prefix:
816             #print( prefixed_names )
817             raw_display_space_name = prefixed_names[raw_display_space_name]
818             log_display_space_name = prefixed_names[log_display_space_name]
819
820         config.addDisplay(single_display_name, 'Raw', raw_display_space_name)
821         views.append('Raw')
822         config.addDisplay(single_display_name, 'Log', log_display_space_name)
823         views.append('Log')
824
825     # Setting the active *displays* and *views*.
826     config.setActiveDisplays(','.join(sorted(displays)))
827     config.setActiveViews(','.join(views))
828
829     print("")
830
831     print('Setting the roles')
832
833     if prefix:
834         set_config_default_roles(
835             config,
836             color_picking=prefixed_names[config_data['roles']['color_picking']],
837             color_timing=prefixed_names[config_data['roles']['color_timing']],
838             compositing_log=prefixed_names[config_data['roles']['compositing_log']],
839             data=prefixed_names[config_data['roles']['data']],
840             default=prefixed_names[config_data['roles']['default']],
841             matte_paint=prefixed_names[config_data['roles']['matte_paint']],
842             reference=prefixed_names[config_data['roles']['reference']],
843             scene_linear=prefixed_names[config_data['roles']['scene_linear']],
844             texture_paint=prefixed_names[config_data['roles']['texture_paint']])
845     else:
846         set_config_default_roles(
847             config,
848             color_picking=config_data['roles']['color_picking'],
849             color_timing=config_data['roles']['color_timing'],
850             compositing_log=config_data['roles']['compositing_log'],
851             data=config_data['roles']['data'],
852             default=config_data['roles']['default'],
853             matte_paint=config_data['roles']['matte_paint'],
854             reference=config_data['roles']['reference'],
855             scene_linear=config_data['roles']['scene_linear'],
856             texture_paint=config_data['roles']['texture_paint'])
857
858     print("")
859
860     # Make sure we didn't create a bad config
861     config.sanityCheck()
862
863     # Reset the colorspace names back to their non-prefixed versions
864     if prefix:
865         # Build the reverse lookup
866         prefixed_names_inverse = {}
867         for original, prefixed in prefixed_names.iteritems():
868             prefixed_names_inverse[prefixed] = original
869
870         # Reet the reference colorspace name
871         reference_data.name = prefixed_names_inverse[reference_data.name]
872
873         # Reset the rest of the colorspace names
874         for colorspace in config_data['colorSpaces']:
875             colorspace.name = prefixed_names_inverse[colorspace.name]
876
877     return config
878
879
880 def generate_LUTs(odt_info,
881                   lmt_info,
882                   shaper_name,
883                   aces_ctl_directory,
884                   lut_directory,
885                   lut_resolution_1d=4096,
886                   lut_resolution_3d=64,
887                   cleanup=True):
888     """
889     Object description.
890
891     Parameters
892     ----------
893     parameter : type
894         Parameter description.
895
896     Returns
897     -------
898     dict
899          Colorspaces and transforms converting between those colorspaces and
900          the reference colorspace, *ACES*.
901     """
902
903     print('generateLUTs - begin')
904     config_data = {}
905
906     # Initialize a few variables
907     config_data['displays'] = {}
908     config_data['colorSpaces'] = []
909
910     # -------------------------------------------------------------------------
911     # *ACES Color Spaces*
912     # -------------------------------------------------------------------------
913
914     # *ACES* colorspaces
915     (aces_reference,
916      aces_colorspaces,
917      aces_displays,
918      aces_log_display_space,
919      aces_roles,
920      aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
921                                                      lut_directory,
922                                                      lut_resolution_1d,
923                                                      lut_resolution_3d,
924                                                      lmt_info,
925                                                      odt_info,
926                                                      shaper_name,
927                                                      cleanup)
928
929     config_data['referenceColorSpace'] = aces_reference
930     config_data['roles'] = aces_roles
931
932     for cs in aces_colorspaces:
933         config_data['colorSpaces'].append(cs)
934
935     for name, data in aces_displays.iteritems():
936         config_data['displays'][name] = data
937
938     config_data['defaultDisplay'] = aces_default_display
939     config_data['linearDisplaySpace'] = aces_reference
940     config_data['logDisplaySpace'] = aces_log_display_space
941
942     # -------------------------------------------------------------------------
943     # *Camera Input Transforms*
944     # -------------------------------------------------------------------------
945
946     # *ARRI Log-C* to *ACES*.
947     arri_colorSpaces = arri.create_colorspaces(lut_directory,
948                                                lut_resolution_1d)
949     for cs in arri_colorSpaces:
950         config_data['colorSpaces'].append(cs)
951
952     # *Canon-Log* to *ACES*.
953     canon_colorspaces = canon.create_colorspaces(lut_directory,
954                                                  lut_resolution_1d)
955     for cs in canon_colorspaces:
956         config_data['colorSpaces'].append(cs)
957
958     # *GoPro Protune* to *ACES*.
959     gopro_colorspaces = gopro.create_colorspaces(lut_directory,
960                                                  lut_resolution_1d)
961     for cs in gopro_colorspaces:
962         config_data['colorSpaces'].append(cs)
963
964     # *Panasonic V-Log* to *ACES*.
965     panasonic_colorSpaces = panasonic.create_colorspaces(lut_directory,
966                                                          lut_resolution_1d)
967     for cs in panasonic_colorSpaces:
968         config_data['colorSpaces'].append(cs)
969
970     # *RED* colorspaces to *ACES*.
971     red_colorspaces = red.create_colorspaces(lut_directory,
972                                              lut_resolution_1d)
973     for cs in red_colorspaces:
974         config_data['colorSpaces'].append(cs)
975
976     # *S-Log* to *ACES*.
977     sony_colorSpaces = sony.create_colorspaces(lut_directory,
978                                                lut_resolution_1d)
979     for cs in sony_colorSpaces:
980         config_data['colorSpaces'].append(cs)
981
982     # -------------------------------------------------------------------------
983     # General Color Spaces
984     # -------------------------------------------------------------------------
985     general_colorSpaces = general.create_colorspaces(lut_directory,
986                                                      lut_resolution_1d,
987                                                      lut_resolution_3d)
988     for cs in general_colorSpaces:
989         config_data['colorSpaces'].append(cs)
990
991     # The *Raw* color space
992     raw = general.create_raw()
993     config_data['colorSpaces'].append(raw)
994
995     # Override certain roles, for now
996     config_data['roles']['data'] = raw.name
997     config_data['roles']['reference'] = raw.name
998     config_data['roles']['texture_paint'] = raw.name
999
1000     print('generateLUTs - end')
1001     return config_data
1002
1003
1004 def generate_baked_LUTs(odt_info,
1005                         shaper_name,
1006                         baked_directory,
1007                         config_path,
1008                         lut_resolution_1d,
1009                         lut_resolution_3d,
1010                         lut_resolution_shaper=1024,
1011                         prefix=False):
1012     """
1013     Object description.
1014
1015     Parameters
1016     ----------
1017     parameter : type
1018         Parameter description.
1019
1020     Returns
1021     -------
1022     type
1023          Return value description.
1024     """
1025
1026     # Create two entries for ODTs that have full and legal range support
1027     odt_info_C = dict(odt_info)
1028     for odt_ctl_name, odt_values in odt_info.iteritems():
1029         if odt_values['transformHasFullLegalSwitch']:
1030             odt_name = odt_values['transformUserName']
1031
1032             odt_values_legal = dict(odt_values)
1033             odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1034             odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
1035
1036             odt_values_full = dict(odt_values)
1037             odt_values_full['transformUserName'] = '%s - Full' % odt_name
1038             odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
1039
1040             del (odt_info_C[odt_ctl_name])
1041
1042     # Generate appropriate LUTs for each ODT
1043     for odt_ctl_name, odt_values in odt_info_C.iteritems():
1044         odt_prefix = odt_values['transformUserNamePrefix']
1045         odt_name = odt_values['transformUserName']
1046
1047         # *Photoshop*
1048         for input_space in ['ACEScc', 'ACESproxy']:
1049             args = ['--iconfig', config_path,
1050                     '-v']
1051             if prefix:
1052                 args += ['--inputspace', "ACES - %s" % input_space]
1053                 args += ['--outputspace', "Output - %s" % odt_name]
1054             else:
1055                 args += ['--inputspace', input_space]
1056                 args += ['--outputspace', odt_name]
1057
1058             args += ['--description',
1059                      '%s - %s for %s data' % (odt_prefix,
1060                                               odt_name,
1061                                               input_space)]
1062             if prefix:
1063                 args += ['--shaperspace', "Utility - %s" % shaper_name,
1064                          '--shapersize', str(lut_resolution_shaper)]
1065             else:
1066                 args += ['--shaperspace', shaper_name,
1067                          '--shapersize', str(lut_resolution_shaper)]
1068             args += ['--cubesize', str(lut_resolution_3d)]
1069             args += ['--format',
1070                      'icc',
1071                      os.path.join(baked_directory,
1072                                   'photoshop',
1073                                   '%s for %s.icc' % (odt_name, input_space))]
1074
1075             bake_lut = Process(description='bake a LUT',
1076                                cmd='ociobakelut',
1077                                args=args)
1078             bake_lut.execute()
1079
1080         # *Flame*, *Lustre*
1081         for input_space in ['ACEScc', 'ACESproxy']:
1082             args = ['--iconfig', config_path,
1083                     '-v']
1084             if prefix:
1085                 args += ['--inputspace', "ACES - %s" % input_space]
1086                 args += ['--outputspace', "Output - %s" % odt_name]
1087             else:
1088                 args += ['--inputspace', input_space]
1089                 args += ['--outputspace', odt_name]
1090             args += ['--description',
1091                      '%s - %s for %s data' % (
1092                          odt_prefix, odt_name, input_space)]
1093             if prefix:
1094                 args += ['--shaperspace', "Utility - %s" % shaper_name,
1095                          '--shapersize', str(lut_resolution_shaper)]
1096             else:
1097                 args += ['--shaperspace', shaper_name,
1098                          '--shapersize', str(lut_resolution_shaper)]
1099             args += ['--cubesize', str(lut_resolution_3d)]
1100
1101             fargs = ['--format',
1102                      'flame',
1103                      os.path.join(
1104                          baked_directory,
1105                          'flame',
1106                          '%s for %s Flame.3dl' % (odt_name, input_space))]
1107             bake_lut = Process(description='bake a LUT',
1108                                cmd='ociobakelut',
1109                                args=(args + fargs))
1110             bake_lut.execute()
1111
1112             largs = ['--format',
1113                      'lustre',
1114                      os.path.join(
1115                          baked_directory,
1116                          'lustre',
1117                          '%s for %s Lustre.3dl' % (odt_name, input_space))]
1118             bake_lut = Process(description='bake a LUT',
1119                                cmd='ociobakelut',
1120                                args=(args + largs))
1121             bake_lut.execute()
1122
1123         # *Maya*, *Houdini*
1124         for input_space in ['ACEScg', 'ACES2065-1']:
1125             args = ['--iconfig', config_path,
1126                     '-v']
1127             if prefix:
1128                 args += ['--inputspace', "ACES - %s" % input_space]
1129                 args += ['--outputspace', "Output - %s" % odt_name]
1130             else:
1131                 args += ['--inputspace', input_space]
1132                 args += ['--outputspace', odt_name]
1133             args += ['--description',
1134                      '%s - %s for %s data' % (
1135                          odt_prefix, odt_name, input_space)]
1136             if input_space == 'ACEScg':
1137                 lin_shaper_name = "%s - AP1" % shaper_name
1138             else:
1139                 lin_shaper_name = shaper_name
1140             if prefix:
1141                 lin_shaper_name = "Utility - %s" % lin_shaper_name
1142             args += ['--shaperspace', lin_shaper_name,
1143                      '--shapersize', str(lut_resolution_shaper)]
1144
1145             args += ['--cubesize', str(lut_resolution_3d)]
1146
1147             margs = ['--format',
1148                      'cinespace',
1149                      os.path.join(
1150                          baked_directory,
1151                          'maya',
1152                          '%s for %s Maya.csp' % (odt_name, input_space))]
1153             bake_lut = Process(description='bake a LUT',
1154                                cmd='ociobakelut',
1155                                args=(args + margs))
1156             bake_lut.execute()
1157
1158             hargs = ['--format',
1159                      'houdini',
1160                      os.path.join(
1161                          baked_directory,
1162                          'houdini',
1163                          '%s for %s Houdini.lut' % (odt_name, input_space))]
1164             bake_lut = Process(description='bake a LUT',
1165                                cmd='ociobakelut',
1166                                args=(args + hargs))
1167             bake_lut.execute()
1168
1169
1170 def create_config_dir(config_directory, 
1171                       bake_secondary_LUTs=False,
1172                       custom_lut_dir=None):
1173     """
1174     Object description.
1175
1176     Parameters
1177     ----------
1178     parameter : type
1179         Parameter description.
1180
1181     Returns
1182     -------
1183     type
1184          Return value description.
1185     """
1186
1187     lut_directory = os.path.join(config_directory, 'luts')
1188     dirs = [config_directory, lut_directory]
1189
1190     if bake_secondary_LUTs:
1191         dirs.extend([os.path.join(config_directory, 'baked'),
1192                      os.path.join(config_directory, 'baked', 'flame'),
1193                      os.path.join(config_directory, 'baked', 'photoshop'),
1194                      os.path.join(config_directory, 'baked', 'houdini'),
1195                      os.path.join(config_directory, 'baked', 'lustre'),
1196                      os.path.join(config_directory, 'baked', 'maya')])
1197
1198     if custom_lut_dir:
1199         dirs.append(os.path.join(config_directory, 'custom'))
1200
1201     for d in dirs:
1202         not os.path.exists(d) and os.mkdir(d)
1203
1204     return lut_directory
1205
1206
1207 def create_ACES_config(aces_ctl_directory,
1208                        config_directory,
1209                        lut_resolution_1d=4096,
1210                        lut_resolution_3d=64,
1211                        bake_secondary_LUTs=True,
1212                        multiple_displays=False,
1213                        look_info=[],
1214                        copy_custom_luts=True,
1215                        cleanup=True,
1216                        prefix_colorspaces_with_family_names=True):
1217     """
1218     Creates the ACES configuration.
1219
1220     Parameters
1221     ----------
1222     parameter : type
1223         Parameter description.
1224
1225     Returns
1226     -------
1227     type
1228          Return value description.
1229     """
1230
1231     # Directory for custom LUTs
1232     custom_lut_dir = None
1233     if copy_custom_luts:
1234         custom_lut_dir = os.path.join(config_directory, "custom")
1235
1236     lut_directory = create_config_dir(config_directory, 
1237                                       bake_secondary_LUTs,
1238                                       custom_lut_dir)
1239
1240     odt_info = aces.get_ODTs_info(aces_ctl_directory)
1241     lmt_info = aces.get_LMTs_info(aces_ctl_directory)
1242
1243     shaper_name = 'Output Shaper'
1244     config_data = generate_LUTs(odt_info,
1245                                 lmt_info,
1246                                 shaper_name,
1247                                 aces_ctl_directory,
1248                                 lut_directory,
1249                                 lut_resolution_1d,
1250                                 lut_resolution_3d,
1251                                 cleanup)
1252
1253     print('Creating config - with prefixes, with aliases')
1254     config = create_config(config_data, 
1255         prefix=prefix_colorspaces_with_family_names, 
1256         aliases=True, 
1257         multiple_displays=multiple_displays,
1258         look_info=look_info,
1259         custom_lut_dir=custom_lut_dir)
1260     print('\n\n\n')
1261
1262     write_config(config,
1263                  os.path.join(config_directory, 'config.ocio'))
1264
1265     if bake_secondary_LUTs:
1266         generate_baked_LUTs(odt_info,
1267                             shaper_name,
1268                             os.path.join(config_directory, 'baked'),
1269                             os.path.join(config_directory, 'config.ocio'),
1270                             lut_resolution_1d,
1271                             lut_resolution_3d,
1272                             lut_resolution_1d,
1273                             prefix=prefix_colorspaces_with_family_names)
1274
1275     return True
1276
1277
1278 def main():
1279     """
1280     Object description.
1281
1282     Parameters
1283     ----------
1284     parameter : type
1285         Parameter description.
1286
1287     Returns
1288     -------
1289     type
1290          Return value description.
1291     """
1292
1293     import optparse
1294
1295     usage  = '%prog [options]\n'
1296     usage += '\n'
1297     usage += 'An OCIO config generation script for ACES 1.0\n'
1298     usage += '\n'
1299     usage += 'Command line examples'
1300     usage += '\n'
1301     usage += 'Create a GUI-friendly ACES 1.0 config with no secondary, baked LUTs : \n'
1302     usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --dontBakeSecondaryLUTs'
1303     usage += '\n'
1304     usage += 'Create a more OCIO-compliant ACES 1.0 config : \n'
1305     usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --createMultipleDisplays'
1306     usage += '\n'
1307     usage += '\n'
1308     usage += 'Adding custom looks'
1309     usage += '\n'
1310     usage += 'Create a GUI-friendly ACES 1.0 config with an ACES-style CDL (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--addACESLookCDL ACESCDLName /path/to/SampleCDL.ccc cc03345'
1312     usage += '\n'
1313     usage += 'Create a GUI-friendly ACES 1.0 config with an general CDL: \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--addCustomLookCDL CustomCDLName "ACES - ACEScc" /path/to/SampleCDL.ccc cc03345'
1315     usage += '\n'
1316     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'
1317     usage += '\n'
1318     usage += 'Create a GUI-friendly ACES 1.0 config with an ACES-style LUT (will be applied in the ACEScc colorspace): \n'
1319     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'
1320     usage += '\n'
1321     usage += 'Create a GUI-friendly ACES 1.0 config with an general LUT: \n'
1322     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'
1323     usage += '\n'
1324     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'
1325     usage += '\n'
1326
1327     look_info = []
1328     def look_info_callback(option, opt_str, value, parser):
1329         print( "look_info_callback" )
1330         print( option, opt_str, value, parser )
1331         if opt_str == "--addCustomLookCDL":
1332             look_info.append(value)
1333         elif opt_str == "--addCustomLookLUT":
1334             look_info.append(value)
1335         elif opt_str == "--addACESLookCDL":
1336             look_info.append([value[0], "ACES - ACEScc", value[1], value[2]])
1337         elif opt_str == "--addACESLookLUT":
1338             look_info.append([value[0], "ACES - ACEScc", value[1]])
1339
1340     p = optparse.OptionParser(description='',
1341                               prog='create_aces_config',
1342                               version='create_aces_config 1.0',
1343                               usage=usage)
1344     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1345         ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1346     p.add_option('--configDir', '-c', default=os.environ.get(
1347         ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1348     p.add_option('--lutResolution1d', default=4096)
1349     p.add_option('--lutResolution3d', default=64)
1350     p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
1351     p.add_option('--keepTempImages', action='store_true', default=False)
1352
1353     p.add_option('--createMultipleDisplays', action='store_true', default=False)
1354
1355     p.add_option('--addCustomLookLUT', '', type='string', nargs=3, 
1356         action="callback", callback=look_info_callback)
1357     p.add_option('--addCustomLookCDL', '', type='string', nargs=4, 
1358         action="callback", callback=look_info_callback)
1359     p.add_option('--addACESLookLUT', '', type='string', nargs=2, 
1360         action="callback", callback=look_info_callback)
1361     p.add_option('--addACESLookCDL', '', type='string', nargs=3, 
1362         action="callback", callback=look_info_callback)
1363     p.add_option('--copyCustomLUTs', action='store_true', default=False)
1364
1365     options, arguments = p.parse_args()
1366
1367     aces_ctl_directory = options.acesCTLDir
1368     config_directory = options.configDir
1369     lut_resolution_1d = int(options.lutResolution1d)
1370     lut_resolution_3d = int(options.lutResolution3d)
1371     bake_secondary_luts = not options.dontBakeSecondaryLUTs
1372     cleanup_temp_images = not options.keepTempImages
1373     multiple_displays = options.createMultipleDisplays
1374     copy_custom_luts = options.copyCustomLUTs
1375
1376     print( look_info )
1377
1378     # TODO: Investigate the following statements.
1379     try:
1380         args_start = sys.argv.index('--') + 1
1381         args = sys.argv[args_start:]
1382     except:
1383         args_start = len(sys.argv) + 1
1384         args = []
1385
1386     print('command line : \n%s\n' % ' '.join(sys.argv))
1387
1388     assert aces_ctl_directory is not None, (
1389         'process: No "{0}" environment variable defined or no "ACES CTL" '
1390         'directory specified'.format(
1391             ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1392
1393     assert config_directory is not None, (
1394         'process: No "{0}" environment variable defined or no configuration '
1395         'directory specified'.format(
1396             ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1397
1398     return create_ACES_config(aces_ctl_directory,
1399                               config_directory,
1400                               lut_resolution_1d,
1401                               lut_resolution_3d,
1402                               bake_secondary_luts,
1403                               multiple_displays,
1404                               look_info,
1405                               copy_custom_luts,
1406                               cleanup_temp_images)
1407
1408 if __name__ == '__main__':
1409     main()