2 # -*- coding: utf-8 -*-
5 Defines objects creating the *ACES* configuration.
8 from __future__ import division
13 import PyOpenColorIO as ocio
14 from aces_ocio.colorspaces import aces
15 from aces_ocio.colorspaces import arri
16 from aces_ocio.colorspaces import canon
17 from aces_ocio.colorspaces import general
18 from aces_ocio.colorspaces import gopro
19 from aces_ocio.colorspaces import panasonic
20 from aces_ocio.colorspaces import red
21 from aces_ocio.colorspaces import sony
22 from aces_ocio.process import Process
24 from aces_ocio.utilities import replace
26 __author__ = 'ACES Developers'
27 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
29 __maintainer__ = 'ACES Developers'
30 __email__ = 'aces@oscars.org'
31 __status__ = 'Production'
33 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
34 'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
35 'set_config_default_roles',
37 'generate_OCIO_transform',
38 'add_colorspace_alias',
41 'generate_baked_LUTs',
46 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
47 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
50 def set_config_default_roles(config,
61 compositing_linear=''):
63 Sets given *OCIO* configuration default roles.
69 color_picking : str or unicode
70 Color picking role title.
71 color_timing : str or unicode
72 Color timing role title.
73 compositing_log : str or unicode
74 Compositing log role title.
77 default : str or unicode
79 matte_paint : str or unicode
80 Matte painting role title.
81 reference : str or unicode
83 scene_linear : str or unicode
84 Scene linear role title.
85 texture_paint : str or unicode
86 Texture painting role title.
95 config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
97 config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
99 config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
101 config.setRole(ocio.Constants.ROLE_DATA, data)
103 config.setRole(ocio.Constants.ROLE_DEFAULT, default)
105 config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
107 config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
109 config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
111 # 'rendering' and 'compositing_linear' roles default to the 'scene_linear'
112 # value if not set explicitly
114 config.setRole("rendering", rendering)
115 if compositing_linear:
116 config.setRole("compositing_linear", compositing_linear)
118 config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
120 config.setRole("rendering", scene_linear)
121 if not compositing_linear:
122 config.setRole("compositing_linear", scene_linear)
127 def write_config(config, config_path, sanity_check=True):
129 Writes the configuration to given path.
134 Parameter description.
139 Return value description.
147 print 'Configuration was not written due to a failed Sanity Check'
150 with open(config_path, mode='w') as fp:
151 fp.write(config.serialize())
154 def generate_OCIO_transform(transforms):
161 Parameter description.
166 Return value description.
169 interpolation_options = {
170 'linear': ocio.Constants.INTERP_LINEAR,
171 'nearest': ocio.Constants.INTERP_NEAREST,
172 'tetrahedral': ocio.Constants.INTERP_TETRAHEDRAL}
174 direction_options = {
175 'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
176 'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE}
180 for transform in transforms:
183 if transform['type'] == 'lutFile':
184 ocio_transform = ocio.FileTransform(
185 src=transform['path'],
186 interpolation=interpolation_options[
187 transform['interpolation']],
188 direction=direction_options[transform['direction']])
189 ocio_transforms.append(ocio_transform)
192 elif transform['type'] == 'matrix':
193 ocio_transform = ocio.MatrixTransform()
194 # MatrixTransform member variables can't be initialized directly.
195 # Each must be set individually.
196 ocio_transform.setMatrix(transform['matrix'])
198 if 'offset' in transform:
199 ocio_transform.setOffset(transform['offset'])
201 if 'direction' in transform:
202 ocio_transform.setDirection(
203 direction_options[transform['direction']])
205 ocio_transforms.append(ocio_transform)
208 elif transform['type'] == 'exponent':
209 ocio_transform = ocio.ExponentTransform()
210 ocio_transform.setValue(transform['value'])
211 ocio_transforms.append(ocio_transform)
214 elif transform['type'] == 'log':
215 ocio_transform = ocio.LogTransform(
216 base=transform['base'],
217 direction=direction_options[transform['direction']])
219 ocio_transforms.append(ocio_transform)
221 # color space transform
222 elif transform['type'] == 'colorspace':
223 ocio_transform = ocio.ColorSpaceTransform(src=transform['src'],
224 dst=transform['dst'],
228 ocio_transforms.append(ocio_transform)
231 print("Ignoring unknown transform type : %s" % transform['type'])
233 if len(ocio_transforms) > 1:
234 group_transform = ocio.GroupTransform()
235 for transform in ocio_transforms:
236 group_transform.push_back(transform)
237 transform = group_transform
239 transform = ocio_transforms[0]
244 def add_colorspace_alias(config,
245 reference_colorspace,
247 colorspace_alias_names):
254 Parameter description.
259 Return value description.
262 for alias_name in colorspace_alias_names:
263 if alias_name.lower() == colorspace.name.lower():
264 print('Skipping alias creation for %s, alias %s, because lower cased names match' % (
265 colorspace.name, alias_name) )
268 print('Adding alias colorspace space %s, alias to %s' % (
269 alias_name, colorspace.name))
271 compact_family_name = 'Aliases'
273 ocio_colorspace_alias = ocio.ColorSpace(
275 bitDepth=colorspace.bit_depth,
276 description=colorspace.description,
277 equalityGroup=colorspace.equality_group,
278 family=compact_family_name,
279 isData=colorspace.is_data,
280 allocation=colorspace.allocation_type,
281 allocationVars=colorspace.allocation_vars)
283 if colorspace.to_reference_transforms:
284 print('Generating To-Reference transforms')
285 ocio_transform = generate_OCIO_transform(
286 [{'type': 'colorspace',
287 'src': colorspace.name,
288 'dst': reference_colorspace.name,
289 'direction': 'forward'}])
290 ocio_colorspace_alias.setTransform(
292 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
294 if colorspace.from_reference_transforms:
295 print('Generating From-Reference transforms')
296 ocio_transform = generate_OCIO_transform(
297 [{'type': 'colorspace',
298 'src': reference_colorspace.name,
299 'dst': colorspace.name,
300 'direction': 'forward'}])
301 ocio_colorspace_alias.setTransform(
303 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
305 config.addColorSpace(ocio_colorspace_alias)
307 def colorspace_prefixed_name(colorspace):
308 prefix = colorspace.family.replace("/", " - ")
309 return "%s - %s" % (prefix, colorspace.name)
311 def create_config(config_data, aliases=False, prefix=False):
318 Parameter description.
323 Return value description.
327 alias_colorspaces = []
329 # Creating the *OCIO* configuration.
330 config = ocio.Config()
332 # Setting configuration overall values.
333 config.setDescription('An ACES config generated from python')
334 config.setSearchPath('luts')
336 # Defining the reference colorspace.
337 reference_data = config_data['referenceColorSpace']
339 # Adding the color space Family into the name
340 # Helps with applications that present colorspaces as one long list
342 prefixed_name = colorspace_prefixed_name(reference_data)
343 prefixed_names[reference_data.name] = prefixed_name
344 reference_data.name = prefixed_name
346 print('Adding the reference color space : %s' % reference_data.name)
348 reference = ocio.ColorSpace(
349 name=reference_data.name,
350 bitDepth=reference_data.bit_depth,
351 description=reference_data.description,
352 equalityGroup=reference_data.equality_group,
353 family=reference_data.family,
354 isData=reference_data.is_data,
355 allocation=reference_data.allocation_type,
356 allocationVars=reference_data.allocation_vars)
358 config.addColorSpace(reference)
362 if reference_data.aliases != []:
363 #add_colorspace_alias(config, reference_data,
364 # reference_data, reference_data.aliases)
365 # defer adding alias colorspaces until end. Helps with some applications
366 alias_colorspaces.append([reference_data, reference_data, reference_data.aliases])
371 #print( "color spaces : %s" % [x.name for x in sorted(config_data['colorSpaces'])])
373 print('Adding the regular color spaces')
375 # Creating the remaining colorspaces.
376 for colorspace in sorted(config_data['colorSpaces']):
377 # Adding the color space Family into the name
378 # Helps with applications that present colorspaces as one long list
380 prefixed_name = colorspace_prefixed_name(colorspace)
381 prefixed_names[colorspace.name] = prefixed_name
382 colorspace.name = prefixed_name
384 print('Creating new color space : %s' % colorspace.name)
386 ocio_colorspace = ocio.ColorSpace(
387 name=colorspace.name,
388 bitDepth=colorspace.bit_depth,
389 description=colorspace.description,
390 equalityGroup=colorspace.equality_group,
391 family=colorspace.family,
392 isData=colorspace.is_data,
393 allocation=colorspace.allocation_type,
394 allocationVars=colorspace.allocation_vars)
396 if colorspace.to_reference_transforms:
397 print('Generating To-Reference transforms')
398 ocio_transform = generate_OCIO_transform(
399 colorspace.to_reference_transforms)
400 ocio_colorspace.setTransform(
402 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
404 if colorspace.from_reference_transforms:
405 print('Generating From-Reference transforms')
406 ocio_transform = generate_OCIO_transform(
407 colorspace.from_reference_transforms)
408 ocio_colorspace.setTransform(
410 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
412 config.addColorSpace(ocio_colorspace)
415 # Add alias to normal colorspace, using compact name
418 if colorspace.aliases != []:
419 #add_colorspace_alias(config, reference_data,
420 # colorspace, colorspace.aliases)
421 # defer adding alias colorspaces until end. Helps with some applications
422 alias_colorspaces.append([reference_data, colorspace, colorspace.aliases])
428 # We add these at the end as some applications use the order of the colorspaces
429 # definitions in the config to order the colorspaces in their selection lists.
430 # Other go alphabetically. This should keep the alias colorspaces out of the way
431 # for the apps that use the order of definition in the config.
432 print('Adding the alias colorspaces')
433 for reference, colorspace, aliases in alias_colorspaces:
434 add_colorspace_alias(config, reference, colorspace, aliases)
438 print('Adding the diplays and views')
440 # Defining the *views* and *displays*.
445 # Defining a *generic* *display* and *view* setup.
447 for display, view_list in config_data['displays'].iteritems():
448 for view_name, colorspace in view_list.iteritems():
449 config.addDisplay(display, view_name, colorspace.name)
450 if not (view_name in views):
451 views.append(view_name)
452 displays.append(display)
456 # Defining the set of *views* and *displays* useful in a *GUI* context.
457 #display_name = 'ACES'
458 display_name = config_data['roles']['scene_linear']
459 displays.append(display_name)
461 display_names = sorted(config_data['displays'])
463 # Make sure the default display is first
464 default_display = config_data['defaultDisplay']
465 display_names.insert(0, display_names.pop(display_names.index(default_display)))
467 for display in display_names:
468 view_list = config_data['displays'][display]
469 for view_name, colorspace in view_list.iteritems():
470 if view_name == 'Output Transform':
471 display_cleaned = replace(display, {')': '', '(': ''})
472 config.addDisplay(display_name, display_cleaned, colorspace.name)
473 if not (display_cleaned in views):
474 views.append(display_cleaned)
476 # Works with Nuke Studio and Mari, but not Nuke
477 # display_name = 'Utility'
478 # displays.append(display_name)
480 linear_display_space_name = config_data['roles']['scene_linear']
481 log_display_space_name = config_data['roles']['compositing_log']
483 # Find the newly-prefixed colorspace names
485 #print( prefixed_names )
486 linear_display_space_name = prefixed_names[linear_display_space_name]
487 log_display_space_name = prefixed_names[log_display_space_name]
489 config.addDisplay(display_name, 'Linear', linear_display_space_name)
490 views.append('Linear')
491 config.addDisplay(display_name, 'Log', log_display_space_name)
494 # Setting the active *displays* and *views*.
495 config.setActiveDisplays(','.join(sorted(displays)))
496 config.setActiveViews(','.join(views))
500 print('Setting the roles')
503 set_config_default_roles(
505 color_picking=prefixed_names[config_data['roles']['color_picking']],
506 color_timing=prefixed_names[config_data['roles']['color_timing']],
507 compositing_log=prefixed_names[config_data['roles']['compositing_log']],
508 data=prefixed_names[config_data['roles']['data']],
509 default=prefixed_names[config_data['roles']['default']],
510 matte_paint=prefixed_names[config_data['roles']['matte_paint']],
511 reference=prefixed_names[config_data['roles']['reference']],
512 scene_linear=prefixed_names[config_data['roles']['scene_linear']],
513 texture_paint=prefixed_names[config_data['roles']['texture_paint']])
515 set_config_default_roles(
517 color_picking=config_data['roles']['color_picking'],
518 color_timing=config_data['roles']['color_timing'],
519 compositing_log=config_data['roles']['compositing_log'],
520 data=config_data['roles']['data'],
521 default=config_data['roles']['default'],
522 matte_paint=config_data['roles']['matte_paint'],
523 reference=config_data['roles']['reference'],
524 scene_linear=config_data['roles']['scene_linear'],
525 texture_paint=config_data['roles']['texture_paint'])
529 # Make sure we didn't create a bad config
532 # Reset the colorspace names back to their non-prefixed versions
534 # Build the reverse lookup
535 prefixed_names_inverse = {}
536 for original, prefixed in prefixed_names.iteritems():
537 prefixed_names_inverse[prefixed] = original
539 # Reet the reference colorspace name
540 reference_data.name = prefixed_names_inverse[reference_data.name]
542 # Reset the rest of the colorspace names
543 for colorspace in config_data['colorSpaces']:
544 colorspace.name = prefixed_names_inverse[colorspace.name]
549 def generate_LUTs(odt_info,
554 lut_resolution_1d=4096,
555 lut_resolution_3d=64,
563 Parameter description.
568 Colorspaces and transforms converting between those colorspaces and
569 the reference colorspace, *ACES*.
572 print('generateLUTs - begin')
575 # Initialize a few variables
576 config_data['displays'] = {}
577 config_data['colorSpaces'] = []
579 # -------------------------------------------------------------------------
580 # *ACES Color Spaces*
581 # -------------------------------------------------------------------------
587 aces_log_display_space,
589 aces_default_display) = aces.create_colorspaces(aces_ctl_directory,
598 config_data['referenceColorSpace'] = aces_reference
599 config_data['roles'] = aces_roles
601 for cs in aces_colorspaces:
602 config_data['colorSpaces'].append(cs)
604 for name, data in aces_displays.iteritems():
605 config_data['displays'][name] = data
607 config_data['defaultDisplay'] = aces_default_display
608 config_data['linearDisplaySpace'] = aces_reference
609 config_data['logDisplaySpace'] = aces_log_display_space
611 # -------------------------------------------------------------------------
612 # *Camera Input Transforms*
613 # -------------------------------------------------------------------------
615 # *ARRI Log-C* to *ACES*.
616 arri_colorSpaces = arri.create_colorspaces(lut_directory,
618 for cs in arri_colorSpaces:
619 config_data['colorSpaces'].append(cs)
621 # *Canon-Log* to *ACES*.
622 canon_colorspaces = canon.create_colorspaces(lut_directory,
624 for cs in canon_colorspaces:
625 config_data['colorSpaces'].append(cs)
627 # *GoPro Protune* to *ACES*.
628 gopro_colorspaces = gopro.create_colorspaces(lut_directory,
630 for cs in gopro_colorspaces:
631 config_data['colorSpaces'].append(cs)
633 # *Panasonic V-Log* to *ACES*.
634 panasonic_colorSpaces = panasonic.create_colorspaces(lut_directory,
636 for cs in panasonic_colorSpaces:
637 config_data['colorSpaces'].append(cs)
639 # *RED* colorspaces to *ACES*.
640 red_colorspaces = red.create_colorspaces(lut_directory,
642 for cs in red_colorspaces:
643 config_data['colorSpaces'].append(cs)
646 sony_colorSpaces = sony.create_colorspaces(lut_directory,
648 for cs in sony_colorSpaces:
649 config_data['colorSpaces'].append(cs)
651 # -------------------------------------------------------------------------
652 # General Color Spaces
653 # -------------------------------------------------------------------------
654 general_colorSpaces = general.create_colorspaces(lut_directory,
657 for cs in general_colorSpaces:
658 config_data['colorSpaces'].append(cs)
660 # The *Raw* color space
661 raw = general.create_raw()
662 config_data['colorSpaces'].append(raw)
664 # Override certain roles, for now
665 config_data['roles']['data'] = raw.name
666 config_data['roles']['reference'] = raw.name
667 config_data['roles']['texture_paint'] = raw.name
669 print('generateLUTs - end')
673 def generate_baked_LUTs(odt_info,
679 lut_resolution_shaper=1024):
686 Parameter description.
691 Return value description.
694 # Create two entries for ODTs that have full and legal range support
695 odt_info_C = dict(odt_info)
696 for odt_ctl_name, odt_values in odt_info.iteritems():
697 if odt_values['transformHasFullLegalSwitch']:
698 odt_name = odt_values['transformUserName']
700 odt_values_legal = dict(odt_values)
701 odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
702 odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal
704 odt_values_full = dict(odt_values)
705 odt_values_full['transformUserName'] = '%s - Full' % odt_name
706 odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full
708 del (odt_info_C[odt_ctl_name])
710 # Generate appropriate LUTs for each ODT
711 for odt_ctl_name, odt_values in odt_info_C.iteritems():
712 odt_prefix = odt_values['transformUserNamePrefix']
713 odt_name = odt_values['transformUserName']
716 for input_space in ['ACEScc', 'ACESproxy']:
717 args = ['--iconfig', config_path,
719 '--inputspace', input_space]
720 args += ['--outputspace', '%s' % odt_name]
721 args += ['--description',
722 '%s - %s for %s data' % (odt_prefix,
725 args += ['--shaperspace', shaper_name,
726 '--shapersize', str(lut_resolution_shaper)]
727 args += ['--cubesize', str(lut_resolution_3d)]
730 os.path.join(baked_directory,
732 '%s for %s.icc' % (odt_name, input_space))]
734 bake_lut = Process(description='bake a LUT',
740 for input_space in ['ACEScc', 'ACESproxy']:
741 args = ['--iconfig', config_path,
743 '--inputspace', input_space]
744 args += ['--outputspace', '%s' % odt_name]
745 args += ['--description',
746 '%s - %s for %s data' % (
747 odt_prefix, odt_name, input_space)]
748 args += ['--shaperspace', shaper_name,
749 '--shapersize', str(lut_resolution_shaper)]
750 args += ['--cubesize', str(lut_resolution_3d)]
757 '%s for %s Flame.3dl' % (odt_name, input_space))]
758 bake_lut = Process(description='bake a LUT',
768 '%s for %s Lustre.3dl' % (odt_name, input_space))]
769 bake_lut = Process(description='bake a LUT',
775 for input_space in ['ACEScg', 'ACES2065-1']:
776 args = ['--iconfig', config_path,
778 '--inputspace', input_space]
779 args += ['--outputspace', '%s' % odt_name]
780 args += ['--description',
781 '%s - %s for %s data' % (
782 odt_prefix, odt_name, input_space)]
783 if input_space == 'ACEScg':
784 lin_shaper_name = '%s - AP1' % shaper_name
786 lin_shaper_name = shaper_name
787 args += ['--shaperspace', lin_shaper_name,
788 '--shapersize', str(lut_resolution_shaper)]
790 args += ['--cubesize', str(lut_resolution_3d)]
797 '%s for %s Maya.csp' % (odt_name, input_space))]
798 bake_lut = Process(description='bake a LUT',
808 '%s for %s Houdini.lut' % (odt_name, input_space))]
809 bake_lut = Process(description='bake a LUT',
815 def create_config_dir(config_directory, bake_secondary_LUTs):
822 Parameter description.
827 Return value description.
830 lut_directory = os.path.join(config_directory, 'luts')
831 dirs = [config_directory, lut_directory]
832 if bake_secondary_LUTs:
833 dirs.extend([os.path.join(config_directory, 'baked'),
834 os.path.join(config_directory, 'baked', 'flame'),
835 os.path.join(config_directory, 'baked', 'photoshop'),
836 os.path.join(config_directory, 'baked', 'houdini'),
837 os.path.join(config_directory, 'baked', 'lustre'),
838 os.path.join(config_directory, 'baked', 'maya')])
841 not os.path.exists(d) and os.mkdir(d)
846 def create_ACES_config(aces_ctl_directory,
848 lut_resolution_1d=4096,
849 lut_resolution_3d=64,
850 bake_secondary_LUTs=True,
853 Creates the ACES configuration.
858 Parameter description.
863 Return value description.
866 lut_directory = create_config_dir(config_directory, bake_secondary_LUTs)
868 odt_info = aces.get_ODTs_info(aces_ctl_directory)
869 lmt_info = aces.get_LMTs_info(aces_ctl_directory)
871 shaper_name = 'Output Shaper'
872 config_data = generate_LUTs(odt_info,
881 print('Creating config - with prefixes, with aliases')
882 gui_config = create_config(config_data, prefix=True, aliases=True)
885 write_config(gui_config,
886 os.path.join(config_directory, 'config.ocio'))
889 print('Creating config - with prefixes, without aliases')
890 gui_config = create_config(config_data, prefix=True, aliases=False)
893 write_config(gui_config,
894 os.path.join(config_directory, 'config_w_prefixes_no_aliases.ocio'))
896 print('Creating config - without prefixes, with aliases')
897 gui_config = create_config(config_data, prefix=False, aliases=True)
900 write_config(gui_config,
901 os.path.join(config_directory, 'config_no_prefixes_w_aliases.ocio'))
903 print('Creating config - without prefixes, without aliases')
904 gui_config = create_config(config_data, prefix=False, aliases=False)
907 write_config(gui_config,
908 os.path.join(config_directory, 'config_no_prefixes_no_aliases.ocio'))
911 if bake_secondary_LUTs:
912 generate_baked_LUTs(odt_info,
914 os.path.join(config_directory, 'baked'),
915 os.path.join(config_directory, 'config.ocio'),
930 Parameter description.
935 Return value description.
940 p = optparse.OptionParser(description='An OCIO config generation script',
941 prog='createACESConfig',
942 version='createACESConfig 0.1',
943 usage='%prog [options]')
944 p.add_option('--acesCTLDir', '-a', default=os.environ.get(
945 ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
946 p.add_option('--configDir', '-c', default=os.environ.get(
947 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
948 p.add_option('--lutResolution1d', default=4096)
949 p.add_option('--lutResolution3d', default=64)
950 p.add_option('--dontBakeSecondaryLUTs', action='store_true')
951 p.add_option('--keepTempImages', action='store_true')
953 options, arguments = p.parse_args()
955 aces_ctl_directory = options.acesCTLDir
956 config_directory = options.configDir
957 lut_resolution_1d = int(options.lutResolution1d)
958 lut_resolution_3d = int(options.lutResolution3d)
959 bake_secondary_luts = not options.dontBakeSecondaryLUTs
960 cleanup_temp_images = not options.keepTempImages
962 # TODO: Investigate the following statements.
964 args_start = sys.argv.index('--') + 1
965 args = sys.argv[args_start:]
967 args_start = len(sys.argv) + 1
970 print('command line : \n%s\n' % ' '.join(sys.argv))
972 assert aces_ctl_directory is not None, (
973 'process: No "{0}" environment variable defined or no "ACES CTL" '
974 'directory specified'.format(
975 ACES_OCIO_CTL_DIRECTORY_ENVIRON))
977 assert config_directory is not None, (
978 'process: No "{0}" environment variable defined or no configuration '
979 'directory specified'.format(
980 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
982 return create_ACES_config(aces_ctl_directory,
990 if __name__ == '__main__':