2 # -*- coding: utf-8 -*-
5 Defines objects creating the *ACES* configuration.
8 from __future__ import division
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
26 from aces_ocio.utilities import replace, ColorSpace, compact
28 __author__ = 'ACES Developers'
29 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
31 __maintainer__ = 'ACES Developers'
32 __email__ = 'aces@oscars.org'
33 __status__ = 'Production'
35 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
36 'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
37 'set_config_default_roles',
39 'generate_OCIO_transform',
40 'add_colorspace_alias',
43 'generate_baked_LUTs',
48 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
49 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
52 def set_config_default_roles(config,
63 compositing_linear=''):
65 Sets given *OCIO* configuration default roles.
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.
79 default : str or unicode
81 matte_paint : str or unicode
82 Matte painting role title.
83 reference : str or unicode
85 scene_linear : str or unicode
86 Scene linear role title.
87 texture_paint : str or unicode
88 Texture painting role title.
97 config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
99 config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
101 config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
103 config.setRole(ocio.Constants.ROLE_DATA, data)
105 config.setRole(ocio.Constants.ROLE_DEFAULT, default)
107 config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
109 config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
111 config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
113 # 'rendering' and 'compositing_linear' roles default to the 'scene_linear'
114 # value if not set explicitly
116 config.setRole("rendering", rendering)
117 if compositing_linear:
118 config.setRole("compositing_linear", compositing_linear)
120 config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
122 config.setRole("rendering", scene_linear)
123 if not compositing_linear:
124 config.setRole("compositing_linear", scene_linear)
129 def write_config(config, config_path, sanity_check=True):
131 Writes the configuration to given path.
136 Parameter description.
141 Return value description.
149 print 'Configuration was not written due to a failed Sanity Check'
152 with open(config_path, mode='w') as fp:
153 fp.write(config.serialize())
156 def generate_OCIO_transform(transforms):
163 Parameter description.
168 Return value description.
171 interpolation_options = {
172 'linear': ocio.Constants.INTERP_LINEAR,
173 'nearest': ocio.Constants.INTERP_NEAREST,
174 'tetrahedral': ocio.Constants.INTERP_TETRAHEDRAL}
176 direction_options = {
177 'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
178 'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
182 for transform in transforms:
185 if transform['type'] == 'lutFile':
187 ocio_transform = ocio.FileTransform()
189 if 'path' in transform:
190 ocio_transform.setSrc(transform['path'])
192 if 'cccid' in transform:
193 ocio_transform.setCCCId(transform['cccid'])
195 if 'interpolation' in transform:
196 ocio_transform.setInterpolation(transform['interpolation'])
198 ocio_transform.setInterpolation(ocio.Constants.INTERP_BEST)
200 if 'direction' in transform:
201 ocio_transform.setDirection(
202 direction_options[transform['direction']])
204 ocio_transforms.append(ocio_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'])
213 if 'offset' in transform:
214 ocio_transform.setOffset(transform['offset'])
216 if 'direction' in transform:
217 ocio_transform.setDirection(
218 direction_options[transform['direction']])
220 ocio_transforms.append(ocio_transform)
223 elif transform['type'] == 'exponent':
224 ocio_transform = ocio.ExponentTransform()
226 if 'value' in transform:
227 ocio_transform.setValue(transform['value'])
229 ocio_transforms.append(ocio_transform)
232 elif transform['type'] == 'log':
233 ocio_transform = ocio.LogTransform()
235 if 'base' in transform:
236 ocio_transform.setBase(transform['base'])
238 if 'direction' in transform:
239 ocio_transform.setDirection(
240 direction_options[transform['direction']])
242 ocio_transforms.append(ocio_transform)
244 # color space transform
245 elif transform['type'] == 'colorspace':
246 ocio_transform = ocio.ColorSpaceTransform()
248 if 'src' in transform:
249 ocio_transform.setSrc(transform['src'])
251 if 'dst' in transform:
252 ocio_transform.setDst(transform['dst'])
254 if 'direction' in transform:
255 ocio_transform.setDirection(
256 direction_options[transform['direction']])
258 ocio_transforms.append(ocio_transform)
261 elif transform['type'] == 'look':
262 ocio_transform = ocio.LookTransform()
263 if 'look' in transform:
264 ocio_transform.setLooks(transform['look'])
266 if 'src' in transform:
267 ocio_transform.setSrc(transform['src'])
269 if 'dst' in transform:
270 ocio_transform.setDst(transform['dst'])
272 if 'direction' in transform:
273 ocio_transform.setDirection(
274 direction_options[transform['direction']])
276 ocio_transforms.append(ocio_transform)
280 print("Ignoring unknown transform type : %s" % transform['type'])
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
288 transform = ocio_transforms[0]
293 def add_colorspace_aliases(config,
294 reference_colorspace,
296 colorspace_alias_names,
304 Parameter description.
309 Return value description.
312 for alias_name in colorspace_alias_names:
313 if alias_name.lower() == colorspace.name.lower():
314 print('Skipping alias creation for %s, alias %s, because lower cased names match' % (
315 colorspace.name, alias_name) )
318 print('Adding alias colorspace space %s, alias to %s' % (
319 alias_name, colorspace.name))
321 compact_family_name = family
323 description = colorspace.description
324 if colorspace.aces_transform_id:
325 description += "\n\nACES Transform ID : %s" % colorspace.aces_transform_id
327 ocio_colorspace_alias = ocio.ColorSpace(
329 bitDepth=colorspace.bit_depth,
330 description=description,
331 equalityGroup=colorspace.equality_group,
332 family=compact_family_name,
333 isData=colorspace.is_data,
334 allocation=colorspace.allocation_type,
335 allocationVars=colorspace.allocation_vars)
337 if colorspace.to_reference_transforms:
338 print('\tGenerating To-Reference transforms')
339 ocio_transform = generate_OCIO_transform(
340 [{'type': 'colorspace',
341 'src': colorspace.name,
342 'dst': reference_colorspace.name,
343 'direction': 'forward'}])
344 ocio_colorspace_alias.setTransform(
346 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
348 if colorspace.from_reference_transforms:
349 print('\tGenerating From-Reference transforms')
350 ocio_transform = generate_OCIO_transform(
351 [{'type': 'colorspace',
352 'src': reference_colorspace.name,
353 'dst': colorspace.name,
354 'direction': 'forward'}])
355 ocio_colorspace_alias.setTransform(
357 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
359 config.addColorSpace(ocio_colorspace_alias)
361 def colorspace_prefixed_name(colorspace):
362 prefix = colorspace.family.replace("/", " - ")
363 return "%s - %s" % (prefix, colorspace.name)
371 multiple_displays=False):
378 Parameter description.
383 Return value description.
387 look_colorspace = look[1]
393 print('Adding look %s - %s' % (look_name, ", ".join(look)) )
399 if not '$' in look_lut:
400 print( "Getting ready to copy look lut : %s" % look_lut )
401 shutil.copy2(look_lut, custom_lut_dir)
402 look_lut = os.path.split(look_lut)[1]
404 print( "Skipping LUT copy because path contains a context variable" )
410 print('Adding look to config' )
412 lk1.setName( look_name )
413 lk1.setProcessSpace( look_colorspace )
415 keys = {'type': 'lutFile',
417 'direction': 'forward'}
419 keys['cccid'] = look_cccid
421 ocio_transform = generate_OCIO_transform([keys])
422 lk1.setTransform( ocio_transform )
425 config.addLook( lk1 )
427 print( "Creating aliased colorspace")
430 # Create OCIO colorspace that references that look
431 # - Needed for some implementations that don't process looks well
432 # - Also needed for some implementations that don't expose looks well
434 look_aliases = ["look_%s" % compact(look_name)]
435 colorspace = ColorSpace(look_name,
436 aliases=look_aliases,
437 description="The %s Look colorspace" % look_name,
440 colorspace.from_reference_transforms = [{'type': 'look',
442 'src': reference_name,
443 'dst': reference_name,
444 'direction': 'forward'}]
446 print('Adding colorspace %s, alias to look %s to config data' % (
447 look_name, look_name))
449 # Add this colorspace into the main list of colorspaces
450 config_data['colorSpaces'].append(colorspace)
454 def integrate_looks_into_views(config,
458 multiple_displays=False):
465 Parameter description.
470 Return value description.
472 look_names = [look[0] for look in looks]
474 # Option 1 - Add a 'look' to each Display
475 # - Assumes there is a Display for each ACES Output Transform
476 if multiple_displays:
477 for look_name in look_names:
478 config_data['looks'].append(look_name)
481 # - Copy each Output Transform colorspace
482 # - For each copy, add a LookTransform at the head of the from_reference
484 # - Add these new copied colorspaces for the Displays / Views
486 for display, view_list in config_data['displays'].iteritems():
487 output_colorspace_copy = None
488 look_names_string = ""
489 for view_name, output_colorspace in view_list.iteritems():
490 if view_name == "Output Transform":
492 print( "Adding new View that incorporates looks" )
494 # Make a copy of the output colorspace
495 output_colorspace_copy = copy.deepcopy(output_colorspace)
497 #for look_name in look_names:
498 for i in range(len(look_names)):
499 look_name = look_names[i]
501 # Add the LookTransform to the head of the from_reference transform list
502 if output_colorspace_copy.from_reference_transforms:
503 output_colorspace_copy.from_reference_transforms.insert(i, {'type': 'look',
505 'src': reference_name,
506 'dst': reference_name,
507 'direction': 'forward'})
509 # Add the LookTransform to the end of the to_reference transform list
510 if output_colorspace_copy.to_reference_transforms:
511 inverse_look_name = look_names[len(look_names) -1 -i]
513 output_colorspace_copy.to_reference_transforms.append({'type': 'look',
514 'look': inverse_look_name,
515 'src': reference_name,
516 'dst': reference_name,
517 'direction': 'inverse'})
519 if not look_name in config_data['looks']:
520 config_data['looks'].append(look_name)
522 look_names_string = ", ".join(look_names)
523 output_colorspace_copy.name = "%s with %s" % (output_colorspace.name, look_names_string)
524 output_colorspace_copy.aliases = ["out_%s" % compact(output_colorspace_copy.name)]
526 print( "Colorspace that incorporates looks created : %s" % output_colorspace_copy.name )
528 config_data['colorSpaces'].append(output_colorspace_copy)
530 if output_colorspace_copy:
531 print( "Adding colorspace that incorporates looks into view list" )
533 # Change the name of the View
534 view_list["Output Transform with %s" % look_names_string] = output_colorspace_copy
535 config_data['displays'][display] = view_list
537 #print( "Display : %s, View List : %s" % (display, ", ".join(view_list)) )
539 def create_config(config_data,
542 multiple_displays=False,
544 custom_lut_dir=None):
551 Parameter description.
556 Return value description.
560 alias_colorspaces = []
562 # Creating the *OCIO* configuration.
563 config = ocio.Config()
565 # Setting configuration description.
566 config.setDescription('An ACES config generated from python')
568 # Setting configuration search path.
569 searchPath = ['luts']
571 searchPath.append('custom')
572 config.setSearchPath(':'.join(searchPath))
574 # Defining the reference colorspace.
575 reference_data = config_data['referenceColorSpace']
577 # Adding the color space Family into the name
578 # Helps with applications that present colorspaces as one long list
580 prefixed_name = colorspace_prefixed_name(reference_data)
581 prefixed_names[reference_data.name] = prefixed_name
582 reference_data.name = prefixed_name
584 print('Adding the reference color space : %s' % reference_data.name)
586 reference = ocio.ColorSpace(
587 name=reference_data.name,
588 bitDepth=reference_data.bit_depth,
589 description=reference_data.description,
590 equalityGroup=reference_data.equality_group,
591 family=reference_data.family,
592 isData=reference_data.is_data,
593 allocation=reference_data.allocation_type,
594 allocationVars=reference_data.allocation_vars)
596 config.addColorSpace(reference)
600 if reference_data.aliases != []:
601 #add_colorspace_alias(config, reference_data,
602 # reference_data, reference_data.aliases)
603 # defer adding alias colorspaces until end. Helps with some applications
604 alias_colorspaces.append([reference_data, reference_data, reference_data.aliases])
609 #print( "color spaces : %s" % [x.name for x in sorted(config_data['colorSpaces'])])
612 # Add Looks and Look colorspaces
615 print('Adding looks')
617 config_data['looks'] = []
619 # Add looks and colorspaces
620 for look in look_info:
628 # Integrate looks with displays, views
629 integrate_looks_into_views(config,
637 print('Adding the regular color spaces')
639 # Creating the remaining colorspaces.
640 for colorspace in sorted(config_data['colorSpaces']):
641 # Adding the color space Family into the name
642 # Helps with applications that present colorspaces as one long list
644 prefixed_name = colorspace_prefixed_name(colorspace)
645 prefixed_names[colorspace.name] = prefixed_name
646 colorspace.name = prefixed_name
648 print('Creating new color space : %s' % colorspace.name)
650 description = colorspace.description
651 if colorspace.aces_transform_id:
652 description += "\n\nACES Transform ID : %s" % colorspace.aces_transform_id
654 ocio_colorspace = ocio.ColorSpace(
655 name=colorspace.name,
656 bitDepth=colorspace.bit_depth,
657 description=description,
658 equalityGroup=colorspace.equality_group,
659 family=colorspace.family,
660 isData=colorspace.is_data,
661 allocation=colorspace.allocation_type,
662 allocationVars=colorspace.allocation_vars)
664 if colorspace.to_reference_transforms:
665 print('\tGenerating To-Reference transforms')
666 ocio_transform = generate_OCIO_transform(
667 colorspace.to_reference_transforms)
668 ocio_colorspace.setTransform(
670 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
672 if colorspace.from_reference_transforms:
673 print('\tGenerating From-Reference transforms')
674 ocio_transform = generate_OCIO_transform(
675 colorspace.from_reference_transforms)
676 ocio_colorspace.setTransform(
678 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
680 config.addColorSpace(ocio_colorspace)
683 # Add alias to normal colorspace, using compact name
686 if colorspace.aliases != []:
687 #add_colorspace_alias(config, reference_data,
688 # colorspace, colorspace.aliases)
689 # defer adding alias colorspaces until end. Helps with some applications
690 alias_colorspaces.append([reference_data, colorspace, colorspace.aliases])
697 # We add roles early so we can create alias colorspaces with the names of the roles
698 # before the rest of the colorspace aliases are added to the config.
700 print('Setting the roles')
703 set_config_default_roles(
705 color_picking=prefixed_names[config_data['roles']['color_picking']],
706 color_timing=prefixed_names[config_data['roles']['color_timing']],
707 compositing_log=prefixed_names[config_data['roles']['compositing_log']],
708 data=prefixed_names[config_data['roles']['data']],
709 default=prefixed_names[config_data['roles']['default']],
710 matte_paint=prefixed_names[config_data['roles']['matte_paint']],
711 reference=prefixed_names[config_data['roles']['reference']],
712 scene_linear=prefixed_names[config_data['roles']['scene_linear']],
713 texture_paint=prefixed_names[config_data['roles']['texture_paint']])
715 # Not allowed for the moment. role names can not overlap with colorspace names.
717 # Add the aliased colorspaces for each role
718 for role_name, role_colorspace_name in config_data['roles'].iteritems():
719 role_colorspace_prefixed_name = prefixed_names[role_colorspace_name]
721 print( "Finding colorspace : %s" % role_colorspace_prefixed_name )
722 # Find the colorspace pointed to by the role
723 role_colorspaces = [colorspace for colorspace in config_data['colorSpaces'] if colorspace.name == role_colorspace_prefixed_name]
724 role_colorspace = None
725 if len(role_colorspaces) > 0:
726 role_colorspace = role_colorspaces[0]
728 if reference_data.name == role_colorspace_prefixed_name:
729 role_colorspace = reference_data
732 print( "Adding an alias colorspace named %s, pointing to %s" % (
733 role_name, role_colorspace.name))
735 add_colorspace_aliases(config, reference_data, role_colorspace, [role_name], 'Roles')
739 set_config_default_roles(
741 color_picking=config_data['roles']['color_picking'],
742 color_timing=config_data['roles']['color_timing'],
743 compositing_log=config_data['roles']['compositing_log'],
744 data=config_data['roles']['data'],
745 default=config_data['roles']['default'],
746 matte_paint=config_data['roles']['matte_paint'],
747 reference=config_data['roles']['reference'],
748 scene_linear=config_data['roles']['scene_linear'],
749 texture_paint=config_data['roles']['texture_paint'])
751 # Not allowed for the moment. role names can not overlap with colorspace names.
753 # Add the aliased colorspaces for each role
754 for role_name, role_colorspace_name in config_data['roles'].iteritems():
755 # Find the colorspace pointed to by the role
756 role_colorspaces = [colorspace for colorspace in config_data['colorSpaces'] if colorspace.name == role_colorspace_name]
757 role_colorspace = None
758 if len(role_colorspaces) > 0:
759 role_colorspace = role_colorspaces[0]
761 if reference_data.name == role_colorspace_name:
762 role_colorspace = reference_data
765 print( "Adding an alias colorspace named %s, pointing to %s" % (
766 role_name, role_colorspace.name))
768 add_colorspace_aliases(config, reference_data, role_colorspace, [role_name], 'Roles')
773 # We add these at the end as some applications use the order of the colorspaces
774 # definitions in the config to order the colorspaces in their selection lists.
775 # Other go alphabetically. This should keep the alias colorspaces out of the way
776 # for the apps that use the order of definition in the config.
777 print('Adding the alias colorspaces')
778 for reference, colorspace, aliases in alias_colorspaces:
779 add_colorspace_aliases(config, reference, colorspace, aliases)
783 print('Adding the diplays and views')
785 # Defining the *views* and *displays*.
790 # Defining a *generic* *display* and *view* setup.
791 if multiple_displays:
792 # Built list of looks to add to Displays
793 looks = config_data['looks'] if ('looks' in config_data) else []
794 looks = ", ".join(looks)
795 print( "Creating multiple displays, with looks : %s" % looks)
797 # Create Displays, Views
798 for display, view_list in config_data['displays'].iteritems():
799 for view_name, colorspace in view_list.iteritems():
800 config.addDisplay(display, view_name, colorspace.name, looks)
801 if 'Output Transform' in view_name and looks != "":
802 # Add normal View, without looks
803 config.addDisplay(display, view_name, colorspace.name)
805 # Add View with looks
806 view_name_with_looks = "%s with %s" % (view_name, looks)
807 config.addDisplay(display, view_name_with_looks, colorspace.name, looks)
809 config.addDisplay(display, view_name, colorspace.name)
810 if not (view_name in views):
811 views.append(view_name)
812 displays.append(display)
814 # Defining the set of *views* and *displays* useful in a *GUI* context.
816 single_display_name = 'ACES'
817 #single_display_name = config_data['roles']['scene_linear']
818 displays.append(single_display_name)
820 display_names = sorted(config_data['displays'])
822 # Make sure the default display is first
823 default_display = config_data['defaultDisplay']
824 display_names.insert(0, display_names.pop(display_names.index(default_display)))
826 # Built list of looks to add to Displays
827 looks = config_data['looks'] if ('looks' in config_data) else []
828 look_names = ", ".join(looks)
830 displays_views_colorspaces = []
832 # Create Displays, Views
833 for display in display_names:
834 view_list = config_data['displays'][display]
835 for view_name, colorspace in view_list.iteritems():
836 if 'Output Transform' in view_name:
837 #print( "Adding view for %s" % view_name )
839 # Maya 2016 doesn't like parentheses in View names
840 display_cleaned = replace(display, {')': '', '(': ''})
842 # We use the Display names as the View names in this case
843 # as there is a single Display that contains all views.
844 # This works for more applications than not, as of the time of this implementation.
846 # If View includes looks
847 if 'with' in view_name:
848 # Integrate looks into view name
849 display_cleaned = "%s with %s" % (display_cleaned, look_names)
851 viewsWithLooksAtEnd = False
852 # Storing combo of display, view and colorspace name in a list so we can
853 # add them to the end of the list
854 if viewsWithLooksAtEnd:
855 displays_views_colorspaces.append([single_display_name, display_cleaned, colorspace.name])
859 config.addDisplay(single_display_name, display_cleaned, colorspace.name)
862 if not (display_cleaned in views):
863 views.append(display_cleaned)
867 config.addDisplay(single_display_name, display_cleaned, colorspace.name)
870 if not (display_cleaned in views):
871 views.append(display_cleaned)
873 # Add to config any display, view combinations that were saved for later
874 for display_view_colorspace in displays_views_colorspaces:
875 single_display_name, display_cleaned, colorspace_name = display_view_colorspace
878 config.addDisplay(single_display_name, display_cleaned, colorspace_name)
881 if not (display_cleaned in views):
882 views.append(display_cleaned)
885 # Works with Nuke Studio and Mari, but not Nuke
886 # single_display_name = 'Utility'
887 # displays.append(single_display_name)
889 raw_display_space_name = config_data['roles']['data']
890 log_display_space_name = config_data['roles']['compositing_log']
892 # Find the newly-prefixed colorspace names
894 #print( prefixed_names )
895 raw_display_space_name = prefixed_names[raw_display_space_name]
896 log_display_space_name = prefixed_names[log_display_space_name]
898 config.addDisplay(single_display_name, 'Raw', raw_display_space_name)
900 config.addDisplay(single_display_name, 'Log', log_display_space_name)
903 # Setting the active *displays* and *views*.
904 config.setActiveDisplays(','.join(sorted(displays)))
905 config.setActiveViews(','.join(views))
909 # Make sure we didn't create a bad config
912 # Reset the colorspace names back to their non-prefixed versions
914 # Build the reverse lookup
915 prefixed_names_inverse = {}
916 for original, prefixed in prefixed_names.iteritems():
917 prefixed_names_inverse[prefixed] = original
919 # Reset the reference colorspace name
920 reference_data.name = prefixed_names_inverse[reference_data.name]
922 # Reset the rest of the colorspace names
924 for colorspace in config_data['colorSpaces']:
925 colorspace.name = prefixed_names_inverse[colorspace.name]
927 print( "Prefixed names")
928 for original, prefixed in prefixed_names.iteritems():
929 print( "%s, %s" % (original, prefixed) )
933 print( "Inverse Lookup")
934 for prefixed, original in prefixed_names_inverse.iteritems():
935 print( "%s, %s" % (prefixed, original) )
941 def generate_LUTs(odt_info,
946 lut_resolution_1d=4096,
947 lut_resolution_3d=64,
955 Parameter description.
960 Colorspaces and transforms converting between those colorspaces and
961 the reference colorspace, *ACES*.
964 print('generateLUTs - begin')
967 # Initialize a few variables
968 config_data['displays'] = {}
969 config_data['colorSpaces'] = []
971 # -------------------------------------------------------------------------
972 # *ACES Color Spaces*
973 # -------------------------------------------------------------------------
979 aces_log_display_space,
981 aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
990 config_data['referenceColorSpace'] = aces_reference
991 config_data['roles'] = aces_roles
993 for cs in aces_colorspaces:
994 config_data['colorSpaces'].append(cs)
996 for name, data in aces_displays.iteritems():
997 config_data['displays'][name] = data
999 config_data['defaultDisplay'] = aces_default_display
1000 config_data['linearDisplaySpace'] = aces_reference
1001 config_data['logDisplaySpace'] = aces_log_display_space
1003 # -------------------------------------------------------------------------
1004 # *Camera Input Transforms*
1005 # -------------------------------------------------------------------------
1007 # *ARRI Log-C* to *ACES*.
1008 arri_colorSpaces = arri.create_colorspaces(lut_directory,
1010 for cs in arri_colorSpaces:
1011 config_data['colorSpaces'].append(cs)
1013 # *Canon-Log* to *ACES*.
1014 canon_colorspaces = canon.create_colorspaces(lut_directory,
1016 for cs in canon_colorspaces:
1017 config_data['colorSpaces'].append(cs)
1019 # *GoPro Protune* to *ACES*.
1020 gopro_colorspaces = gopro.create_colorspaces(lut_directory,
1022 for cs in gopro_colorspaces:
1023 config_data['colorSpaces'].append(cs)
1025 # *Panasonic V-Log* to *ACES*.
1026 panasonic_colorSpaces = panasonic.create_colorspaces(lut_directory,
1028 for cs in panasonic_colorSpaces:
1029 config_data['colorSpaces'].append(cs)
1031 # *RED* colorspaces to *ACES*.
1032 red_colorspaces = red.create_colorspaces(lut_directory,
1034 for cs in red_colorspaces:
1035 config_data['colorSpaces'].append(cs)
1037 # *S-Log* to *ACES*.
1038 sony_colorSpaces = sony.create_colorspaces(lut_directory,
1040 for cs in sony_colorSpaces:
1041 config_data['colorSpaces'].append(cs)
1043 # -------------------------------------------------------------------------
1044 # General Color Spaces
1045 # -------------------------------------------------------------------------
1046 general_colorSpaces = general.create_colorspaces(lut_directory,
1049 for cs in general_colorSpaces:
1050 config_data['colorSpaces'].append(cs)
1052 # The *Raw* color space
1053 raw = general.create_raw()
1054 config_data['colorSpaces'].append(raw)
1056 # Override certain roles, for now
1057 config_data['roles']['data'] = raw.name
1058 config_data['roles']['reference'] = raw.name
1059 config_data['roles']['texture_paint'] = raw.name
1061 print('generateLUTs - end')
1065 def generate_baked_LUTs(odt_info,
1071 lut_resolution_shaper=1024,
1079 Parameter description.
1084 Return value description.
1087 odt_info_C = dict(odt_info)
1089 # Uncomment if you would like to support the older behavior where ODTs
1090 # that have support for full and legal range output generate a LUT for each.
1092 # Create two entries for ODTs that have full and legal range support
1093 for odt_ctl_name, odt_values in odt_info.iteritems():
1094 if odt_values['transformHasFullLegalSwitch']:
1095 odt_name = odt_values['transformUserName']
1097 odt_values_legal = dict(odt_values)
1098 odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1099 odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
1101 odt_values_full = dict(odt_values)
1102 odt_values_full['transformUserName'] = '%s - Full' % odt_name
1103 odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
1105 del (odt_info_C[odt_ctl_name])
1108 # Generate appropriate LUTs for each ODT
1109 for odt_ctl_name, odt_values in odt_info_C.iteritems():
1110 odt_prefix = odt_values['transformUserNamePrefix']
1111 odt_name = odt_values['transformUserName']
1114 for input_space in ['ACEScc', 'ACESproxy']:
1115 args = ['--iconfig', config_path,
1118 args += ['--inputspace', "ACES - %s" % input_space]
1119 args += ['--outputspace', "Output - %s" % odt_name]
1121 args += ['--inputspace', input_space]
1122 args += ['--outputspace', odt_name]
1124 args += ['--description',
1125 '%s - %s for %s data' % (odt_prefix,
1129 args += ['--shaperspace', "Utility - %s" % shaper_name,
1130 '--shapersize', str(lut_resolution_shaper)]
1132 args += ['--shaperspace', shaper_name,
1133 '--shapersize', str(lut_resolution_shaper)]
1134 args += ['--cubesize', str(lut_resolution_3d)]
1135 args += ['--format',
1137 os.path.join(baked_directory,
1139 '%s for %s.icc' % (odt_name, input_space))]
1141 bake_lut = Process(description='bake a LUT',
1147 for input_space in ['ACEScc', 'ACESproxy']:
1148 args = ['--iconfig', config_path,
1151 args += ['--inputspace', "ACES - %s" % input_space]
1152 args += ['--outputspace', "Output - %s" % odt_name]
1154 args += ['--inputspace', input_space]
1155 args += ['--outputspace', odt_name]
1156 args += ['--description',
1157 '%s - %s for %s data' % (
1158 odt_prefix, odt_name, input_space)]
1160 args += ['--shaperspace', "Utility - %s" % shaper_name,
1161 '--shapersize', str(lut_resolution_shaper)]
1163 args += ['--shaperspace', shaper_name,
1164 '--shapersize', str(lut_resolution_shaper)]
1165 args += ['--cubesize', str(lut_resolution_3d)]
1167 fargs = ['--format',
1172 '%s for %s Flame.3dl' % (odt_name, input_space))]
1173 bake_lut = Process(description='bake a LUT',
1175 args=(args + fargs))
1178 largs = ['--format',
1183 '%s for %s Lustre.3dl' % (odt_name, input_space))]
1184 bake_lut = Process(description='bake a LUT',
1186 args=(args + largs))
1190 for input_space in ['ACEScg', 'ACES2065-1']:
1191 args = ['--iconfig', config_path,
1194 args += ['--inputspace', "ACES - %s" % input_space]
1195 args += ['--outputspace', "Output - %s" % odt_name]
1197 args += ['--inputspace', input_space]
1198 args += ['--outputspace', odt_name]
1199 args += ['--description',
1200 '%s - %s for %s data' % (
1201 odt_prefix, odt_name, input_space)]
1202 if input_space == 'ACEScg':
1203 lin_shaper_name = "%s - AP1" % shaper_name
1205 lin_shaper_name = shaper_name
1207 lin_shaper_name = "Utility - %s" % lin_shaper_name
1208 args += ['--shaperspace', lin_shaper_name,
1209 '--shapersize', str(lut_resolution_shaper)]
1211 args += ['--cubesize', str(lut_resolution_3d)]
1213 margs = ['--format',
1218 '%s for %s Maya.csp' % (odt_name, input_space))]
1219 bake_lut = Process(description='bake a LUT',
1221 args=(args + margs))
1224 hargs = ['--format',
1229 '%s for %s Houdini.lut' % (odt_name, input_space))]
1230 bake_lut = Process(description='bake a LUT',
1232 args=(args + hargs))
1236 def create_config_dir(config_directory,
1237 bake_secondary_LUTs=False,
1238 custom_lut_dir=None):
1245 Parameter description.
1250 Return value description.
1253 lut_directory = os.path.join(config_directory, 'luts')
1254 dirs = [config_directory, lut_directory]
1256 if bake_secondary_LUTs:
1257 dirs.extend([os.path.join(config_directory, 'baked'),
1258 os.path.join(config_directory, 'baked', 'flame'),
1259 os.path.join(config_directory, 'baked', 'photoshop'),
1260 os.path.join(config_directory, 'baked', 'houdini'),
1261 os.path.join(config_directory, 'baked', 'lustre'),
1262 os.path.join(config_directory, 'baked', 'maya')])
1265 dirs.append(os.path.join(config_directory, 'custom'))
1268 not os.path.exists(d) and os.mkdir(d)
1270 return lut_directory
1273 def create_ACES_config(aces_ctl_directory,
1275 lut_resolution_1d=4096,
1276 lut_resolution_3d=64,
1277 bake_secondary_LUTs=True,
1278 multiple_displays=False,
1280 copy_custom_luts=True,
1282 prefix_colorspaces_with_family_names=True):
1284 Creates the ACES configuration.
1289 Parameter description.
1294 Return value description.
1297 # Directory for custom LUTs
1298 custom_lut_dir = None
1299 if copy_custom_luts:
1300 custom_lut_dir = os.path.join(config_directory, "custom")
1302 lut_directory = create_config_dir(config_directory,
1303 bake_secondary_LUTs,
1306 odt_info = aces.get_ODTs_info(aces_ctl_directory)
1307 lmt_info = aces.get_LMTs_info(aces_ctl_directory)
1309 shaper_name = 'Output Shaper'
1310 config_data = generate_LUTs(odt_info,
1319 print('Creating config - with prefixes, with aliases')
1320 config = create_config(config_data,
1321 prefix=prefix_colorspaces_with_family_names,
1323 multiple_displays=multiple_displays,
1324 look_info=look_info,
1325 custom_lut_dir=custom_lut_dir)
1328 write_config(config,
1329 os.path.join(config_directory, 'config.ocio'))
1331 if bake_secondary_LUTs:
1332 generate_baked_LUTs(odt_info,
1334 os.path.join(config_directory, 'baked'),
1335 os.path.join(config_directory, 'config.ocio'),
1339 prefix=prefix_colorspaces_with_family_names)
1351 Parameter description.
1356 Return value description.
1361 usage = '%prog [options]\n'
1363 usage += 'An OCIO config generation script for ACES 1.0\n'
1365 usage += 'Command line examples'
1367 usage += 'Create a GUI-friendly ACES 1.0 config with no secondary, baked LUTs : \n'
1368 usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --dontBakeSecondaryLUTs'
1370 usage += 'Create a more OCIO-compliant ACES 1.0 config : \n'
1371 usage += '\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl --lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 --createMultipleDisplays'
1374 usage += 'Adding custom looks'
1376 usage += 'Create a GUI-friendly ACES 1.0 config with an ACES-style CDL (will be applied in the ACEScc colorspace): \n'
1377 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'
1379 usage += 'Create a GUI-friendly ACES 1.0 config with an general CDL: \n'
1380 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'
1382 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'
1384 usage += 'Create a GUI-friendly ACES 1.0 config with an ACES-style LUT (will be applied in the ACEScc colorspace): \n'
1385 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'
1387 usage += 'Create a GUI-friendly ACES 1.0 config with an general LUT: \n'
1388 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'
1390 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'
1394 def look_info_callback(option, opt_str, value, parser):
1395 print( "look_info_callback" )
1396 print( option, opt_str, value, parser )
1397 if opt_str == "--addCustomLookCDL":
1398 look_info.append(value)
1399 elif opt_str == "--addCustomLookLUT":
1400 look_info.append(value)
1401 elif opt_str == "--addACESLookCDL":
1402 look_info.append([value[0], "ACES - ACEScc", value[1], value[2]])
1403 elif opt_str == "--addACESLookLUT":
1404 look_info.append([value[0], "ACES - ACEScc", value[1]])
1406 p = optparse.OptionParser(description='',
1407 prog='create_aces_config',
1408 version='create_aces_config 1.0',
1410 p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1411 ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1412 p.add_option('--configDir', '-c', default=os.environ.get(
1413 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1414 p.add_option('--lutResolution1d', default=4096)
1415 p.add_option('--lutResolution3d', default=64)
1416 p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
1417 p.add_option('--keepTempImages', action='store_true', default=False)
1419 p.add_option('--createMultipleDisplays', action='store_true', default=False)
1421 p.add_option('--addCustomLookLUT', '', type='string', nargs=3,
1422 action="callback", callback=look_info_callback)
1423 p.add_option('--addCustomLookCDL', '', type='string', nargs=4,
1424 action="callback", callback=look_info_callback)
1425 p.add_option('--addACESLookLUT', '', type='string', nargs=2,
1426 action="callback", callback=look_info_callback)
1427 p.add_option('--addACESLookCDL', '', type='string', nargs=3,
1428 action="callback", callback=look_info_callback)
1429 p.add_option('--copyCustomLUTs', action='store_true', default=False)
1431 options, arguments = p.parse_args()
1433 aces_ctl_directory = options.acesCTLDir
1434 config_directory = options.configDir
1435 lut_resolution_1d = int(options.lutResolution1d)
1436 lut_resolution_3d = int(options.lutResolution3d)
1437 bake_secondary_luts = not options.dontBakeSecondaryLUTs
1438 cleanup_temp_images = not options.keepTempImages
1439 multiple_displays = options.createMultipleDisplays
1440 copy_custom_luts = options.copyCustomLUTs
1444 # TODO: Investigate the following statements.
1446 args_start = sys.argv.index('--') + 1
1447 args = sys.argv[args_start:]
1449 args_start = len(sys.argv) + 1
1452 print('command line : \n%s\n' % ' '.join(sys.argv))
1454 assert aces_ctl_directory is not None, (
1455 'process: No "{0}" environment variable defined or no "ACES CTL" '
1456 'directory specified'.format(
1457 ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1459 assert config_directory is not None, (
1460 'process: No "{0}" environment variable defined or no configuration '
1461 'directory specified'.format(
1462 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1464 return create_ACES_config(aces_ctl_directory,
1468 bake_secondary_luts,
1472 cleanup_temp_images)
1474 if __name__ == '__main__':