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',
42 'set_config_default_roles',
44 'generate_OCIO_transform',
45 'add_colorspace_aliases',
47 'integrate_looks_into_views',
50 'generate_baked_LUTs',
55 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
56 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
59 def set_config_default_roles(config,
70 compositing_linear=''):
72 Sets given *OCIO* configuration default roles.
78 color_picking : str or unicode
79 Color picking role title.
80 color_timing : str or unicode
81 Color timing role title.
82 compositing_log : str or unicode
83 Compositing log role title.
86 default : str or unicode
88 matte_paint : str or unicode
89 Matte painting role title.
90 reference : str or unicode
92 scene_linear : str or unicode
93 Scene linear role title.
94 texture_paint : str or unicode
95 Texture painting role title.
104 config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
106 config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
108 config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
110 config.setRole(ocio.Constants.ROLE_DATA, data)
112 config.setRole(ocio.Constants.ROLE_DEFAULT, default)
114 config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
116 config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
118 config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
120 # *rendering* and *compositing_linear* roles default to the *scene_linear*
121 # value if not set explicitly.
123 config.setRole('rendering', rendering)
124 if compositing_linear:
125 config.setRole('compositing_linear', compositing_linear)
127 config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
129 config.setRole('rendering', scene_linear)
130 if not compositing_linear:
131 config.setRole('compositing_linear', scene_linear)
136 def write_config(config, config_path, sanity_check=True):
138 Writes the configuration to given path.
143 Parameter description.
148 Return value description.
156 print 'Configuration was not written due to a failed Sanity Check'
159 with open(config_path, mode='w') as fp:
160 fp.write(config.serialize())
163 def generate_OCIO_transform(transforms):
170 Parameter description.
175 Return value description.
178 direction_options = {
179 'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
180 'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
184 for transform in transforms:
186 # *lutFile* transform
187 if transform['type'] == 'lutFile':
188 ocio_transform = ocio.FileTransform()
190 if 'path' in transform:
191 ocio_transform.setSrc(transform['path'])
193 if 'cccid' in transform:
194 ocio_transform.setCCCId(transform['cccid'])
196 if 'interpolation' in transform:
197 ocio_transform.setInterpolation(transform['interpolation'])
199 ocio_transform.setInterpolation(ocio.Constants.INTERP_BEST)
201 if 'direction' in transform:
202 ocio_transform.setDirection(
203 direction_options[transform['direction']])
205 ocio_transforms.append(ocio_transform)
208 elif transform['type'] == 'matrix':
209 ocio_transform = ocio.MatrixTransform()
210 # `MatrixTransform` member variables can't be initialized directly,
211 # each must be set individually.
212 ocio_transform.setMatrix(transform['matrix'])
214 if 'offset' in transform:
215 ocio_transform.setOffset(transform['offset'])
217 if 'direction' in transform:
218 ocio_transform.setDirection(
219 direction_options[transform['direction']])
221 ocio_transforms.append(ocio_transform)
223 # *exponent* transform
224 elif transform['type'] == 'exponent':
225 ocio_transform = ocio.ExponentTransform()
227 if 'value' in transform:
228 ocio_transform.setValue(transform['value'])
230 ocio_transforms.append(ocio_transform)
233 elif transform['type'] == 'log':
234 ocio_transform = ocio.LogTransform()
236 if 'base' in transform:
237 ocio_transform.setBase(transform['base'])
239 if 'direction' in transform:
240 ocio_transform.setDirection(
241 direction_options[transform['direction']])
243 ocio_transforms.append(ocio_transform)
245 # *colorspace* transform
246 elif transform['type'] == 'colorspace':
247 ocio_transform = ocio.ColorSpaceTransform()
249 if 'src' in transform:
250 ocio_transform.setSrc(transform['src'])
252 if 'dst' in transform:
253 ocio_transform.setDst(transform['dst'])
255 if 'direction' in transform:
256 ocio_transform.setDirection(
257 direction_options[transform['direction']])
259 ocio_transforms.append(ocio_transform)
262 elif transform['type'] == 'look':
263 ocio_transform = ocio.LookTransform()
264 if 'look' in transform:
265 ocio_transform.setLooks(transform['look'])
267 if 'src' in transform:
268 ocio_transform.setSrc(transform['src'])
270 if 'dst' in transform:
271 ocio_transform.setDst(transform['dst'])
273 if 'direction' in transform:
274 ocio_transform.setDirection(
275 direction_options[transform['direction']])
277 ocio_transforms.append(ocio_transform)
281 print('Ignoring unknown transform type : %s' % transform['type'])
283 if len(ocio_transforms) > 1:
284 group_transform = ocio.GroupTransform()
285 for transform in ocio_transforms:
286 group_transform.push_back(transform)
287 transform = group_transform
289 transform = ocio_transforms[0]
294 def add_colorspace_aliases(config,
295 reference_colorspace,
297 colorspace_alias_names,
305 Parameter description.
310 Return value description.
313 for alias_name in colorspace_alias_names:
314 if alias_name.lower() == colorspace.name.lower():
315 print('Skipping alias creation for %s, alias %s, '
316 'because lower cased names match' % (
317 colorspace.name, alias_name))
320 print('Adding alias colorspace space %s, alias to %s' % (
321 alias_name, colorspace.name))
323 compact_family_name = family
325 description = colorspace.description
326 if colorspace.aces_transform_id:
328 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
330 ocio_colorspace_alias = ocio.ColorSpace(
332 bitDepth=colorspace.bit_depth,
333 description=description,
334 equalityGroup=colorspace.equality_group,
335 family=compact_family_name,
336 isData=colorspace.is_data,
337 allocation=colorspace.allocation_type,
338 allocationVars=colorspace.allocation_vars)
340 if colorspace.to_reference_transforms:
341 print('\tGenerating To-Reference transforms')
342 ocio_transform = generate_OCIO_transform(
343 [{'type': 'colorspace',
344 'src': colorspace.name,
345 'dst': reference_colorspace.name,
346 'direction': 'forward'}])
347 ocio_colorspace_alias.setTransform(
349 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
351 if colorspace.from_reference_transforms:
352 print('\tGenerating From-Reference transforms')
353 ocio_transform = generate_OCIO_transform(
354 [{'type': 'colorspace',
355 'src': reference_colorspace.name,
356 'dst': colorspace.name,
357 'direction': 'forward'}])
358 ocio_colorspace_alias.setTransform(
360 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
362 config.addColorSpace(ocio_colorspace_alias)
376 Parameter description.
381 Return value description.
384 look_name, look_colorspace, look_lut, look_cccid = unpack_default(look, 4)
386 print('Adding look %s - %s' % (look_name, ', '.join(look)))
388 # Copy *look LUT* if `custom_lut_dir` is provided.
390 if '$' not in look_lut:
391 print('Getting ready to copy look lut : %s' % look_lut)
392 shutil.copy2(look_lut, custom_lut_dir)
393 look_lut = os.path.split(look_lut)[1]
395 print('Skipping LUT copy because path contains a context variable')
397 print('Adding look to config')
398 ocio_look = ocio.Look()
399 ocio_look.setName(look_name)
400 ocio_look.setProcessSpace(look_colorspace)
402 keys = {'type': 'lutFile',
404 'direction': 'forward'}
406 keys['cccid'] = look_cccid
408 ocio_transform = generate_OCIO_transform([keys])
409 ocio_look.setTransform(ocio_transform)
411 config.addLook(ocio_look)
413 print('Creating aliased colorspace')
415 # Creating *OCIO* colorspace referencing the look:
416 # - Needed for implementations that don't process looks properly.
417 # - Needed for implementations that don't expose looks properly.
418 look_aliases = ['look_%s' % compact(look_name)]
419 colorspace = ColorSpace(look_name,
420 aliases=look_aliases,
421 description='The %s Look colorspace' % look_name,
424 colorspace.from_reference_transforms = [{'type': 'look',
426 'src': reference_name,
427 'dst': reference_name,
428 'direction': 'forward'}]
430 print('Adding colorspace %s, alias to look %s to config data' % (
431 look_name, look_name))
433 config_data['colorSpaces'].append(colorspace)
438 def integrate_looks_into_views(looks,
441 multiple_displays=False):
448 Parameter description.
453 Return value description.
455 look_names = [look[0] for look in looks]
458 # - Adding a *look* per *Display*.
459 # - Assuming there is a *Display* for each *ACES* *Output Transform*.
460 if multiple_displays:
461 for look_name in look_names:
462 config_data['looks'].append(look_name)
465 # - Copy each *Output Transform* colorspace.
466 # - For each copy, add a *LookTransform* to the head of the
467 # `from_reference` transform list.
468 # - Add these the copy colorspaces for the *Displays* / *Views*.
470 for display, view_list in config_data['displays'].iteritems():
472 look_names_string = ''
473 for view_name, output_colorspace in view_list.iteritems():
474 if view_name == 'Output Transform':
476 print('Adding new View that incorporates looks')
478 colorspace_c = copy.deepcopy(output_colorspace)
480 for i, look_name in enumerate(look_names):
481 look_name = look_names[i]
483 # Add the `LookTransform` to the head of the
484 # `from_reference` transform list.
485 if colorspace_c.from_reference_transforms:
486 colorspace_c.from_reference_transforms.insert(
490 'src': reference_name,
491 'dst': reference_name,
492 'direction': 'forward'})
494 # Add the `LookTransform` to the end of
495 # the `to_reference` transform list.
496 if colorspace_c.to_reference_transforms:
497 inverse_look_name = look_names[
498 len(look_names) - 1 - i]
500 colorspace_c.to_reference_transforms.append(
502 'look': inverse_look_name,
503 'src': reference_name,
504 'dst': reference_name,
505 'direction': 'inverse'})
507 if look_name not in config_data['looks']:
508 config_data['looks'].append(look_name)
510 look_names_string = ', '.join(look_names)
511 colorspace_c.name = '%s with %s' % (
512 output_colorspace.name, look_names_string)
513 colorspace_c.aliases = [
514 'out_%s' % compact(colorspace_c.name)]
516 print('Colorspace that incorporates looks '
517 'created : %s' % colorspace_c.name)
519 config_data['colorSpaces'].append(colorspace_c)
522 print('Adding colorspace that incorporates looks '
525 # Updating the *View* name.
526 view_list['Output Transform with %s' % look_names_string] = (
528 config_data['displays'][display] = view_list
531 def create_config(config_data,
534 multiple_displays=False,
536 custom_lut_dir=None):
543 Parameter description.
548 Return value description.
551 if look_info is None:
555 alias_colorspaces = []
557 config = ocio.Config()
559 config.setDescription('An ACES config generated from python')
561 search_path = ['luts']
563 search_path.append('custom')
564 config.setSearchPath(':'.join(search_path))
566 reference_data = config_data['referenceColorSpace']
568 # Adding the colorspace *Family* into the name which helps with
569 # applications that presenting colorspaces as one a flat list.
571 prefixed_name = colorspace_prefixed_name(reference_data)
572 prefixed_names[reference_data.name] = prefixed_name
573 reference_data.name = prefixed_name
575 print('Adding the reference color space : %s' % reference_data.name)
577 reference = ocio.ColorSpace(
578 name=reference_data.name,
579 bitDepth=reference_data.bit_depth,
580 description=reference_data.description,
581 equalityGroup=reference_data.equality_group,
582 family=reference_data.family,
583 isData=reference_data.is_data,
584 allocation=reference_data.allocation_type,
585 allocationVars=reference_data.allocation_vars)
587 config.addColorSpace(reference)
590 if reference_data.aliases:
591 # TODO: Explain context for following comment.
592 # Deferring adding alias colorspaces until end, which helps with
594 alias_colorspaces.append(
595 [reference_data, reference_data, reference_data.aliases])
600 print('Adding looks')
602 config_data['looks'] = []
604 for look in look_info:
611 integrate_looks_into_views(look_info,
618 print('Adding regular colorspaces')
620 for colorspace in sorted(config_data['colorSpaces']):
621 # Adding the colorspace *Family* into the name which helps with
622 # applications that presenting colorspaces as one a flat list.
624 prefixed_name = colorspace_prefixed_name(colorspace)
625 prefixed_names[colorspace.name] = prefixed_name
626 colorspace.name = prefixed_name
628 print('Creating new color space : %s' % colorspace.name)
630 description = colorspace.description
631 if colorspace.aces_transform_id:
633 '\n\nACES Transform ID : %s' % colorspace.aces_transform_id)
635 ocio_colorspace = ocio.ColorSpace(
636 name=colorspace.name,
637 bitDepth=colorspace.bit_depth,
638 description=description,
639 equalityGroup=colorspace.equality_group,
640 family=colorspace.family,
641 isData=colorspace.is_data,
642 allocation=colorspace.allocation_type,
643 allocationVars=colorspace.allocation_vars)
645 if colorspace.to_reference_transforms:
646 print('\tGenerating To-Reference transforms')
647 ocio_transform = generate_OCIO_transform(
648 colorspace.to_reference_transforms)
649 ocio_colorspace.setTransform(
651 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
653 if colorspace.from_reference_transforms:
654 print('\tGenerating From-Reference transforms')
655 ocio_transform = generate_OCIO_transform(
656 colorspace.from_reference_transforms)
657 ocio_colorspace.setTransform(
659 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
661 config.addColorSpace(ocio_colorspace)
664 if colorspace.aliases:
665 # TODO: Explain context for following comment.
666 # Deferring adding alias colorspaces until end, which helps
667 # with some applications.
668 alias_colorspaces.append(
669 [reference_data, colorspace, colorspace.aliases])
675 # Adding roles early so that alias colorspaces can be created
676 # with roles names before remaining colorspace aliases are added
677 # to the configuration.
678 print('Setting the roles')
681 set_config_default_roles(
683 color_picking=prefixed_names[
684 config_data['roles']['color_picking']],
685 color_timing=prefixed_names[config_data['roles']['color_timing']],
686 compositing_log=prefixed_names[
687 config_data['roles']['compositing_log']],
688 data=prefixed_names[config_data['roles']['data']],
689 default=prefixed_names[config_data['roles']['default']],
690 matte_paint=prefixed_names[config_data['roles']['matte_paint']],
691 reference=prefixed_names[config_data['roles']['reference']],
692 scene_linear=prefixed_names[config_data['roles']['scene_linear']],
693 texture_paint=prefixed_names[
694 config_data['roles']['texture_paint']])
696 # TODO: Should we remove this dead code path?
697 # Not allowed at the moment as role names can not overlap
698 # with colorspace names.
700 # Add the aliased colorspaces for each role
701 for role_name, role_colorspace_name in config_data['roles'].iteritems():
702 role_colorspace_prefixed_name = prefixed_names[role_colorspace_name]
704 print( 'Finding colorspace : %s' % role_colorspace_prefixed_name )
705 # Find the colorspace pointed to by the role
706 role_colorspaces = [colorspace
707 for colorspace in config_data['colorSpaces']
708 if colorspace.name == role_colorspace_prefixed_name]
709 role_colorspace = None
710 if len(role_colorspaces) > 0:
711 role_colorspace = role_colorspaces[0]
713 if reference_data.name == role_colorspace_prefixed_name:
714 role_colorspace = reference_data
717 print( 'Adding an alias colorspace named %s, pointing to %s' % (
718 role_name, role_colorspace.name))
720 add_colorspace_aliases(
721 config, reference_data, role_colorspace, [role_name], 'Roles')
725 set_config_default_roles(
727 color_picking=config_data['roles']['color_picking'],
728 color_timing=config_data['roles']['color_timing'],
729 compositing_log=config_data['roles']['compositing_log'],
730 data=config_data['roles']['data'],
731 default=config_data['roles']['default'],
732 matte_paint=config_data['roles']['matte_paint'],
733 reference=config_data['roles']['reference'],
734 scene_linear=config_data['roles']['scene_linear'],
735 texture_paint=config_data['roles']['texture_paint'])
737 # TODO: Should we remove this dead code path?
738 # Not allowed at the moment as role names can not overlap
739 # with colorspace names.
741 # Add the aliased colorspaces for each role
742 for role_name, role_colorspace_name in config_data['roles'].iteritems():
743 # Find the colorspace pointed to by the role
744 role_colorspaces = [colorspace
745 for colorspace in config_data['colorSpaces']
746 if colorspace.name == role_colorspace_name]
747 role_colorspace = None
748 if len(role_colorspaces) > 0:
749 role_colorspace = role_colorspaces[0]
751 if reference_data.name == role_colorspace_name:
752 role_colorspace = reference_data
755 print('Adding an alias colorspace named %s, pointing to %s' % (
756 role_name, role_colorspace.name))
758 add_colorspace_aliases(
759 config, reference_data, role_colorspace, [role_name], 'Roles')
764 # Adding alias colorspaces at the end as some applications use
765 # colorspaces definitions order of the configuration to order
766 # the colorspaces in their selection lists, some applications
767 # use alphabetical ordering.
768 # This should keep the alias colorspaces out of the way for applications
769 # using the configuration order.
770 print('Adding the alias colorspaces')
771 for reference, colorspace, aliases in alias_colorspaces:
772 add_colorspace_aliases(config, reference, colorspace, aliases)
776 print('Adding the diplays and views')
778 # Setting the *color_picking* role to be the first *Display*'s
779 # *Output Transform* *View*.
780 default_display_name = config_data['defaultDisplay']
781 default_display_views = config_data['displays'][default_display_name]
782 default_display_colorspace = default_display_views['Output Transform']
784 set_config_default_roles(
786 color_picking=default_display_colorspace.name)
788 # Defining *Displays* and *Views*.
789 displays, views = [], []
791 # Defining a generic *Display* and *View* setup.
792 if multiple_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 # *Displays* are not reordered to put the *defaultDisplay* first
798 # because *OCIO* will order them alphabetically when the configuration
799 # is written to disk.
800 for display, view_list in config_data['displays'].iteritems():
801 for view_name, colorspace in view_list.iteritems():
802 config.addDisplay(display, view_name, colorspace.name, looks)
803 if 'Output Transform' in view_name and looks != '':
804 # *Views* without *Looks*.
805 config.addDisplay(display, view_name, colorspace.name)
807 # *Views* with *Looks*.
808 view_name_with_looks = '%s with %s' % (view_name, looks)
809 config.addDisplay(display, view_name_with_looks,
810 colorspace.name, looks)
812 config.addDisplay(display, view_name, colorspace.name)
813 if not (view_name in views):
814 views.append(view_name)
815 displays.append(display)
817 # *Displays* and *Views* useful in a *GUI* context.
819 single_display_name = 'ACES'
820 displays.append(single_display_name)
822 # Ensuring the *defaultDisplay* is first.
823 display_names = sorted(config_data['displays'])
824 display_names.insert(0, display_names.pop(
825 display_names.index(default_display_name)))
827 looks = config_data['looks'] if ('looks' in config_data) else []
828 look_names = ', '.join(looks)
830 displays_views_colorspaces = []
832 for display in display_names:
833 view_list = config_data['displays'][display]
834 for view_name, colorspace in view_list.iteritems():
835 if 'Output Transform' in view_name:
837 # We use the *Display* names as the *View* names in this
838 # case as there is a single *Display* containing all the
840 # This works for more applications than not,as of the time
841 # of this implementation.
843 # Autodesk Maya 2016 doesn't support parentheses in
845 sanitised_display = replace(display, {')': '', '(': ''})
847 # *View* with *Looks*.
848 if 'with' in view_name:
849 sanitised_display = '%s with %s' % (
850 sanitised_display, look_names)
852 views_with_looks_at_end = False
853 # Storing combo of *Display*, *View* and *Colorspace*
854 # name so they can be added to the end of the list.
855 if views_with_looks_at_end:
856 displays_views_colorspaces.append(
857 [single_display_name, sanitised_display,
860 config.addDisplay(single_display_name,
864 if not (sanitised_display in views):
865 views.append(sanitised_display)
867 # *View* without *Looks*.
869 config.addDisplay(single_display_name,
873 if not (sanitised_display in views):
874 views.append(sanitised_display)
876 # Adding to the configuration any *Display*, *View* combinations that
877 # were saved for later.
878 # This list should be empty unless `views_with_looks_at_end` is
880 for display_view_colorspace in displays_views_colorspaces:
881 single_display_name, sanitised_display, colorspace_name = (
882 display_view_colorspace)
884 config.addDisplay(single_display_name,
888 if not (sanitised_display in views):
889 views.append(sanitised_display)
891 raw_display_space_name = config_data['roles']['data']
892 log_display_space_name = config_data['roles']['compositing_log']
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 config.setActiveDisplays(','.join(sorted(displays)))
904 config.setActiveViews(','.join(views))
908 # Ensuring the configuration is valid.
911 # Resetting colorspace names to their non-prefixed versions.
913 prefixed_names_inverse = {}
914 for original, prefixed in prefixed_names.iteritems():
915 prefixed_names_inverse[prefixed] = original
917 reference_data.name = prefixed_names_inverse[reference_data.name]
920 for colorspace in config_data['colorSpaces']:
921 colorspace.name = prefixed_names_inverse[colorspace.name]
923 print('Prefixed names')
924 for original, prefixed in prefixed_names.iteritems():
925 print('%s, %s' % (original, prefixed))
929 print('Inverse Lookup of Prefixed names')
930 for prefixed, original in prefixed_names_inverse.iteritems():
931 print('%s, %s' % (prefixed, original))
937 def generate_LUTs(odt_info,
942 lut_resolution_1d=4096,
943 lut_resolution_3d=64,
951 Parameter description.
956 Colorspaces and transforms converting between those colorspaces and
957 the reference colorspace, *ACES*.
960 print('generateLUTs - begin')
963 config_data['displays'] = {}
964 config_data['colorSpaces'] = []
966 # -------------------------------------------------------------------------
967 # *ACES Color Spaces*
968 # -------------------------------------------------------------------------
974 aces_log_display_space,
976 aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
985 config_data['referenceColorSpace'] = aces_reference
986 config_data['roles'] = aces_roles
988 for cs in aces_colorspaces:
989 config_data['colorSpaces'].append(cs)
991 for name, data in aces_displays.iteritems():
992 config_data['displays'][name] = data
994 config_data['defaultDisplay'] = aces_default_display
995 config_data['linearDisplaySpace'] = aces_reference
996 config_data['logDisplaySpace'] = aces_log_display_space
998 # -------------------------------------------------------------------------
999 # *Camera Input Transforms*
1000 # -------------------------------------------------------------------------
1002 # *ARRI Log-C* to *ACES*
1003 arri_colorspaces = arri.create_colorspaces(lut_directory,
1005 for cs in arri_colorspaces:
1006 config_data['colorSpaces'].append(cs)
1008 # *Canon-Log* to *ACES*
1009 canon_colorspaces = canon.create_colorspaces(lut_directory,
1011 for cs in canon_colorspaces:
1012 config_data['colorSpaces'].append(cs)
1014 # *GoPro Protune* to *ACES*
1015 gopro_colorspaces = gopro.create_colorspaces(lut_directory,
1017 for cs in gopro_colorspaces:
1018 config_data['colorSpaces'].append(cs)
1020 # *Panasonic V-Log* to *ACES*
1021 panasonic_colorspaces = panasonic.create_colorspaces(lut_directory,
1023 for cs in panasonic_colorspaces:
1024 config_data['colorSpaces'].append(cs)
1026 # *RED* colorspaces to *ACES*
1027 red_colorspaces = red.create_colorspaces(lut_directory,
1029 for cs in red_colorspaces:
1030 config_data['colorSpaces'].append(cs)
1033 sony_colorspaces = sony.create_colorspaces(lut_directory,
1035 for cs in sony_colorspaces:
1036 config_data['colorSpaces'].append(cs)
1038 # -------------------------------------------------------------------------
1039 # General Colorspaces
1040 # -------------------------------------------------------------------------
1041 general_colorspaces = general.create_colorspaces(lut_directory,
1043 for cs in general_colorspaces:
1044 config_data['colorSpaces'].append(cs)
1046 # The *Raw* colorspace
1047 raw = general.create_raw()
1048 config_data['colorSpaces'].append(raw)
1050 # Overriding various roles
1051 config_data['roles']['data'] = raw.name
1052 config_data['roles']['reference'] = raw.name
1053 config_data['roles']['texture_paint'] = raw.name
1055 print('generateLUTs - end')
1060 def generate_baked_LUTs(odt_info,
1065 lut_resolution_shaper=1024,
1073 Parameter description.
1078 Return value description.
1081 odt_info_C = dict(odt_info)
1083 # Older behavior for *ODTs* that have support for full and legal ranges,
1084 # generating a LUT for both ranges.
1086 # Create two entries for ODTs that have full and legal range support
1087 for odt_ctl_name, odt_values in odt_info.iteritems():
1088 if odt_values['transformHasFullLegalSwitch']:
1089 odt_name = odt_values['transformUserName']
1091 odt_values_legal = dict(odt_values)
1092 odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
1093 odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
1095 odt_values_full = dict(odt_values)
1096 odt_values_full['transformUserName'] = '%s - Full' % odt_name
1097 odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
1099 del (odt_info_C[odt_ctl_name])
1102 for odt_ctl_name, odt_values in odt_info_C.iteritems():
1103 odt_prefix = odt_values['transformUserNamePrefix']
1104 odt_name = odt_values['transformUserName']
1107 for input_space in ['ACEScc', 'ACESproxy']:
1108 args = ['--iconfig', config_path,
1111 args += ['--inputspace', 'ACES - %s' % input_space]
1112 args += ['--outputspace', 'Output - %s' % odt_name]
1114 args += ['--inputspace', input_space]
1115 args += ['--outputspace', odt_name]
1117 args += ['--description',
1118 '%s - %s for %s data' % (odt_prefix,
1122 args += ['--shaperspace', 'Utility - %s' % shaper_name,
1123 '--shapersize', str(lut_resolution_shaper)]
1125 args += ['--shaperspace', shaper_name,
1126 '--shapersize', str(lut_resolution_shaper)]
1127 args += ['--cubesize', str(lut_resolution_3d)]
1128 args += ['--format',
1130 os.path.join(baked_directory,
1132 '%s for %s.icc' % (odt_name, input_space))]
1134 bake_lut = Process(description='bake a LUT',
1140 for input_space in ['ACEScc', 'ACESproxy']:
1141 args = ['--iconfig', config_path,
1144 args += ['--inputspace', 'ACES - %s' % input_space]
1145 args += ['--outputspace', 'Output - %s' % odt_name]
1147 args += ['--inputspace', input_space]
1148 args += ['--outputspace', odt_name]
1149 args += ['--description',
1150 '%s - %s for %s data' % (
1151 odt_prefix, odt_name, input_space)]
1153 args += ['--shaperspace', 'Utility - %s' % shaper_name,
1154 '--shapersize', str(lut_resolution_shaper)]
1156 args += ['--shaperspace', shaper_name,
1157 '--shapersize', str(lut_resolution_shaper)]
1158 args += ['--cubesize', str(lut_resolution_3d)]
1160 fargs = ['--format',
1165 '%s for %s Flame.3dl' % (odt_name, input_space))]
1166 bake_lut = Process(description='bake a LUT',
1168 args=(args + fargs))
1171 largs = ['--format',
1176 '%s for %s Lustre.3dl' % (odt_name, input_space))]
1177 bake_lut = Process(description='bake a LUT',
1179 args=(args + largs))
1183 for input_space in ['ACEScg', 'ACES2065-1']:
1184 args = ['--iconfig', config_path,
1187 args += ['--inputspace', 'ACES - %s' % input_space]
1188 args += ['--outputspace', 'Output - %s' % odt_name]
1190 args += ['--inputspace', input_space]
1191 args += ['--outputspace', odt_name]
1192 args += ['--description',
1193 '%s - %s for %s data' % (
1194 odt_prefix, odt_name, input_space)]
1195 if input_space == 'ACEScg':
1196 lin_shaper_name = '%s - AP1' % shaper_name
1198 lin_shaper_name = shaper_name
1200 lin_shaper_name = 'Utility - %s' % lin_shaper_name
1201 args += ['--shaperspace', lin_shaper_name,
1202 '--shapersize', str(lut_resolution_shaper)]
1204 args += ['--cubesize', str(lut_resolution_3d)]
1206 margs = ['--format',
1211 '%s for %s Maya.csp' % (odt_name, input_space))]
1212 bake_lut = Process(description='bake a LUT',
1214 args=(args + margs))
1217 hargs = ['--format',
1222 '%s for %s Houdini.lut' % (odt_name, input_space))]
1223 bake_lut = Process(description='bake a LUT',
1225 args=(args + hargs))
1229 def create_config_dir(config_directory,
1230 bake_secondary_luts=False,
1231 custom_lut_dir=None):
1238 Parameter description.
1243 Return value description.
1246 lut_directory = os.path.join(config_directory, 'luts')
1247 dirs = [config_directory, lut_directory]
1249 if bake_secondary_luts:
1250 dirs.extend([os.path.join(config_directory, 'baked'),
1251 os.path.join(config_directory, 'baked', 'flame'),
1252 os.path.join(config_directory, 'baked', 'photoshop'),
1253 os.path.join(config_directory, 'baked', 'houdini'),
1254 os.path.join(config_directory, 'baked', 'lustre'),
1255 os.path.join(config_directory, 'baked', 'maya')])
1258 dirs.append(os.path.join(config_directory, 'custom'))
1261 not os.path.exists(d) and os.mkdir(d)
1263 return lut_directory
1266 def create_ACES_config(aces_ctl_directory,
1268 lut_resolution_1d=4096,
1269 lut_resolution_3d=64,
1270 bake_secondary_luts=True,
1271 multiple_displays=False,
1273 copy_custom_luts=True,
1275 prefix_colorspaces_with_family_names=True):
1277 Creates the ACES configuration.
1282 Parameter description.
1287 Return value description.
1290 if look_info is None:
1293 custom_lut_dir = None
1294 if copy_custom_luts:
1295 custom_lut_dir = os.path.join(config_directory, 'custom')
1297 lut_directory = create_config_dir(config_directory,
1298 bake_secondary_luts,
1301 odt_info = aces.get_ODTs_info(aces_ctl_directory)
1302 lmt_info = aces.get_LMTs_info(aces_ctl_directory)
1304 shaper_name = 'Output Shaper'
1305 config_data = generate_LUTs(odt_info,
1314 print('Creating config - with prefixes, with aliases')
1315 config = create_config(config_data,
1316 prefix=prefix_colorspaces_with_family_names,
1318 multiple_displays=multiple_displays,
1319 look_info=look_info,
1320 custom_lut_dir=custom_lut_dir)
1323 write_config(config,
1324 os.path.join(config_directory, 'config.ocio'))
1326 if bake_secondary_luts:
1327 generate_baked_LUTs(odt_info,
1329 os.path.join(config_directory, 'baked'),
1330 os.path.join(config_directory, 'config.ocio'),
1333 prefix=prefix_colorspaces_with_family_names)
1345 Parameter description.
1350 Return value description.
1355 usage = '%prog [options]\n'
1357 usage += 'An OCIO config generation script for ACES 1.0\n'
1359 usage += 'Command line examples'
1361 usage += ('Create a GUI-friendly ACES 1.0 config with no secondary, '
1363 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1364 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1365 '--dontBakeSecondaryLUTs')
1367 usage += 'Create a more OCIO-compliant ACES 1.0 config : \n'
1368 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1369 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1370 '--createMultipleDisplays')
1373 usage += 'Adding custom looks'
1375 usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style CDL '
1376 '(will be applied in the ACEScc colorspace): \n')
1377 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1378 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1379 '\n\t\t--addACESLookCDL ACESCDLName '
1380 '/path/to/SampleCDL.ccc cc03345')
1382 usage += 'Create a GUI-friendly ACES 1.0 config with an general CDL: \n'
1383 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1384 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1385 '\n\t\t--addCustomLookCDL CustomCDLName "ACES - ACEScc" '
1386 '/path/to/SampleCDL.ccc cc03345')
1388 usage += ('\tIn this example, the CDL will be applied in the '
1389 'ACEScc colorspace, but the user could choose other spaces '
1390 'by changing the argument after the name of the look. \n')
1392 usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style LUT '
1393 '(will be applied in the ACEScc colorspace): \n')
1394 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1395 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1396 '\n\t\t--addACESLookLUT ACESLUTName '
1397 '/path/to/SampleCDL.ccc cc03345')
1399 usage += 'Create a GUI-friendly ACES 1.0 config with an general LUT: \n'
1400 usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl '
1401 '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 '
1402 '\n\t\t--addCustomLookLUT CustomLUTName "ACES - ACEScc" '
1403 '/path/to/SampleCDL.ccc cc03345')
1405 usage += ('\tIn this example, the LUT will be applied in the '
1406 'ACEScc colorspace, but the user could choose other spaces '
1407 'by changing the argument after the name of the look. \n')
1412 def look_info_callback(option, opt_str, value, parser):
1413 print('look_info_callback')
1414 print(option, opt_str, value, parser)
1415 if opt_str == '--addCustomLookCDL':
1416 look_info.append(value)
1417 elif opt_str == '--addCustomLookLUT':
1418 look_info.append(value)
1419 elif opt_str == '--addACESLookCDL':
1420 look_info.append([value[0], 'ACES - ACEScc', value[1], value[2]])
1421 elif opt_str == '--addACESLookLUT':
1422 look_info.append([value[0], 'ACES - ACEScc', value[1]])
1424 p = optparse.OptionParser(description='',
1425 prog='create_aces_config',
1426 version='create_aces_config 1.0',
1428 p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1429 ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
1430 p.add_option('--configDir', '-c', default=os.environ.get(
1431 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
1432 p.add_option('--lutResolution1d', default=4096)
1433 p.add_option('--lutResolution3d', default=64)
1434 p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False)
1435 p.add_option('--keepTempImages', action='store_true', default=False)
1437 p.add_option('--createMultipleDisplays', action='store_true',
1440 p.add_option('--addCustomLookLUT', '', type='string', nargs=3,
1441 action='callback', callback=look_info_callback)
1442 p.add_option('--addCustomLookCDL', '', type='string', nargs=4,
1443 action='callback', callback=look_info_callback)
1444 p.add_option('--addACESLookLUT', '', type='string', nargs=2,
1445 action='callback', callback=look_info_callback)
1446 p.add_option('--addACESLookCDL', '', type='string', nargs=3,
1447 action='callback', callback=look_info_callback)
1448 p.add_option('--copyCustomLUTs', action='store_true', default=False)
1450 options, arguments = p.parse_args()
1452 aces_ctl_directory = options.acesCTLDir
1453 config_directory = options.configDir
1454 lut_resolution_1d = int(options.lutResolution1d)
1455 lut_resolution_3d = int(options.lutResolution3d)
1456 bake_secondary_luts = not options.dontBakeSecondaryLUTs
1457 cleanup_temp_images = not options.keepTempImages
1458 multiple_displays = options.createMultipleDisplays
1459 copy_custom_luts = options.copyCustomLUTs
1463 print('command line : \n%s\n' % ' '.join(sys.argv))
1465 assert aces_ctl_directory is not None, (
1466 'process: No "{0}" environment variable defined or no "ACES CTL" '
1467 'directory specified'.format(
1468 ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1470 assert config_directory is not None, (
1471 'process: No "{0}" environment variable defined or no configuration '
1472 'directory specified'.format(
1473 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1475 return create_ACES_config(aces_ctl_directory,
1479 bake_secondary_luts,
1483 cleanup_temp_images)
1486 if __name__ == '__main__':