X-Git-Url: http://users.mur.at/ms/git/gitweb/?p=OpenColorIO-Configs.git;a=blobdiff_plain;f=aces_1.0.1%2Fpython%2Faces_ocio%2Fgenerate_config.py;fp=aces_1.0.1%2Fpython%2Faces_ocio%2Fgenerate_config.py;h=5ceae55fe6504c2964ecbef6a5d501e0a73dbfd9;hp=0000000000000000000000000000000000000000;hb=22e6f32dc4ea25f99c83a6226cc4907b30b1cfcd;hpb=18a75430917db571c2846873510f77086d885479 diff --git a/aces_1.0.1/python/aces_ocio/generate_config.py b/aces_1.0.1/python/aces_ocio/generate_config.py new file mode 100755 index 0000000..5ceae55 --- /dev/null +++ b/aces_1.0.1/python/aces_ocio/generate_config.py @@ -0,0 +1,1625 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines objects creating the *ACES* configuration. +""" + +from __future__ import division + +import copy +import os +import shutil +import sys + +import PyOpenColorIO as ocio +from aces_ocio.colorspaces import aces +from aces_ocio.colorspaces import arri +from aces_ocio.colorspaces import canon +from aces_ocio.colorspaces import general +from aces_ocio.colorspaces import gopro +from aces_ocio.colorspaces import panasonic +from aces_ocio.colorspaces import red +from aces_ocio.colorspaces import sony +from aces_ocio.process import Process + +from aces_ocio.utilities import ( + ColorSpace, + colorspace_prefixed_name, + compact, + replace, + unpack_default) + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON', + 'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON', + 'set_config_roles', + 'create_ocio_transform', + 'add_colorspace_aliases', + 'add_look', + 'add_looks_to_views', + 'create_config', + 'create_config_data', + 'write_config', + 'generate_baked_LUTs', + 'generate_config_directory', + 'generate_config', + 'main'] + +ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY' +ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY' + + +def set_config_roles(config, + color_picking=None, + color_timing=None, + compositing_log=None, + data=None, + default=None, + matte_paint=None, + reference=None, + scene_linear=None, + texture_paint=None, + rendering=None, + compositing_linear=None): + """ + Sets given *OCIO* configuration roles to the config. + Parameters + ---------- + config : Config + *OCIO* configuration. + color_picking : str or unicode, optional + Color Picking role title. + color_timing : str or unicode, optional + Color Timing role title. + compositing_log : str or unicode, optional + Compositing Log role title. + data : str or unicode, optional + Data role title. + default : str or unicode, optional + Default role title. + matte_paint : str or unicode, optional + Matte Painting role title. + reference : str or unicode, optional + Reference role title. + scene_linear : str or unicode, optional + Scene Linear role title. + texture_paint : str or unicode, optional + Texture Painting role title. + rendering : str or unicode, optional + Rendering role title. + compositing_linear : str or unicode, optional + Compositing Linear role title. + Returns + ------- + bool + Definition success. + """ + + if color_picking is not None: + config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking) + if color_timing is not None: + config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing) + if compositing_log is not None: + config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log) + if data is not None: + config.setRole(ocio.Constants.ROLE_DATA, data) + if default is not None: + config.setRole(ocio.Constants.ROLE_DEFAULT, default) + if matte_paint is not None: + config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint) + if reference is not None: + config.setRole(ocio.Constants.ROLE_REFERENCE, reference) + if texture_paint is not None: + config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint) + + # *rendering* and *compositing_linear* roles default to the *scene_linear* + # value if not set explicitly. + if rendering is not None: + config.setRole('rendering', rendering) + if compositing_linear is not None: + config.setRole('compositing_linear', compositing_linear) + if scene_linear is not None: + config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear) + if rendering is None: + config.setRole('rendering', scene_linear) + if compositing_linear is None: + config.setRole('compositing_linear', scene_linear) + + return True + + +def create_ocio_transform(transforms): + """ + Returns an *OCIO* transform from given array of transform descriptions. + + Parameters + ---------- + transforms : array_like + Transform descriptions as an array_like of dicts: + {'type', 'src', 'dst', 'direction'} + + Returns + ------- + Transform + *OCIO* transform. + """ + + direction_options = { + 'forward': ocio.Constants.TRANSFORM_DIR_FORWARD, + 'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE} + + ocio_transforms = [] + + for transform in transforms: + + # *lutFile* transform + if transform['type'] == 'lutFile': + ocio_transform = ocio.FileTransform() + + if 'path' in transform: + ocio_transform.setSrc(transform['path']) + + if 'cccid' in transform: + ocio_transform.setCCCId(transform['cccid']) + + if 'interpolation' in transform: + ocio_transform.setInterpolation(transform['interpolation']) + else: + ocio_transform.setInterpolation(ocio.Constants.INTERP_BEST) + + if 'direction' in transform: + ocio_transform.setDirection( + direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + + # *matrix* transform + elif transform['type'] == 'matrix': + ocio_transform = ocio.MatrixTransform() + # `MatrixTransform` member variables can't be initialized directly, + # each must be set individually. + ocio_transform.setMatrix(transform['matrix']) + + if 'offset' in transform: + ocio_transform.setOffset(transform['offset']) + + if 'direction' in transform: + ocio_transform.setDirection( + direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + + # *exponent* transform + elif transform['type'] == 'exponent': + ocio_transform = ocio.ExponentTransform() + + if 'value' in transform: + ocio_transform.setValue(transform['value']) + + ocio_transforms.append(ocio_transform) + + # *log* transform + elif transform['type'] == 'log': + ocio_transform = ocio.LogTransform() + + if 'base' in transform: + ocio_transform.setBase(transform['base']) + + if 'direction' in transform: + ocio_transform.setDirection( + direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + + # *colorspace* transform + elif transform['type'] == 'colorspace': + ocio_transform = ocio.ColorSpaceTransform() + + if 'src' in transform: + ocio_transform.setSrc(transform['src']) + + if 'dst' in transform: + ocio_transform.setDst(transform['dst']) + + if 'direction' in transform: + ocio_transform.setDirection( + direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + + # *look* transform + elif transform['type'] == 'look': + ocio_transform = ocio.LookTransform() + if 'look' in transform: + ocio_transform.setLooks(transform['look']) + + if 'src' in transform: + ocio_transform.setSrc(transform['src']) + + if 'dst' in transform: + ocio_transform.setDst(transform['dst']) + + if 'direction' in transform: + ocio_transform.setDirection( + direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + + # *unknown* type + else: + print('Ignoring unknown transform type : %s' % transform['type']) + + if len(ocio_transforms) > 1: + group_transform = ocio.GroupTransform() + for transform in ocio_transforms: + group_transform.push_back(transform) + transform = group_transform + else: + transform = ocio_transforms[0] + + return transform + + +def add_colorspace_aliases(config, + reference_colorspace, + colorspace, + colorspace_alias_names, + family='Aliases'): + """ + Adds given colorspace aliases to the *OCIO* config. + + Parameters + ---------- + config : Config + *OCIO* configuration. + reference_colorspace : Colorspace + Reference colorspace. + colorspace : Colorspace + Colorspace to set the aliases into the *OCIO* config. + family : unicode + Family. + + Returns + ------- + bool + Definition success. + """ + + for alias_name in colorspace_alias_names: + if alias_name.lower() == colorspace.name.lower(): + print('Skipping alias creation for %s, alias %s, ' + 'because lower cased names match' % ( + colorspace.name, alias_name)) + continue + + print('Adding alias colorspace space %s, alias to %s' % ( + alias_name, colorspace.name)) + + compact_family_name = family + + description = colorspace.description + if colorspace.aces_transform_id: + description += ( + '\n\nACES Transform ID : %s' % colorspace.aces_transform_id) + + ocio_colorspace_alias = ocio.ColorSpace( + name=alias_name, + bitDepth=colorspace.bit_depth, + description=description, + equalityGroup=colorspace.equality_group, + family=compact_family_name, + isData=colorspace.is_data, + allocation=colorspace.allocation_type, + allocationVars=colorspace.allocation_vars) + + if colorspace.to_reference_transforms: + print('\tGenerating To-Reference transforms') + ocio_transform = create_ocio_transform( + [{'type': 'colorspace', + 'src': colorspace.name, + 'dst': reference_colorspace.name, + 'direction': 'forward'}]) + ocio_colorspace_alias.setTransform( + ocio_transform, + ocio.Constants.COLORSPACE_DIR_TO_REFERENCE) + + if colorspace.from_reference_transforms: + print('\tGenerating From-Reference transforms') + ocio_transform = create_ocio_transform( + [{'type': 'colorspace', + 'src': reference_colorspace.name, + 'dst': colorspace.name, + 'direction': 'forward'}]) + ocio_colorspace_alias.setTransform( + ocio_transform, + ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE) + + config.addColorSpace(ocio_colorspace_alias) + + +def add_look(config, + look, + custom_lut_dir, + reference_name, + config_data): + """ + Adds given look to the *OCIO* config. + + Parameters + ---------- + config : Config + *OCIO* configuration. + look : array_like + Look description: {'name', 'colorspace', 'lut', 'cccid'} + custom_lut_dir : str or unicode + Directory to copy the look lut into. + reference_name : str or unicode + Reference name. + config_data : dict + Colorspaces and transforms converting between those colorspaces and + the reference colorspace, *ACES*. + + Returns + ------- + bool + Definition success. + """ + + look_name, look_colorspace, look_lut, look_cccid = unpack_default(look, 4) + + print('Adding look %s - %s' % (look_name, ', '.join(look))) + + # Copy *look LUT* if `custom_lut_dir` is provided. + if custom_lut_dir: + if '$' not in look_lut: + print('Getting ready to copy look lut : %s' % look_lut) + shutil.copy2(look_lut, custom_lut_dir) + look_lut = os.path.split(look_lut)[1] + else: + print('Skipping LUT copy because path contains a context variable') + + print('Adding look to config') + ocio_look = ocio.Look() + ocio_look.setName(look_name) + ocio_look.setProcessSpace(look_colorspace) + + keys = {'type': 'lutFile', + 'path': look_lut, + 'direction': 'forward'} + if look_cccid: + keys['cccid'] = look_cccid + + ocio_transform = create_ocio_transform([keys]) + ocio_look.setTransform(ocio_transform) + + config.addLook(ocio_look) + + print('Creating aliased colorspace') + + # Creating *OCIO* colorspace referencing the look: + # - Needed for implementations that don't process looks properly. + # - Needed for implementations that don't expose looks properly. + look_aliases = ['look_%s' % compact(look_name)] + colorspace = ColorSpace(look_name, + aliases=look_aliases, + description='The %s Look colorspace' % look_name, + family='Look') + + colorspace.from_reference_transforms = [{'type': 'look', + 'look': look_name, + 'src': reference_name, + 'dst': reference_name, + 'direction': 'forward'}] + + print('Adding colorspace %s, alias to look %s to config data' % ( + look_name, look_name)) + + config_data['colorSpaces'].append(colorspace) + + print('') + + +def add_looks_to_views(looks, + reference_name, + config_data, + multiple_displays=False): + """ + Integrates a set of looks into the *OCIO* config's Displays and Views + + Parameters + ---------- + looks : array of str or unicode + Names of looks + reference_name : str or unicode + The name of the *OCIO* reference colorspace + config_data : dict + Colorspaces and transforms converting between those colorspaces and + the reference colorspace, *ACES*. + multiple_displays : bool + If true, looks are added to the config_data looks list + If false, looks are integrated directly into the list of displays and + views. This may be necessary due to limitations of some applications' + currently implementation of OCIO, ex. Maya 2016. + + Returns + ------- + None + """ + look_names = [look[0] for look in looks] + + # Option 1 + # - Adding a *look* per *Display*. + # - Assuming there is a *Display* for each *ACES* *Output Transform*. + if multiple_displays: + for look_name in look_names: + config_data['looks'].append(look_name) + + # Option 2 + # - Copy each *Output Transform* colorspace. + # - For each copy, add a *LookTransform* to the head of the + # `from_reference` transform list. + # - Add these the copy colorspaces for the *Displays* / *Views*. + else: + for display, view_list in config_data['displays'].iteritems(): + colorspace_c = None + look_names_string = '' + for view_name, output_colorspace in view_list.iteritems(): + if view_name == 'Output Transform': + + print('Adding new View that incorporates looks') + + colorspace_c = copy.deepcopy(output_colorspace) + + for i, look_name in enumerate(look_names): + look_name = look_names[i] + + # Add the `LookTransform` to the head of the + # `from_reference` transform list. + if colorspace_c.from_reference_transforms: + colorspace_c.from_reference_transforms.insert( + i, + {'type': 'look', + 'look': look_name, + 'src': reference_name, + 'dst': reference_name, + 'direction': 'forward'}) + + # Add the `LookTransform` to the end of + # the `to_reference` transform list. + if colorspace_c.to_reference_transforms: + inverse_look_name = look_names[ + len(look_names) - 1 - i] + + colorspace_c.to_reference_transforms.append( + {'type': 'look', + 'look': inverse_look_name, + 'src': reference_name, + 'dst': reference_name, + 'direction': 'inverse'}) + + if look_name not in config_data['looks']: + config_data['looks'].append(look_name) + + look_names_string = ', '.join(look_names) + colorspace_c.name = '%s with %s' % ( + output_colorspace.name, look_names_string) + colorspace_c.aliases = [ + 'out_%s' % compact(colorspace_c.name)] + + print('Colorspace that incorporates looks ' + 'created : %s' % colorspace_c.name) + + config_data['colorSpaces'].append(colorspace_c) + + if colorspace_c: + print('Adding colorspace that incorporates looks ' + 'into view list') + + # Updating the *View* name. + view_list['Output Transform with %s' % look_names_string] = ( + colorspace_c) + config_data['displays'][display] = view_list + + +def create_config(config_data, + aliases=False, + prefix=False, + multiple_displays=False, + look_info=None, + custom_lut_dir=None): + """ + Create the *OCIO* config based on the configuration data + + Parameters + ---------- + config_data : dict + Colorspaces and transforms converting between those colorspaces and + the reference colorspace, *ACES*, along with other data needed to + generate a complete *OCIO* configuration + aliases : bool, optional + Whether or not to include Alias colorspaces + prefix : bool, optional + Whether or not to prefix the colorspace names with their Family names + multiple_displays : bool, optional + Whether to create a single display named *ACES* with Views for each + Output Transform or multiple displays, one for each Output Transform + look_info : array of str or unicode, optional + Paths and names for look data + custom_lut_dir : str or unicode, optional + Directory to use for storing custom look files + + Returns + ------- + *OCIO* config + The constructed OCIO configuration + """ + + if look_info is None: + look_info = [] + + prefixed_names = {} + alias_colorspaces = [] + + config = ocio.Config() + + config.setDescription('An ACES config generated from python') + + search_path = ['luts'] + if custom_lut_dir: + search_path.append('custom') + config.setSearchPath(':'.join(search_path)) + + reference_data = config_data['referenceColorSpace'] + + # Adding the colorspace *Family* into the name which helps with + # applications that presenting colorspaces as one a flat list. + if prefix: + prefixed_name = colorspace_prefixed_name(reference_data) + prefixed_names[reference_data.name] = prefixed_name + reference_data.name = prefixed_name + + print('Adding the reference color space : %s' % reference_data.name) + + reference = ocio.ColorSpace( + name=reference_data.name, + bitDepth=reference_data.bit_depth, + description=reference_data.description, + equalityGroup=reference_data.equality_group, + family=reference_data.family, + isData=reference_data.is_data, + allocation=reference_data.allocation_type, + allocationVars=reference_data.allocation_vars) + + config.addColorSpace(reference) + + if aliases: + if reference_data.aliases: + # Deferring adding alias colorspaces until end, which helps with + # applications listing the colorspaces in the order that they were + # defined in the configuration: alias colorspaces are usually named + # lower case with spaces but normal colorspaces names are longer + # and more verbose, thus it becomes harder for user to visually + # parse the list of colorspaces when there are names such as + # "crv_canonlog" interspersed with names like + # "Input - Canon - Curve - Canon-Log". + # Moving the alias colorspace definitions to the end of the + # configuration avoids the above problem. + alias_colorspaces.append( + [reference_data, reference_data, reference_data.aliases]) + + print('') + + if look_info: + print('Adding looks') + + config_data['looks'] = [] + + for look in look_info: + add_look(config, + look, + custom_lut_dir, + reference_data.name, + config_data) + + add_looks_to_views(look_info, + reference_data.name, + config_data, + multiple_displays) + + print('') + + print('Adding regular colorspaces') + + for colorspace in sorted(config_data['colorSpaces'], + cmp=lambda x,y: cmp(x.family.lower(), y.family.lower())): + # Adding the colorspace *Family* into the name which helps with + # applications that presenting colorspaces as one a flat list. + if prefix: + prefixed_name = colorspace_prefixed_name(colorspace) + prefixed_names[colorspace.name] = prefixed_name + colorspace.name = prefixed_name + + print('Creating new color space : %s' % colorspace.name) + + description = colorspace.description + if colorspace.aces_transform_id: + description += ( + '\n\nACES Transform ID : %s' % colorspace.aces_transform_id) + + ocio_colorspace = ocio.ColorSpace( + name=colorspace.name, + bitDepth=colorspace.bit_depth, + description=description, + equalityGroup=colorspace.equality_group, + family=colorspace.family, + isData=colorspace.is_data, + allocation=colorspace.allocation_type, + allocationVars=colorspace.allocation_vars) + + if colorspace.to_reference_transforms: + print('\tGenerating To-Reference transforms') + ocio_transform = create_ocio_transform( + colorspace.to_reference_transforms) + ocio_colorspace.setTransform( + ocio_transform, + ocio.Constants.COLORSPACE_DIR_TO_REFERENCE) + + if colorspace.from_reference_transforms: + print('\tGenerating From-Reference transforms') + ocio_transform = create_ocio_transform( + colorspace.from_reference_transforms) + ocio_colorspace.setTransform( + ocio_transform, + ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE) + + config.addColorSpace(ocio_colorspace) + + if aliases: + if colorspace.aliases: + # Deferring adding alias colorspaces until end, which helps + # with applications listing the colorspaces in the order that + # they were defined in the configuration. + alias_colorspaces.append( + [reference_data, colorspace, colorspace.aliases]) + + print('') + + print('') + + # Adding roles early so that alias colorspaces can be created + # with roles names before remaining colorspace aliases are added + # to the configuration. + print('Setting the roles') + + if prefix: + set_config_roles( + config, + color_picking=prefixed_names[ + config_data['roles']['color_picking']], + color_timing=prefixed_names[config_data['roles']['color_timing']], + compositing_log=prefixed_names[ + config_data['roles']['compositing_log']], + data=prefixed_names[config_data['roles']['data']], + default=prefixed_names[config_data['roles']['default']], + matte_paint=prefixed_names[config_data['roles']['matte_paint']], + reference=prefixed_names[config_data['roles']['reference']], + scene_linear=prefixed_names[config_data['roles']['scene_linear']], + compositing_linear=prefixed_names[config_data['roles']['scene_linear']], + rendering=prefixed_names[config_data['roles']['scene_linear']], + texture_paint=prefixed_names[ + config_data['roles']['texture_paint']]) + + # Add the aliased colorspaces for each role + for role_name, role_colorspace_name in config_data['roles'].iteritems(): + role_colorspace_prefixed_name = prefixed_names[role_colorspace_name] + + #print( 'Finding colorspace : %s' % role_colorspace_prefixed_name ) + # Find the colorspace pointed to by the role + role_colorspaces = [colorspace + for colorspace in config_data['colorSpaces'] + if colorspace.name == role_colorspace_prefixed_name] + role_colorspace = None + if len(role_colorspaces) > 0: + role_colorspace = role_colorspaces[0] + else: + if reference_data.name == role_colorspace_prefixed_name: + role_colorspace = reference_data + + if role_colorspace: + # The alias colorspace shouldn't match the role name exactly + role_name_alias1 = "role_%s" % role_name + role_name_alias2 = "Role - %s" % role_name + + print( 'Adding a role colorspace named %s, pointing to %s' % ( + role_name_alias2, role_colorspace.name)) + + alias_colorspaces.append( + (reference_data, role_colorspace, [role_name_alias1])) + + add_colorspace_aliases( + config, reference_data, role_colorspace, [role_name_alias2], 'Roles') + + else: + set_config_roles( + config, + color_picking=config_data['roles']['color_picking'], + color_timing=config_data['roles']['color_timing'], + compositing_log=config_data['roles']['compositing_log'], + data=config_data['roles']['data'], + default=config_data['roles']['default'], + matte_paint=config_data['roles']['matte_paint'], + reference=config_data['roles']['reference'], + scene_linear=config_data['roles']['scene_linear'], + compositing_linear=config_data['roles']['scene_linear'], + rendering=config_data['roles']['scene_linear'], + texture_paint=config_data['roles']['texture_paint']) + + # Add the aliased colorspaces for each role + for role_name, role_colorspace_name in config_data['roles'].iteritems(): + # Find the colorspace pointed to by the role + role_colorspaces = [colorspace + for colorspace in config_data['colorSpaces'] + if colorspace.name == role_colorspace_name] + role_colorspace = None + if len(role_colorspaces) > 0: + role_colorspace = role_colorspaces[0] + else: + if reference_data.name == role_colorspace_name: + role_colorspace = reference_data + + if role_colorspace: + # The alias colorspace shouldn't match the role name exactly + role_name_alias1 = "role_%s" % role_name + role_name_alias2 = "Role - %s" % role_name + + print('Adding a role colorspace named %s, pointing to %s' % ( + role_name_alias2, role_colorspace.name)) + + alias_colorspaces.append( + (reference_data, role_colorspace, [role_name_alias1])) + + add_colorspace_aliases( + config, reference_data, role_colorspace, [role_name_alias2], 'Roles') + + print('') + + # Adding alias colorspaces at the end as some applications use + # colorspaces definitions order of the configuration to order + # the colorspaces in their selection lists, some applications + # use alphabetical ordering. + # This should keep the alias colorspaces out of the way for applications + # using the configuration order. + print('Adding the alias colorspaces') + for reference, colorspace, aliases in alias_colorspaces: + add_colorspace_aliases(config, reference, colorspace, aliases) + + print('') + + print('Adding the diplays and views') + + # Setting the *color_picking* role to be the first *Display*'s + # *Output Transform* *View*. + default_display_name = config_data['defaultDisplay'] + default_display_views = config_data['displays'][default_display_name] + default_display_colorspace = default_display_views['Output Transform'] + + # Defining *Displays* and *Views*. + displays, views = [], [] + + # Defining a generic *Display* and *View* setup. + if multiple_displays: + looks = config_data['looks'] if ('looks' in config_data) else [] + looks = ', '.join(looks) + print('Creating multiple displays, with looks : %s' % looks) + + # *Displays* are not reordered to put the *defaultDisplay* first + # because *OCIO* will order them alphabetically when the configuration + # is written to disk. + for display, view_list in config_data['displays'].iteritems(): + for view_name, colorspace in view_list.iteritems(): + config.addDisplay(display, view_name, colorspace.name, looks) + if 'Output Transform' in view_name and looks != '': + # *Views* without *Looks*. + config.addDisplay(display, view_name, colorspace.name) + + # *Views* with *Looks*. + view_name_with_looks = '%s with %s' % (view_name, looks) + config.addDisplay(display, view_name_with_looks, + colorspace.name, looks) + else: + config.addDisplay(display, view_name, colorspace.name) + if not (view_name in views): + views.append(view_name) + displays.append(display) + + # *Displays* and *Views* useful in a *GUI* context. + else: + single_display_name = 'ACES' + displays.append(single_display_name) + + # Ensuring the *defaultDisplay* is first. + display_names = sorted(config_data['displays']) + display_names.insert(0, display_names.pop( + display_names.index(default_display_name))) + + looks = config_data['looks'] if ('looks' in config_data) else [] + look_names = ', '.join(looks) + + displays_views_colorspaces = [] + + for display in display_names: + view_list = config_data['displays'][display] + for view_name, colorspace in view_list.iteritems(): + if 'Output Transform' in view_name: + + # We use the *Display* names as the *View* names in this + # case as there is a single *Display* containing all the + # *Views*. + # This works for more applications than not,as of the time + # of this implementation. + + # Autodesk Maya 2016 doesn't support parentheses in + # *View* names. + sanitised_display = replace(display, {')': '', '(': ''}) + + # *View* with *Looks*. + if 'with' in view_name: + sanitised_display = '%s with %s' % ( + sanitised_display, look_names) + + views_with_looks_at_end = False + # Storing combo of *Display*, *View* and *Colorspace* + # name so they can be added to the end of the list. + if views_with_looks_at_end: + displays_views_colorspaces.append( + [single_display_name, sanitised_display, + colorspace.name]) + else: + config.addDisplay(single_display_name, + sanitised_display, + colorspace.name) + + if not (sanitised_display in views): + views.append(sanitised_display) + + # *View* without *Looks*. + else: + config.addDisplay(single_display_name, + sanitised_display, + colorspace.name) + + if not (sanitised_display in views): + views.append(sanitised_display) + + # Adding to the configuration any *Display*, *View* combinations that + # were saved for later. + # This list should be empty unless `views_with_looks_at_end` is + # set `True` above. + for display_view_colorspace in displays_views_colorspaces: + single_display_name, sanitised_display, colorspace_name = ( + display_view_colorspace) + + config.addDisplay(single_display_name, + sanitised_display, + colorspace_name) + + if not (sanitised_display in views): + views.append(sanitised_display) + + raw_display_space_name = config_data['roles']['data'] + log_display_space_name = config_data['roles']['compositing_log'] + + if prefix: + raw_display_space_name = prefixed_names[raw_display_space_name] + log_display_space_name = prefixed_names[log_display_space_name] + + config.addDisplay(single_display_name, 'Raw', raw_display_space_name) + views.append('Raw') + config.addDisplay(single_display_name, 'Log', log_display_space_name) + views.append('Log') + + config.setActiveDisplays(','.join(sorted(displays))) + config.setActiveViews(','.join(views)) + + print('') + + # Ensuring the configuration is valid. + config.sanityCheck() + + # Resetting colorspace names to their non-prefixed versions. + if prefix: + prefixed_names_inverse = {} + for original, prefixed in prefixed_names.iteritems(): + prefixed_names_inverse[prefixed] = original + + reference_data.name = prefixed_names_inverse[reference_data.name] + + try: + for colorspace in config_data['colorSpaces']: + colorspace.name = prefixed_names_inverse[colorspace.name] + except: + print('Error with Prefixed names') + for original, prefixed in prefixed_names.iteritems(): + print('%s, %s' % (original, prefixed)) + + print('\n') + + print('Inverse Lookup of Prefixed names') + for prefixed, original in prefixed_names_inverse.iteritems(): + print('%s, %s' % (prefixed, original)) + raise + + return config + + +def create_config_data(odt_info, + lmt_info, + shaper_name, + aces_ctl_directory, + lut_directory, + lut_resolution_1d=4096, + lut_resolution_3d=64, + cleanup=True): + """ + Create the *ACES* LUTs and data structures needed for later *OCIO* + configuration generation + + Parameters + ---------- + odt_info : array of dicts of str or unicode + Descriptions of the *ACES* Output Transforms + lmt_info : array of dicts of str or unicode + Descriptions of the *ACES* Look Transforms + shaper_name : str or unicode + The name of the Shaper function to use when generating LUTs. + Options: Log2, DolbyPQ + aces_ctl_directory : str or unicode + The path to the aces 'transforms/ctl/utilities' + lut_directory : str or unicode + The path to use when writing LUTs + lut_resolution_1d : int, optional + The resolution of generated 1D LUTs + lut_resolution_3d : int, optional + The resolution of generated 3D LUTs + cleanup : bool + Whether or not to clean up the intermediate images + + Returns + ------- + dict + Colorspaces, LUT paths and transforms converting between those + colorspaces and the reference colorspace, *ACES*. + """ + + print('create_config_data - begin') + config_data = {} + + config_data['displays'] = {} + config_data['colorSpaces'] = [] + + # ------------------------------------------------------------------------- + # *ACES Color Spaces* + # ------------------------------------------------------------------------- + + # *ACES* colorspaces + (aces_reference, + aces_colorspaces, + aces_displays, + aces_log_display_space, + aces_roles, + aces_default_display) = aces.create_colorspaces(aces_ctl_directory, + lut_directory, + lut_resolution_1d, + lut_resolution_3d, + lmt_info, + odt_info, + shaper_name, + cleanup) + + config_data['referenceColorSpace'] = aces_reference + config_data['roles'] = aces_roles + + for cs in aces_colorspaces: + config_data['colorSpaces'].append(cs) + + for name, data in aces_displays.iteritems(): + config_data['displays'][name] = data + + config_data['defaultDisplay'] = aces_default_display + config_data['linearDisplaySpace'] = aces_reference + config_data['logDisplaySpace'] = aces_log_display_space + + # ------------------------------------------------------------------------- + # *Camera Input Transforms* + # ------------------------------------------------------------------------- + + # *ARRI Log-C* to *ACES* + arri_colorspaces = arri.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in arri_colorspaces: + config_data['colorSpaces'].append(cs) + + # *Canon-Log* to *ACES* + canon_colorspaces = canon.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in canon_colorspaces: + config_data['colorSpaces'].append(cs) + + # *GoPro Protune* to *ACES* + gopro_colorspaces = gopro.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in gopro_colorspaces: + config_data['colorSpaces'].append(cs) + + # *Panasonic V-Log* to *ACES* + panasonic_colorspaces = panasonic.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in panasonic_colorspaces: + config_data['colorSpaces'].append(cs) + + # *RED* colorspaces to *ACES* + red_colorspaces = red.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in red_colorspaces: + config_data['colorSpaces'].append(cs) + + # *S-Log* to *ACES* + sony_colorspaces = sony.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in sony_colorspaces: + config_data['colorSpaces'].append(cs) + + # ------------------------------------------------------------------------- + # General Colorspaces + # ------------------------------------------------------------------------- + general_colorspaces = general.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in general_colorspaces: + config_data['colorSpaces'].append(cs) + + # The *Raw* colorspace + raw = general.create_raw() + config_data['colorSpaces'].append(raw) + + # Overriding various roles + config_data['roles']['data'] = raw.name + config_data['roles']['reference'] = raw.name + config_data['roles']['texture_paint'] = raw.name + + print('create_config_data - end') + + return config_data + + +def write_config(config, config_path, sanity_check=True): + """ + Writes the configuration to given path. + + Parameters + ---------- + config : Config + *OCIO* configuration. + config_path : str or unicode + Path to write the configuration path. + sanity_check : bool + Performs configuration sanity checking prior to writing it on disk. + + Returns + ------- + bool + Definition success. + """ + + if sanity_check: + try: + config.sanityCheck() + except Exception, e: + print e + print 'Configuration was not written due to a failed Sanity Check' + return + + with open(config_path, mode='w') as fp: + fp.write(config.serialize()) + + +def generate_baked_LUTs(odt_info, + shaper_name, + baked_directory, + config_path, + lut_resolution_3d, + lut_resolution_shaper=1024, + prefix=False): + """ + Generate baked representations of the transforms from the *ACES* *OCIO* + configuration + + Parameters + ---------- + odt_info : array of dicts of str or unicode + Descriptions of the *ACES* Output Transforms + shaper_name : str or unicode + The name of the Shaper function to use when generating LUTs. + Options: Log2, DolbyPQ + baked_directory : str or unicode + The path to use when writing baked LUTs + config_path : str or unicode + The path to the *OCIO* configuration + lut_resolution_3d : int, optional + The resolution of generated 3D LUTs + lut_resolution_shaper : int, optional + The resolution of shaper used as part of some 3D LUTs + prefix : bool, optional + Whether or not colorspace names will use their Family names as prefixes + in the *OCIO* config + + Returns + ------- + None + """ + + odt_info_C = dict(odt_info) + + # Older behavior for *ODTs* that have support for full and legal ranges, + # generating a LUT for both ranges. + """ + # Create two entries for ODTs that have full and legal range support + for odt_ctl_name, odt_values in odt_info.iteritems(): + if odt_values['transformHasFullLegalSwitch']: + odt_name = odt_values['transformUserName'] + + odt_values_legal = dict(odt_values) + odt_values_legal['transformUserName'] = '%s - Legal' % odt_name + odt_info_C['%s - Legal' % odt_ctl_name] = odt_values_legal + + odt_values_full = dict(odt_values) + odt_values_full['transformUserName'] = '%s - Full' % odt_name + odt_info_C['%s - Full' % odt_ctl_name] = odt_values_full + + del (odt_info_C[odt_ctl_name]) + """ + + for odt_ctl_name, odt_values in odt_info_C.iteritems(): + odt_prefix = odt_values['transformUserNamePrefix'] + odt_name = odt_values['transformUserName'] + + if odt_name in ['P3-D60 ST2048 (1000 nits)', 'Rec.2020 ST2048 (1000 nits)']: + odt_shaper = shaper_name.replace("48 nits", "1000 nits") + elif odt_name in ['P3-D60 ST2048 (2000 nits)']: + odt_shaper = shaper_name.replace("48 nits", "2000 nits") + elif odt_name in ['P3-D60 ST2048 (4000 nits)']: + odt_shaper = shaper_name.replace("48 nits", "4000 nits") + else: + odt_shaper = shaper_name + + # *Photoshop* + for input_space in ['ACEScc', 'ACESproxy']: + args = ['--iconfig', config_path, + '-v'] + if prefix: + args += ['--inputspace', 'ACES - %s' % input_space] + args += ['--outputspace', 'Output - %s' % odt_name] + else: + args += ['--inputspace', input_space] + args += ['--outputspace', odt_name] + + args += ['--description', + '%s - %s for %s data' % (odt_prefix, + odt_name, + input_space)] + if prefix: + args += ['--shaperspace', 'Utility - %s' % odt_shaper, + '--shapersize', str(lut_resolution_shaper)] + else: + args += ['--shaperspace', odt_shaper, + '--shapersize', str(lut_resolution_shaper)] + args += ['--cubesize', str(lut_resolution_3d)] + args += ['--format', + 'icc', + os.path.join(baked_directory, + 'photoshop', + '%s for %s.icc' % (odt_name, input_space))] + + bake_lut = Process(description='bake a LUT', + cmd='ociobakelut', + args=args) + bake_lut.execute() + + # *Flame*, *Lustre* + for input_space in ['ACEScc', 'ACESproxy']: + args = ['--iconfig', config_path, + '-v'] + if prefix: + args += ['--inputspace', 'ACES - %s' % input_space] + args += ['--outputspace', 'Output - %s' % odt_name] + else: + args += ['--inputspace', input_space] + args += ['--outputspace', odt_name] + args += ['--description', + '%s - %s for %s data' % ( + odt_prefix, odt_name, input_space)] + if prefix: + args += ['--shaperspace', 'Utility - %s' % odt_shaper, + '--shapersize', str(lut_resolution_shaper)] + else: + args += ['--shaperspace', odt_shaper, + '--shapersize', str(lut_resolution_shaper)] + args += ['--cubesize', str(lut_resolution_3d)] + + fargs = ['--format', + 'flame', + os.path.join( + baked_directory, + 'flame', + '%s for %s Flame.3dl' % (odt_name, input_space))] + bake_lut = Process(description='bake a LUT', + cmd='ociobakelut', + args=(args + fargs)) + bake_lut.execute() + + largs = ['--format', + 'lustre', + os.path.join( + baked_directory, + 'lustre', + '%s for %s Lustre.3dl' % (odt_name, input_space))] + bake_lut = Process(description='bake a LUT', + cmd='ociobakelut', + args=(args + largs)) + bake_lut.execute() + + # *Maya*, *Houdini* + for input_space in ['ACEScg', 'ACES2065-1']: + args = ['--iconfig', config_path, + '-v'] + if prefix: + args += ['--inputspace', 'ACES - %s' % input_space] + args += ['--outputspace', 'Output - %s' % odt_name] + else: + args += ['--inputspace', input_space] + args += ['--outputspace', odt_name] + args += ['--description', + '%s - %s for %s data' % ( + odt_prefix, odt_name, input_space)] + if input_space == 'ACEScg': + lin_shaper_name = '%s - AP1' % odt_shaper + else: + lin_shaper_name = odt_shaper + if prefix: + lin_shaper_name = 'Utility - %s' % lin_shaper_name + args += ['--shaperspace', lin_shaper_name, + '--shapersize', str(lut_resolution_shaper)] + + args += ['--cubesize', str(lut_resolution_3d)] + + margs = ['--format', + 'cinespace', + os.path.join( + baked_directory, + 'maya', + '%s for %s Maya.csp' % (odt_name, input_space))] + bake_lut = Process(description='bake a LUT', + cmd='ociobakelut', + args=(args + margs)) + bake_lut.execute() + + hargs = ['--format', + 'houdini', + os.path.join( + baked_directory, + 'houdini', + '%s for %s Houdini.lut' % (odt_name, input_space))] + bake_lut = Process(description='bake a LUT', + cmd='ociobakelut', + args=(args + hargs)) + bake_lut.execute() + + +def generate_config_directory(config_directory, + bake_secondary_luts=False, + custom_lut_dir=None): + """ + Create the directories needed for configuration generation + + Parameters + ---------- + config_directory : str or unicode + The base config directory + bake_secondary_luts : bool, optional + Whether or not to create directories for baked LUTs + custom_lut_dir : bool, optional + Whether or not to create directories for custom Look LUTs + + Returns + ------- + None + """ + + lut_directory = os.path.join(config_directory, 'luts') + dirs = [config_directory, lut_directory] + + if bake_secondary_luts: + dirs.extend([os.path.join(config_directory, 'baked'), + os.path.join(config_directory, 'baked', 'flame'), + os.path.join(config_directory, 'baked', 'photoshop'), + os.path.join(config_directory, 'baked', 'houdini'), + os.path.join(config_directory, 'baked', 'lustre'), + os.path.join(config_directory, 'baked', 'maya')]) + + if custom_lut_dir: + dirs.append(os.path.join(config_directory, 'custom')) + + for d in dirs: + not os.path.exists(d) and os.mkdir(d) + + return lut_directory + + +def generate_config(aces_ctl_directory, + config_directory, + lut_resolution_1d=4096, + lut_resolution_3d=64, + bake_secondary_luts=True, + multiple_displays=False, + look_info=None, + copy_custom_luts=True, + cleanup=True, + prefix_colorspaces_with_family_names=True, + shaper_base_name='Log2'): + """ + Generates LUTs, matrices and configuration data and then creates the + *ACES* configuration. + + Parameters + ---------- + aces_ctl_directory : str or unicode + The path to the aces 'transforms/ctl/utilities' + config_directory : str or unicode + The directory that will hold the generated configuration and LUTs + lut_resolution_1d : int, optional + The resolution of generated 1D LUTs + lut_resolution_3d : int, optional + The resolution of generated 3D LUTs + bake_secondary_luts : bool, optional + Whether or not to create directories for baked LUTs + multiple_displays : bool, optional + Whether to create a single display named *ACES* with Views for each + Output Transform or multiple displays, one for each Output Transform + look_info : array of str or unicode, optional + Paths and names for look data + copy_custom_luts : bool, optional + Whether to reference custom look LUTs directly or to copy them into a + directory within the generated configuration + cleanup : bool, optional + Whether or not to clean up the intermediate images + prefix_colorspaces_with_family_names : bool, optional + Whether or not colorspace names will use their Family names as prefixes + in the *OCIO* config + shaper_base_name : str or unicode + The name of the Shaper function to use when generating LUTs. + Options: Log2, DolbyPQ + + Returns + ------- + bool + Success or failure of configuration generation process + """ + + if look_info is None: + look_info = [] + + custom_lut_dir = None + if copy_custom_luts: + custom_lut_dir = os.path.join(config_directory, 'custom') + + lut_directory = generate_config_directory(config_directory, + bake_secondary_luts, + custom_lut_dir) + odt_info = aces.get_ODTs_info(aces_ctl_directory) + lmt_info = aces.get_LMTs_info(aces_ctl_directory) + + if shaper_base_name == 'DolbyPQ': + shaper_name = 'Dolby PQ 48 nits Shaper' + else: + shaper_name = 'Log2 48 nits Shaper' + + config_data = create_config_data(odt_info, + lmt_info, + shaper_name, + aces_ctl_directory, + lut_directory, + lut_resolution_1d, + lut_resolution_3d, + cleanup) + + print('Creating config - with prefixes, with aliases') + config = create_config(config_data, + prefix=prefix_colorspaces_with_family_names, + aliases=True, + multiple_displays=multiple_displays, + look_info=look_info, + custom_lut_dir=custom_lut_dir) + print('\n\n\n') + + write_config(config, + os.path.join(config_directory, 'config.ocio')) + + if bake_secondary_luts: + generate_baked_LUTs(odt_info, + shaper_name, + os.path.join(config_directory, 'baked'), + os.path.join(config_directory, 'config.ocio'), + lut_resolution_3d, + lut_resolution_1d, + prefix=prefix_colorspaces_with_family_names) + + return True + + +def main(): + """ + A simple main that allows the user to exercise the various functions + defined in this file + + Parameters + ---------- + None + + Returns + ------- + None + """ + + import optparse + + usage = '%prog [options]\n' + usage += '\n' + usage += 'An OCIO config generation script for ACES 1.0\n' + usage += '\n' + usage += 'Command line examples' + usage += '\n' + usage += ('Create a GUI-friendly ACES 1.0 config with no secondary, ' + 'baked LUTs : \n') + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '--dontBakeSecondaryLUTs') + usage += '\n' + usage += 'Create a more OCIO-compliant ACES 1.0 config : \n' + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '--createMultipleDisplays') + usage += '\n' + usage += '\n' + usage += 'Adding custom looks' + usage += '\n' + usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style CDL ' + '(will be applied in the ACEScc colorspace): \n') + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '\n\t\t--addACESLookCDL ACESCDLName ' + '/path/to/SampleCDL.ccc cc03345') + usage += '\n' + usage += 'Create a GUI-friendly ACES 1.0 config with an general CDL: \n' + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '\n\t\t--addCustomLookCDL CustomCDLName "ACES - ACEScc" ' + '/path/to/SampleCDL.ccc cc03345') + usage += '\n' + usage += ('\tIn this example, the CDL will be applied in the ' + 'ACEScc colorspace, but the user could choose other spaces ' + 'by changing the argument after the name of the look. \n') + usage += '\n' + usage += ('Create a GUI-friendly ACES 1.0 config with an ACES-style LUT ' + '(will be applied in the ACEScc colorspace): \n') + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '\n\t\t--addACESLookLUT ACESLUTName ' + '/path/to/SampleCDL.ccc cc03345') + usage += '\n' + usage += 'Create a GUI-friendly ACES 1.0 config with an general LUT: \n' + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '\n\t\t--addCustomLookLUT CustomLUTName "ACES - ACEScc" ' + '/path/to/SampleCDL.ccc cc03345') + usage += '\n' + usage += ('\tIn this example, the LUT will be applied in the ' + 'ACEScc colorspace, but the user could choose other spaces ' + 'by changing the argument after the name of the look. \n') + usage += '\n' + usage += ('Create a GUI-friendly ACES 1.0 config using the Dolby PQ ' + 'transfer function as the shaper: \n') + usage += ('\tcreate_aces_config -a /path/to/aces-dev/transforms/ctl ' + '--lutResolution1d 1024 --lutResolution3d 33 -c aces_1.0.0 ' + '--shaper DolbyPQ') + usage += '\n' + + look_info = [] + + def look_info_callback(option, opt_str, value, parser): + print('look_info_callback') + print(option, opt_str, value, parser) + if opt_str == '--addCustomLookCDL': + look_info.append(value) + elif opt_str == '--addCustomLookLUT': + look_info.append(value) + elif opt_str == '--addACESLookCDL': + look_info.append([value[0], 'ACES - ACEScc', value[1], value[2]]) + elif opt_str == '--addACESLookLUT': + look_info.append([value[0], 'ACES - ACEScc', value[1]]) + + p = optparse.OptionParser(description='', + prog='create_aces_config', + version='create_aces_config 1.0', + usage=usage) + p.add_option('--acesCTLDir', '-a', default=os.environ.get( + ACES_OCIO_CTL_DIRECTORY_ENVIRON, None)) + p.add_option('--configDir', '-c', default=os.environ.get( + ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None)) + p.add_option('--lutResolution1d', default=4096) + p.add_option('--lutResolution3d', default=64) + p.add_option('--dontBakeSecondaryLUTs', action='store_true', default=False) + p.add_option('--keepTempImages', action='store_true', default=False) + + p.add_option('--createMultipleDisplays', action='store_true', + default=False) + + p.add_option('--addCustomLookLUT', '', type='string', nargs=3, + action='callback', callback=look_info_callback) + p.add_option('--addCustomLookCDL', '', type='string', nargs=4, + action='callback', callback=look_info_callback) + p.add_option('--addACESLookLUT', '', type='string', nargs=2, + action='callback', callback=look_info_callback) + p.add_option('--addACESLookCDL', '', type='string', nargs=3, + action='callback', callback=look_info_callback) + p.add_option('--copyCustomLUTs', action='store_true', default=False) + + p.add_option('--shaper', '-s', default='Log2') + + options, arguments = p.parse_args() + + aces_ctl_directory = options.acesCTLDir + config_directory = options.configDir + lut_resolution_1d = int(options.lutResolution1d) + lut_resolution_3d = int(options.lutResolution3d) + bake_secondary_luts = not options.dontBakeSecondaryLUTs + cleanup_temp_images = not options.keepTempImages + multiple_displays = options.createMultipleDisplays + copy_custom_luts = options.copyCustomLUTs + shaper_base_name = options.shaper + prefix = True + + print(look_info) + + print('command line : \n%s\n' % ' '.join(sys.argv)) + + assert aces_ctl_directory is not None, ( + 'process: No "{0}" environment variable defined or no "ACES CTL" ' + 'directory specified'.format( + ACES_OCIO_CTL_DIRECTORY_ENVIRON)) + + assert config_directory is not None, ( + 'process: No "{0}" environment variable defined or no configuration ' + 'directory specified'.format( + ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON)) + + return generate_config(aces_ctl_directory, + config_directory, + lut_resolution_1d, + lut_resolution_3d, + bake_secondary_luts, + multiple_displays, + look_info, + copy_custom_luts, + cleanup_temp_images, + prefix, + shaper_base_name) + + +if __name__ == '__main__': + main()