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 (
28 colorspace_prefixed_name,
33 __author__ = 'ACES Developers'
34 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
36 __maintainer__ = 'ACES Developers'
37 __email__ = 'aces@oscars.org'
38 __status__ = 'Production'
40 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
41 'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
43 'create_ocio_transform',
44 'add_colorspace_aliases',
50 'generate_baked_LUTs',
51 'generate_config_directory',
55 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
56 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
59 def set_config_roles(config,
70 compositing_linear=None):
72 Sets given *OCIO* configuration roles to the config.
77 color_picking : str or unicode, optional
78 Color picking role title.
79 color_timing : str or unicode, optional
80 Color timing role title.
81 compositing_log : str or unicode, optional
82 Compositing log role title.
83 data : str or unicode, optional
85 default : str or unicode, optional
87 matte_paint : str or unicode, optional
88 Matte painting role title.
89 reference : str or unicode, optional
91 scene_linear : str or unicode, optional
92 Scene linear role title.
93 texture_paint : str or unicode, optional
94 Texture painting role title.
101 if color_picking is not None:
102 config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
103 if color_timing is not None:
104 config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
105 if compositing_log is not None:
106 config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
108 config.setRole(ocio.Constants.ROLE_DATA, data)
109 if default is not None:
110 config.setRole(ocio.Constants.ROLE_DEFAULT, default)
111 if matte_paint is not None:
112 config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
113 if reference is not None:
114 config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
115 if texture_paint is not None:
116 config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
118 # *rendering* and *compositing_linear* roles default to the *scene_linear*
119 # value if not set explicitly.
120 if rendering is not None:
121 config.setRole('rendering', rendering)
122 if compositing_linear is not None:
123 config.setRole('compositing_linear', compositing_linear)
124 if scene_linear is not None:
125 config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
126 if rendering is None:
127 config.setRole('rendering', scene_linear)
128 if compositing_linear is None:
129 config.setRole('compositing_linear', scene_linear)
134 def create_ocio_transform(transforms):
136 Returns an *OCIO* transform from given array of transform descriptions.
140 transforms : array_like
141 Transform descriptions as an array_like of dicts:
142 {'type', 'src', 'dst', 'direction'}
150 direction_options = {
151 'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
152 'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
156 for transform in transforms:
158 # *lutFile* transform
159 if transform['type'] == 'lutFile':
160 ocio_transform = ocio.FileTransform()
162 if 'path' in transform:
163 ocio_transform.setSrc(transform['path'])
165 if 'cccid' in transform:
166 ocio_transform.setCCCId(transform['cccid'])
168 if 'interpolation' in transform:
169 ocio_transform.setInterpolation(transform['interpolation'])
171 ocio_transform.setInterpolation(ocio.Constants.INTERP_BEST)
173 if 'direction' in transform:
174 ocio_transform.setDirection(
175 direction_options[transform['direction']])
177 ocio_transforms.append(ocio_transform)
180 elif transform['type'] == 'matrix':
181 ocio_transform = ocio.MatrixTransform()
182 # `MatrixTransform` member variables can't be initialized directly,
183 # each must be set individually.
184 ocio_transform.setMatrix(transform['matrix'])
186 if 'offset' in transform:
187 ocio_transform.setOffset(transform['offset'])
189 if 'direction' in transform:
190 ocio_transform.setDirection(
191 direction_options[transform['direction']])
193 ocio_transforms.append(ocio_transform)
195 # *exponent* transform
196 elif transform['type'] == 'exponent':
197 ocio_transform = ocio.ExponentTransform()
199 if 'value' in transform:
200 ocio_transform.setValue(transform['value'])
202 ocio_transforms.append(ocio_transform)
205 elif transform['type'] == 'log':
206 ocio_transform = ocio.LogTransform()
208 if 'base' in transform:
209 ocio_transform.setBase(transform['base'])
211 if 'direction' in transform:
212 ocio_transform.setDirection(
213 direction_options[transform['direction']])
215 ocio_transforms.append(ocio_transform)
217 # *colorspace* transform
218 elif transform['type'] == 'colorspace':
219 ocio_transform = ocio.ColorSpaceTransform()
221 if 'src' in transform:
222 ocio_transform.setSrc(transform['src'])
224 if 'dst' in transform:
225 ocio_transform.setDst(transform['dst'])
227 if 'direction' in transform:
228 ocio_transform.setDirection(
229 direction_options[transform['direction']])
231 ocio_transforms.append(ocio_transform)
234 elif transform['type'] == 'look':
235 ocio_transform = ocio.LookTransform()
236 if 'look' in transform:
237 ocio_transform.setLooks(transform['look'])
239 if 'src' in transform:
240 ocio_transform.setSrc(transform['src'])
242 if 'dst' in transform:
243 ocio_transform.setDst(transform['dst'])
245 if 'direction' in transform:
246 ocio_transform.setDirection(
247 direction_options[transform['direction']])
249 ocio_transforms.append(ocio_transform)
253 print('Ignoring unknown transform type : %s' % transform['type'])
255 if len(ocio_transforms) > 1:
256 group_transform = ocio.GroupTransform()
257 for transform in ocio_transforms:
258 group_transform.push_back(transform)
259 transform = group_transform
261 transform = ocio_transforms[0]
266 def add_colorspace_aliases(config,
267 reference_colorspace,
269 colorspace_alias_names,
272 Adds given colorspace aliases to the *OCIO* config.
277 *OCIO* configuration.
278 reference_colorspace : Colorspace
279 Reference colorspace.
280 colorspace : Colorspace
281 Colorspace to set the aliases into the *OCIO* config.
291 for alias_name in colorspace_alias_names:
292 if alias_name.lower() == colorspace.name.lower():
293 print('Skipping alias creation for %s, alias %s, '
294 'because lower cased names match' % (
295 colorspace.name, alias_name))
298 print('Adding alias colorspace space %s, alias to %s' % (
299 alias_name, colorspace.name))
301 compact_family_name = family
303 description = colorspace.description
304 if colorspace.aces_transform_id:
306 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
308 ocio_colorspace_alias = ocio.ColorSpace(
310 bitDepth=colorspace.bit_depth,
311 description=description,
312 equalityGroup=colorspace.equality_group,
313 family=compact_family_name,
314 isData=colorspace.is_data,
315 allocation=colorspace.allocation_type,
316 allocationVars=colorspace.allocation_vars)
318 if colorspace.to_reference_transforms:
319 print('\tGenerating To-Reference transforms')
320 ocio_transform = create_ocio_transform(
321 [{'type': 'colorspace',
322 'src': colorspace.name,
323 'dst': reference_colorspace.name,
324 'direction': 'forward'}])
325 ocio_colorspace_alias.setTransform(
327 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
329 if colorspace.from_reference_transforms:
330 print('\tGenerating From-Reference transforms')
331 ocio_transform = create_ocio_transform(
332 [{'type': 'colorspace',
333 'src': reference_colorspace.name,
334 'dst': colorspace.name,
335 'direction': 'forward'}])
336 ocio_colorspace_alias.setTransform(
338 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
340 config.addColorSpace(ocio_colorspace_alias)
349 Adds given look to the *OCIO* config.
354 *OCIO* configuration.
356 Look description: {'name', 'colorspace', 'lut', 'cccid'}
357 custom_lut_dir : str or unicode
358 Directory to copy the look lut into.
359 reference_name : str or unicode
362 Colorspaces and transforms converting between those colorspaces and
363 the reference colorspace, *ACES*.
371 look_name, look_colorspace, look_lut, look_cccid = unpack_default(look, 4)
373 print('Adding look %s - %s' % (look_name, ', '.join(look)))
375 # Copy *look LUT* if `custom_lut_dir` is provided.
377 if '$' not in look_lut:
378 print('Getting ready to copy look lut : %s' % look_lut)
379 shutil.copy2(look_lut, custom_lut_dir)
380 look_lut = os.path.split(look_lut)[1]
382 print('Skipping LUT copy because path contains a context variable')
384 print('Adding look to config')
385 ocio_look = ocio.Look()
386 ocio_look.setName(look_name)
387 ocio_look.setProcessSpace(look_colorspace)
389 keys = {'type': 'lutFile',
391 'direction': 'forward'}
393 keys['cccid'] = look_cccid
395 ocio_transform = create_ocio_transform([keys])
396 ocio_look.setTransform(ocio_transform)
398 config.addLook(ocio_look)
400 print('Creating aliased colorspace')
402 # Creating *OCIO* colorspace referencing the look:
403 # - Needed for implementations that don't process looks properly.
404 # - Needed for implementations that don't expose looks properly.
405 look_aliases = ['look_%s' % compact(look_name)]
406 colorspace = ColorSpace(look_name,
407 aliases=look_aliases,
408 description='The %s Look colorspace' % look_name,
411 colorspace.from_reference_transforms = [{'type': 'look',
413 'src': reference_name,
414 'dst': reference_name,
415 'direction': 'forward'}]
417 print('Adding colorspace %s, alias to look %s to config data' % (
418 look_name, look_name))
420 config_data['colorSpaces'].append(colorspace)
425 def add_looks_to_views(looks,
428 multiple_displays=False):
435 Parameter description.
440 Return value description.
442 look_names = [look[0] for look in looks]
445 # - Adding a *look* per *Display*.
446 # - Assuming there is a *Display* for each *ACES* *Output Transform*.
447 if multiple_displays:
448 for look_name in look_names:
449 config_data['looks'].append(look_name)
452 # - Copy each *Output Transform* colorspace.
453 # - For each copy, add a *LookTransform* to the head of the
454 # `from_reference` transform list.
455 # - Add these the copy colorspaces for the *Displays* / *Views*.
457 for display, view_list in config_data['displays'].iteritems():
459 look_names_string = ''
460 for view_name, output_colorspace in view_list.iteritems():
461 if view_name == 'Output Transform':
463 print('Adding new View that incorporates looks')
465 colorspace_c = copy.deepcopy(output_colorspace)
467 for i, look_name in enumerate(look_names):
468 look_name = look_names[i]
470 # Add the `LookTransform` to the head of the
471 # `from_reference` transform list.
472 if colorspace_c.from_reference_transforms:
473 colorspace_c.from_reference_transforms.insert(
477 'src': reference_name,
478 'dst': reference_name,
479 'direction': 'forward'})
481 # Add the `LookTransform` to the end of
482 # the `to_reference` transform list.
483 if colorspace_c.to_reference_transforms:
484 inverse_look_name = look_names[
485 len(look_names) - 1 - i]
487 colorspace_c.to_reference_transforms.append(
489 'look': inverse_look_name,
490 'src': reference_name,
491 'dst': reference_name,
492 'direction': 'inverse'})
494 if look_name not in config_data['looks']:
495 config_data['looks'].append(look_name)
497 look_names_string = ', '.join(look_names)
498 colorspace_c.name = '%s with %s' % (
499 output_colorspace.name, look_names_string)
500 colorspace_c.aliases = [
501 'out_%s' % compact(colorspace_c.name)]
503 print('Colorspace that incorporates looks '
504 'created : %s' % colorspace_c.name)
506 config_data['colorSpaces'].append(colorspace_c)
509 print('Adding colorspace that incorporates looks '
512 # Updating the *View* name.
513 view_list['Output Transform with %s' % look_names_string] = (
515 config_data['displays'][display] = view_list
518 def create_config(config_data,
521 multiple_displays=False,
523 custom_lut_dir=None):
530 Parameter description.
535 Return value description.
538 if look_info is None:
542 alias_colorspaces = []
544 config = ocio.Config()
546 config.setDescription('An ACES config generated from python')
548 search_path = ['luts']
550 search_path.append('custom')
551 config.setSearchPath(':'.join(search_path))
553 reference_data = config_data['referenceColorSpace']
555 # Adding the colorspace *Family* into the name which helps with
556 # applications that presenting colorspaces as one a flat list.
558 prefixed_name = colorspace_prefixed_name(reference_data)
559 prefixed_names[reference_data.name] = prefixed_name
560 reference_data.name = prefixed_name
562 print('Adding the reference color space : %s' % reference_data.name)
564 reference = ocio.ColorSpace(
565 name=reference_data.name,
566 bitDepth=reference_data.bit_depth,
567 description=reference_data.description,
568 equalityGroup=reference_data.equality_group,
569 family=reference_data.family,
570 isData=reference_data.is_data,
571 allocation=reference_data.allocation_type,
572 allocationVars=reference_data.allocation_vars)
574 config.addColorSpace(reference)
577 if reference_data.aliases:
578 # Deferring adding alias colorspaces until end, which helps with
579 # applications listing the colorspaces in the order that they were
580 # defined in the configuration: alias colorspaces are usually named
581 # lower case with spaces but normal colorspaces names are longer
582 # and more verbose, thus it becomes harder for user to visually
583 # parse the list of colorspaces when there are names such as
584 # "crv_canonlog" interspersed with names like
585 # "Input - Canon - Curve - Canon-Log".
586 # Moving the alias colorspace definitions to the end of the
587 # configuration avoids the above problem.
588 alias_colorspaces.append(
589 [reference_data, reference_data, reference_data.aliases])
594 print('Adding looks')
596 config_data['looks'] = []
598 for look in look_info:
605 add_looks_to_views(look_info,
612 print('Adding regular colorspaces')
614 for colorspace in sorted(config_data['colorSpaces'],
615 cmp=lambda x,y: cmp(x.family.lower(), y.family.lower())):
616 # Adding the colorspace *Family* into the name which helps with
617 # applications that presenting colorspaces as one a flat list.
619 prefixed_name = colorspace_prefixed_name(colorspace)
620 prefixed_names[colorspace.name] = prefixed_name
621 colorspace.name = prefixed_name
623 print('Creating new color space : %s' % colorspace.name)
625 description = colorspace.description
626 if colorspace.aces_transform_id:
628 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
630 ocio_colorspace = ocio.ColorSpace(
631 name=colorspace.name,
632 bitDepth=colorspace.bit_depth,
633 description=description,
634 equalityGroup=colorspace.equality_group,
635 family=colorspace.family,
636 isData=colorspace.is_data,
637 allocation=colorspace.allocation_type,
638 allocationVars=colorspace.allocation_vars)
640 if colorspace.to_reference_transforms:
641 print('\tGenerating To-Reference transforms')
642 ocio_transform = create_ocio_transform(
643 colorspace.to_reference_transforms)
644 ocio_colorspace.setTransform(
646 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
648 if colorspace.from_reference_transforms:
649 print('\tGenerating From-Reference transforms')
650 ocio_transform = create_ocio_transform(
651 colorspace.from_reference_transforms)
652 ocio_colorspace.setTransform(
654 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
656 config.addColorSpace(ocio_colorspace)
659 if colorspace.aliases:
660 # Deferring adding alias colorspaces until end, which helps
661 # with applications listing the colorspaces in the order that
662 # they were defined in the configuration.
663 alias_colorspaces.append(
664 [reference_data, colorspace, colorspace.aliases])
670 # Adding roles early so that alias colorspaces can be created
671 # with roles names before remaining colorspace aliases are added
672 # to the configuration.
673 print('Setting the roles')
678 color_picking=prefixed_names[
679 config_data['roles']['color_picking']],
680 color_timing=prefixed_names[config_data['roles']['color_timing']],
681 compositing_log=prefixed_names[
682 config_data['roles']['compositing_log']],
683 data=prefixed_names[config_data['roles']['data']],
684 default=prefixed_names[config_data['roles']['default']],
685 matte_paint=prefixed_names[config_data['roles']['matte_paint']],
686 reference=prefixed_names[config_data['roles']['reference']],
687 scene_linear=prefixed_names[config_data['roles']['scene_linear']],
688 compositing_linear=prefixed_names[config_data['roles']['scene_linear']],
689 rendering=prefixed_names[config_data['roles']['scene_linear']],
690 texture_paint=prefixed_names[
691 config_data['roles']['texture_paint']])
693 # Add the aliased colorspaces for each role
694 for role_name, role_colorspace_name in config_data['roles'].iteritems():
695 role_colorspace_prefixed_name = prefixed_names[role_colorspace_name]
697 #print( 'Finding colorspace : %s' % role_colorspace_prefixed_name )
698 # Find the colorspace pointed to by the role
699 role_colorspaces = [colorspace
700 for colorspace in config_data['colorSpaces']
701 if colorspace.name == role_colorspace_prefixed_name]
702 role_colorspace = None
703 if len(role_colorspaces) > 0:
704 role_colorspace = role_colorspaces[0]
706 if reference_data.name == role_colorspace_prefixed_name:
707 role_colorspace = reference_data
710 # The alias colorspace shouldn't match the role name exactly
711 role_name_alias1 = "role_%s" % role_name
712 role_name_alias2 = "Role - %s" % role_name
714 print( 'Adding a role colorspace named %s, pointing to %s' % (
715 role_name_alias2, role_colorspace.name))
717 alias_colorspaces.append(
718 (reference_data, role_colorspace, [role_name_alias1]))
720 add_colorspace_aliases(
721 config, reference_data, role_colorspace, [role_name_alias2], 'Roles')
726 color_picking=config_data['roles']['color_picking'],
727 color_timing=config_data['roles']['color_timing'],
728 compositing_log=config_data['roles']['compositing_log'],
729 data=config_data['roles']['data'],
730 default=config_data['roles']['default'],
731 matte_paint=config_data['roles']['matte_paint'],
732 reference=config_data['roles']['reference'],
733 scene_linear=config_data['roles']['scene_linear'],
734 compositing_linear=config_data['roles']['scene_linear'],
735 rendering=config_data['roles']['scene_linear'],
736 texture_paint=config_data['roles']['texture_paint'])
738 # Add the aliased colorspaces for each role
739 for role_name, role_colorspace_name in config_data['roles'].iteritems():
740 # Find the colorspace pointed to by the role
741 role_colorspaces = [colorspace
742 for colorspace in config_data['colorSpaces']
743 if colorspace.name == role_colorspace_name]
744 role_colorspace = None
745 if len(role_colorspaces) > 0:
746 role_colorspace = role_colorspaces[0]
748 if reference_data.name == role_colorspace_name:
749 role_colorspace = reference_data
752 # The alias colorspace shouldn't match the role name exactly
753 role_name_alias1 = "role_%s" % role_name
754 role_name_alias2 = "Role - %s" % role_name
756 print('Adding a role colorspace named %s, pointing to %s' % (
757 role_name_alias2, role_colorspace.name))
759 alias_colorspaces.append(
760 (reference_data, role_colorspace, [role_name_alias1]))
762 add_colorspace_aliases(
763 config, reference_data, role_colorspace, [role_name_alias2], 'Roles')
767 # Adding alias colorspaces at the end as some applications use
768 # colorspaces definitions order of the configuration to order
769 # the colorspaces in their selection lists, some applications
770 # use alphabetical ordering.
771 # This should keep the alias colorspaces out of the way for applications
772 # using the configuration order.
773 print('Adding the alias colorspaces')
774 for reference, colorspace, aliases in alias_colorspaces:
775 add_colorspace_aliases(config, reference, colorspace, aliases)
779 print('Adding the diplays and views')
781 # Setting the *color_picking* role to be the first *Display*'s
782 # *Output Transform* *View*.
783 default_display_name = config_data['defaultDisplay']
784 default_display_views = config_data['displays'][default_display_name]
785 default_display_colorspace = default_display_views['Output Transform']
787 # Defining *Displays* and *Views*.
788 displays, views = [], []
790 # Defining a generic *Display* and *View* setup.
791 if multiple_displays:
792 looks = config_data['looks'] if ('looks' in config_data) else []
793 looks = ', '.join(looks)
794 print('Creating multiple displays, with looks : %s' % looks)
796 # *Displays* are not reordered to put the *defaultDisplay* first
797 # because *OCIO* will order them alphabetically when the configuration
798 # is written to disk.
799 for display, view_list in config_data['displays'].iteritems():
800 for view_name, colorspace in view_list.iteritems():
801 config.addDisplay(display, view_name, colorspace.name, looks)
802 if 'Output Transform' in view_name and looks != '':
803 # *Views* without *Looks*.
804 config.addDisplay(display, view_name, colorspace.name)
806 # *Views* with *Looks*.
807 view_name_with_looks = '%s with %s' % (view_name, looks)
808 config.addDisplay(display, view_name_with_looks,
809 colorspace.name, looks)
811 config.addDisplay(display, view_name, colorspace.name)
812 if not (view_name in views):
813 views.append(view_name)
814 displays.append(display)
816 # *Displays* and *Views* useful in a *GUI* context.
818 single_display_name = 'ACES'
819 displays.append(single_display_name)
821 # Ensuring the *defaultDisplay* is first.
822 display_names = sorted(config_data['displays'])
823 display_names.insert(0, display_names.pop(
824 display_names.index(default_display_name)))
826 looks = config_data['looks'] if ('looks' in config_data) else []
827 look_names = ', '.join(looks)
829 displays_views_colorspaces = []
831 for display in display_names:
832 view_list = config_data['displays'][display]
833 for view_name, colorspace in view_list.iteritems():
834 if 'Output Transform' in view_name:
836 # We use the *Display* names as the *View* names in this
837 # case as there is a single *Display* containing all the
839 # This works for more applications than not,as of the time
840 # of this implementation.
842 # Autodesk Maya 2016 doesn't support parentheses in
844 sanitised_display = replace(display, {')': '', '(': ''})
846 # *View* with *Looks*.
847 if 'with' in view_name:
848 sanitised_display = '%s with %s' % (
849 sanitised_display, look_names)
851 views_with_looks_at_end = False
852 # Storing combo of *Display*, *View* and *Colorspace*
853 # name so they can be added to the end of the list.
854 if views_with_looks_at_end:
855 displays_views_colorspaces.append(
856 [single_display_name, sanitised_display,
859 config.addDisplay(single_display_name,
863 if not (sanitised_display in views):
864 views.append(sanitised_display)
866 # *View* without *Looks*.
868 config.addDisplay(single_display_name,
872 if not (sanitised_display in views):
873 views.append(sanitised_display)
875 # Adding to the configuration any *Display*, *View* combinations that
876 # were saved for later.
877 # This list should be empty unless `views_with_looks_at_end` is
879 for display_view_colorspace in displays_views_colorspaces:
880 single_display_name, sanitised_display, colorspace_name = (
881 display_view_colorspace)
883 config.addDisplay(single_display_name,
887 if not (sanitised_display in views):
888 views.append(sanitised_display)
890 raw_display_space_name = config_data['roles']['data']
891 log_display_space_name = config_data['roles']['compositing_log']
894 raw_display_space_name = prefixed_names[raw_display_space_name]
895 log_display_space_name = prefixed_names[log_display_space_name]
897 config.addDisplay(single_display_name, 'Raw', raw_display_space_name)
899 config.addDisplay(single_display_name, 'Log', log_display_space_name)
902 config.setActiveDisplays(','.join(sorted(displays)))
903 config.setActiveViews(','.join(views))
907 # Ensuring the configuration is valid.
910 # Resetting colorspace names to their non-prefixed versions.
912 prefixed_names_inverse = {}
913 for original, prefixed in prefixed_names.iteritems():
914 prefixed_names_inverse[prefixed] = original
916 reference_data.name = prefixed_names_inverse[reference_data.name]
919 for colorspace in config_data['colorSpaces']:
920 colorspace.name = prefixed_names_inverse[colorspace.name]
922 print('Prefixed names')
923 for original, prefixed in prefixed_names.iteritems():
924 print('%s, %s' % (original, prefixed))
928 print('Inverse Lookup of Prefixed names')
929 for prefixed, original in prefixed_names_inverse.iteritems():
930 print('%s, %s' % (prefixed, original))
936 def create_config_data(odt_info,
941 lut_resolution_1d=4096,
942 lut_resolution_3d=64,
950 Parameter description.
955 Colorspaces and transforms converting between those colorspaces and
956 the reference colorspace, *ACES*.
959 print('create_config_data - begin')
962 config_data['displays'] = {}
963 config_data['colorSpaces'] = []
965 # -------------------------------------------------------------------------
966 # *ACES Color Spaces*
967 # -------------------------------------------------------------------------
973 aces_log_display_space,
975 aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
984 config_data['referenceColorSpace'] = aces_reference
985 config_data['roles'] = aces_roles
987 for cs in aces_colorspaces:
988 config_data['colorSpaces'].append(cs)
990 for name, data in aces_displays.iteritems():
991 config_data['displays'][name] = data
993 config_data['defaultDisplay'] = aces_default_display
994 config_data['linearDisplaySpace'] = aces_reference
995 config_data['logDisplaySpace'] = aces_log_display_space
997 # -------------------------------------------------------------------------
998 # *Camera Input Transforms*
999 # -------------------------------------------------------------------------
1001 # *ARRI Log-C* to *ACES*
1002 arri_colorspaces = arri.create_colorspaces(lut_directory,
1004 for cs in arri_colorspaces:
1005 config_data['colorSpaces'].append(cs)
1007 # *Canon-Log* to *ACES*
1008 canon_colorspaces = canon.create_colorspaces(lut_directory,
1010 for cs in canon_colorspaces:
1011 config_data['colorSpaces'].append(cs)
1013 # *GoPro Protune* to *ACES*
1014 gopro_colorspaces = gopro.create_colorspaces(lut_directory,
1016 for cs in gopro_colorspaces:
1017 config_data['colorSpaces'].append(cs)
1019 # *Panasonic V-Log* to *ACES*
1020 panasonic_colorspaces = panasonic.create_colorspaces(lut_directory,
1022 for cs in panasonic_colorspaces:
1023 config_data['colorSpaces'].append(cs)
1025 # *RED* colorspaces to *ACES*
1026 red_colorspaces = red.create_colorspaces(lut_directory,
1028 for cs in red_colorspaces:
1029 config_data['colorSpaces'].append(cs)
1032 sony_colorspaces = sony.create_colorspaces(lut_directory,
1034 for cs in sony_colorspaces:
1035 config_data['colorSpaces'].append(cs)
1037 # -------------------------------------------------------------------------
1038 # General Colorspaces
1039 # -------------------------------------------------------------------------
1040 general_colorspaces = general.create_colorspaces(lut_directory,
1042 for cs in general_colorspaces:
1043 config_data['colorSpaces'].append(cs)
1045 # The *Raw* colorspace
1046 raw = general.create_raw()
1047 config_data['colorSpaces'].append(raw)
1049 # Overriding various roles
1050 config_data['roles']['data'] = raw.name
1051 config_data['roles']['reference'] = raw.name
1052 config_data['roles']['texture_paint'] = raw.name
1054 print('create_config_data - end')
1059 def write_config(config, config_path, sanity_check=True):
1061 Writes the configuration to given path.
1066 *OCIO* configuration.
1067 config_path : str or unicode
1068 Path to write the configuration path.
1070 Performs configuration sanity checking prior to writing it on disk.
1080 config.sanityCheck()
1081 except Exception, e:
1083 print 'Configuration was not written due to a failed Sanity Check'
1086 with open(config_path, mode='w') as fp:
1087 fp.write(config.serialize())
1090 def generate_baked_LUTs(odt_info,
1095 lut_resolution_shaper=1024,
1103 Parameter description.
1108 Return value description.
1111 odt_info_C = dict(odt_info)
1113 # Older behavior for *ODTs* that have support for full and legal ranges,
1114 # generating a LUT for both ranges.
1116 # Create two entries for ODTs that have full and legal range support
1117 for odt_ctl_name, odt_values in odt_info.iteritems():
1118 if odt_values['transformHasFullLegalSwitch']:
1119 odt_name = odt_values['transformUserName']
1121 odt_values_legal = dict(odt_values)
1122 odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1123 odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
1125 odt_values_full = dict(odt_values)
1126 odt_values_full['transformUserName'] = '%s - Full' % odt_name
1127 odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
1129 del (odt_info_C[odt_ctl_name])
1132 for odt_ctl_name, odt_values in odt_info_C.iteritems():
1133 odt_prefix = odt_values['transformUserNamePrefix']
1134 odt_name = odt_values['transformUserName']
1137 for input_space in ['ACEScc', 'ACESproxy']:
1138 args = ['--iconfig', config_path,
1141 args += ['--inputspace', 'ACES - %s' % input_space]
1142 args += ['--outputspace', 'Output - %s' % odt_name]
1144 args += ['--inputspace', input_space]
1145 args += ['--outputspace', odt_name]
1147 args += ['--description',
1148 '%s - %s for %s data' % (odt_prefix,
1152 args += ['--shaperspace', 'Utility - %s' % shaper_name,
1153 '--shapersize', str(lut_resolution_shaper)]
1155 args += ['--shaperspace', shaper_name,
1156 '--shapersize', str(lut_resolution_shaper)]
1157 args += ['--cubesize', str(lut_resolution_3d)]
1158 args += ['--format',
1160 os.path.join(baked_directory,
1162 '%s for %s.icc' % (odt_name, input_space))]
1164 bake_lut = Process(description='bake a LUT',
1170 for input_space in ['ACEScc', 'ACESproxy']:
1171 args = ['--iconfig', config_path,
1174 args += ['--inputspace', 'ACES - %s' % input_space]
1175 args += ['--outputspace', 'Output - %s' % odt_name]
1177 args += ['--inputspace', input_space]
1178 args += ['--outputspace', odt_name]
1179 args += ['--description',
1180 '%s - %s for %s data' % (
1181 odt_prefix, odt_name, input_space)]
1183 args += ['--shaperspace', 'Utility - %s' % shaper_name,
1184 '--shapersize', str(lut_resolution_shaper)]
1186 args += ['--shaperspace', shaper_name,
1187 '--shapersize', str(lut_resolution_shaper)]
1188 args += ['--cubesize', str(lut_resolution_3d)]
1190 fargs = ['--format',
1195 '%s for %s Flame.3dl' % (odt_name, input_space))]
1196 bake_lut = Process(description='bake a LUT',
1198 args=(args + fargs))
1201 largs = ['--format',
1206 '%s for %s Lustre.3dl' % (odt_name, input_space))]
1207 bake_lut = Process(description='bake a LUT',
1209 args=(args + largs))
1213 for input_space in ['ACEScg', 'ACES2065-1']:
1214 args = ['--iconfig', config_path,
1217 args += ['--inputspace', 'ACES - %s' % input_space]
1218 args += ['--outputspace', 'Output - %s' % odt_name]
1220 args += ['--inputspace', input_space]
1221 args += ['--outputspace', odt_name]
1222 args += ['--description',
1223 '%s - %s for %s data' % (
1224 odt_prefix, odt_name, input_space)]
1225 if input_space == 'ACEScg':
1226 lin_shaper_name = '%s - AP1' % shaper_name
1228 lin_shaper_name = shaper_name
1230 lin_shaper_name = 'Utility - %s' % lin_shaper_name
1231 args += ['--shaperspace', lin_shaper_name,
1232 '--shapersize', str(lut_resolution_shaper)]
1234 args += ['--cubesize', str(lut_resolution_3d)]
1236 margs = ['--format',
1241 '%s for %s Maya.csp' % (odt_name, input_space))]
1242 bake_lut = Process(description='bake a LUT',
1244 args=(args + margs))
1247 hargs = ['--format',
1252 '%s for %s Houdini.lut' % (odt_name, input_space))]
1253 bake_lut = Process(description='bake a LUT',
1255 args=(args + hargs))
1259 def generate_config_directory(config_directory,
1260 bake_secondary_luts=False,
1261 custom_lut_dir=None):
1268 Parameter description.
1273 Return value description.
1276 lut_directory = os.path.join(config_directory, 'luts')
1277 dirs = [config_directory, lut_directory]
1279 if bake_secondary_luts:
1280 dirs.extend([os.path.join(config_directory, 'baked'),
1281 os.path.join(config_directory, 'baked', 'flame'),
1282 os.path.join(config_directory, 'baked', 'photoshop'),
1283 os.path.join(config_directory, 'baked', 'houdini'),
1284 os.path.join(config_directory, 'baked', 'lustre'),
1285 os.path.join(config_directory, 'baked', 'maya')])
1288 dirs.append(os.path.join(config_directory, 'custom'))
1291 not os.path.exists(d) and os.mkdir(d)
1293 return lut_directory
1296 def generate_config(aces_ctl_directory,
1298 lut_resolution_1d=4096,
1299 lut_resolution_3d=64,
1300 bake_secondary_luts=True,
1301 multiple_displays=False,
1303 copy_custom_luts=True,
1305 prefix_colorspaces_with_family_names=True):
1307 Creates the ACES configuration.
1312 Parameter description.
1317 Return value description.
1320 if look_info is None:
1323 custom_lut_dir = None
1324 if copy_custom_luts:
1325 custom_lut_dir = os.path.join(config_directory, 'custom')
1327 lut_directory = generate_config_directory(config_directory,
1328 bake_secondary_luts,
1331 odt_info = aces.get_ODTs_info(aces_ctl_directory)
1332 lmt_info = aces.get_LMTs_info(aces_ctl_directory)
1334 shaper_name = 'Output Shaper'
1335 config_data = create_config_data(odt_info,
1344 print('Creating config - with prefixes, with aliases')
1345 config = create_config(config_data,
1346 prefix=prefix_colorspaces_with_family_names,
1348 multiple_displays=multiple_displays,
1349 look_info=look_info,
1350 custom_lut_dir=custom_lut_dir)
1353 write_config(config,
1354 os.path.join(config_directory, 'config.ocio'))
1356 if bake_secondary_luts:
1357 generate_baked_LUTs(odt_info,
1359 os.path.join(config_directory, 'baked'),
1360 os.path.join(config_directory, 'config.ocio'),
1363 prefix=prefix_colorspaces_with_family_names)
1375 Parameter description.
1380 Return value description.
1385 usage = '%prog [options]\n'
1387 usage += 'An OCIO config generation script for ACES 1.0\n'
1389 usage += 'Command line examples'
1391 usage += ('Create a GUI-friendly ACES 1.0 config with no secondary, '
1393 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1394 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1395 '--dontBakeSecondaryLUTs')
1397 usage += 'Create a more OCIO-compliant ACES 1.0 config : \n'
1398 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1399 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1400 '--createMultipleDisplays')
1403 usage += 'Adding custom looks'
1405 usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style CDL '
1406 '(will be applied in the ACEScc colorspace): \n')
1407 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1408 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1409 '\n\t\t--addACESLookCDL ACESCDLName '
1410 '/path/to/SampleCDL.ccc cc03345')
1412 usage += 'Create a GUI-friendly ACES 1.0 config with an general CDL: \n'
1413 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1414 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1415 '\n\t\t--addCustomLookCDL CustomCDLName "ACES - ACEScc" '
1416 '/path/to/SampleCDL.ccc cc03345')
1418 usage += ('\tIn this example, the CDL will be applied in the '
1419 'ACEScc colorspace, but the user could choose other spaces '
1420 'by changing the argument after the name of the look. \n')
1422 usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style LUT '
1423 '(will be applied in the ACEScc colorspace): \n')
1424 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1425 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1426 '\n\t\t--addACESLookLUT ACESLUTName '
1427 '/path/to/SampleCDL.ccc cc03345')
1429 usage += 'Create a GUI-friendly ACES 1.0 config with an general LUT: \n'
1430 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1431 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1432 '\n\t\t--addCustomLookLUT CustomLUTName "ACES - ACEScc" '
1433 '/path/to/SampleCDL.ccc cc03345')
1435 usage += ('\tIn this example, the LUT will be applied in the '
1436 'ACEScc colorspace, but the user could choose other spaces '
1437 'by changing the argument after the name of the look. \n')
1442 def look_info_callback(option, opt_str, value, parser):
1443 print('look_info_callback')
1444 print(option, opt_str, value, parser)
1445 if opt_str == '--addCustomLookCDL':
1446 look_info.append(value)
1447 elif opt_str == '--addCustomLookLUT':
1448 look_info.append(value)
1449 elif opt_str == '--addACESLookCDL':
1450 look_info.append([value[0], 'ACES - ACEScc', value[1], value[2]])
1451 elif opt_str == '--addACESLookLUT':
1452 look_info.append([value[0], 'ACES - ACEScc', value[1]])
1454 p = optparse.OptionParser(description='',
1455 prog='create_aces_config',
1456 version='create_aces_config 1.0',
1458 p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1459 ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1460 p.add_option('--configDir', '-c', default=os.environ.get(
1461 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1462 p.add_option('--lutResolution1d', default=4096)
1463 p.add_option('--lutResolution3d', default=64)
1464 p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
1465 p.add_option('--keepTempImages', action='store_true', default=False)
1467 p.add_option('--createMultipleDisplays', action='store_true',
1470 p.add_option('--addCustomLookLUT', '', type='string', nargs=3,
1471 action='callback', callback=look_info_callback)
1472 p.add_option('--addCustomLookCDL', '', type='string', nargs=4,
1473 action='callback', callback=look_info_callback)
1474 p.add_option('--addACESLookLUT', '', type='string', nargs=2,
1475 action='callback', callback=look_info_callback)
1476 p.add_option('--addACESLookCDL', '', type='string', nargs=3,
1477 action='callback', callback=look_info_callback)
1478 p.add_option('--copyCustomLUTs', action='store_true', default=False)
1480 options, arguments = p.parse_args()
1482 aces_ctl_directory = options.acesCTLDir
1483 config_directory = options.configDir
1484 lut_resolution_1d = int(options.lutResolution1d)
1485 lut_resolution_3d = int(options.lutResolution3d)
1486 bake_secondary_luts = not options.dontBakeSecondaryLUTs
1487 cleanup_temp_images = not options.keepTempImages
1488 multiple_displays = options.createMultipleDisplays
1489 copy_custom_luts = options.copyCustomLUTs
1493 print('command line : \n%s\n' % ' '.join(sys.argv))
1495 assert aces_ctl_directory is not None, (
1496 'process: No "{0}" environment variable defined or no "ACES CTL" '
1497 'directory specified'.format(
1498 ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1500 assert config_directory is not None, (
1501 'process: No "{0}" environment variable defined or no configuration '
1502 'directory specified'.format(
1503 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1505 return generate_config(aces_ctl_directory,
1509 bake_secondary_luts,
1513 cleanup_temp_images)
1516 if __name__ == '__main__':