From: hpd Date: Fri, 16 Jan 2015 20:35:15 +0000 (-0800) Subject: Merge pull request #1 from colour-science/code_style X-Git-Url: http://users.mur.at/ms/git/gitweb/?p=OpenColorIO-Configs.git;a=commitdiff_plain;h=355167cd6a9c220f9828791d46620548af0798bb;hp=9aeb815e08619fa78309415b6b5b96694a811440 Merge pull request #1 from colour-science/code_style PR: Implement basic unit tests and apply PEP8 code formatting. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c10666e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*.pyc diff --git a/aces_1.0.0/python/aces_ocio/__init__.py b/aces_1.0.0/python/aces_ocio/__init__.py new file mode 100644 index 0000000..55576c9 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/__init__.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +ACES OCIO +========= + +Usage +----- + +Python +****** + +>>> import sys +>>> sys.path.append("/path/to/script") +>>> import create_aces_config as cac +>>> acesReleaseCTLDir = "/path/to/github/checkout/releases/v0.7.1/transforms/ctl" +>>> configDir = "/path/to/config/dir" +>>> cac.createACESConfig(acesReleaseCTLDir, configDir, 1024, 33, True) + +Command Line +************ + +From the directory with 'create_aces_config.py': + +$ python create_aces_config.py -a "/path/to/github/checkout/releases/v0.7.1/transforms/ctl" -c "/path/to/config/dir" --lut_resolution_1d 1024 --lut_resolution_3d 33 --keepTempImages + +Build +----- + +Mac OS X - Required packages +**************************** + +OpenColorIO +___________ + +$ brew install -vd opencolorio --with-python + +OpenImageIO +___________ + +$ brew tap homebrew/science + +Optional Dependencies +_____________________ + +$ brew install -vd libRaw +$ brew install -vd OpenCV +$ brew install -vd openimageio --with-python + +CTL +___ + +$ brew install -vd CTL + +OpenColorIO +___________ + +*ociolutimage* will build with *openimageio* installed. + +$ brew uninstall -vd opencolorio +$ brew install -vd opencolorio --with-python +""" + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__major_version__ = '1' +__minor_version__ = '0' +__change_version__ = '0' +__version__ = '.'.join((__major_version__, + __minor_version__, + __change_version__)) \ No newline at end of file diff --git a/aces_1.0.0/python/aces_ocio/create_aces_config.py b/aces_1.0.0/python/aces_ocio/create_aces_config.py new file mode 100755 index 0000000..a18ba48 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/create_aces_config.py @@ -0,0 +1,1845 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines objects creating the *ACES* configuration. +""" + +import math +import numpy +import os +import pprint +import shutil +import string +import sys + +# TODO: This restores the capability of running the script without having +# added the package to PYTHONPATH, this is ugly and should ideally replaced by +# dedicated executable in a /bin directory. +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + +import PyOpenColorIO as OCIO + +import aces_ocio.create_arri_colorspaces as arri +import aces_ocio.create_canon_colorspaces as canon +import aces_ocio.create_red_colorspaces as red +import aces_ocio.create_sony_colorspaces as sony +from aces_ocio.generate_lut import ( + generate_1d_LUT_from_CTL, + generate_3d_LUT_from_CTL, + write_SPI_1d) +from aces_ocio.process import Process +from aces_ocio.utilities import ColorSpace, mat44_from_mat33 + +__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_default_roles', + 'write_config', + 'generate_OCIO_transform', + 'create_config', + 'generate_LUTs', + 'generate_baked_LUTs', + 'create_config_dir', + 'get_transform_info', + 'get_ODT_info', + 'get_LMT_info', + 'create_ACES_config', + 'main'] + +ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY' +ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY' + + +def set_config_default_roles(config, + color_picking="", + color_timing="", + compositing_log="", + data="", + default="", + matte_paint="", + reference="", + scene_linear="", + texture_paint=""): + """ + Sets given *OCIO* configuration default roles. + + Parameters + ---------- + config : config + *OCIO* configuration. + color_picking : str or unicode + Color picking role title. + color_timing : str or unicode + Color timing role title. + compositing_log : str or unicode + Compositing log role title. + data : str or unicode + Data role title. + default : str or unicode + Default role title. + matte_paint : str or unicode + Matte painting role title. + reference : str or unicode + Reference role title. + scene_linear : str or unicode + Scene linear role title. + texture_paint : str or unicode + Texture painting role title. + + Returns + ------- + bool + Definition success. + """ + + if color_picking: + config.setRole(OCIO.Constants.ROLE_COLOR_PICKING, color_picking) + if color_timing: + config.setRole(OCIO.Constants.ROLE_COLOR_TIMING, color_timing) + if compositing_log: + config.setRole(OCIO.Constants.ROLE_COMPOSITING_LOG, compositing_log) + if data: + config.setRole(OCIO.Constants.ROLE_DATA, data) + if default: + config.setRole(OCIO.Constants.ROLE_DEFAULT, default) + if matte_paint: + config.setRole(OCIO.Constants.ROLE_MATTE_PAINT, matte_paint) + if reference: + config.setRole(OCIO.Constants.ROLE_REFERENCE, reference) + if scene_linear: + config.setRole(OCIO.Constants.ROLE_SCENE_LINEAR, scene_linear) + if texture_paint: + config.setRole(OCIO.Constants.ROLE_TEXTURE_PAINT, texture_paint) + + return True + + +def write_config(config, config_path, sanity_check=True): + """ + Writes the configuration to given path. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if sanity_check: + try: + config.sanityCheck() + except Exception, e: + print e + print "Configuration was not written due to a failed Sanity Check" + return + # sys.exit() + + file_handle = open(config_path, mode='w') + file_handle.write(config.serialize()) + file_handle.close() + + +def generate_OCIO_transform(transforms): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # print("Generating transforms") + + interpolation_options = { + 'linear': OCIO.Constants.INTERP_LINEAR, + 'nearest': OCIO.Constants.INTERP_NEAREST, + 'tetrahedral': OCIO.Constants.INTERP_TETRAHEDRAL + } + direction_options = { + 'forward': OCIO.Constants.TRANSFORM_DIR_FORWARD, + 'inverse': OCIO.Constants.TRANSFORM_DIR_INVERSE + } + + ocio_transforms = [] + + for transform in transforms: + if transform['type'] == 'lutFile': + ocio_transform = OCIO.FileTransform( + src=transform['path'], + interpolation=interpolation_options[ + transform['interpolation']], + direction=direction_options[transform['direction']]) + ocio_transforms.append(ocio_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) + elif transform['type'] == 'exponent': + ocio_transform = OCIO.ExponentTransform() + ocio_transform.setValue(transform['value']) + ocio_transforms.append(ocio_transform) + elif transform['type'] == 'log': + ocio_transform = OCIO.LogTransform( + base=transform['base'], + direction=direction_options[transform['direction']]) + + ocio_transforms.append(ocio_transform) + else: + print("Ignoring unknown transform type : %s" % transform['type']) + + # Build a group transform if necessary + if len(ocio_transforms) > 1: + transform_G = OCIO.GroupTransform() + for transform in ocio_transforms: + transform_G.push_back(transform) + transform = transform_G + + # Or take the first transform from the list + else: + transform = ocio_transforms[0] + + return transform + + +def create_config(config_data, nuke=False): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # Create the config + config = OCIO.Config() + + # + # Set config wide values + # + config.setDescription("An ACES config generated from python") + config.setSearchPath("luts") + + # + # Define the reference color space + # + reference_data = config_data['referenceColorSpace'] + print("Adding the reference color space : %s" % reference_data.name) + + # Create a color space + 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) + + # Add to config + config.addColorSpace(reference) + + # + # Create the rest of the color spaces + # + for colorspace in sorted(config_data['colorSpaces']): + print("Creating new color space : %s" % colorspace.name) + + ocio_colorspace = OCIO.ColorSpace( + name=colorspace.name, + bitDepth=colorspace.bit_depth, + description=colorspace.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("Generating To-Reference transforms") + ocio_transform = generate_OCIO_transform( + colorspace.to_reference_transforms) + ocio_colorspace.setTransform( + ocio_transform, + OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) + + if colorspace.from_reference_transforms != []: + print("Generating From-Reference transforms") + ocio_transform = generate_OCIO_transform( + colorspace.from_reference_transforms) + ocio_colorspace.setTransform( + ocio_transform, + OCIO.Constants.COLORSPACE_DIR_FROM_REFERENCE) + + config.addColorSpace(ocio_colorspace) + + print("") + + # + # Define the views and displays + # + displays = [] + views = [] + + # Generic display and view setup + if not nuke: + for display, view_list in config_data['displays'].iteritems(): + for view_name, colorspace in view_list.iteritems(): + config.addDisplay(display, view_name, colorspace.name) + if not (view_name in views): + views.append(view_name) + displays.append(display) + # A Nuke specific set of views and displays + # + # XXX + # A few names: Output Transform, ACES, ACEScc, are hard-coded here. + # Would be better to automate. + # + else: + for display, view_list in config_data['displays'].iteritems(): + for view_name, colorspace in view_list.iteritems(): + if (view_name == 'Output Transform'): + view_name = 'View' + config.addDisplay(display, view_name, colorspace.name) + if not (view_name in views): + views.append(view_name) + displays.append(display) + + config.addDisplay('linear', 'View', 'ACES2065-1') + displays.append('linear') + config.addDisplay('log', 'View', 'ACEScc') + displays.append('log') + + # Set active displays and views + config.setActiveDisplays(','.join(sorted(displays))) + config.setActiveViews(','.join(views)) + + # + # Need to generalize this at some point + # + + # Add Default Roles + set_config_default_roles( + config, + color_picking=reference.getName(), + color_timing=reference.getName(), + compositing_log=reference.getName(), + data=reference.getName(), + default=reference.getName(), + matte_paint=reference.getName(), + reference=reference.getName(), + scene_linear=reference.getName(), + texture_paint=reference.getName()) + + # Check to make sure we didn't screw something up + config.sanityCheck() + + return config + + +def generate_LUTs(odt_info, + lmt_info, + shaper_name, + aces_CTL_directory, + lut_directory, + lut_resolution_1d=4096, + lut_resolution_3d=64, + cleanup=True): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + dict + Colorspaces and transforms converting between those colorspaces and + the reference colorspace, *ACES*. + """ + + print("generateLUTs - begin") + config_data = {} + + # + # Define the reference color space + # + ACES = ColorSpace('ACES2065-1') + ACES.description = ( + 'The Academy Color Encoding System reference color space') + ACES.equality_group = '' + ACES.family = 'ACES' + ACES.is_data = False + ACES.allocation_type = OCIO.Constants.ALLOCATION_LG2 + ACES.allocation_vars = [-15, 6] + + config_data['referenceColorSpace'] = ACES + + # + # Define the displays + # + config_data['displays'] = {} + + # + # Define the other color spaces + # + config_data['colorSpaces'] = [] + + # Matrix converting ACES AP1 primaries to AP0 + ACES_AP1_to_AP0 = [0.6954522414, 0.1406786965, 0.1638690622, + 0.0447945634, 0.8596711185, 0.0955343182, + -0.0055258826, 0.0040252103, 1.0015006723] + + # Matrix converting ACES AP0 primaries to XYZ + ACES_AP0_to_XYZ = [0.9525523959, 0.0000000000, 0.0000936786, + 0.3439664498, 0.7281660966, -0.0721325464, + 0.0000000000, 0.0000000000, 1.0088251844] + + # + # ACEScc + # + def create_ACEScc(name='ACEScc', + min_value=0.0, + max_value=1.0, + input_scale=1.0): + cs = ColorSpace(name) + cs.description = "The %s color space" % name + cs.equality_group = '' + cs.family = 'ACES' + cs.is_data = False + + ctls = [ + '%s/ACEScc/ACEScsc.ACEScc_to_ACES.a1.0.0.ctl' % aces_CTL_directory, + # This transform gets back to the AP1 primaries + # Useful as the 1d LUT is only covering the transfer function + # The primaries switch is covered by the matrix below + '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % aces_CTL_directory + ] + lut = "%s_to_ACES.spi1d" % name + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_1d_LUT_from_CTL( + lut_directory + "/" + lut, + ctls, + lut_resolution_1d, + 'float', + input_scale, + 1.0, + {}, + cleanup, + aces_CTL_directory, + min_value, + max_value) + + cs.to_reference_transforms = [] + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + # AP1 primaries to AP0 primaries + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(ACES_AP1_to_AP0), + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + ACEScc = create_ACEScc() + config_data['colorSpaces'].append(ACEScc) + + # + # ACESproxy + # + def create_ACESproxy(name='ACESproxy'): + cs = ColorSpace(name) + cs.description = "The %s color space" % name + cs.equality_group = '' + cs.family = 'ACES' + cs.is_data = False + + ctls = [ + '%s/ACESproxy/ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl' % ( + aces_CTL_directory), + # This transform gets back to the AP1 primaries + # Useful as the 1d LUT is only covering the transfer function + # The primaries switch is covered by the matrix below + '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % aces_CTL_directory + ] + lut = "%s_to_aces.spi1d" % name + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_1d_LUT_from_CTL( + lut_directory + "/" + lut, + ctls, + lut_resolution_1d, + 'uint16', + 64.0, + 1.0, + {}, + cleanup, + aces_CTL_directory) + + cs.to_reference_transforms = [] + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + # AP1 primaries to AP0 primaries + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(ACES_AP1_to_AP0), + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + ACESproxy = create_ACESproxy() + config_data['colorSpaces'].append(ACESproxy) + + # + # ACEScg + # + def create_ACEScg(name='ACEScg'): + cs = ColorSpace(name) + cs.description = "The %s color space" % name + cs.equality_group = '' + cs.family = 'ACES' + cs.is_data = False + + cs.to_reference_transforms = [] + + # AP1 primaries to AP0 primaries + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(ACES_AP1_to_AP0), + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + ACEScg = create_ACEScg() + config_data['colorSpaces'].append(ACEScg) + + # + # ADX + # + def create_ADX(bit_depth=10, name='ADX'): + name = "%s%s" % (name, bit_depth) + cs = ColorSpace(name) + cs.description = "%s color space - used for film scans" % name + cs.equality_group = '' + cs.family = 'ADX' + cs.is_data = False + + if bit_depth == 10: + cs.bit_depth = bit_depth = OCIO.Constants.BIT_DEPTH_UINT10 + adx_to_cdd = [1023.0 / 500.0, 0.0, 0.0, 0.0, + 0.0, 1023.0 / 500.0, 0.0, 0.0, + 0.0, 0.0, 1023.0 / 500.0, 0.0, + 0.0, 0.0, 0.0, 1.0] + offset = [-95.0 / 500.0, -95.0 / 500.0, -95.0 / 500.0, 0.0] + elif bit_depth == 16: + cs.bit_depth = bit_depth = OCIO.Constants.BIT_DEPTH_UINT16 + adx_to_cdd = [65535.0 / 8000.0, 0.0, 0.0, 0.0, + 0.0, 65535.0 / 8000.0, 0.0, 0.0, + 0.0, 0.0, 65535.0 / 8000.0, 0.0, + 0.0, 0.0, 0.0, 1.0] + offset = [-1520.0 / 8000.0, -1520.0 / 8000.0, -1520.0 / 8000.0, + 0.0] + + cs.to_reference_transforms = [] + + # Convert from ADX to Channel-Dependent Density + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': adx_to_cdd, + 'offset': offset, + 'direction': 'forward' + }) + + # Convert from Channel-Dependent Density to Channel-Independent Density + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.75573, 0.22197, 0.02230, 0, + 0.05901, 0.96928, -0.02829, 0, + 0.16134, 0.07406, 0.76460, 0, + 0.0, 0.0, 0.0, 1.0], + 'direction': 'forward' + }) + + # Copied from Alex Fry's adx_cid_to_rle.py + def create_CID_to_RLE_LUT(): + def interpolate_1D(x, xp, fp): + return numpy.interp(x, xp, fp) + + LUT_1D_xp = [-0.190000000000000, + 0.010000000000000, + 0.028000000000000, + 0.054000000000000, + 0.095000000000000, + 0.145000000000000, + 0.220000000000000, + 0.300000000000000, + 0.400000000000000, + 0.500000000000000, + 0.600000000000000] + + LUT_1D_fp = [-6.000000000000000, + -2.721718645000000, + -2.521718645000000, + -2.321718645000000, + -2.121718645000000, + -1.921718645000000, + -1.721718645000000, + -1.521718645000000, + -1.321718645000000, + -1.121718645000000, + -0.926545676714876] + + REF_PT = ((7120.0 - 1520.0) / 8000.0 * (100.0 / 55.0) - + math.log(0.18, 10.0)) + + def cid_to_rle(x): + if x <= 0.6: + return interpolate_1D(x, LUT_1D_xp, LUT_1D_fp) + return (100.0 / 55.0) * x - REF_PT + + def fit(value, from_min, from_max, to_min, to_max): + if from_min == from_max: + raise ValueError("from_min == from_max") + return (value - from_min) / (from_max - from_min) * ( + to_max - to_min) + to_min + + NUM_SAMPLES = 2 ** 12 + RANGE = (-0.19, 3.0) + data = [] + for i in xrange(NUM_SAMPLES): + x = i / (NUM_SAMPLES - 1.0) + x = fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) + data.append(cid_to_rle(x)) + + lut = 'ADX_CID_to_RLE.spi1d' + write_SPI_1d(lut_directory + "/" + lut, RANGE[0], RANGE[1], + data, + NUM_SAMPLES, 1) + + return lut + + # Convert Channel Independent Density values to Relative Log Exposure + # values. + lut = create_CID_to_RLE_LUT() + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + # Convert Relative Log Exposure values to Relative Exposure values + cs.to_reference_transforms.append({ + 'type': 'log', + 'base': 10, + 'direction': 'inverse' + }) + + # Convert Relative Exposure values to ACES values + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.72286, 0.12630, 0.15084, 0, + 0.11923, 0.76418, 0.11659, 0, + 0.01427, 0.08213, 0.90359, 0, + 0.0, 0.0, 0.0, 1.0], + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + ADX10 = create_ADX(bit_depth=10) + config_data['colorSpaces'].append(ADX10) + + ADX16 = create_ADX(bit_depth=16) + config_data['colorSpaces'].append(ADX16) + + # + # Camera Input Transforms + # + + # RED color spaces to ACES + red_colorspaces = red.create_colorspaces(lut_directory, lut_resolution_1d) + for cs in red_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) + + # S-Log to ACES + sony_colorSpaces = sony.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in sony_colorSpaces: + config_data['colorSpaces'].append(cs) + + # Log-C to ACES + arri_colorSpaces = arri.create_colorspaces(lut_directory, + lut_resolution_1d) + for cs in arri_colorSpaces: + config_data['colorSpaces'].append(cs) + + # + # Generic log transform + # + def create_generic_log(name='log', + min_value=0.0, + max_value=1.0, + input_scale=1.0, + middle_grey=0.18, + min_exposure=-6.0, + max_exposure=6.5, + lut_resolution_1d=lut_resolution_1d): + cs = ColorSpace(name) + cs.description = "The %s color space" % name + cs.equality_group = name + cs.family = 'Utility' + cs.is_data = False + + ctls = [ + '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl' % ( + aces_CTL_directory)] + lut = "%s_to_aces.spi1d" % name + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_1d_LUT_from_CTL( + lut_directory + "/" + lut, + ctls, + lut_resolution_1d, + 'float', + input_scale, + 1.0, + { + 'middleGrey': middle_grey, + 'minExposure': min_exposure, + 'maxExposure': max_exposure + }, + cleanup, + aces_CTL_directory, + min_value, + max_value) + + cs.to_reference_transforms = [] + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + # + # ACES LMTs + # + def create_ACES_LMT(lmt_name, + lmt_values, + shaper_info, + lut_resolution_1d=1024, + lut_resolution_3d=64, + cleanup=True): + cs = ColorSpace("%s" % lmt_name) + cs.description = "The ACES Look Transform: %s" % lmt_name + cs.equality_group = '' + cs.family = 'Look' + cs.is_data = False + + pprint.pprint(lmt_values) + + # + # Generate the shaper transform + # + (shaper_name, + shaper_to_ACES_CTL, + shaper_from_ACES_CTL, + shaper_input_scale, + shaper_params) = shaper_info + + shaper_lut = "%s_to_aces.spi1d" % shaper_name + if (not os.path.exists(lut_directory + "/" + shaper_lut)): + ctls = [shaper_to_ACES_CTL % aces_CTL_directory] + + # Remove spaces and parentheses + shaper_lut = shaper_lut.replace( + ' ', '_').replace(')', '_').replace('(', '_') + + generate_1d_LUT_from_CTL( + lut_directory + "/" + shaper_lut, + ctls, + lut_resolution_1d, + 'float', + 1.0 / shaper_input_scale, + 1.0, + shaper_params, + cleanup, + aces_CTL_directory) + + shaper_OCIO_transform = { + 'type': 'lutFile', + 'path': shaper_lut, + 'interpolation': 'linear', + 'direction': 'inverse' + } + + # + # Generate the forward transform + # + cs.from_reference_transforms = [] + + if 'transformCTL' in lmt_values: + ctls = [ + shaper_to_ACES_CTL % aces_CTL_directory, + '%s/%s' % (aces_CTL_directory, lmt_values['transformCTL']) + ] + lut = "%s.%s.spi3d" % (shaper_name, lmt_name) + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_3d_LUT_from_CTL( + lut_directory + "/" + lut, + ctls, + lut_resolution_3d, + 'float', + 1.0 / shaper_input_scale, + 1.0, + shaper_params, + cleanup, + aces_CTL_directory) + + cs.from_reference_transforms.append(shaper_OCIO_transform) + cs.from_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + + # + # Generate the inverse transform + # + cs.to_reference_transforms = [] + + if 'transformCTLInverse' in lmt_values: + ctls = [ + '%s/%s' % ( + aces_CTL_directory, odt_values['transformCTLInverse']), + shaper_from_ACES_CTL % aces_CTL_directory + ] + lut = "Inverse.%s.%s.spi3d" % (odt_name, shaper_name) + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_3d_LUT_from_CTL( + lut_directory + "/" + lut, + ctls, + lut_resolution_3d, + 'half', + 1.0, + shaper_input_scale, + shaper_params, + cleanup, + aces_CTL_directory) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + + shaper_inverse = shaper_OCIO_transform.copy() + shaper_inverse['direction'] = 'forward' + cs.to_reference_transforms.append(shaper_inverse) + + return cs + + # + # LMT Shaper + # + + lmt_lut_resolution_1d = max(4096, lut_resolution_1d) + lmt_lut_resolution_3d = max(65, lut_resolution_3d) + + # Log 2 shaper + lmt_shaper_name = 'LMT Shaper' + lmt_params = { + 'middleGrey': 0.18, + 'minExposure': -10.0, + 'maxExposure': 6.5 + } + lmt_shaper = create_generic_log(name=lmt_shaper_name, + middle_grey=lmt_params['middleGrey'], + min_exposure=lmt_params['minExposure'], + max_exposure=lmt_params['maxExposure'], + lut_resolution_1d=lmt_lut_resolution_1d) + config_data['colorSpaces'].append(lmt_shaper) + + shaper_input_scale_generic_log2 = 1.0 + + # Log 2 shaper name and CTL transforms bundled up + lmt_shaper_data = [ + lmt_shaper_name, + '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl', + '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl', + shaper_input_scale_generic_log2, + lmt_params + ] + + sorted_LMTs = sorted(lmt_info.iteritems(), key=lambda x: x[1]) + print(sorted_LMTs) + for lmt in sorted_LMTs: + (lmt_name, lmt_values) = lmt + cs = create_ACES_LMT( + lmt_values['transformUserName'], + lmt_values, + lmt_shaper_data, + lmt_lut_resolution_1d, + lmt_lut_resolution_3d, + cleanup) + config_data['colorSpaces'].append(cs) + + # + # ACES RRT with the supplied ODT + # + def create_ACES_RRT_plus_ODT(odt_name, + odt_values, + shaper_info, + lut_resolution_1d=1024, + lut_resolution_3d=64, + cleanup=True): + cs = ColorSpace("%s" % odt_name) + cs.description = "%s - %s Output Transform" % ( + odt_values['transformUserNamePrefix'], odt_name) + cs.equality_group = '' + cs.family = 'Output' + cs.is_data = False + + pprint.pprint(odt_values) + + # + # Generate the shaper transform + # + # if 'shaperCTL' in odtValues: + (shaper_name, + shaper_to_ACES_CTL, + shaper_from_ACES_CTL, + shaper_input_scale, + shaper_params) = shaper_info + + if 'legalRange' in odt_values: + shaper_params['legalRange'] = odt_values['legalRange'] + else: + shaper_params['legalRange'] = 0 + + shaper_lut = "%s_to_aces.spi1d" % shaper_name + if (not os.path.exists(lut_directory + "/" + shaper_lut)): + ctls = [shaper_to_ACES_CTL % aces_CTL_directory] + + # Remove spaces and parentheses + shaper_lut = shaper_lut.replace( + ' ', '_').replace(')', '_').replace('(', '_') + + generate_1d_LUT_from_CTL( + lut_directory + "/" + shaper_lut, + ctls, + lut_resolution_1d, + 'float', + 1.0 / shaper_input_scale, + 1.0, + shaper_params, + cleanup, + aces_CTL_directory) + + shaper_OCIO_transform = { + 'type': 'lutFile', + 'path': shaper_lut, + 'interpolation': 'linear', + 'direction': 'inverse' + } + + # + # Generate the forward transform + # + cs.from_reference_transforms = [] + + if 'transformLUT' in odt_values: + # Copy into the lut dir + transform_LUT_file_name = os.path.basename( + odt_values['transformLUT']) + lut = lut_directory + "/" + transform_LUT_file_name + shutil.copy(odt_values['transformLUT'], lut) + + cs.from_reference_transforms.append(shaper_OCIO_transform) + cs.from_reference_transforms.append({ + 'type': 'lutFile', + 'path': transform_LUT_file_name, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + elif 'transformCTL' in odt_values: + # shaperLut + + ctls = [ + shaper_to_ACES_CTL % aces_CTL_directory, + '%s/rrt/RRT.a1.0.0.ctl' % aces_CTL_directory, + '%s/odt/%s' % (aces_CTL_directory, odt_values['transformCTL']) + ] + lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaper_name, odt_name) + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_3d_LUT_from_CTL(lut_directory + "/" + lut, + # shaperLUT, + ctls, + lut_resolution_3d, + 'float', + 1.0 / shaper_input_scale, + 1.0, + shaper_params, + cleanup, + aces_CTL_directory) + + cs.from_reference_transforms.append(shaper_OCIO_transform) + cs.from_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + + # + # Generate the inverse transform + # + cs.to_reference_transforms = [] + + if 'transformLUTInverse' in odt_values: + # Copy into the lut dir + transform_LUT_inverse_file_name = os.path.basename( + odt_values['transformLUTInverse']) + lut = lut_directory + "/" + transform_LUT_inverse_file_name + shutil.copy(odt_values['transformLUTInverse'], lut) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': transform_LUT_inverse_file_name, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + + shaper_inverse = shaper_OCIO_transform.copy() + shaper_inverse['direction'] = 'forward' + cs.to_reference_transforms.append(shaper_inverse) + elif 'transformCTLInverse' in odt_values: + ctls = [ + '%s/odt/%s' % ( + aces_CTL_directory, odt_values['transformCTLInverse']), + '%s/rrt/InvRRT.a1.0.0.ctl' % aces_CTL_directory, + shaper_from_ACES_CTL % aces_CTL_directory + ] + lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odt_name, shaper_name) + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + generate_3d_LUT_from_CTL( + lut_directory + "/" + lut, + # None, + ctls, + lut_resolution_3d, + 'half', + 1.0, + shaper_input_scale, + shaper_params, + cleanup, + aces_CTL_directory) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'tetrahedral', + 'direction': 'forward' + }) + + shaper_inverse = shaper_OCIO_transform.copy() + shaper_inverse['direction'] = 'forward' + cs.to_reference_transforms.append(shaper_inverse) + + return cs + + # + # RRT/ODT shaper options + # + shaper_data = {} + + # Log 2 shaper + log2_shaper_name = shaper_name + log2_params = { + 'middleGrey': 0.18, + 'minExposure': -6.0, + 'maxExposure': 6.5 + } + log2_shaper = create_generic_log( + name=log2_shaper_name, + middle_grey=log2_params['middleGrey'], + min_exposure=log2_params['minExposure'], + max_exposure=log2_params['maxExposure']) + config_data['colorSpaces'].append(log2_shaper) + + shaper_input_scale_generic_log2 = 1.0 + + # Log 2 shaper name and CTL transforms bundled up + log2_shaper_data = [ + log2_shaper_name, + '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl', + '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl', + shaper_input_scale_generic_log2, + log2_params + ] + + shaper_data[log2_shaper_name] = log2_shaper_data + + # + # Shaper that also includes the AP1 primaries + # - Needed for some LUT baking steps + # + log2_shaper_AP1 = create_generic_log( + name=log2_shaper_name, + middle_grey=log2_params['middleGrey'], + min_exposure=log2_params['minExposure'], + max_exposure=log2_params['maxExposure']) + log2_shaper_AP1.name = "%s - AP1" % log2_shaper_AP1.name + # AP1 primaries to AP0 primaries + log2_shaper_AP1.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(ACES_AP1_to_AP0), + 'direction': 'forward' + }) + config_data['colorSpaces'].append(log2_shaper_AP1) + + # + # Choose your shaper + # + rrt_shaper_name = log2_shaper_name + rrt_shaper = log2_shaper_data + + # + # RRT + ODT Combinations + # + sorted_odts = sorted(odt_info.iteritems(), key=lambda x: x[1]) + print(sorted_odts) + for odt in sorted_odts: + (odt_name, odt_values) = odt + + # Have to handle ODTs that can generate either legal or full output + if odt_name in ['Academy.Rec2020_100nits_dim.a1.0.0', + 'Academy.Rec709_100nits_dim.a1.0.0', + 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: + odt_name_legal = '%s - Legal' % odt_values['transformUserName'] + else: + odt_name_legal = odt_values['transformUserName'] + + odt_legal = odt_values.copy() + odt_legal['legalRange'] = 1 + + cs = create_ACES_RRT_plus_ODT( + odt_name_legal, + odt_legal, + rrt_shaper, + lut_resolution_1d, + lut_resolution_3d, + cleanup) + config_data['colorSpaces'].append(cs) + + # Create a display entry using this color space + config_data['displays'][odt_name_legal] = { + 'Linear': ACES, + 'Log': ACEScc, + 'Output Transform': cs} + + if odt_name in ['Academy.Rec2020_100nits_dim.a1.0.0', + 'Academy.Rec709_100nits_dim.a1.0.0', + 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: + print("Generating full range ODT for %s" % odt_name) + + odt_name_full = "%s - Full" % odt_values['transformUserName'] + odt_full = odt_values.copy() + odt_full['legalRange'] = 0 + + cs_full = create_ACES_RRT_plus_ODT( + odt_name_full, + odt_full, + rrt_shaper, + lut_resolution_1d, + lut_resolution_3d, + cleanup) + config_data['colorSpaces'].append(cs_full) + + # Create a display entry using this color space + config_data['displays'][odt_name_full] = { + 'Linear': ACES, + 'Log': ACEScc, + 'Output Transform': cs_full} + + # + # Generic Matrix transform + # + def create_generic_matrix(name='matrix', + from_reference_values=[], + to_reference_values=[]): + cs = ColorSpace(name) + cs.description = "The %s color space" % name + cs.equality_group = name + cs.family = 'Utility' + cs.is_data = False + + cs.to_reference_transforms = [] + if to_reference_values != []: + for matrix in to_reference_values: + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(matrix), + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + if from_reference_values != []: + for matrix in from_reference_values: + cs.from_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33(matrix), + 'direction': 'forward' + }) + + return cs + + cs = create_generic_matrix('XYZ', from_reference_values=[ACES_AP0_to_XYZ]) + config_data['colorSpaces'].append(cs) + + cs = create_generic_matrix( + 'Linear - AP1', to_reference_values=[ACES_AP1_to_AP0]) + config_data['colorSpaces'].append(cs) + + # ACES to Linear, P3D60 primaries + XYZ_to_P3D60 = [2.4027414142, -0.8974841639, -0.3880533700, + -0.8325796487, 1.7692317536, 0.0237127115, + 0.0388233815, -0.0824996856, 1.0363685997] + + cs = create_generic_matrix( + 'Linear - P3-D60', + from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_P3D60]) + config_data['colorSpaces'].append(cs) + + # ACES to Linear, P3D60 primaries + XYZ_to_P3DCI = [2.7253940305, -1.0180030062, -0.4401631952, + -0.7951680258, 1.6897320548, 0.0226471906, + 0.0412418914, -0.0876390192, 1.1009293786] + + cs = create_generic_matrix( + 'Linear - P3-DCI', + from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_P3DCI]) + config_data['colorSpaces'].append(cs) + + # ACES to Linear, Rec 709 primaries + XYZ_to_Rec709 = [3.2409699419, -1.5373831776, -0.4986107603, + -0.9692436363, 1.8759675015, 0.0415550574, + 0.0556300797, -0.2039769589, 1.0569715142] + + cs = create_generic_matrix( + 'Linear - Rec.709', + from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_Rec709]) + config_data['colorSpaces'].append(cs) + + # ACES to Linear, Rec 2020 primaries + XYZ_to_Rec2020 = [1.7166511880, -0.3556707838, -0.2533662814, + -0.6666843518, 1.6164812366, 0.0157685458, + 0.0176398574, -0.0427706133, 0.9421031212] + + cs = create_generic_matrix( + 'Linear - Rec.2020', + from_reference_values=[ACES_AP0_to_XYZ, XYZ_to_Rec2020]) + config_data['colorSpaces'].append(cs) + + print("generateLUTs - end") + return config_data + + +def generate_baked_LUTs(odt_info, + shaper_name, + baked_directory, + config_path, + lut_resolution_1d, + lut_resolution_3d, + lut_resolution_shaper=1024): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # Add the legal and full variations into this list + odt_info_C = dict(odt_info) + for odt_CTL_name, odt_values in odt_info.iteritems(): + if odt_CTL_name in ['Academy.Rec2020_100nits_dim.a1.0.0', + 'Academy.Rec709_100nits_dim.a1.0.0', + 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: + 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"] + + # For Photoshop + for input_space in ["ACEScc", "ACESproxy"]: + args = ["--iconfig", config_path, + "-v", + "--inputspace", input_space] + args += ["--outputspace", "%s" % odt_name] + args += ["--description", + "%s - %s for %s data" % (odt_prefix, + odt_name, + input_space)] + args += ["--shaperspace", shaper_name, + "--shapersize", str(lut_resolution_shaper)] + args += ["--cubesize", str(lut_resolution_3d)] + args += ["--format", + "icc", + "%s/photoshop/%s for %s.icc" % (baked_directory, + odt_name, + input_space)] + + bake_LUT = Process(description="bake a LUT", + cmd="ociobakelut", + args=args) + bake_LUT.execute() + + # For Flame, Lustre + for input_space in ["ACEScc", "ACESproxy"]: + args = ["--iconfig", config_path, + "-v", + "--inputspace", input_space] + args += ["--outputspace", "%s" % odt_name] + args += ["--description", + "%s - %s for %s data" % ( + odt_prefix, odt_name, input_space)] + args += ["--shaperspace", shaper_name, + "--shapersize", str(lut_resolution_shaper)] + args += ["--cubesize", str(lut_resolution_3d)] + + fargs = ["--format", "flame", "%s/flame/%s for %s Flame.3dl" % ( + baked_directory, odt_name, input_space)] + bake_LUT = Process(description="bake a LUT", + cmd="ociobakelut", + args=(args + fargs)) + bake_LUT.execute() + + largs = ["--format", "lustre", "%s/lustre/%s for %s Lustre.3dl" % ( + baked_directory, odt_name, input_space)] + bake_LUT = Process(description="bake a LUT", + cmd="ociobakelut", + args=(args + largs)) + bake_LUT.execute() + + # For Maya, Houdini + for input_space in ["ACEScg", "ACES2065-1"]: + args = ["--iconfig", config_path, + "-v", + "--inputspace", input_space] + args += ["--outputspace", "%s" % odt_name] + args += ["--description", + "%s - %s for %s data" % ( + odt_prefix, odt_name, input_space)] + if input_space == 'ACEScg': + lin_shaper_name = "%s - AP1" % shaper_name + else: + lin_shaper_name = shaper_name + args += ["--shaperspace", lin_shaper_name, + "--shapersize", str(lut_resolution_shaper)] + + args += ["--cubesize", str(lut_resolution_3d)] + + margs = ["--format", "cinespace", "%s/maya/%s for %s Maya.csp" % ( + baked_directory, odt_name, input_space)] + bake_LUT = Process(description="bake a LUT", + cmd="ociobakelut", + args=(args + margs)) + bake_LUT.execute() + + hargs = ["--format", "houdini", + "%s/houdini/%s for %s Houdini.lut" % ( + baked_directory, odt_name, input_space)] + bake_LUT = Process(description="bake a LUT", + cmd="ociobakelut", + args=(args + hargs)) + bake_LUT.execute() + + +def create_config_dir(config_directory, bake_secondary_LUTs): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + dirs = [config_directory, "%s/luts" % config_directory] + if bake_secondary_LUTs: + dirs.extend(["%s/baked" % config_directory, + "%s/baked/flame" % config_directory, + "%s/baked/photoshop" % config_directory, + "%s/baked/houdini" % config_directory, + "%s/baked/lustre" % config_directory, + "%s/baked/maya" % config_directory]) + + for d in dirs: + not os.path.exists(d) and os.mkdir(d) + + +def get_transform_info(ctl_transform): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # TODO: Use *with* statement. + fp = open(ctl_transform, 'rb') + + # Read lines + lines = fp.readlines() + + # Grab transform ID and User Name + transform_ID = lines[1][3:].split('<')[1].split('>')[1].strip() + # print(transformID) + transform_user_name = '-'.join( + lines[2][3:].split('<')[1].split('>')[1].split('-')[1:]).strip() + transform_user_name_prefix = ( + lines[2][3:].split('<')[1].split('>')[1].split('-')[0].strip()) + # print(transformUserName) + fp.close() + + return transform_ID, transform_user_name, transform_user_name_prefix + + +def get_ODT_info(aces_CTL_directory): + """ + Object description. + + For versions after WGR9. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # TODO: Investigate usage of *files_walker* definition here. + # Credit to Alex Fry for the original approach here + odt_dir = os.path.join(aces_CTL_directory, "odt") + all_odt = [] + for dir_name, subdir_list, file_list in os.walk(odt_dir): + for fname in file_list: + all_odt.append((os.path.join(dir_name, fname))) + + odt_CTLs = [x for x in all_odt if + ("InvODT" not in x) and (os.path.split(x)[-1][0] != '.')] + + # print odtCTLs + + odts = {} + + for odt_CTL in odt_CTLs: + odt_tokens = os.path.split(odt_CTL) + # print(odtTokens) + + # Handle nested directories + odt_path_tokens = os.path.split(odt_tokens[-2]) + odt_dir = odt_path_tokens[-1] + while odt_path_tokens[-2][-3:] != 'odt': + odt_path_tokens = os.path.split(odt_path_tokens[-2]) + odt_dir = os.path.join(odt_path_tokens[-1], odt_dir) + + # Build full name + # print("odtDir : %s" % odtDir) + transform_CTL = odt_tokens[-1] + # print(transformCTL) + odt_name = string.join(transform_CTL.split('.')[1:-1], '.') + # print(odtName) + + # Find id, user name and user name prefix + (transform_ID, + transform_user_name, + transform_user_name_prefix) = get_transform_info( + "%s/odt/%s/%s" % (aces_CTL_directory, odt_dir, transform_CTL)) + + # Find inverse + transform_CTL_inverse = "InvODT.%s.ctl" % odt_name + if not os.path.exists( + os.path.join(odt_tokens[-2], transform_CTL_inverse)): + transform_CTL_inverse = None + # print(transformCTLInverse) + + # Add to list of ODTs + odts[odt_name] = {} + odts[odt_name]['transformCTL'] = os.path.join(odt_dir, transform_CTL) + if transform_CTL_inverse != None: + odts[odt_name]['transformCTLInverse'] = os.path.join( + odt_dir, transform_CTL_inverse) + + odts[odt_name]['transformID'] = transform_ID + odts[odt_name]['transformUserNamePrefix'] = transform_user_name_prefix + odts[odt_name]['transformUserName'] = transform_user_name + + print("ODT : %s" % odt_name) + print("\tTransform ID : %s" % transform_ID) + print("\tTransform User Name Prefix : %s" % transform_user_name_prefix) + print("\tTransform User Name : %s" % transform_user_name) + print("\tForward ctl : %s" % ( + odts[odt_name]['transformCTL'])) + if 'transformCTLInverse' in odts[odt_name]: + print("\tInverse ctl : %s" % ( + odts[odt_name]['transformCTLInverse'])) + else: + print("\tInverse ctl : %s" % "None") + + print("\n") + + return odts + + +def get_LMT_info(aces_CTL_directory): + """ + Object description. + + For versions after WGR9. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # TODO: Investigate refactoring with previous definition. + + # Credit to Alex Fry for the original approach here + lmt_dir = os.path.join(aces_CTL_directory, "lmt") + all_lmt = [] + for dir_name, subdir_list, file_list in os.walk(lmt_dir): + for fname in file_list: + all_lmt.append((os.path.join(dir_name, fname))) + + lmt_CTLs = [x for x in all_lmt if + ("InvLMT" not in x) and ("README" not in x) and ( + os.path.split(x)[-1][0] != '.')] + + # print lmtCTLs + + lmts = {} + + for lmt_CTL in lmt_CTLs: + lmt_tokens = os.path.split(lmt_CTL) + # print(lmtTokens) + + # Handle nested directories + lmt_path_tokens = os.path.split(lmt_tokens[-2]) + lmt_dir = lmt_path_tokens[-1] + while lmt_path_tokens[-2][-3:] != 'ctl': + lmt_path_tokens = os.path.split(lmt_path_tokens[-2]) + lmt_dir = os.path.join(lmt_path_tokens[-1], lmt_dir) + + # Build full name + # print("lmtDir : %s" % lmtDir) + transform_CTL = lmt_tokens[-1] + # print(transformCTL) + lmt_name = string.join(transform_CTL.split('.')[1:-1], '.') + # print(lmtName) + + # Find id, user name and user name prefix + (transform_ID, + transform_user_name, + transform_user_name_prefix) = get_transform_info( + "%s/%s/%s" % (aces_CTL_directory, lmt_dir, transform_CTL)) + + # Find inverse + transform_CTL_inverse = "InvLMT.%s.ctl" % lmt_name + if not os.path.exists( + os.path.join(lmt_tokens[-2], transform_CTL_inverse)): + transform_CTL_inverse = None + # print(transformCTLInverse) + + # Add to list of LMTs + lmts[lmt_name] = {} + lmts[lmt_name]['transformCTL'] = os.path.join(lmt_dir, transform_CTL) + if transform_CTL_inverse != None: + # TODO: Check unresolved *odt_name* referemce. + lmts[odt_name]['transformCTLInverse'] = os.path.join( + lmt_dir, transform_CTL_inverse) + + lmts[lmt_name]['transformID'] = transform_ID + lmts[lmt_name]['transformUserNamePrefix'] = transform_user_name_prefix + lmts[lmt_name]['transformUserName'] = transform_user_name + + print("LMT : %s" % lmt_name) + print("\tTransform ID : %s" % transform_ID) + print("\tTransform User Name Prefix : %s" % transform_user_name_prefix) + print("\tTransform User Name : %s" % transform_user_name) + print("\t Forward ctl : %s" % lmts[lmt_name]['transformCTL']) + if 'transformCTLInverse' in lmts[lmt_name]: + print("\t Inverse ctl : %s" % ( + lmts[lmt_name]['transformCTLInverse'])) + else: + print("\t Inverse ctl : %s" % "None") + + print("\n") + + return lmts + + +def create_ACES_config(aces_CTL_directory, + config_directory, + lut_resolution_1d=4096, + lut_resolution_3d=64, + bake_secondary_LUTs=True, + cleanup=True): + """ + Creates the ACES configuration. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # Get ODT names and CTL paths + odt_info = get_ODT_info(aces_CTL_directory) + + # Get ODT names and CTL paths + lmt_info = get_LMT_info(aces_CTL_directory) + + # Create config dir + create_config_dir(config_directory, bake_secondary_LUTs) + + # Generate config data and LUTs for different transforms + lut_directory = "%s/luts" % config_directory + shaper_name = 'Output Shaper' + config_data = generate_LUTs(odt_info, + lmt_info, + shaper_name, + aces_CTL_directory, + lut_directory, + lut_resolution_1d, + lut_resolution_3d, + cleanup) + + # Create the config using the generated LUTs + print("Creating generic config") + config = create_config(config_data) + print("\n\n\n") + + # Write the config to disk + write_config(config, "%s/config.ocio" % config_directory) + + # Create a config that will work well with Nuke using the previously + # generated LUTs. + print("Creating Nuke-specific config") + nuke_config = create_config(config_data, nuke=True) + print("\n\n\n") + + # Write the config to disk + write_config(nuke_config, "%s/nuke_config.ocio" % config_directory) + + # Bake secondary LUTs using the config + if bake_secondary_LUTs: + generate_baked_LUTs(odt_info, + shaper_name, + "%s/baked" % config_directory, + "%s/config.ocio" % config_directory, + lut_resolution_1d, + lut_resolution_3d, + lut_resolution_1d) + + return True + + +def main(): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import optparse + + p = optparse.OptionParser(description='An OCIO config generation script', + prog='createACESConfig', + version='createACESConfig 0.1', + usage='%prog [options]') + p.add_option('--acesCTLDir', '-a', default=os.environ.get( + 'ACES_OCIO_CTL_DIRECTORY', None)) + p.add_option('--configDir', '-c', default=os.environ.get( + 'ACES_OCIO_CONFIGURATION_DIRECTORY', None)) + p.add_option('--lutResolution1d', default=4096) + p.add_option('--lutResolution3d', default=64) + p.add_option('--dontBakeSecondaryLUTs', action="store_true") + p.add_option('--keepTempImages', action="store_true") + + options, arguments = p.parse_args() + + # + # Get options + # + 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) + + try: + args_start = sys.argv.index('--') + 1 + args = sys.argv[args_start:] + except: + args_start = len(sys.argv) + 1 + args = [] + + print("command line : \n%s\n" % " ".join(sys.argv)) + + # TODO: Use assertion and mention environment variables. + if not aces_CTL_directory: + print("process: No ACES CTL directory specified") + return + if not config_directory: + print("process: No configuration directory specified") + return + # + # Generate the configuration + # + return create_ACES_config(aces_CTL_directory, + config_directory, + lut_resolution_1d, + lut_resolution_3d, + bake_secondary_LUTs, + cleanup_temp_images) + + +if __name__ == '__main__': + main() diff --git a/aces_1.0.0/python/aces_ocio/create_arri_colorspaces.py b/aces_1.0.0/python/aces_ocio/create_arri_colorspaces.py new file mode 100644 index 0000000..be56c9d --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/create_arri_colorspaces.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Implements support for *ARRI* colorspaces conversions and transfer functions. +""" + +import array +import math + +import aces_ocio.generate_lut as genlut +from aces_ocio.utilities import ColorSpace, mat44_from_mat33 + + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['create_log_c', + 'create_colorspaces'] + + +def create_log_c(gamut, + transfer_function, + exposure_index, + name, + lut_directory, + lut_resolution_1d): + """ + Object description. + + LogC to ACES. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + name = "%s (EI%s) - %s" % (transfer_function, exposure_index, gamut) + if transfer_function == "": + name = "Linear - %s" % gamut + if gamut == "": + name = "%s (EI%s)" % (transfer_function, exposure_index) + + cs = ColorSpace(name) + cs.description = name + cs.equality_group = '' + cs.family = 'ARRI' + cs.is_data = False + + # Globals + IDT_maker_version = "0.08" + + nominal_EI = 400.0 + black_signal = 0.003907 + mid_gray_signal = 0.01 + encoding_gain = 0.256598 + encoding_offset = 0.391007 + + def gain_for_EI(EI): + return (math.log(EI / nominal_EI) / math.log(2) * ( + 0.89 - 1) / 3 + 1) * encoding_gain + + def log_c_inverse_parameters_for_EI(EI): + cut = 1.0 / 9.0 + slope = 1.0 / (cut * math.log(10)) + offset = math.log10(cut) - slope * cut + gain = EI / nominal_EI + gray = mid_gray_signal / gain + # The higher the EI, the lower the gamma + enc_gain = gain_for_EI(EI) + enc_offset = encoding_offset + for i in range(0, 3): + nz = ((95.0 / 1023.0 - enc_offset) / enc_gain - offset) / slope + enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain + # Calculate some intermediate values + a = 1.0 / gray + b = nz - black_signal / gray + e = slope * a * enc_gain + f = enc_gain * (slope * b + offset) + enc_offset + # Manipulations so we can return relative exposure + s = 4 / (0.18 * EI) + t = black_signal + b += a * t + a *= s + f += e * t + e *= s + + return {'a': a, + 'b': b, + 'cut': (cut - b) / a, + 'c': enc_gain, + 'd': enc_offset, + 'e': e, + 'f': f} + + def log_c_to_linear(code_value, exposure_index): + p = log_c_inverse_parameters_for_EI(exposure_index) + breakpoint = p['e'] * p['cut'] + p['f'] + if (code_value > breakpoint): + linear = ((pow(10, (code_value / 1023.0 - p['d']) / p['c']) - + p['b']) / p['a']) + else: + linear = (code_value / 1023.0 - p['f']) / p['e'] + + # print(codeValue, linear) + return linear + + + cs.to_reference_transforms = [] + + if transfer_function == "V3 LogC": + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = log_c_to_linear(1023.0 * c / (lut_resolution_1d - 1), + int(exposure_index)) + + lut = "%s_to_linear.spi1d" % ( + "%s_%s" % (transfer_function, exposure_index)) + + # Remove spaces and parentheses + lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') + + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + # print("Writing %s" % lut) + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + if gamut == 'Wide Gamut': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.680206, 0.236137, 0.083658, + 0.085415, 1.017471, -0.102886, + 0.002057, -0.062563, 1.060506]), + 'direction': 'forward' + }) + + cs.from_reference_transforms = [] + return cs + + +def create_colorspaces(lut_directory, lut_resolution_1d): + """ + Generates the colorspace conversions. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + colorspaces = [] + + transfer_function = "V3 LogC" + gamut = "Wide Gamut" + + # EIs = [160.0, 200.0, 250.0, 320.0, 400.0, 500.0, 640.0, 800.0, + # 1000.0, 1280.0, 1600.0, 2000.0, 2560.0, 3200.0] + EIs = [160, 200, 250, 320, 400, 500, 640, 800, + 1000, 1280, 1600, 2000, 2560, 3200] + default_EI = 800 + + # Full conversion + for EI in EIs: + log_c_EI_full = create_log_c( + gamut, + transfer_function, + EI, + "LogC", + lut_directory, + lut_resolution_1d) + colorspaces.append(log_c_EI_full) + + # Linearization only + for EI in [800]: + log_c_EI_linearization = create_log_c( + "", + transfer_function, + EI, + "LogC", + lut_directory, + lut_resolution_1d) + colorspaces.append(log_c_EI_linearization) + + # Primaries + log_c_EI_primaries = create_log_c( + gamut, + "", + default_EI, + "LogC", + lut_directory, + lut_resolution_1d) + colorspaces.append(log_c_EI_primaries) + + return colorspaces diff --git a/aces_1.0.0/python/aces_ocio/create_canon_colorspaces.py b/aces_1.0.0/python/aces_ocio/create_canon_colorspaces.py new file mode 100644 index 0000000..5ac15b3 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/create_canon_colorspaces.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Implements support for *Canon* colorspaces conversions and transfer functions. +""" + +import array + +import aces_ocio.generate_lut as genlut +from aces_ocio.utilities import ColorSpace + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['create_c_log', + 'create_colorspaces'] + + +def create_c_log(gamut, + transfer_function, + name, + lut_directory, + lut_resolution_1d): + """ + Object description. + + Canon-Log to ACES. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + name = "%s - %s" % (transfer_function, gamut) + if transfer_function == "": + name = "Linear - %s" % gamut + if gamut == "": + name = "%s" % transfer_function + + cs = ColorSpace(name) + cs.description = name + cs.equality_group = '' + cs.family = 'Canon' + cs.is_data = False + + def legal_to_full(codeValue): + return (codeValue - 64.0) / (940.0 - 64.0) + + def c_log_to_linear(codeValue): + # log = fullToLegal(c1 * log10(c2*linear + 1) + c3) + # linear = (pow(10, (legalToFul(log) - c3)/c1) - 1)/c2 + c1 = 0.529136 + c2 = 10.1596 + c3 = 0.0730597 + + linear = (pow(10.0, (legal_to_full(codeValue) - c3) / c1) - 1.0) / c2 + linear *= 0.9 + # print(codeValue, linear) + return linear + + cs.to_reference_transforms = [] + + if transfer_function == "Canon-Log": + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = c_log_to_linear(1023.0 * c / (lut_resolution_1d - 1)) + + lut = "%s_to_linear.spi1d" % transfer_function + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward'}) + + if gamut == 'Rec. 709 Daylight': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.561538969, 0.402060105, 0.036400926, 0.0, + 0.092739623, 0.924121198, -0.016860821, 0.0, + 0.084812961, 0.006373835, 0.908813204, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + elif gamut == 'Rec. 709 Tungsten': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.566996399, 0.365079418, 0.067924183, 0.0, + 0.070901044, 0.880331008, 0.048767948, 0.0, + 0.073013542, -0.066540862, 0.99352732, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + elif gamut == 'DCI-P3 Daylight': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.607160575, 0.299507286, 0.093332140, 0.0, + 0.004968120, 1.050982224, -0.055950343, 0.0, + -0.007839939, 0.000809127, 1.007030813, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + elif gamut == 'DCI-P3 Tungsten': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.650279125, 0.253880169, 0.095840706, 0.0, + -0.026137986, 1.017900530, 0.008237456, 0.0, + 0.007757558, -0.063081669, 1.055324110, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + elif gamut == 'Cinema Gamut Daylight': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.763064455, 0.149021161, 0.087914384, 0.0, + 0.003657457, 1.10696038, -0.110617837, 0.0, + -0.009407794, -0.218383305, 1.227791099, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + elif gamut == 'Cinema Gamut Tungsten': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': [0.817416293, 0.090755698, 0.091828009, 0.0, + -0.035361374, 1.065690585, -0.030329211, 0.0, + 0.010390366, -0.299271107, 1.288880741, 0.0, + 0, 0, 0, 1.0], + 'direction': 'forward'}) + + cs.from_reference_transforms = [] + return cs + + +def create_colorspaces(lut_directory, lut_resolution_1d): + """ + Generates the colorspace conversions. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + colorspaces = [] + + # Full conversion + c_log_1 = create_c_log( + "Rec. 709 Daylight", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_1) + + c_log_2 = create_c_log( + "Rec. 709 Tungsten", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_2) + + c_log_3 = create_c_log( + "DCI-P3 Daylight", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_3) + + c_log_4 = create_c_log( + "DCI-P3 Tungsten", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_4) + + c_log_5 = create_c_log( + "Cinema Gamut Daylight", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_5) + + c_log_6 = create_c_log( + "Cinema Gamut Tungsten", + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_6) + + # Linearization only + c_log_7 = create_c_log( + '', + "Canon-Log", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_7) + + # Primaries only + c_log_8 = create_c_log( + "Rec. 709 Daylight", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_8) + + c_log_9 = create_c_log( + "Rec. 709 Tungsten", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_9) + + c_log_10 = create_c_log( + "DCI-P3 Daylight", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_10) + + c_log_11 = create_c_log( + "DCI-P3 Tungsten", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_11) + + c_log_12 = create_c_log( + "Cinema Gamut Daylight", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_12) + + c_log_13 = create_c_log( + "Cinema Gamut Tungsten", + "", + "Canon-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(c_log_13) + + return colorspaces diff --git a/aces_1.0.0/python/aces_ocio/create_red_colorspaces.py b/aces_1.0.0/python/aces_ocio/create_red_colorspaces.py new file mode 100644 index 0000000..4c09151 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/create_red_colorspaces.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Implements support for *RED* colorspaces conversions and transfer functions. +""" + +import array + +import aces_ocio.generate_lut as genlut +from aces_ocio.utilities import ColorSpace, mat44_from_mat33 + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['create_RED_log_film', + 'create_colorspaces'] + + +def create_RED_log_film(gamut, + transfer_function, + name, + lut_directory, + lut_resolution_1d): + """ + Object description. + + RED colorspaces to ACES. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + name = "%s - %s" % (transfer_function, gamut) + if transfer_function == "": + name = "Linear - %s" % gamut + if gamut == "": + name = "%s" % transfer_function + + cs = ColorSpace(name) + cs.description = name + cs.equality_group = '' + cs.family = 'RED' + cs.is_data = False + + def cineon_to_linear(code_value): + n_gamma = 0.6 + black_point = 95.0 + white_point = 685.0 + code_value_to_density = 0.002 + + black_linear = pow(10.0, (black_point - white_point) * ( + code_value_to_density / n_gamma)) + code_linear = pow(10.0, (code_value - white_point) * ( + code_value_to_density / n_gamma)) + + return (code_linear - black_linear) / (1.0 - black_linear) + + cs.to_reference_transforms = [] + + if transfer_function == 'REDlogFilm': + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = cineon_to_linear(1023.0 * c / (lut_resolution_1d - 1)) + + lut = "CineonLog_to_linear.spi1d" + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward'}) + + if gamut == 'DRAGONcolor': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.532279, 0.376648, 0.091073, + 0.046344, 0.974513, -0.020860, + -0.053976, -0.000320, 1.054267]), + 'direction': 'forward'}) + elif gamut == 'DRAGONcolor2': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.468452, 0.331484, 0.200064, + 0.040787, 0.857658, 0.101553, + -0.047504, -0.000282, 1.047756]), + 'direction': 'forward'}) + elif gamut == 'REDcolor2': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.480997, 0.402289, 0.116714, + -0.004938, 1.000154, 0.004781, + -0.105257, 0.025320, 1.079907]), + 'direction': 'forward'}) + elif gamut == 'REDcolor3': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.512136, 0.360370, 0.127494, + 0.070377, 0.903884, 0.025737, + -0.020824, 0.017671, 1.003123]), + 'direction': 'forward'}) + elif gamut == 'REDcolor4': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33([0.474202, 0.333677, 0.192121, + 0.065164, 0.836932, 0.097901, + -0.019281, 0.016362, 1.002889]), + 'direction': 'forward'}) + + cs.from_reference_transforms = [] + return cs + + +def create_colorspaces(lut_directory, lut_resolution_1d): + """ + Generates the colorspace conversions. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + colorspaces = [] + + # Full conversion + RED_log_film_dragon = create_RED_log_film( + "DRAGONcolor", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_dragon) + + RED_log_film_dragon2 = create_RED_log_film( + "DRAGONcolor2", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_dragon2) + + RED_log_film_color2 = create_RED_log_film( + "REDcolor2", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color2) + + RED_log_film_color3 = create_RED_log_film( + "REDcolor3", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color3) + + RED_log_film_color4 = create_RED_log_film( + "REDcolor4", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color4) + + # Linearization only + RED_log_film_dragon = create_RED_log_film( + "", + "REDlogFilm", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_dragon) + + # Primaries only + RED_log_film_dragon = create_RED_log_film( + "DRAGONcolor", + "", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_dragon) + + RED_log_film_dragon2 = create_RED_log_film( + "DRAGONcolor2", + "", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_dragon2) + + RED_log_film_color2 = create_RED_log_film( + "REDcolor2", + "", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color2) + + RED_log_film_color3 = create_RED_log_film( + "REDcolor3", + "", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color3) + + RED_log_film_color4 = create_RED_log_film( + "REDcolor4", + "", + "REDlogFilm", + lut_directory, + lut_resolution_1d) + colorspaces.append(RED_log_film_color4) + + return colorspaces diff --git a/aces_1.0.0/python/aces_ocio/create_sony_colorspaces.py b/aces_1.0.0/python/aces_ocio/create_sony_colorspaces.py new file mode 100644 index 0000000..3867e87 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/create_sony_colorspaces.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Implements support for *Sony* colorspaces conversions and transfer functions. +""" + +import array + +import aces_ocio.generate_lut as genlut +from aces_ocio.utilities import ColorSpace, mat44_from_mat33 + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['create_s_log', + 'create_colorspaces'] + + +def create_s_log(gamut, + transfer_function, + name, + lut_directory, + lut_resolution_1d): + """ + Object description. + + SLog to ACES. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + name = "%s - %s" % (transfer_function, gamut) + if transfer_function == "": + name = "Linear - %s" % gamut + if gamut == "": + name = "%s" % transfer_function + + cs = ColorSpace(name) + cs.description = name + cs.equality_group = '' + cs.family = 'Sony' + cs.is_data = False + + def s_log1_to_linear(s_log): + b = 64. + ab = 90. + w = 940. + + if (s_log >= ab): + linear = ((pow(10., + ( ((s_log - b) / + (w - b) - 0.616596 - 0.03) / 0.432699)) - + 0.037584) * 0.9) + else: + linear = ( + ((s_log - b) / ( + w - b) - 0.030001222851889303) / 5.) * 0.9 + return linear + + def s_log2_to_linear(s_log): + b = 64. + ab = 90. + w = 940. + + if (s_log >= ab): + linear = ((219. * (pow(10., + (((s_log - b) / + (w - b) - 0.616596 - 0.03) / 0.432699)) - + 0.037584) / 155.) * 0.9) + else: + linear = (((s_log - b) / ( + w - b) - 0.030001222851889303) / 3.53881278538813) * 0.9 + return linear + + def s_log3_to_linear(code_value): + if code_value >= (171.2102946929): + linear = (pow(10.0, ((code_value - 420.0) / 261.5)) * + (0.18 + 0.01) - 0.01) + else: + linear = (code_value - 95.0) * 0.01125000 / (171.2102946929 - 95.0) + # print(codeValue, linear) + return linear + + cs.to_reference_transforms = [] + + if transfer_function == "S-Log1": + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = s_log1_to_linear(1023.0 * c / (lut_resolution_1d - 1)) + + lut = "%s_to_linear.spi1d" % transfer_function + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + # print("Writing %s" % lut) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + elif transfer_function == "S-Log2": + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = s_log2_to_linear(1023.0 * c / (lut_resolution_1d - 1)) + + lut = "%s_to_linear.spi1d" % transfer_function + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + # print("Writing %s" % lut) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + elif transfer_function == "S-Log3": + data = array.array('f', "\0" * lut_resolution_1d * 4) + for c in range(lut_resolution_1d): + data[c] = s_log3_to_linear(1023.0 * c / (lut_resolution_1d - 1)) + + lut = "%s_to_linear.spi1d" % transfer_function + genlut.write_SPI_1d(lut_directory + "/" + lut, + 0.0, + 1.0, + data, + lut_resolution_1d, + 1) + + # print("Writing %s" % lut) + + cs.to_reference_transforms.append({ + 'type': 'lutFile', + 'path': lut, + 'interpolation': 'linear', + 'direction': 'forward' + }) + + if gamut == 'S-Gamut': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33( + [0.754338638, 0.133697046, 0.111968437, + 0.021198141, 1.005410934, -0.026610548, + -0.009756991, 0.004508563, 1.005253201]), + 'direction': 'forward'}) + elif gamut == 'S-Gamut Daylight': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33( + [0.8764457030, 0.0145411681, 0.1090131290, + 0.0774075345, 0.9529571767, -0.0303647111, + 0.0573564351, -0.1151066335, 1.0577501984]), + 'direction': 'forward'}) + elif gamut == 'S-Gamut Tungsten': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33( + [1.0110238740, -0.1362526051, 0.1252287310, + 0.1011994504, 0.9562196265, -0.0574190769, + 0.0600766530, -0.1010185315, 1.0409418785]), + 'direction': 'forward'}) + elif gamut == 'S-Gamut3.Cine': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33( + [0.6387886672, 0.2723514337, 0.0888598992, + -0.0039159061, 1.0880732308, -0.0841573249, + -0.0299072021, -0.0264325799, 1.0563397820]), + 'direction': 'forward'}) + elif gamut == 'S-Gamut3': + cs.to_reference_transforms.append({ + 'type': 'matrix', + 'matrix': mat44_from_mat33( + [0.7529825954, 0.1433702162, 0.1036471884, + 0.0217076974, 1.0153188355, -0.0370265329, + -0.0094160528, 0.0033704179, 1.0060456349]), + 'direction': 'forward'}) + + cs.from_reference_transforms = [] + return cs + + +def create_colorspaces(lut_directory, lut_resolution_1d): + """ + Generates the colorspace conversions. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + colorspaces = [] + + # S-Log1 + s_log1_s_gamut = create_s_log( + "S-Gamut", + "S-Log1", + "S-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log1_s_gamut) + + # S-Log2 + s_log2_s_gamut = create_s_log( + "S-Gamut", + "S-Log2", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log2_s_gamut) + + s_log2_s_gamut_daylight = create_s_log( + "S-Gamut Daylight", + "S-Log2", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log2_s_gamut_daylight) + + s_log2_s_gamut_tungsten = create_s_log( + "S-Gamut Tungsten", + "S-Log2", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log2_s_gamut_tungsten) + + # S-Log3 + s_log3_s_gamut3Cine = create_s_log( + "S-Gamut3.Cine", + "S-Log3", + "S-Log3", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log3_s_gamut3Cine) + + s_log3_s_gamut3 = create_s_log( + "S-Gamut3", + "S-Log3", + "S-Log3", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log3_s_gamut3) + + # Linearization only + s_log1 = create_s_log( + "", + "S-Log1", + "S-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log1) + + s_log2 = create_s_log( + "", + "S-Log2", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log2) + + s_log3 = create_s_log( + "", + "S-Log3", + "S-Log3", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_log3) + + # Primaries only + s_gamut = create_s_log( + "S-Gamut", + "", + "S-Log", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_gamut) + + s_gamut_daylight = create_s_log( + "S-Gamut Daylight", + "", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_gamut_daylight) + + s_gamut_tungsten = create_s_log( + "S-Gamut Tungsten", + "", + "S-Log2", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_gamut_tungsten) + + s_gamut3Cine = create_s_log( + "S-Gamut3.Cine", + "", + "S-Log3", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_gamut3Cine) + + s_gamut3 = create_s_log( + "S-Gamut3", + "", + "S-Log3", + lut_directory, + lut_resolution_1d) + colorspaces.append(s_gamut3) + + return colorspaces diff --git a/aces_1.0.0/python/aces_ocio/generate_lut.py b/aces_1.0.0/python/aces_ocio/generate_lut.py new file mode 100644 index 0000000..e0d4dfa --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/generate_lut.py @@ -0,0 +1,638 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines objects to generate various kind of 1d, 2d and 3d LUTs in various file +formats. +""" + +import array +import os +import sys + +import OpenImageIO as oiio + +from aces_ocio.process import Process + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['generate_1d_LUT_image', + 'write_SPI_1d', + 'generate_1d_LUT_from_image', + 'generate_3d_LUT_image', + 'generate_3d_LUT_from_image', + 'apply_CTL_to_image', + 'convert_bit_depth', + 'generate_1d_LUT_from_CTL', + 'correct_LUT_image', + 'generate_3d_LUT_from_CTL', + 'main'] + + +def generate_1d_LUT_image(ramp_1d_path, + resolution=1024, + min_value=0.0, + max_value=1.0): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # print("Generate 1d LUT image - %s" % ramp1dPath) + + # open image + format = os.path.splitext(ramp_1d_path)[1] + ramp = oiio.ImageOutput.create(ramp_1d_path) + + # set image specs + spec = oiio.ImageSpec() + spec.set_format(oiio.FLOAT) + # spec.format.basetype = oiio.FLOAT + spec.width = resolution + spec.height = 1 + spec.nchannels = 3 + + ramp.open(ramp_1d_path, spec, oiio.Create) + + data = array.array("f", + "\0" * spec.width * spec.height * spec.nchannels * 4) + for i in range(resolution): + value = float(i) / (resolution - 1) * ( + max_value - min_value) + min_value + data[i * spec.nchannels + 0] = value + data[i * spec.nchannels + 1] = value + data[i * spec.nchannels + 2] = value + + ramp.write_image(spec.format, data) + ramp.close() + + +def write_SPI_1d(filename, from_min, from_max, data, entries, channels): + """ + Object description. + + Credit to *Alex Fry* for the original single channel version of the spi1d + writer. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + f = file(filename, 'w') + f.write("Version 1\n") + f.write("From %f %f\n" % (from_min, from_max)) + f.write("Length %d\n" % entries) + f.write("Components %d\n" % (min(3, channels))) + f.write("{\n") + for i in range(0, entries): + entry = "" + for j in range(0, min(3, channels)): + entry = "%s %s" % (entry, data[i * channels + j]) + f.write(" %s\n" % entry) + f.write("}\n") + f.close() + + +def generate_1d_LUT_from_image(ramp_1d_path, + output_path=None, + min_value=0.0, + max_value=1.0): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if output_path is None: + output_path = ramp_1d_path + ".spi1d" + + # open image + ramp = oiio.ImageInput.open(ramp_1d_path) + + # get image specs + spec = ramp.spec() + type = spec.format.basetype + width = spec.width + height = spec.height + channels = spec.nchannels + + # get data + # Force data to be read as float. The Python API doesn't handle + # half-floats well yet. + type = oiio.FLOAT + data = ramp.read_image(type) + + write_SPI_1d(output_path, min_value, max_value, data, width, channels) + + +def generate_3d_LUT_image(ramp_3d_path, resolution=32): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + args = ["--generate", + "--cubesize", + str(resolution), + "--maxwidth", + str(resolution * resolution), + "--output", + ramp_3d_path] + lut_extract = Process(description="generate a 3d LUT image", + cmd="ociolutimage", + args=args) + lut_extract.execute() + + +def generate_3d_LUT_from_image(ramp_3d_path, output_path=None, resolution=32): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if output_path is None: + output_path = ramp_3d_path + ".spi3d" + + args = ["--extract", + "--cubesize", + str(resolution), + "--maxwidth", + str(resolution * resolution), + "--input", + ramp_3d_path, + "--output", + output_path] + lut_extract = Process(description="extract a 3d LUT", + cmd="ociolutimage", + args=args) + lut_extract.execute() + + +def apply_CTL_to_image(input_image, + output_image, + ctl_paths=[], + input_scale=1.0, + output_scale=1.0, + global_params={}, + aces_CTL_directory=None): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if len(ctl_paths) > 0: + ctlenv = os.environ + if aces_CTL_directory != None: + if os.path.split(aces_CTL_directory)[1] != "utilities": + ctl_module_path = "%s/utilities" % aces_CTL_directory + else: + ctl_module_path = aces_CTL_directory + ctlenv['CTL_MODULE_PATH'] = ctl_module_path + + args = [] + for ctl in ctl_paths: + args += ['-ctl', ctl] + args += ["-force"] + # args += ["-verbose"] + args += ["-input_scale", str(input_scale)] + args += ["-output_scale", str(output_scale)] + args += ["-global_param1", "aIn", "1.0"] + for key, value in global_params.iteritems(): + args += ["-global_param1", key, str(value)] + args += [input_image] + args += [output_image] + + # print("args : %s" % args) + + ctlp = Process(description="a ctlrender process", + cmd="ctlrender", + args=args, env=ctlenv) + + ctlp.execute() + + +def convert_bit_depth(input_image, output_image, depth): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + args = [input_image, + "-d", + depth, + "-o", + output_image] + convert = Process(description="convert image bit depth", + cmd="oiiotool", + args=args) + convert.execute() + + +def generate_1d_LUT_from_CTL(lut_path, + ctl_paths, + lut_resolution=1024, + identity_LUT_bit_depth='half', + input_scale=1.0, + output_scale=1.0, + global_params={}, + cleanup=True, + aces_CTL_directory=None, + min_value=0.0, + max_value=1.0): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # print(lutPath) + # print(ctlPaths) + + lut_path_base = os.path.splitext(lut_path)[0] + + identity_LUT_image_float = lut_path_base + ".float.tiff" + generate_1d_LUT_image(identity_LUT_image_float, + lut_resolution, + min_value, + max_value) + + if identity_LUT_bit_depth != 'half': + identity_LUT_image = lut_path_base + ".uint16.tiff" + convert_bit_depth(identity_LUT_image_float, + identity_LUT_image, + identity_LUT_bit_depth) + else: + identity_LUT_image = identity_LUT_image_float + + transformed_LUT_image = lut_path_base + ".transformed.exr" + apply_CTL_to_image(identity_LUT_image, + transformed_LUT_image, + ctl_paths, + input_scale, + output_scale, + global_params, + aces_CTL_directory) + + generate_1d_LUT_from_image(transformed_LUT_image, + lut_path, + min_value, + max_value) + + if cleanup: + os.remove(identity_LUT_image) + if identity_LUT_image != identity_LUT_image_float: + os.remove(identity_LUT_image_float) + os.remove(transformed_LUT_image) + + +def correct_LUT_image(transformed_LUT_image, + corrected_LUT_image, + lut_resolution): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # open image + transformed = oiio.ImageInput.open(transformed_LUT_image) + + # get image specs + transformed_spec = transformed.spec() + type = transformed_spec.format.basetype + width = transformed_spec.width + height = transformed_spec.height + channels = transformed_spec.nchannels + + # rotate or not + if width != lut_resolution * lut_resolution or height != lut_resolution: + print(("Correcting image as resolution is off. " + "Found %d x %d. Expected %d x %d") % ( + width, + height, + lut_resolution * lut_resolution, + lut_resolution)) + print("Generating %s" % corrected_LUT_image) + + # + # We're going to generate a new correct image + # + + # Get the source data + # Force data to be read as float. The Python API doesn't handle + # half-floats well yet. + type = oiio.FLOAT + source_data = transformed.read_image(type) + + format = os.path.splitext(corrected_LUT_image)[1] + correct = oiio.ImageOutput.create(corrected_LUT_image) + + # set image specs + correct_spec = oiio.ImageSpec() + correct_spec.set_format(oiio.FLOAT) + correct_spec.width = height + correct_spec.height = width + correct_spec.nchannels = channels + + correct.open(corrected_LUT_image, correct_spec, oiio.Create) + + dest_data = array.array("f", + ("\0" * correct_spec.width * + correct_spec.height * + correct_spec.nchannels * 4)) + for j in range(0, correct_spec.height): + for i in range(0, correct_spec.width): + for c in range(0, correct_spec.nchannels): + # print(i, j, c) + dest_data[(correct_spec.nchannels * + correct_spec.width * j + + correct_spec.nchannels * i + c)] = ( + source_data[correct_spec.nchannels * + correct_spec.width * j + + correct_spec.nchannels * i + c]) + + correct.write_image(correct_spec.format, dest_data) + correct.close() + else: + # shutil.copy(transformedLUTImage, correctedLUTImage) + corrected_LUT_image = transformed_LUT_image + + transformed.close() + + return corrected_LUT_image + + +def generate_3d_LUT_from_CTL(lut_path, + ctl_paths, + lut_resolution=64, + identity_LUT_bit_depth='half', + input_scale=1.0, + output_scale=1.0, + global_params={}, + cleanup=True, + aces_CTL_directory=None): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + # print(lutPath) + # print(ctlPaths) + + lut_path_base = os.path.splitext(lut_path)[0] + + identity_LUT_image_float = lut_path_base + ".float.tiff" + generate_3d_LUT_image(identity_LUT_image_float, lut_resolution) + + if identity_LUT_bit_depth != 'half': + identity_LUT_image = (lut_path_base + + "." + + identity_LUT_bit_depth + + ".tiff") + convert_bit_depth(identity_LUT_image_float, + identity_LUT_image, + identity_LUT_bit_depth) + else: + identity_LUT_image = identity_LUT_image_float + + transformed_LUT_image = lut_path_base + ".transformed.exr" + apply_CTL_to_image(identity_LUT_image, + transformed_LUT_image, + ctl_paths, + input_scale, + output_scale, + global_params, + aces_CTL_directory) + + corrected_LUT_image = lut_path_base + ".correct.exr" + corrected_LUT_image = correct_LUT_image(transformed_LUT_image, + corrected_LUT_image, + lut_resolution) + + generate_3d_LUT_from_image(corrected_LUT_image, lut_path, lut_resolution) + + if cleanup: + os.remove(identity_LUT_image) + if identity_LUT_image != identity_LUT_image_float: + os.remove(identity_LUT_image_float) + os.remove(transformed_LUT_image) + if corrected_LUT_image != transformed_LUT_image: + os.remove(corrected_LUT_image) + # os.remove(correctedLUTImage) + + +def main(): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import optparse + + p = optparse.OptionParser( + description='A utility to generate LUTs from CTL', + prog='generateLUT', + version='0.01', + usage='%prog [options]') + + p.add_option('--lut', '-l', type="string", default="") + p.add_option('--ctl', '-c', type="string", action="append") + p.add_option('--lut_resolution_1d', '', type="int", default=1024) + p.add_option('--lut_resolution_3d', '', type="int", default=33) + p.add_option('--ctlReleasePath', '-r', type="string", default="") + p.add_option('--bitDepth', '-b', type="string", default="float") + p.add_option('--keepTempImages', '', action="store_true") + p.add_option('--minValue', '', type="float", default=0.0) + p.add_option('--maxValue', '', type="float", default=1.0) + p.add_option('--inputScale', '', type="float", default=1.0) + p.add_option('--outputScale', '', type="float", default=1.0) + p.add_option('--ctlRenderParam', '-p', type="string", nargs=2, + action="append") + + p.add_option('--generate1d', '', action="store_true") + p.add_option('--generate3d', '', action="store_true") + + options, arguments = p.parse_args() + + # + # Get options + # + lut = options.lut + ctls = options.ctl + lut_resolution_1d = options.lut_resolution_1d + lut_resolution_3d = options.lut_resolution_3d + min_value = options.minValue + max_value = options.maxValue + input_scale = options.inputScale + output_scale = options.outputScale + ctl_release_path = options.ctlReleasePath + generate_1d = options.generate1d is True + generate_3d = options.generate3d is True + bitdepth = options.bitDepth + cleanup = not options.keepTempImages + + params = {} + if options.ctlRenderParam != None: + for param in options.ctlRenderParam: + params[param[0]] = float(param[1]) + + try: + args_start = sys.argv.index('--') + 1 + args = sys.argv[args_start:] + except: + args_start = len(sys.argv) + 1 + args = [] + + # print("command line : \n%s\n" % " ".join(sys.argv)) + + # + # Generate LUTs + # + if generate_1d: + print("1D LUT generation options") + else: + print("3D LUT generation options") + + print("lut : %s" % lut) + print("ctls : %s" % ctls) + print("lut res 1d : %s" % lut_resolution_1d) + print("lut res 3d : %s" % lut_resolution_3d) + print("min value : %s" % min_value) + print("max value : %s" % max_value) + print("input scale : %s" % input_scale) + print("output scale : %s" % output_scale) + print("ctl render params : %s" % params) + print("ctl release path : %s" % ctl_release_path) + print("bit depth of input : %s" % bitdepth) + print("cleanup temp images : %s" % cleanup) + + if generate_1d: + generate_1d_LUT_from_CTL(lut, + ctls, + lut_resolution_1d, + bitdepth, + input_scale, + output_scale, + params, + cleanup, + ctl_release_path, + min_value, + max_value) + + elif generate_3d: + generate_3d_LUT_from_CTL(lut, + ctls, + lut_resolution_3d, + bitdepth, + input_scale, + output_scale, + params, + cleanup, + ctl_release_path) + else: + print(("\n\nNo LUT generated. " + "You must choose either 1D or 3D LUT generation\n\n")) + + +if __name__ == '__main__': + main() + diff --git a/aces_1.0.0/python/aces_ocio/process.py b/aces_1.0.0/python/aces_ocio/process.py new file mode 100755 index 0000000..6445b46 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/process.py @@ -0,0 +1,757 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +A process wrapper class that maintains the text output and execution status of +a process or a list of other process wrappers which carry such data. +""" + +import os +import sys +import traceback + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['read_text', + 'write_text', + 'Process', + 'ProcessList', + 'main'] + + +def read_text(text_file): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if (text_file != ""): + fp = open(text_file, 'rb') + # Create a text/plain message + text = (fp.read()) + fp.close() + return text + + +def write_text(text, text_file): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if (text_file != ""): + fp = open(text_file, 'wb') + # Create a text/plain message + fp.write(text) + fp.close() + return text + + +class Process: + """ + A process with logged output. + """ + + def __init__(self, + description=None, + cmd=None, + args=[], + cwd=None, + env=None, + batch_wrapper=False): + """ + Initialize the standard class variables. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + self.cmd = cmd + if not description: + self.description = cmd + else: + self.description = description + self.status = None + self.args = args + self.start = None + self.end = None + self.log = [] + self.echo = True + self.cwd = cwd + self.env = env + self.batch_wrapper = batch_wrapper + self.process_keys = [] + + def get_elapsed_seconds(self): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import math + + if self.end and self.start: + delta = (self.end - self.start) + formatted = "%s.%s" % (delta.days * 86400 + delta.seconds, + int(math.floor(delta.microseconds / 1e3))) + else: + formatted = None + return formatted + + def write_key(self, write_dict, key=None, value=None, start_stop=None): + """ + Writes a key / value pair in a supported format. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if key != None and (value != None or start_stop != None): + indent = '\t' * write_dict['indentationLevel'] + if write_dict['format'] == 'xml': + if start_stop == 'start': + write_dict['logHandle'].write("%s<%s>\n" % (indent, key)) + elif start_stop == 'stop': + write_dict['logHandle'].write("%s\n" % (indent, key)) + else: + write_dict['logHandle'].write( + "%s<%s>%s\n" % (indent, key, value, key)) + else: # writeDict['format'] == 'txt': + write_dict['logHandle'].write( + "%s%40s : %s\n" % (indent, key, value)) + + def write_log_header(self, write_dict): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import platform + + # Retrieve operating environment information + user = None + try: + user = os.getlogin() + except: + try: + user = os.getenv("USERNAME") + if user is None: + user = os.getenv("USER") + except: + user = "unknown_user" + try: + (sysname, nodename, release, version, machine, + processor) = platform.uname() + except: + (sysname, nodename, release, version, machine, processor) = ( + "unknown_sysname", "unknown_nodename", "unknown_release", + "unknown_version", "unknown_machine", "unknown_processor") + try: + hostname = platform.node() + except: + hostname = "unknown_hostname" + + self.write_key(write_dict, 'process', None, 'start') + write_dict['indentationLevel'] += 1 + + self.write_key(write_dict, 'description', self.description) + self.write_key(write_dict, 'cmd', self.cmd) + if self.args: + self.write_key(write_dict, 'args', ' '.join(self.args)) + self.write_key(write_dict, 'start', self.start) + self.write_key(write_dict, 'end', self.end) + self.write_key(write_dict, 'elapsed', self.get_elapsed_seconds()) + + self.write_key(write_dict, 'user', user) + self.write_key(write_dict, 'sysname', sysname) + self.write_key(write_dict, 'nodename', nodename) + self.write_key(write_dict, 'release', release) + self.write_key(write_dict, 'version', version) + self.write_key(write_dict, 'machine', machine) + self.write_key(write_dict, 'processor', processor) + + if len(self.process_keys) > 0: + self.write_key(write_dict, 'processKeys', None, 'start') + for pair in self.process_keys: + (key, value) = pair + write_dict['indentationLevel'] += 1 + self.write_key(write_dict, key, value) + write_dict['indentationLevel'] -= 1 + self.write_key(write_dict, 'processKeys', None, 'stop') + + self.write_key(write_dict, 'status', self.status) + + def write_log_footer(self, write_dict): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + write_dict['indentationLevel'] -= 1 + self.write_key(write_dict, 'process', None, 'stop') + + def write_log(self, + log_handle=sys.stdout, + indentation_level=0, + format='xml'): + """ + Writes logging information to the specified handle. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + write_dict = {} + write_dict['logHandle'] = log_handle + write_dict['indentationLevel'] = indentation_level + write_dict['format'] = format + + if log_handle: + self.write_log_header(write_dict) + + if self.log: + self.write_key(write_dict, 'output', None, 'start') + if format == 'xml': + log_handle.write("\n") + self.write_key(write_dict, 'output', None, 'stop') + + self.write_log_footer(write_dict) + + def write_log_to_disk(self, log_filename=None, format='xml', header=None): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if log_filename: + try: + # This also doesn't seem like the best structure... + # 3.1 + try: + log_handle = open(log_filename, + mode='wt', + encoding="utf-8") + # 2.6 + except: + log_handle = open(log_filename, + mode='wt') + except: + print("Couldn't open log : %s" % log_filename) + log_handle = None + + if log_handle: + if header: + if format == 'xml': + log_handle.write("\n") + self.write_log(log_handle) + log_handle.close() + + def log_line(self, line): + """ + Adds a line of text to the log. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + self.log.append(line.rstrip()) + if self.echo: + print("%s" % line.rstrip()) + + def execute(self): + """ + Executes the current process. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import datetime + import traceback + + try: + import subprocess as sp + except: + sp = None + + self.start = datetime.datetime.now() + + cmdargs = [self.cmd] + cmdargs.extend(self.args) + + if self.echo: + if sp: + print( + "\n%s : %s\n" % (self.__class__, sp.list2cmdline(cmdargs))) + else: + print("\n%s : %s\n" % (self.__class__, " ".join(cmdargs))) + + # intialize a few variables that may or may not be set later + process = None + tmp_wrapper = None + stdout = None + stdin = None + parentenv = os.environ + parentcwd = os.getcwd() + + try: + # Using subprocess + if sp: + if self.batch_wrapper: + cmd = " ".join(cmdargs) + tmp_wrapper = os.path.join(self.cwd, "process.bat") + write_text(cmd, tmp_wrapper) + print("%s : Running process through wrapper %s\n" % ( + self.__class__, tmp_wrapper)) + process = sp.Popen([tmp_wrapper], stdout=sp.PIPE, + stderr=sp.STDOUT, + cwd=self.cwd, env=self.env) + else: + process = sp.Popen(cmdargs, stdout=sp.PIPE, + stderr=sp.STDOUT, + cwd=self.cwd, env=self.env) + + # using os.popen4 + else: + if self.env: + os.environ = self.env + if self.cwd: + os.chdir(self.cwd) + + stdin, stdout = os.popen4(cmdargs, 'r') + except: + print("Couldn't execute command : %s" % cmdargs[0]) + traceback.print_exc() + + # Using subprocess + if sp: + if process != None: + # pid = process.pid + # log.logLine("process id %s\n" % pid) + + try: + # This is more proper python, and resolves some issues with + # a process ending before all of its output has been + # processed, but it also seems to stall when the read + # buffer is near or over it's limit. this happens + # relatively frequently with processes that generate lots + # of print statements. + for line in process.stdout: + self.log_line(line) + # + # So we go with the, um, uglier option below + + # This is now used to ensure that the process has finished + line = "" + while line != None and process.poll() is None: + try: + line = process.stdout.readline() + except: + break + # 3.1 + try: + self.log_line(str(line, encoding="utf-8")) + # 2.6 + except: + self.log_line(line) + except: + self.log_line("Logging error : %s" % sys.exc_info()[0]) + + self.status = process.returncode + + if self.batch_wrapper and tmp_wrapper: + try: + os.remove(tmp_wrapper) + except: + print( + "Couldn't remove temp wrapper : %s" % tmp_wrapper) + traceback.print_exc() + + # Using os.popen4 + else: + exit_code = -1 + try: + # print("reading stdout lines") + stdout_lines = stdout.readlines() + exit_code = stdout.close() + + stdout.close() + stdin.close() + + if self.env: + os.environ = parentenv + if self.cwd: + os.chdir(parentcwd) + + if len(stdout_lines) > 0: + for line in stdout_lines: + self.log_line(line) + + if not exit_code: + exit_code = 0 + except: + self.log_line("Logging error : %s" % sys.exc_info()[0]) + + self.status = exit_code + + self.end = datetime.datetime.now() + + +class ProcessList(Process): + """ + A list of processes with logged output. + """ + + def __init__(self, description, blocking=True, cwd=None, env=None): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + Process.__init__(self, description, None, None, cwd, env) + "Initialize the standard class variables" + self.processes = [] + self.blocking = blocking + + def generate_report(self, write_dict): + """ + Generates a log based on the success of the child processes. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + if self.processes: + _status = True + indent = '\t' * (write_dict['indentationLevel'] + 1) + + self.log = [] + + for child in self.processes: + if isinstance(child, ProcessList): + child.generate_report(write_dict) + + child_result = "" + key = child.description + value = child.status + if write_dict['format'] == 'xml': + child_result = ( + "%s%s" % ( + indent, key, value)) + else: # writeDict['format'] == 'txt': + child_result = ("%s%40s : %s" % (indent, key, value)) + self.log.append(child_result) + + if child.status != 0: + _status = False + if not _status: + self.status = -1 + else: + self.status = 0 + else: + self.log = ["No child processes available to generate a report"] + self.status = -1 + + def write_log_header(self, write_dict): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + self.write_key(write_dict, 'processList', None, 'start') + write_dict['indentationLevel'] += 1 + + self.write_key(write_dict, 'description', self.description) + self.write_key(write_dict, 'start', self.start) + self.write_key(write_dict, 'end', self.end) + self.write_key(write_dict, 'elapsed', self.get_elapsed_seconds()) + + self.generate_report(write_dict) + + self.write_key(write_dict, 'status', self.status) + + def write_log_footer(self, write_dict): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + write_dict['indentationLevel'] -= 1 + self.write_key(write_dict, 'processList', None, 'stop') + + def write_log(self, + log_handle=sys.stdout, + indentation_level=0, + format='xml'): + """ + Writes logging information to the specified handle. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + write_dict = {} + write_dict['logHandle'] = log_handle + write_dict['indentationLevel'] = indentation_level + write_dict['format'] = format + + if log_handle: + self.write_log_header(write_dict) + + if self.log: + self.write_key(write_dict, 'output', None, 'start') + for line in self.log: + log_handle.write('%s%s\n' % ("", line)) + self.write_key(write_dict, 'output', None, 'stop') + + if self.processes: + self.write_key(write_dict, 'processes', None, 'start') + for child in self.processes: + child.write_log(log_handle, indentation_level + 1, format) + self.write_key(write_dict, 'processes', None, 'stop') + + self.write_log_footer(write_dict) + + def execute(self): + """ + Executes the list of processes. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import datetime + + self.start = datetime.datetime.now() + + self.status = 0 + if self.processes: + for child in self.processes: + if child: + try: + child.execute() + except: + print("%s : caught exception in child class %s" % ( + self.__class__, child.__class__)) + traceback.print_exc() + child.status = -1 + + if self.blocking and child.status != 0: + print("%s : child class %s finished with an error" % ( + self.__class__, child.__class__)) + self.status = -1 + break + + self.end = datetime.datetime.now() + + +def main(): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + import optparse + + p = optparse.OptionParser(description='A process logging script', + prog='process', + version='process 0.1', + usage=('%prog [options] ' + '[options for the logged process]')) + p.add_option('--cmd', '-c', default=None) + p.add_option('--log', '-l', default=None) + + options, arguments = p.parse_args() + + # + # Get options + # + cmd = options.cmd + log_filename = options.log + + try: + args_start = sys.argv.index('--') + 1 + args = sys.argv[args_start:] + except: + args_start = len(sys.argv) + 1 + args = [] + + if cmd is None: + print("process: No command specified") + + # + # Test regular logging + # + process = Process(description="a process", cmd=cmd, args=args) + + # + # Test report generation and writing a log + # + process_list = ProcessList("a process list") + process_list.processes.append(process) + process_list.echo = True + process_list.execute() + + process_list.write_log_to_disk(log_filename) + + +if __name__ == '__main__': + main() diff --git a/aces_1.0.0/python/aces_ocio/tests/__init__.py b/aces_1.0.0/python/aces_ocio/tests/__init__.py new file mode 100644 index 0000000..a5682fb --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- \ No newline at end of file diff --git a/aces_1.0.0/python/aces_ocio/tests/tests_aces_config.py b/aces_1.0.0/python/aces_ocio/tests/tests_aces_config.py new file mode 100644 index 0000000..61acfa5 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/tests/tests_aces_config.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines unit tests for *ACES* configuration. +""" + +import hashlib +import os +import re +import shutil +import sys + +# TODO: Temporary ugly thing to be discussed, ideally the package should be +# in PYTHONPATH. +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) + +import tempfile +import unittest + +from aces_ocio.utilities import files_walker +from aces_ocio.create_aces_config import ( + ACES_OCIO_CTL_DIRECTORY_ENVIRON, + create_ACES_config) + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['REFERENCE_CONFIG_ROOT_DIRECTORY', + 'HASH_TEST_PATTERNS', + 'UNHASHABLE_TEST_PATTERNS', + 'TestACESConfig'] + + +# TODO: Investigate how the current config has been generated to use it for +# tests. +# REFERENCE_CONFIG_ROOT_DIRECTORY = os.path.abspath( +# os.path.join(os.path.dirname(__file__), '..', '..', '..')) +REFERENCE_CONFIG_ROOT_DIRECTORY = '/colour-science/colour-ramblings/ocio/aces' + +HASH_TEST_PATTERNS = ('\.3dl', '\.lut', '\.csp') +UNHASHABLE_TEST_PATTERNS = ('\.icc', '\.ocio') + + +class TestACESConfig(unittest.TestCase): + """ + Performs tests on the *ACES* configuration. + """ + + def setUp(self): + """ + Initialises common tests attributes. + """ + + self.__aces_ocio_ctl_directory = os.environ.get( + ACES_OCIO_CTL_DIRECTORY_ENVIRON, None) + + assert self.__aces_ocio_ctl_directory is not None, ( + 'Undefined "{0}" environment variable!'.format( + ACES_OCIO_CTL_DIRECTORY_ENVIRON)) + + assert os.path.exists(self.__aces_ocio_ctl_directory) is True, ( + '"{0}" directory does not exists!'.format( + self.__aces_ocio_ctl_directory)) + + self.maxDiff = None + self.__temporary_directory = tempfile.mkdtemp() + + def tearDown(self): + """ + Post tests actions. + """ + + shutil.rmtree(self.__temporary_directory) + + @staticmethod + def directory_hashes(directory, + filters_in=None, + filters_out=None, + flags=0): + """ + Recursively computes the hashes from the file within given directory. + + Parameters + ---------- + directory : str or unicode + Directory to compute the file hashes. + filters_in : array_like + Included patterns. + filters_out : array_like + Excluded patterns. + flags : int + Regex flags. + + Returns + ------- + dict + Directory file hashes. + """ + + hashes = {} + for path in files_walker(directory, + filters_in=filters_in, + filters_out=filters_out): + with open(path) as file: + hash = hashlib.md5( + re.sub('\s', '', file.read())).hexdigest() + hashes[path.replace(directory, '')] = hash + return hashes + + def test_ACES_config(self): + """ + Performs tests on the *ACES* configuration by computing hashes on the + generated configuration and comparing them to the existing one. + """ + + self.assertTrue(create_ACES_config(self.__aces_ocio_ctl_directory, + self.__temporary_directory)) + + reference_hashes = self.directory_hashes( + REFERENCE_CONFIG_ROOT_DIRECTORY, + HASH_TEST_PATTERNS) + test_hashes = self.directory_hashes( + self.__temporary_directory, + HASH_TEST_PATTERNS) + + self.assertDictEqual(reference_hashes, test_hashes) + + # Checking that unashable files ('.icc', '.ocio') are generated. + unashable = lambda x: ( + sorted([file.replace(x, '') for file in + files_walker(x, UNHASHABLE_TEST_PATTERNS)])) + + self.assertListEqual(unashable(REFERENCE_CONFIG_ROOT_DIRECTORY), + unashable(self.__temporary_directory)) + + +if __name__ == '__main__': + unittest.main() diff --git a/aces_1.0.0/python/aces_ocio/utilities.py b/aces_1.0.0/python/aces_ocio/utilities.py new file mode 100644 index 0000000..00804e3 --- /dev/null +++ b/aces_1.0.0/python/aces_ocio/utilities.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines various package utilities objects. +""" + +import os +import re + +import PyOpenColorIO as OCIO + +__author__ = 'ACES Developers' +__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers' +__license__ = '' +__maintainer__ = 'ACES Developers' +__email__ = 'aces@oscars.org' +__status__ = 'Production' + +__all__ = ['ColorSpace', + 'mat44_from_mat33', + 'filter_words', + 'files_walker'] + +# +# Utility classes and functions +# + +class ColorSpace(object): + """ + A container for data needed to define an *OCIO* *ColorSpace*. + """ + + def __init__(self, + name, + description=None, + bit_depth=OCIO.Constants.BIT_DEPTH_F32, + equality_group=None, + family=None, + is_data=False, + to_reference_transforms=[], + from_reference_transforms=[], + allocation_type=OCIO.Constants.ALLOCATION_UNIFORM, + allocation_vars=[0.0, 1.0]): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + self.name = name + self.bit_depth = bit_depth + self.description = description + self.equality_group = equality_group + self.family = family + self.is_data = is_data + self.to_reference_transforms = to_reference_transforms + self.from_reference_transforms = from_reference_transforms + self.allocation_type = allocation_type + self.allocation_vars = allocation_vars + + +def mat44_from_mat33(mat33): + """ + Creates a 4x4 matrix from given 3x3 matrix. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + return [mat33[0], mat33[1], mat33[2], 0.0, + mat33[3], mat33[4], mat33[5], 0.0, + mat33[6], mat33[7], mat33[8], 0.0, + 0, 0, 0, 1.0] + + +def filter_words(words, filters_in=None, filters_out=None, flags=0): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + filtered_words = [] + for word in words: + if filters_in: + filter_matched = False + for filter in filters_in: + if re.search(filter, word, flags): + filter_matched = True + break + if not filter_matched: + continue + + if filters_out: + filter_matched = False + for filter in filters_out: + if re.search(filter, word, flags): + filter_matched = True + break + if filter_matched: + continue + filtered_words.append(word) + return filtered_words + + +def files_walker(directory, filters_in=None, filters_out=None, flags=0): + """ + Object description. + + Parameters + ---------- + parameter : type + Parameter description. + + Returns + ------- + type + Return value description. + """ + + for parent_directory, directories, files in os.walk(directory, + topdown=False, + followlinks=True): + for file in files: + path = os.path.join(parent_directory, file) + if os.path.isfile(path): + if not filter_words((path,), filters_in, filters_out, flags): + continue + + yield path diff --git a/aces_1.0.0/python/createARRIColorSpaces.py b/aces_1.0.0/python/createARRIColorSpaces.py deleted file mode 100644 index 73e175b..0000000 --- a/aces_1.0.0/python/createARRIColorSpaces.py +++ /dev/null @@ -1,137 +0,0 @@ -import math -import array - -from util import * -import generateLUT as genlut - - -# -# LogC to ACES -# -def createLogC(gamut, transferFunction, exposureIndex, name, lutDir, lutResolution1d): - name = "%s (EI%s) - %s" % (transferFunction, exposureIndex, gamut) - if transferFunction == "": - name = "Linear - %s" % gamut - if gamut == "": - name = "%s (EI%s)" % (transferFunction, exposureIndex) - - cs = ColorSpace(name) - cs.description = name - cs.equalityGroup = '' - cs.family = 'ARRI' - cs.isData=False - - # Globals - IDT_maker_version = "0.08" - - nominalEI = 400.0 - blackSignal = 0.003907 - midGraySignal = 0.01 - encodingGain = 0.256598 - encodingOffset = 0.391007 - - def gainForEI(EI) : - return (math.log(EI/nominalEI)/math.log(2) * (0.89 - 1) / 3 + 1) * encodingGain - - def LogCInverseParametersForEI(EI) : - cut = 1.0 / 9.0 - slope = 1.0 / (cut * math.log(10)) - offset = math.log10(cut) - slope * cut - gain = EI / nominalEI - gray = midGraySignal / gain - # The higher the EI, the lower the gamma - encGain = gainForEI(EI) - encOffset = encodingOffset - for i in range(0,3) : - nz = ((95.0 / 1023.0 - encOffset) / encGain - offset) / slope - encOffset = encodingOffset - math.log10(1 + nz) * encGain - # Calculate some intermediate values - a = 1.0 / gray - b = nz - blackSignal / gray - e = slope * a * encGain - f = encGain * (slope * b + offset) + encOffset - # Manipulations so we can return relative exposure - s = 4 / (0.18 * EI) - t = blackSignal - b = b + a * t - a = a * s - f = f + e * t - e = e * s - return { 'a' : a, - 'b' : b, - 'cut' : (cut - b) / a, - 'c' : encGain, - 'd' : encOffset, - 'e' : e, - 'f' : f } - - def logCtoLinear(codeValue, exposureIndex): - p = LogCInverseParametersForEI(exposureIndex) - breakpoint = p['e'] * p['cut'] + p['f'] - if (codeValue > breakpoint): - linear = (pow(10,(codeValue/1023.0 - p['d']) / p['c']) - p['b']) / p['a'] - else: - linear = (codeValue/1023.0 - p['f']) / p['e'] - - #print( codeValue, linear ) - return linear - - - cs.toReferenceTransforms = [] - - if transferFunction == "V3 LogC": - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = logCtoLinear(1023.0*c/(lutResolution1d-1), int(exposureIndex)) - - lut = "%s_to_linear.spi1d" % ("%s_%s" % (transferFunction, exposureIndex)) - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - #print( "Writing %s" % lut) - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - if gamut == 'Wide Gamut': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.680206, 0.236137, 0.083658, - 0.085415, 1.017471, -0.102886, - 0.002057, -0.062563, 1.060506]), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - -def createColorSpaces(lutDir, lutResolution1d): - colorspaces = [] - - transferFunction = "V3 LogC" - gamut = "Wide Gamut" - #EIs = [160.0, 200.0, 250.0, 320.0, 400.0, 500.0, 640.0, 800.0, 1000.0, 1280.0, 1600.0, 2000.0, 2560.0, 3200.0] - EIs = [160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600, 2000, 2560, 3200] - defaultEI = 800 - - # Full conversion - for EI in EIs: - LogCEIfull = createLogC(gamut, transferFunction, EI, "LogC", lutDir, lutResolution1d) - colorspaces.append(LogCEIfull) - - # Linearization only - for EI in [800]: - LogCEIlinearization = createLogC("", transferFunction, EI, "LogC", lutDir, lutResolution1d) - colorspaces.append(LogCEIlinearization) - - # Primaries - LogCEIprimaries = createLogC(gamut, "", defaultEI, "LogC", lutDir, lutResolution1d) - colorspaces.append(LogCEIprimaries) - - return colorspaces diff --git a/aces_1.0.0/python/createCanonColorSpaces.py b/aces_1.0.0/python/createCanonColorSpaces.py deleted file mode 100644 index 194cc2e..0000000 --- a/aces_1.0.0/python/createCanonColorSpaces.py +++ /dev/null @@ -1,158 +0,0 @@ -import array - -from util import * -import generateLUT as genlut - -# -# Canon-Log to ACES -# -def createCanonLog(gamut, transferFunction, name, lutDir, lutResolution1d): - name = "%s - %s" % (transferFunction, gamut) - if transferFunction == "": - name = "Linear - %s" % gamut - if gamut == "": - name = "%s" % transferFunction - - cs = ColorSpace(name) - cs.description = name - cs.equalityGroup = '' - cs.family = 'Canon' - cs.isData=False - - def legalToFull(codeValue): - return (codeValue - 64.0)/(940.0 - 64.0) - - def canonLogToLinear(codeValue): - # log = fullToLegal(c1 * log10(c2*linear + 1) + c3) - # linear = (pow(10, (legalToFul(log) - c3)/c1) - 1)/c2 - c1 = 0.529136 - c2 = 10.1596 - c3 = 0.0730597 - - linear = (pow(10.0, (legalToFull(codeValue) - c3)/c1) -1.0)/c2 - linear = 0.9 * linear - #print( codeValue, linear ) - return linear - - cs.toReferenceTransforms = [] - - if transferFunction == "Canon-Log": - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = canonLogToLinear(1023.0*c/(lutResolution1d-1)) - - lut = "%s_to_linear.spi1d" % transferFunction - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - if gamut == 'Rec. 709 Daylight': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.561538969, 0.402060105, 0.036400926, 0.0, - 0.092739623, 0.924121198, -0.016860821, 0.0, - 0.084812961, 0.006373835, 0.908813204, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - elif gamut == 'Rec. 709 Tungsten': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.566996399, 0.365079418, 0.067924183, 0.0, - 0.070901044, 0.880331008, 0.048767948, 0.0, - 0.073013542, -0.066540862, 0.99352732, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - elif gamut == 'DCI-P3 Daylight': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.607160575, 0.299507286, 0.093332140, 0.0, - 0.004968120, 1.050982224, -0.055950343, 0.0, - -0.007839939, 0.000809127, 1.007030813, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - elif gamut == 'DCI-P3 Tungsten': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.650279125, 0.253880169, 0.095840706, 0.0, - -0.026137986, 1.017900530, 0.008237456, 0.0, - 0.007757558, -0.063081669, 1.055324110, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - elif gamut == 'Cinema Gamut Daylight': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.763064455, 0.149021161, 0.087914384, 0.0, - 0.003657457, 1.10696038, -0.110617837, 0.0, - -0.009407794,-0.218383305, 1.227791099, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - elif gamut == 'Cinema Gamut Tungsten': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.817416293, 0.090755698, 0.091828009, 0.0, - -0.035361374, 1.065690585, -0.030329211, 0.0, - 0.010390366, -0.299271107, 1.288880741, 0.0, - 0,0,0,1.0], - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - -# Generate all color spaces conversion -def createColorSpaces(lutDir, lutResolution1d): - colorspaces = [] - - # Full conversion - CanonLog1 = createCanonLog("Rec. 709 Daylight", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog1) - - CanonLog2 = createCanonLog("Rec. 709 Tungsten", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog2) - - CanonLog3 = createCanonLog("DCI-P3 Daylight", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog3) - - CanonLog4 = createCanonLog("DCI-P3 Tungsten", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog4) - - CanonLog5 = createCanonLog("Cinema Gamut Daylight", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog5) - - CanonLog6 = createCanonLog("Cinema Gamut Tungsten", "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog6) - - # Linearization only - CanonLog7 = createCanonLog('', "Canon-Log", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog7) - - # Primaries only - CanonLog8 = createCanonLog("Rec. 709 Daylight", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog8) - - CanonLog9 = createCanonLog("Rec. 709 Tungsten", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog9) - - CanonLog10 = createCanonLog("DCI-P3 Daylight", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog10) - - CanonLog11 = createCanonLog("DCI-P3 Tungsten", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog11) - - CanonLog12 = createCanonLog("Cinema Gamut Daylight", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog12) - - CanonLog13 = createCanonLog("Cinema Gamut Tungsten", "", "Canon-Log", lutDir, lutResolution1d) - colorspaces.append(CanonLog13) - - return colorspaces diff --git a/aces_1.0.0/python/createREDColorSpaces.py b/aces_1.0.0/python/createREDColorSpaces.py deleted file mode 100644 index 0e3823d..0000000 --- a/aces_1.0.0/python/createREDColorSpaces.py +++ /dev/null @@ -1,134 +0,0 @@ -import array - -from util import * -import generateLUT as genlut - -# -# RED color spaces to ACES -# -def createREDlogFilm(gamut, transferFunction, name, lutDir, lutResolution1d): - name = "%s - %s" % (transferFunction, gamut) - if transferFunction == "": - name = "Linear - %s" % gamut - if gamut == "": - name = "%s" % transferFunction - - cs = ColorSpace(name) - cs.description = name - cs.equalityGroup = '' - cs.family = 'RED' - cs.isData=False - - def cineonToLinear(codeValue): - nGamma = 0.6 - blackPoint = 95.0 - whitePoint = 685.0 - codeValueToDensity = 0.002 - - blackLinear = pow(10.0, (blackPoint - whitePoint) * (codeValueToDensity / nGamma)) - codeLinear = pow(10.0, (codeValue - whitePoint) * (codeValueToDensity / nGamma)) - - return (codeLinear - blackLinear)/(1.0 - blackLinear) - - cs.toReferenceTransforms = [] - - if transferFunction == 'REDlogFilm': - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = cineonToLinear(1023.0*c/(lutResolution1d-1)) - - lut = "CineonLog_to_linear.spi1d" - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - if gamut == 'DRAGONcolor': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.532279, 0.376648, 0.091073, - 0.046344, 0.974513, -0.020860, - -0.053976, -0.000320, 1.054267]), - 'direction':'forward' - }) - elif gamut == 'DRAGONcolor2': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.468452, 0.331484, 0.200064, - 0.040787, 0.857658, 0.101553, - -0.047504, -0.000282, 1.047756]), - 'direction':'forward' - }) - elif gamut == 'REDcolor2': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.480997, 0.402289, 0.116714, - -0.004938, 1.000154, 0.004781, - -0.105257, 0.025320, 1.079907]), - 'direction':'forward' - }) - elif gamut == 'REDcolor3': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.512136, 0.360370, 0.127494, - 0.070377, 0.903884, 0.025737, - -0.020824, 0.017671, 1.003123]), - 'direction':'forward' - }) - elif gamut == 'REDcolor4': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.474202, 0.333677, 0.192121, - 0.065164, 0.836932, 0.097901, - -0.019281, 0.016362, 1.002889]), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - -# Generate all color spaces conversion -def createColorSpaces(lutDir, lutResolution1d): - colorspaces = [] - - # Full conversion - REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmDRAGON) - - REDlogFilmDRAGON2 = createREDlogFilm("DRAGONcolor2", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmDRAGON2) - - REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor2) - - REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor3) - - REDlogFilmREDcolor4 = createREDlogFilm("REDcolor4", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor4) - - # Linearization only - REDlogFilmDRAGON = createREDlogFilm("", "REDlogFilm", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmDRAGON) - - # Primaries only - REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmDRAGON) - - REDlogFilmDRAGON2 = createREDlogFilm("DRAGONcolor2", "", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmDRAGON2) - - REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor2) - - REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor3) - - REDlogFilmREDcolor4 = createREDlogFilm("REDcolor4", "", "REDlogFilm", lutDir, lutResolution1d) - colorspaces.append(REDlogFilmREDcolor4) - - return colorspaces diff --git a/aces_1.0.0/python/createSonyColorSpaces.py b/aces_1.0.0/python/createSonyColorSpaces.py deleted file mode 100644 index dcfbb43..0000000 --- a/aces_1.0.0/python/createSonyColorSpaces.py +++ /dev/null @@ -1,198 +0,0 @@ -import array - -from util import * -import generateLUT as genlut - -# -# SLog to ACES -# -def createSlog(gamut, transferFunction, name, lutDir, lutResolution1d): - name = "%s - %s" % (transferFunction, gamut) - if transferFunction == "": - name = "Linear - %s" % gamut - if gamut == "": - name = "%s" % transferFunction - - cs = ColorSpace(name) - cs.description = name - cs.equalityGroup = '' - cs.family = 'Sony' - cs.isData=False - - def sLog1ToLinear(SLog): - b = 64. - ab = 90. - w = 940. - - if (SLog >= ab): - lin = ( pow(10., ( ( ( SLog - b) / ( w - b) - 0.616596 - 0.03) / 0.432699)) - 0.037584) * 0.9 - else: - lin = ( ( ( SLog - b) / ( w - b) - 0.030001222851889303) / 5.) * 0.9 - return lin - - def sLog2ToLinear(SLog): - b = 64. - ab = 90. - w = 940. - - if (SLog >= ab): - lin = ( 219. * ( pow(10., ( ( ( SLog - b) / ( w - b) - 0.616596 - 0.03) / 0.432699)) - 0.037584) / 155.) * 0.9 - else: - lin = ( ( ( SLog - b) / ( w - b) - 0.030001222851889303) / 3.53881278538813) * 0.9 - return lin - - def sLog3ToLinear(codeValue): - if codeValue >= (171.2102946929): - linear = pow(10.0, ((codeValue - 420.0) / 261.5)) * (0.18 + 0.01) - 0.01 - else: - linear = (codeValue - 95.0)*0.01125000/(171.2102946929 - 95.0) - #print( codeValue, linear ) - return linear - - cs.toReferenceTransforms = [] - - if transferFunction == "S-Log1": - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = sLog1ToLinear(1023.0*c/(lutResolution1d-1)) - - lut = "%s_to_linear.spi1d" % transferFunction - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - #print( "Writing %s" % lut) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - elif transferFunction == "S-Log2": - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = sLog2ToLinear(1023.0*c/(lutResolution1d-1)) - - lut = "%s_to_linear.spi1d" % transferFunction - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - #print( "Writing %s" % lut) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - elif transferFunction == "S-Log3": - data = array.array('f', "\0" * lutResolution1d * 4) - for c in range(lutResolution1d): - data[c] = sLog3ToLinear(1023.0*c/(lutResolution1d-1)) - - lut = "%s_to_linear.spi1d" % transferFunction - genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1) - - #print( "Writing %s" % lut) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - if gamut == 'S-Gamut': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.754338638, 0.133697046, 0.111968437, - 0.021198141, 1.005410934, -0.026610548, - -0.009756991, 0.004508563, 1.005253201]), - 'direction':'forward' - }) - elif gamut == 'S-Gamut Daylight': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.8764457030, 0.0145411681, 0.1090131290, - 0.0774075345, 0.9529571767, -0.0303647111, - 0.0573564351, -0.1151066335, 1.0577501984]), - 'direction':'forward' - }) - elif gamut == 'S-Gamut Tungsten': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([1.0110238740, -0.1362526051, 0.1252287310, - 0.1011994504, 0.9562196265, -0.0574190769, - 0.0600766530, -0.1010185315, 1.0409418785]), - 'direction':'forward' - }) - elif gamut == 'S-Gamut3.Cine': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.6387886672, 0.2723514337, 0.0888598992, - -0.0039159061, 1.0880732308, -0.0841573249, - -0.0299072021, -0.0264325799, 1.0563397820]), - 'direction':'forward' - }) - elif gamut == 'S-Gamut3': - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33([0.7529825954, 0.1433702162, 0.1036471884, - 0.0217076974, 1.0153188355, -0.0370265329, - -0.0094160528, 0.0033704179, 1.0060456349]), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - -def createColorSpaces(lutDir, lutResolution1d): - colorspaces = [] - - # SLog1 - SLog1SGamut = createSlog("S-Gamut", "S-Log1", "S-Log", lutDir, lutResolution1d) - colorspaces.append(SLog1SGamut) - - # SLog2 - SLog2SGamut = createSlog("S-Gamut", "S-Log2", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SLog2SGamut) - - SLog2SGamutDaylight = createSlog("S-Gamut Daylight", "S-Log2", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SLog2SGamutDaylight) - - SLog2SGamutTungsten = createSlog("S-Gamut Tungsten", "S-Log2", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SLog2SGamutTungsten) - - # SLog3 - SLog3SGamut3Cine = createSlog("S-Gamut3.Cine", "S-Log3", "S-Log3", lutDir, lutResolution1d) - colorspaces.append(SLog3SGamut3Cine) - - SLog3SGamut3 = createSlog("S-Gamut3", "S-Log3", "S-Log3", lutDir, lutResolution1d) - colorspaces.append(SLog3SGamut3) - - # Linearization only - SLog1 = createSlog("", "S-Log1", "S-Log", lutDir, lutResolution1d) - colorspaces.append(SLog1) - - SLog2 = createSlog("", "S-Log2", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SLog2) - - SLog3 = createSlog("", "S-Log3", "S-Log3", lutDir, lutResolution1d) - colorspaces.append(SLog3) - - # Primaries only - SGamut = createSlog("S-Gamut", "", "S-Log", lutDir, lutResolution1d) - colorspaces.append(SGamut) - - SGamutDaylight = createSlog("S-Gamut Daylight", "", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SGamutDaylight) - - SGamutTungsten = createSlog("S-Gamut Tungsten", "", "S-Log2", lutDir, lutResolution1d) - colorspaces.append(SGamutTungsten) - - SGamut3Cine = createSlog("S-Gamut3.Cine", "", "S-Log3", lutDir, lutResolution1d) - colorspaces.append(SGamut3Cine) - - SGamut3 = createSlog("S-Gamut3", "", "S-Log3", lutDir, lutResolution1d) - colorspaces.append(SGamut3) - - return colorspaces - diff --git a/aces_1.0.0/python/create_aces_config.py b/aces_1.0.0/python/create_aces_config.py deleted file mode 100755 index 2ff8b85..0000000 --- a/aces_1.0.0/python/create_aces_config.py +++ /dev/null @@ -1,1505 +0,0 @@ -''' -usage from python - -import sys -sys.path.append( "/path/to/script" ) -import create_aces_config as cac -acesReleaseCTLDir = "/path/to/github/checkout/releases/v0.7.1/transforms/ctl" -configDir = "/path/to/config/dir" -cac.createACESConfig(acesReleaseCTLDir, configDir, 1024, 33, True) - -usage from command line, from the directory with 'create_aces_config.py' -python create_aces_config.py -a "/path/to/github/checkout/releases/v0.7.1/transforms/ctl" -c "/path/to/config/dir" --lutResolution1d 1024 --lutResolution3d 33 --keepTempImages - - - -build instructions for osx for needed packages. - -#opencolorio -brew install -vd opencolorio --with-python - -#openimageio -brew tap homebrew/science - -# optional installs -brew install -vd libRaw -brew install -vd OpenCV - -brew install -vd openimageio --with-python - -#ctl -brew install -vd CTL - -#opencolorio - again. -# this time, 'ociolutimage' will build because openimageio is installed -brew uninstall -vd opencolorio -brew install -vd opencolorio --with-python -''' - -import sys -import os -import array -import shutil -import string -import pprint -import math -import numpy - -import OpenImageIO as oiio -import PyOpenColorIO as OCIO - -import process -from util import * - -import generateLUT as genlut -import createREDColorSpaces as red -import createCanonColorSpaces as canon -import createSonyColorSpaces as sony -import createARRIColorSpaces as arri - -# -# Utility functions -# -def setConfigDefaultRoles( config, - color_picking="", - color_timing="", - compositing_log="", - data="", - default="", - matte_paint="", - reference="", - scene_linear="", - texture_paint=""): - - # Add Roles - if color_picking: config.setRole( OCIO.Constants.ROLE_COLOR_PICKING, color_picking ) - if color_timing: config.setRole( OCIO.Constants.ROLE_COLOR_TIMING, color_timing ) - if compositing_log: config.setRole( OCIO.Constants.ROLE_COMPOSITING_LOG, compositing_log ) - if data: config.setRole( OCIO.Constants.ROLE_DATA, data ) - if default: config.setRole( OCIO.Constants.ROLE_DEFAULT, default ) - if matte_paint: config.setRole( OCIO.Constants.ROLE_MATTE_PAINT, matte_paint ) - if reference: config.setRole( OCIO.Constants.ROLE_REFERENCE, reference ) - if scene_linear: config.setRole( OCIO.Constants.ROLE_SCENE_LINEAR, scene_linear ) - if texture_paint: config.setRole( OCIO.Constants.ROLE_TEXTURE_PAINT, texture_paint ) - -# Write config to disk -def writeConfig( config, configPath, sanityCheck=True ): - if sanityCheck: - try: - config.sanityCheck() - except Exception,e: - print e - print "Configuration was not written due to a failed Sanity Check" - return - #sys.exit() - - fileHandle = open( configPath, mode='w' ) - fileHandle.write( config.serialize() ) - fileHandle.close() - -def generateOCIOTransform(transforms): - #print( "Generating transforms") - - interpolationOptions = { - 'linear':OCIO.Constants.INTERP_LINEAR, - 'nearest':OCIO.Constants.INTERP_NEAREST, - 'tetrahedral':OCIO.Constants.INTERP_TETRAHEDRAL - } - directionOptions = { - 'forward':OCIO.Constants.TRANSFORM_DIR_FORWARD, - 'inverse':OCIO.Constants.TRANSFORM_DIR_INVERSE - } - - ocioTransforms = [] - - for transform in transforms: - if transform['type'] == 'lutFile': - - ocioTransform = OCIO.FileTransform( src=transform['path'], - interpolation=interpolationOptions[transform['interpolation']], - direction=directionOptions[transform['direction']] ) - - ocioTransforms.append(ocioTransform) - elif transform['type'] == 'matrix': - ocioTransform = OCIO.MatrixTransform() - # MatrixTransform member variables can't be initialized directly. Each must be set individually - ocioTransform.setMatrix( transform['matrix'] ) - - if 'offset' in transform: - ocioTransform.setOffset( transform['offset'] ) - if 'direction' in transform: - ocioTransform.setDirection( directionOptions[transform['direction']] ) - - ocioTransforms.append(ocioTransform) - elif transform['type'] == 'exponent': - ocioTransform = OCIO.ExponentTransform() - ocioTransform.setValue( transform['value'] ) - - ocioTransforms.append(ocioTransform) - elif transform['type'] == 'log': - ocioTransform = OCIO.LogTransform(base=transform['base'], - direction=directionOptions[transform['direction']]) - - ocioTransforms.append(ocioTransform) - else: - print( "Ignoring unknown transform type : %s" % transform['type'] ) - - # Build a group transform if necessary - if len(ocioTransforms) > 1: - transformG = OCIO.GroupTransform() - for transform in ocioTransforms: - transformG.push_back( transform ) - transform = transformG - - # Or take the first transform from the list - else: - transform = ocioTransforms[0] - - return transform - -def createConfig(configData, nuke=False): - # Create the config - config = OCIO.Config() - - # - # Set config wide values - # - config.setDescription( "An ACES config generated from python" ) - config.setSearchPath( "luts" ) - - # - # Define the reference color space - # - referenceData = configData['referenceColorSpace'] - print( "Adding the reference color space : %s" % referenceData.name) - - # Create a color space - reference = OCIO.ColorSpace( name=referenceData.name, - bitDepth=referenceData.bitDepth, - description=referenceData.description, - equalityGroup=referenceData.equalityGroup, - family=referenceData.family, - isData=referenceData.isData, - allocation=referenceData.allocationType, - allocationVars=referenceData.allocationVars ) - - # Add to config - config.addColorSpace( reference ) - - # - # Create the rest of the color spaces - # - for colorspace in sorted(configData['colorSpaces']): - print( "Creating new color space : %s" % colorspace.name) - - ocioColorspace = OCIO.ColorSpace( name=colorspace.name, - bitDepth=colorspace.bitDepth, - description=colorspace.description, - equalityGroup=colorspace.equalityGroup, - family=colorspace.family, - isData=colorspace.isData, - allocation=colorspace.allocationType, - allocationVars=colorspace.allocationVars ) - - if colorspace.toReferenceTransforms != []: - print( "Generating To-Reference transforms") - ocioTransform = generateOCIOTransform(colorspace.toReferenceTransforms) - ocioColorspace.setTransform( ocioTransform, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE ) - - if colorspace.fromReferenceTransforms != []: - print( "Generating From-Reference transforms") - ocioTransform = generateOCIOTransform(colorspace.fromReferenceTransforms) - ocioColorspace.setTransform( ocioTransform, OCIO.Constants.COLORSPACE_DIR_FROM_REFERENCE ) - - config.addColorSpace(ocioColorspace) - - print( "" ) - - # - # Define the views and displays - # - displays = [] - views = [] - - # Generic display and view setup - if not nuke: - for display, viewList in configData['displays'].iteritems(): - for viewName, colorspace in viewList.iteritems(): - config.addDisplay( display, viewName, colorspace.name ) - if not (viewName in views): - views.append(viewName) - displays.append(display) - # A Nuke specific set of views and displays - # - # XXX - # A few names: Output Transform, ACES, ACEScc, are hard-coded here. Would be better to automate - # - else: - for display, viewList in configData['displays'].iteritems(): - for viewName, colorspace in viewList.iteritems(): - if( viewName == 'Output Transform'): - viewName = 'View' - config.addDisplay( display, viewName, colorspace.name ) - if not (viewName in views): - views.append(viewName) - displays.append(display) - - config.addDisplay( 'linear', 'View', 'ACES2065-1' ) - displays.append('linear') - config.addDisplay( 'log', 'View', 'ACEScc' ) - displays.append('log') - - # Set active displays and views - config.setActiveDisplays( ','.join(sorted(displays)) ) - config.setActiveViews( ','.join(views) ) - - # - # Need to generalize this at some point - # - - # Add Default Roles - setConfigDefaultRoles( config, - color_picking=reference.getName(), - color_timing=reference.getName(), - compositing_log=reference.getName(), - data=reference.getName(), - default=reference.getName(), - matte_paint=reference.getName(), - reference=reference.getName(), - scene_linear=reference.getName(), - texture_paint=reference.getName() ) - - # Check to make sure we didn't screw something up - config.sanityCheck() - - return config - -# -# Functions to generate color space definitions and LUTs for transforms for a specific ACES release -# - -# Output is a list of colorspaces and transforms that convert between those -# colorspaces and reference color space, ACES -def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutResolution1d=4096, lutResolution3d=64, cleanup=True): - print( "generateLUTs - begin" ) - configData = {} - - # - # Define the reference color space - # - ACES = ColorSpace('ACES2065-1') - ACES.description = "The Academy Color Encoding System reference color space" - ACES.equalityGroup = '' - ACES.family = 'ACES' - ACES.isData=False - ACES.allocationType=OCIO.Constants.ALLOCATION_LG2 - ACES.allocationVars=[-15, 6] - - configData['referenceColorSpace'] = ACES - - # - # Define the displays - # - configData['displays'] = {} - - # - # Define the other color spaces - # - configData['colorSpaces'] = [] - - # Matrix converting ACES AP1 primaries to AP0 - acesAP1toAP0 = [ 0.6954522414, 0.1406786965, 0.1638690622, - 0.0447945634, 0.8596711185, 0.0955343182, - -0.0055258826, 0.0040252103, 1.0015006723] - - # Matrix converting ACES AP0 primaries to XYZ - acesAP0toXYZ = [0.9525523959, 0.0000000000, 0.0000936786, - 0.3439664498, 0.7281660966, -0.0721325464, - 0.0000000000, 0.0000000000, 1.0088251844] - - # - # ACEScc - # - def createACEScc(name='ACEScc', minValue=0.0, maxValue=1.0, inputScale=1.0): - cs = ColorSpace(name) - cs.description = "The %s color space" % name - cs.equalityGroup = '' - cs.family = 'ACES' - cs.isData=False - - ctls = [ - '%s/ACEScc/ACEScsc.ACEScc_to_ACES.a1.0.0.ctl' % acesCTLReleaseDir, - # This transform gets back to the AP1 primaries - # Useful as the 1d LUT is only covering the transfer function - # The primaries switch is covered by the matrix below - '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir - ] - lut = "%s_to_ACES.spi1d" % name - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate1dLUTFromCTL( lutDir + "/" + lut, - ctls, - lutResolution1d, - 'float', - inputScale, - 1.0, - {}, - cleanup, - acesCTLReleaseDir, - minValue, - maxValue) - - cs.toReferenceTransforms = [] - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - # AP1 primaries to AP0 primaries - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(acesAP1toAP0), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - - ACEScc = createACEScc() - configData['colorSpaces'].append(ACEScc) - - # - # ACESproxy - # - def createACESProxy(name='ACESproxy'): - cs = ColorSpace(name) - cs.description = "The %s color space" % name - cs.equalityGroup = '' - cs.family = 'ACES' - cs.isData=False - - ctls = [ - '%s/ACESproxy/ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl' % acesCTLReleaseDir, - # This transform gets back to the AP1 primaries - # Useful as the 1d LUT is only covering the transfer function - # The primaries switch is covered by the matrix below - '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir - ] - lut = "%s_to_aces.spi1d" % name - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate1dLUTFromCTL( lutDir + "/" + lut, - ctls, - lutResolution1d, - 'uint16', - 64.0, - 1.0, - {}, - cleanup, - acesCTLReleaseDir ) - - cs.toReferenceTransforms = [] - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - # AP1 primaries to AP0 primaries - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(acesAP1toAP0), - 'direction':'forward' - }) - - - cs.fromReferenceTransforms = [] - return cs - - ACESproxy = createACESProxy() - configData['colorSpaces'].append(ACESproxy) - - # - # ACEScg - # - def createACEScg(name='ACEScg'): - cs = ColorSpace(name) - cs.description = "The %s color space" % name - cs.equalityGroup = '' - cs.family = 'ACES' - cs.isData=False - - cs.toReferenceTransforms = [] - - # AP1 primaries to AP0 primaries - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(acesAP1toAP0), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - - ACEScg = createACEScg() - configData['colorSpaces'].append(ACEScg) - - # - # ADX - # - def createADX(bitdepth=10, name='ADX'): - name = "%s%s" % (name, bitdepth) - cs = ColorSpace(name) - cs.description = "%s color space - used for film scans" % name - cs.equalityGroup = '' - cs.family = 'ADX' - cs.isData=False - - if bitdepth == 10: - cs.bitDepth = bitDepth=OCIO.Constants.BIT_DEPTH_UINT10 - adx_to_cdd = [1023.0/500.0, 0.0, 0.0, 0.0, - 0.0, 1023.0/500.0, 0.0, 0.0, - 0.0, 0.0, 1023.0/500.0, 0.0, - 0.0, 0.0, 0.0, 1.0] - offset = [-95.0/500.0, -95.0/500.0, -95.0/500.0, 0.0] - elif bitdepth == 16: - cs.bitDepth = bitDepth=OCIO.Constants.BIT_DEPTH_UINT16 - adx_to_cdd = [65535.0/8000.0, 0.0, 0.0, 0.0, - 0.0, 65535.0/8000.0, 0.0, 0.0, - 0.0, 0.0, 65535.0/8000.0, 0.0, - 0.0, 0.0, 0.0, 1.0] - offset = [-1520.0/8000.0, -1520.0/8000.0, -1520.0/8000.0, 0.0] - - cs.toReferenceTransforms = [] - - # Convert from ADX to Channel-Dependent Density - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':adx_to_cdd, - 'offset':offset, - 'direction':'forward' - }) - - # Convert from Channel-Dependent Density to Channel-Independent Density - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.75573, 0.22197, 0.02230, 0, - 0.05901, 0.96928, -0.02829, 0, - 0.16134, 0.07406, 0.76460, 0, - 0.0, 0.0, 0.0, 1.0], - 'direction':'forward' - }) - - # Copied from Alex Fry's adx_cid_to_rle.py - def createCIDtoRLELUT(): - def interpolate1D(x, xp, fp): - return numpy.interp(x, xp, fp) - - LUT_1D_xp = [-0.190000000000000, - 0.010000000000000, - 0.028000000000000, - 0.054000000000000, - 0.095000000000000, - 0.145000000000000, - 0.220000000000000, - 0.300000000000000, - 0.400000000000000, - 0.500000000000000, - 0.600000000000000] - - LUT_1D_fp = [-6.000000000000000, - -2.721718645000000, - -2.521718645000000, - -2.321718645000000, - -2.121718645000000, - -1.921718645000000, - -1.721718645000000, - -1.521718645000000, - -1.321718645000000, - -1.121718645000000, - -0.926545676714876] - - REF_PT = (7120.0 - 1520.0) / 8000.0 * (100.0 / 55.0) - math.log(0.18, 10.0) - - def cid_to_rle(x): - if x <= 0.6: - return interpolate1D(x, LUT_1D_xp, LUT_1D_fp) - return (100.0 / 55.0) * x - REF_PT - - def Fit(value, fromMin, fromMax, toMin, toMax): - if fromMin == fromMax: - raise ValueError("fromMin == fromMax") - return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin - - NUM_SAMPLES = 2**12 - RANGE = (-0.19, 3.0) - data = [] - for i in xrange(NUM_SAMPLES): - x = i/(NUM_SAMPLES-1.0) - x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) - data.append(cid_to_rle(x)) - - lut = 'ADX_CID_to_RLE.spi1d' - genlut.writeSPI1D(lutDir + "/" + lut, RANGE[0], RANGE[1], data, NUM_SAMPLES, 1) - - return lut - - # Convert Channel Independent Density values to Relative Log Exposure values - lut = createCIDtoRLELUT() - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - }) - - # Convert Relative Log Exposure values to Relative Exposure values - cs.toReferenceTransforms.append( { - 'type':'log', - 'base':10, - 'direction':'inverse' - }) - - # Convert Relative Exposure values to ACES values - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':[0.72286, 0.12630, 0.15084, 0, - 0.11923, 0.76418, 0.11659, 0, - 0.01427, 0.08213, 0.90359, 0, - 0.0, 0.0, 0.0, 1.0], - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - return cs - - ADX10 = createADX(bitdepth=10) - configData['colorSpaces'].append(ADX10) - - ADX16 = createADX(bitdepth=16) - configData['colorSpaces'].append(ADX16) - - # - # Camera Input Transforms - # - - # RED color spaces to ACES - redColorSpaces = red.createColorSpaces(lutDir, lutResolution1d) - for cs in redColorSpaces: - configData['colorSpaces'].append(cs) - - # Canon-Log to ACES - canonColorSpaces = canon.createColorSpaces(lutDir, lutResolution1d) - for cs in canonColorSpaces: - configData['colorSpaces'].append(cs) - - # SLog to ACES - sonyColorSpaces = sony.createColorSpaces(lutDir, lutResolution1d) - for cs in sonyColorSpaces: - configData['colorSpaces'].append(cs) - - # LogC to ACES - arriColorSpaces = arri.createColorSpaces(lutDir, lutResolution1d) - for cs in arriColorSpaces: - configData['colorSpaces'].append(cs) - - # - # Generic log transform - # - def createGenericLog(name='log', - minValue=0.0, - maxValue=1.0, - inputScale=1.0, - middleGrey=0.18, - minExposure=-6.0, - maxExposure=6.5, - lutResolution1d=lutResolution1d): - cs = ColorSpace(name) - cs.description = "The %s color space" % name - cs.equalityGroup = name - cs.family = 'Utility' - cs.isData=False - - ctls = [ - '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl' % acesCTLReleaseDir - ] - lut = "%s_to_aces.spi1d" % name - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate1dLUTFromCTL( lutDir + "/" + lut, - ctls, - lutResolution1d, - 'float', - inputScale, - 1.0, - { - 'middleGrey' : middleGrey, - 'minExposure' : minExposure, - 'maxExposure' : maxExposure - }, - cleanup, - acesCTLReleaseDir, - minValue, - maxValue) - - cs.toReferenceTransforms = [] - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'linear', - 'direction':'forward' - } ) - - cs.fromReferenceTransforms = [] - return cs - - # - # ACES LMTs - # - def createACESLMT(lmtName, - lmtValues, - shaperInfo, - lutResolution1d=1024, - lutResolution3d=64, - cleanup=True): - cs = ColorSpace("%s" % lmtName) - cs.description = "The ACES Look Transform: %s" % lmtName - cs.equalityGroup = '' - cs.family = 'Look' - cs.isData=False - - import pprint - pprint.pprint( lmtValues ) - - # - # Generate the shaper transform - # - (shaperName, shaperToACESCTL, shaperFromACESCTL, shaperInputScale, shaperParams) = shaperInfo - - shaperLut = "%s_to_aces.spi1d" % shaperName - if( not os.path.exists( lutDir + "/" + shaperLut ) ): - ctls = [ - shaperToACESCTL % acesCTLReleaseDir - ] - - # Remove spaces and parentheses - shaperLut = shaperLut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut, - ctls, - lutResolution1d, - 'float', - 1.0/shaperInputScale, - 1.0, - shaperParams, - cleanup, - acesCTLReleaseDir) - - shaperOCIOTransform = { - 'type':'lutFile', - 'path':shaperLut, - 'interpolation':'linear', - 'direction':'inverse' - } - - # - # Generate the forward transform - # - cs.fromReferenceTransforms = [] - - if 'transformCTL' in lmtValues: - ctls = [ - shaperToACESCTL % acesCTLReleaseDir, - '%s/%s' % (acesCTLReleaseDir, lmtValues['transformCTL']) - ] - lut = "%s.%s.spi3d" % (shaperName, lmtName) - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate3dLUTFromCTL( lutDir + "/" + lut, - ctls, - lutResolution3d, - 'float', - 1.0/shaperInputScale, - 1.0, - shaperParams, - cleanup, - acesCTLReleaseDir ) - - cs.fromReferenceTransforms.append( shaperOCIOTransform ) - cs.fromReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - - # - # Generate the inverse transform - # - cs.toReferenceTransforms = [] - - if 'transformCTLInverse' in lmtValues: - ctls = [ - '%s/%s' % (acesCTLReleaseDir, odtValues['transformCTLInverse']), - shaperFromACESCTL % acesCTLReleaseDir - ] - lut = "Inverse.%s.%s.spi3d" % (odtName, shaperName) - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate3dLUTFromCTL( lutDir + "/" + lut, - ctls, - lutResolution3d, - 'half', - 1.0, - shaperInputScale, - shaperParams, - cleanup, - acesCTLReleaseDir ) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - - shaperInverse = shaperOCIOTransform.copy() - shaperInverse['direction'] = 'forward' - cs.toReferenceTransforms.append( shaperInverse ) - - return cs - - # - # LMT Shaper - # - - lmtLutResolution1d = max(4096, lutResolution1d) - lmtLutResolution3d = max(65, lutResolution3d) - - # Log 2 shaper - lmtShaperName = 'LMT Shaper' - lmtParams = { - 'middleGrey' : 0.18, - 'minExposure' : -10.0, - 'maxExposure' : 6.5 - } - lmtShaper = createGenericLog(name=lmtShaperName, - middleGrey=lmtParams['middleGrey'], - minExposure=lmtParams['minExposure'], - maxExposure=lmtParams['maxExposure'], - lutResolution1d=lmtLutResolution1d) - configData['colorSpaces'].append(lmtShaper) - - shaperInputScale_genericLog2 = 1.0 - - # Log 2 shaper name and CTL transforms bundled up - lmtShaperData = [ - lmtShaperName, - '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl', - '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl', - shaperInputScale_genericLog2, - lmtParams - ] - - sortedLMTs = sorted(lmtInfo.iteritems(), key=lambda x: x[1]) - print( sortedLMTs ) - for lmt in sortedLMTs: - (lmtName, lmtValues) = lmt - cs = createACESLMT( - lmtValues['transformUserName'], - lmtValues, - lmtShaperData, - lmtLutResolution1d, - lmtLutResolution3d, - cleanup) - configData['colorSpaces'].append(cs) - - # - # ACES RRT with the supplied ODT - # - def createACESRRTplusODT(odtName, - odtValues, - shaperInfo, - lutResolution1d=1024, - lutResolution3d=64, - cleanup=True): - cs = ColorSpace("%s" % odtName) - cs.description = "%s - %s Output Transform" % (odtValues['transformUserNamePrefix'], odtName) - cs.equalityGroup = '' - cs.family = 'Output' - cs.isData=False - - import pprint - pprint.pprint( odtValues ) - - # - # Generate the shaper transform - # - #if 'shaperCTL' in odtValues: - (shaperName, shaperToACESCTL, shaperFromACESCTL, shaperInputScale, shaperParams) = shaperInfo - - if 'legalRange' in odtValues: - shaperParams['legalRange'] = odtValues['legalRange'] - else: - shaperParams['legalRange'] = 0 - - shaperLut = "%s_to_aces.spi1d" % shaperName - if( not os.path.exists( lutDir + "/" + shaperLut ) ): - ctls = [ - shaperToACESCTL % acesCTLReleaseDir - ] - - # Remove spaces and parentheses - shaperLut = shaperLut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut, - ctls, - lutResolution1d, - 'float', - 1.0/shaperInputScale, - 1.0, - shaperParams, - cleanup, - acesCTLReleaseDir) - - shaperOCIOTransform = { - 'type':'lutFile', - 'path':shaperLut, - 'interpolation':'linear', - 'direction':'inverse' - } - - # - # Generate the forward transform - # - cs.fromReferenceTransforms = [] - - if 'transformLUT' in odtValues: - # Copy into the lut dir - transformLUTFileName = os.path.basename(odtValues['transformLUT']) - lut = lutDir + "/" + transformLUTFileName - shutil.copy(odtValues['transformLUT'], lut) - - cs.fromReferenceTransforms.append( shaperOCIOTransform ) - cs.fromReferenceTransforms.append( { - 'type':'lutFile', - 'path': transformLUTFileName, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - elif 'transformCTL' in odtValues: - #shaperLut - - ctls = [ - shaperToACESCTL % acesCTLReleaseDir, - '%s/rrt/RRT.a1.0.0.ctl' % acesCTLReleaseDir, - '%s/odt/%s' % (acesCTLReleaseDir, odtValues['transformCTL']) - ] - lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaperName, odtName) - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate3dLUTFromCTL( lutDir + "/" + lut, - #shaperLUT, - ctls, - lutResolution3d, - 'float', - 1.0/shaperInputScale, - 1.0, - shaperParams, - cleanup, - acesCTLReleaseDir ) - - cs.fromReferenceTransforms.append( shaperOCIOTransform ) - cs.fromReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - - # - # Generate the inverse transform - # - cs.toReferenceTransforms = [] - - if 'transformLUTInverse' in odtValues: - # Copy into the lut dir - transformLUTInverseFileName = os.path.basename(odtValues['transformLUTInverse']) - lut = lutDir + "/" + transformLUTInverseFileName - shutil.copy(odtValues['transformLUTInverse'], lut) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path': transformLUTInverseFileName, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - - shaperInverse = shaperOCIOTransform.copy() - shaperInverse['direction'] = 'forward' - cs.toReferenceTransforms.append( shaperInverse ) - elif 'transformCTLInverse' in odtValues: - ctls = [ - '%s/odt/%s' % (acesCTLReleaseDir, odtValues['transformCTLInverse']), - '%s/rrt/InvRRT.a1.0.0.ctl' % acesCTLReleaseDir, - shaperFromACESCTL % acesCTLReleaseDir - ] - lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odtName, shaperName) - - # Remove spaces and parentheses - lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_') - - genlut.generate3dLUTFromCTL( lutDir + "/" + lut, - #None, - ctls, - lutResolution3d, - 'half', - 1.0, - shaperInputScale, - shaperParams, - cleanup, - acesCTLReleaseDir ) - - cs.toReferenceTransforms.append( { - 'type':'lutFile', - 'path':lut, - 'interpolation':'tetrahedral', - 'direction':'forward' - } ) - - shaperInverse = shaperOCIOTransform.copy() - shaperInverse['direction'] = 'forward' - cs.toReferenceTransforms.append( shaperInverse ) - - return cs - - # - # RRT/ODT shaper options - # - shaperData = {} - - # Log 2 shaper - log2ShaperName = shaperName - log2Params = { - 'middleGrey' : 0.18, - 'minExposure' : -6.0, - 'maxExposure' : 6.5 - } - log2Shaper = createGenericLog(name=log2ShaperName, - middleGrey=log2Params['middleGrey'], - minExposure=log2Params['minExposure'], - maxExposure=log2Params['maxExposure']) - configData['colorSpaces'].append(log2Shaper) - - shaperInputScale_genericLog2 = 1.0 - - # Log 2 shaper name and CTL transforms bundled up - log2ShaperData = [ - log2ShaperName, - '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl', - '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl', - shaperInputScale_genericLog2, - log2Params - ] - - shaperData[log2ShaperName] = log2ShaperData - - # - # Shaper that also includes the AP1 primaries - # - Needed for some LUT baking steps - # - log2ShaperAP1 = createGenericLog(name=log2ShaperName, - middleGrey=log2Params['middleGrey'], - minExposure=log2Params['minExposure'], - maxExposure=log2Params['maxExposure']) - log2ShaperAP1.name = "%s - AP1" % log2ShaperAP1.name - # AP1 primaries to AP0 primaries - log2ShaperAP1.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(acesAP1toAP0), - 'direction':'forward' - }) - configData['colorSpaces'].append(log2ShaperAP1) - - # - # Choose your shaper - # - rrtShaperName = log2ShaperName - rrtShaper = log2ShaperData - - # - # RRT + ODT Combinations - # - sortedOdts = sorted(odtInfo.iteritems(), key=lambda x: x[1]) - print( sortedOdts ) - for odt in sortedOdts: - (odtName, odtValues) = odt - - # Have to handle ODTs that can generate either legal or full output - if odtName in ['Academy.Rec2020_100nits_dim.a1.0.0', - 'Academy.Rec709_100nits_dim.a1.0.0', - 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: - odtNameLegal = '%s - Legal' % odtValues['transformUserName'] - else: - odtNameLegal = odtValues['transformUserName'] - - odtLegal = odtValues.copy() - odtLegal['legalRange'] = 1 - - cs = createACESRRTplusODT( - odtNameLegal, - odtLegal, - rrtShaper, - lutResolution1d, - lutResolution3d, - cleanup) - configData['colorSpaces'].append(cs) - - # Create a display entry using this color space - configData['displays'][odtNameLegal] = { - 'Linear':ACES, - 'Log':ACEScc, - 'Output Transform':cs } - - if odtName in ['Academy.Rec2020_100nits_dim.a1.0.0', - 'Academy.Rec709_100nits_dim.a1.0.0', - 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: - - print( "Generating full range ODT for %s" % odtName) - - odtNameFull = "%s - Full" % odtValues['transformUserName'] - odtFull = odtValues.copy() - odtFull['legalRange'] = 0 - - csFull = createACESRRTplusODT( - odtNameFull, - odtFull, - rrtShaper, - lutResolution1d, - lutResolution3d, - cleanup) - configData['colorSpaces'].append(csFull) - - # Create a display entry using this color space - configData['displays'][odtNameFull] = { - 'Linear':ACES, - 'Log':ACEScc, - 'Output Transform':csFull } - - # - # Generic Matrix transform - # - def createGenericMatrix(name='matrix', - fromReferenceValues=[], - toReferenceValues=[]): - cs = ColorSpace(name) - cs.description = "The %s color space" % name - cs.equalityGroup = name - cs.family = 'Utility' - cs.isData=False - - cs.toReferenceTransforms = [] - if toReferenceValues != []: - for matrix in toReferenceValues: - cs.toReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(matrix), - 'direction':'forward' - }) - - cs.fromReferenceTransforms = [] - if fromReferenceValues != []: - for matrix in fromReferenceValues: - cs.fromReferenceTransforms.append( { - 'type':'matrix', - 'matrix':mat44FromMat33(matrix), - 'direction':'forward' - }) - - return cs - - cs = createGenericMatrix('XYZ', fromReferenceValues=[acesAP0toXYZ]) - configData['colorSpaces'].append(cs) - - cs = createGenericMatrix('Linear - AP1', toReferenceValues=[acesAP1toAP0]) - configData['colorSpaces'].append(cs) - - # ACES to Linear, P3D60 primaries - xyzToP3D60 = [ 2.4027414142, -0.8974841639, -0.3880533700, - -0.8325796487, 1.7692317536, 0.0237127115, - 0.0388233815, -0.0824996856, 1.0363685997] - - cs = createGenericMatrix('Linear - P3-D60', fromReferenceValues=[acesAP0toXYZ, xyzToP3D60]) - configData['colorSpaces'].append(cs) - - # ACES to Linear, P3D60 primaries - xyzToP3DCI = [ 2.7253940305, -1.0180030062, -0.4401631952, - -0.7951680258, 1.6897320548, 0.0226471906, - 0.0412418914, -0.0876390192, 1.1009293786] - - cs = createGenericMatrix('Linear - P3-DCI', fromReferenceValues=[acesAP0toXYZ, xyzToP3DCI]) - configData['colorSpaces'].append(cs) - - # ACES to Linear, Rec 709 primaries - xyzToRec709 = [ 3.2409699419, -1.5373831776, -0.4986107603, - -0.9692436363, 1.8759675015, 0.0415550574, - 0.0556300797, -0.2039769589, 1.0569715142] - - cs = createGenericMatrix('Linear - Rec.709', fromReferenceValues=[acesAP0toXYZ, xyzToRec709]) - configData['colorSpaces'].append(cs) - - # ACES to Linear, Rec 2020 primaries - xyzToRec2020 = [ 1.7166511880, -0.3556707838, -0.2533662814, - -0.6666843518, 1.6164812366, 0.0157685458, - 0.0176398574, -0.0427706133, 0.9421031212] - - cs = createGenericMatrix('Linear - Rec.2020', fromReferenceValues=[acesAP0toXYZ, xyzToRec2020]) - configData['colorSpaces'].append(cs) - - print( "generateLUTs - end" ) - return configData - -def generateBakedLUTs(odtInfo, shaperName, bakedDir, configPath, lutResolution1d, lutResolution3d, lutResolutionShaper=1024): - # Add the legal and full variations into this list - odtInfoC = dict(odtInfo) - for odtCTLName, odtValues in odtInfo.iteritems(): - if odtCTLName in ['Academy.Rec2020_100nits_dim.a1.0.0', - 'Academy.Rec709_100nits_dim.a1.0.0', - 'Academy.Rec709_D60sim_100nits_dim.a1.0.0']: - odtName = odtValues["transformUserName"] - - odtValuesLegal = dict(odtValues) - odtValuesLegal["transformUserName"] = "%s - Legal" % odtName - odtInfoC["%s - Legal" % odtCTLName] = odtValuesLegal - - odtValuesFull = dict(odtValues) - odtValuesFull["transformUserName"] = "%s - Full" % odtName - odtInfoC["%s - Full" % odtCTLName] = odtValuesFull - - del( odtInfoC[odtCTLName] ) - - for odtCTLName, odtValues in odtInfoC.iteritems(): - odtPrefix = odtValues["transformUserNamePrefix"] - odtName = odtValues["transformUserName"] - - # For Photoshop - for inputspace in ["ACEScc", "ACESproxy"]: - args = ["--iconfig", configPath, "-v", "--inputspace", inputspace ] - args += ["--outputspace", "%s" % odtName ] - args += ["--description", "%s - %s for %s data" % (odtPrefix, odtName, inputspace) ] - args += ["--shaperspace", shaperName, "--shapersize", str(lutResolutionShaper) ] - args += ["--cubesize", str(lutResolution3d) ] - args += ["--format", "icc", "%s/photoshop/%s for %s.icc" % (bakedDir, odtName, inputspace) ] - - bakeLUT = process.Process(description="bake a LUT", cmd="ociobakelut", args=args) - bakeLUT.execute() - - # For Flame, Lustre - for inputspace in ["ACEScc", "ACESproxy"]: - args = ["--iconfig", configPath, "-v", "--inputspace", inputspace ] - args += ["--outputspace", "%s" % odtName ] - args += ["--description", "%s - %s for %s data" % (odtPrefix, odtName, inputspace) ] - args += ["--shaperspace", shaperName, "--shapersize", str(lutResolutionShaper) ] - args += ["--cubesize", str(lutResolution3d) ] - - fargs = ["--format", "flame", "%s/flame/%s for %s Flame.3dl" % (bakedDir, odtName, inputspace) ] - bakeLUT = process.Process(description="bake a LUT", cmd="ociobakelut", args=(args + fargs)) - bakeLUT.execute() - - largs = ["--format", "lustre", "%s/lustre/%s for %s Lustre.3dl" % (bakedDir, odtName, inputspace) ] - bakeLUT = process.Process(description="bake a LUT", cmd="ociobakelut", args=(args + largs)) - bakeLUT.execute() - - # For Maya, Houdini - for inputspace in ["ACEScg", "ACES2065-1"]: - args = ["--iconfig", configPath, "-v", "--inputspace", inputspace ] - args += ["--outputspace", "%s" % odtName ] - args += ["--description", "%s - %s for %s data" % (odtPrefix, odtName, inputspace) ] - if inputspace == 'ACEScg': - linShaperName = "%s - AP1" % shaperName - else: - linShaperName = shaperName - args += ["--shaperspace", linShaperName, "--shapersize", str(lutResolutionShaper) ] - - args += ["--cubesize", str(lutResolution3d) ] - - margs = ["--format", "cinespace", "%s/maya/%s for %s Maya.csp" % (bakedDir, odtName, inputspace) ] - bakeLUT = process.Process(description="bake a LUT", cmd="ociobakelut", args=(args + margs)) - bakeLUT.execute() - - hargs = ["--format", "houdini", "%s/houdini/%s for %s Houdini.lut" % (bakedDir, odtName, inputspace) ] - bakeLUT = process.Process(description="bake a LUT", cmd="ociobakelut", args=(args + hargs)) - bakeLUT.execute() - - -def createConfigDir(configDir, bakeSecondaryLUTs): - dirs = [configDir, "%s/luts" % configDir] - if bakeSecondaryLUTs: - dirs.extend(["%s/baked" % configDir, - "%s/baked/flame" % configDir, "%s/baked/photoshop" % configDir, - "%s/baked/houdini" % configDir, "%s/baked/lustre" % configDir, - "%s/baked/maya" % configDir]) - - for d in dirs: - if not os.path.exists(d): - os.mkdir(d) - -def getTransformInfo(ctlTransform): - fp = open(ctlTransform, 'rb') - - # Read lines - lines = fp.readlines() - - # Grab transform ID and User Name - transformID = lines[1][3:].split('<')[1].split('>')[1].lstrip().rstrip() - #print( transformID ) - transformUserName = '-'.join(lines[2][3:].split('<')[1].split('>')[1].split('-')[1:]).lstrip().rstrip() - transformUserNamePrefix = lines[2][3:].split('<')[1].split('>')[1].split('-')[0].lstrip().rstrip() - #print( transformUserName ) - fp.close() - - return (transformID, transformUserName, transformUserNamePrefix) - -# For versions after WGR9 -def getODTInfo(acesCTLReleaseDir): - # Credit to Alex Fry for the original approach here - odtDir = os.path.join(acesCTLReleaseDir, "odt") - allodt = [] - for dirName, subdirList, fileList in os.walk(odtDir): - for fname in fileList: - allodt.append((os.path.join(dirName,fname))) - - odtCTLs = [x for x in allodt if ("InvODT" not in x) and (os.path.split(x)[-1][0] != '.')] - - #print odtCTLs - - odts = {} - - for odtCTL in odtCTLs: - odtTokens = os.path.split(odtCTL) - #print( odtTokens ) - - # Handle nested directories - odtPathTokens = os.path.split(odtTokens[-2]) - odtDir = odtPathTokens[-1] - while odtPathTokens[-2][-3:] != 'odt': - odtPathTokens = os.path.split(odtPathTokens[-2]) - odtDir = os.path.join(odtPathTokens[-1], odtDir) - - # Build full name - #print( "odtDir : %s" % odtDir ) - transformCTL = odtTokens[-1] - #print( transformCTL ) - odtName = string.join(transformCTL.split('.')[1:-1], '.') - #print( odtName ) - - # Find id, user name and user name prefix - (transformID, transformUserName, transformUserNamePrefix) = getTransformInfo( - "%s/odt/%s/%s" % (acesCTLReleaseDir, odtDir, transformCTL) ) - - # Find inverse - transformCTLInverse = "InvODT.%s.ctl" % odtName - if not os.path.exists(os.path.join(odtTokens[-2], transformCTLInverse)): - transformCTLInverse = None - #print( transformCTLInverse ) - - # Add to list of ODTs - odts[odtName] = {} - odts[odtName]['transformCTL'] = os.path.join(odtDir, transformCTL) - if transformCTLInverse != None: - odts[odtName]['transformCTLInverse'] = os.path.join(odtDir, transformCTLInverse) - - odts[odtName]['transformID'] = transformID - odts[odtName]['transformUserNamePrefix'] = transformUserNamePrefix - odts[odtName]['transformUserName'] = transformUserName - - print( "ODT : %s" % odtName ) - print( "\tTransform ID : %s" % transformID ) - print( "\tTransform User Name Prefix : %s" % transformUserNamePrefix ) - print( "\tTransform User Name : %s" % transformUserName ) - print( "\tForward ctl : %s" % odts[odtName]['transformCTL']) - if 'transformCTLInverse' in odts[odtName]: - print( "\tInverse ctl : %s" % odts[odtName]['transformCTLInverse']) - else: - print( "\tInverse ctl : %s" % "None" ) - - print( "\n" ) - - return odts - -# For versions after WGR9 -def getLMTInfo(acesCTLReleaseDir): - # Credit to Alex Fry for the original approach here - lmtDir = os.path.join(acesCTLReleaseDir, "lmt") - alllmt = [] - for dirName, subdirList, fileList in os.walk(lmtDir): - for fname in fileList: - alllmt.append((os.path.join(dirName,fname))) - - lmtCTLs = [x for x in alllmt if ("InvLMT" not in x) and ("README" not in x) and (os.path.split(x)[-1][0] != '.')] - - #print lmtCTLs - - lmts = {} - - for lmtCTL in lmtCTLs: - lmtTokens = os.path.split(lmtCTL) - #print( lmtTokens ) - - # Handle nested directories - lmtPathTokens = os.path.split(lmtTokens[-2]) - lmtDir = lmtPathTokens[-1] - while lmtPathTokens[-2][-3:] != 'ctl': - lmtPathTokens = os.path.split(lmtPathTokens[-2]) - lmtDir = os.path.join(lmtPathTokens[-1], lmtDir) - - # Build full name - #print( "lmtDir : %s" % lmtDir ) - transformCTL = lmtTokens[-1] - #print( transformCTL ) - lmtName = string.join(transformCTL.split('.')[1:-1], '.') - #print( lmtName ) - - # Find id, user name and user name prefix - (transformID, transformUserName, transformUserNamePrefix) = getTransformInfo( - "%s/%s/%s" % (acesCTLReleaseDir, lmtDir, transformCTL) ) - - # Find inverse - transformCTLInverse = "InvLMT.%s.ctl" % lmtName - if not os.path.exists(os.path.join(lmtTokens[-2], transformCTLInverse)): - transformCTLInverse = None - #print( transformCTLInverse ) - - # Add to list of LMTs - lmts[lmtName] = {} - lmts[lmtName]['transformCTL'] = os.path.join(lmtDir, transformCTL) - if transformCTLInverse != None: - lmts[odtName]['transformCTLInverse'] = os.path.join(lmtDir, transformCTLInverse) - - lmts[lmtName]['transformID'] = transformID - lmts[lmtName]['transformUserNamePrefix'] = transformUserNamePrefix - lmts[lmtName]['transformUserName'] = transformUserName - - print( "LMT : %s" % lmtName ) - print( "\tTransform ID : %s" % transformID ) - print( "\tTransform User Name Prefix : %s" % transformUserNamePrefix ) - print( "\tTransform User Name : %s" % transformUserName ) - print( "\t Forward ctl : %s" % lmts[lmtName]['transformCTL']) - if 'transformCTLInverse' in lmts[lmtName]: - print( "\t Inverse ctl : %s" % lmts[lmtName]['transformCTLInverse']) - else: - print( "\t Inverse ctl : %s" % "None" ) - - print( "\n" ) - - return lmts - -# -# Create the ACES config -# -def createACESConfig(acesCTLReleaseDir, - configDir, - lutResolution1d=4096, - lutResolution3d=64, - bakeSecondaryLUTs=True, - cleanup=True): - - # Get ODT names and CTL paths - odtInfo = getODTInfo(acesCTLReleaseDir) - - # Get ODT names and CTL paths - lmtInfo = getLMTInfo(acesCTLReleaseDir) - - # Create config dir - createConfigDir(configDir, bakeSecondaryLUTs) - - # Generate config data and LUTs for different transforms - lutDir = "%s/luts" % configDir - shaperName = 'Output Shaper' - configData = generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutResolution1d, lutResolution3d, cleanup) - - # Create the config using the generated LUTs - print( "Creating generic config") - config = createConfig(configData) - print( "\n\n\n" ) - - # Write the config to disk - writeConfig(config, "%s/config.ocio" % configDir ) - - # Create a config that will work well with Nuke using the previously generated LUTs - print( "Creating Nuke-specific config") - nuke_config = createConfig(configData, nuke=True) - print( "\n\n\n" ) - - # Write the config to disk - writeConfig(nuke_config, "%s/nuke_config.ocio" % configDir ) - - # Bake secondary LUTs using the config - if bakeSecondaryLUTs: - generateBakedLUTs(odtInfo, shaperName, "%s/baked" % configDir, "%s/config.ocio" % configDir, lutResolution1d, lutResolution3d, lutResolution1d) - -# -# Main -# -def main(): - import optparse - - p = optparse.OptionParser(description='An OCIO config generation script', - prog='createACESConfig', - version='createACESConfig 0.1', - usage='%prog [options]') - p.add_option('--acesCTLDir', '-a', default=None) - p.add_option('--configDir', '-c', default=None) - p.add_option('--lutResolution1d', default=4096) - p.add_option('--lutResolution3d', default=64) - p.add_option('--dontBakeSecondaryLUTs', action="store_true") - p.add_option('--keepTempImages', action="store_true") - - options, arguments = p.parse_args() - - # - # Get options - # - acesCTLDir = options.acesCTLDir - configDir = options.configDir - lutResolution1d = int(options.lutResolution1d) - lutResolution3d = int(options.lutResolution3d) - bakeSecondaryLUTs = not(options.dontBakeSecondaryLUTs) - cleanupTempImages = not(options.keepTempImages) - - try: - argsStart = sys.argv.index('--') + 1 - args = sys.argv[argsStart:] - except: - argsStart = len(sys.argv)+1 - args = [] - - print( "command line : \n%s\n" % " ".join(sys.argv) ) - - if configDir == None: - print( "process: No ACES CTL directory specified" ) - return - - # - # Generate the configuration - # - createACESConfig(acesCTLDir, configDir, lutResolution1d, lutResolution3d, bakeSecondaryLUTs, cleanupTempImages) -# main - -if __name__ == '__main__': - main() diff --git a/aces_1.0.0/python/generateLUT.py b/aces_1.0.0/python/generateLUT.py deleted file mode 100644 index 874157b..0000000 --- a/aces_1.0.0/python/generateLUT.py +++ /dev/null @@ -1,391 +0,0 @@ -''' -build instructions for osx for needed packages. - -#opencolorio -brew install -vd opencolorio --with-python - -#openimageio -brew tap homebrew/science - -# optional installs -brew install -vd libRaw -brew install -vd OpenCV - -brew install -vd openimageio --with-python - -#ctl -brew install -vd CTL - -#opencolorio - again. -# this time, 'ociolutimage' will build because openimageio is installed -brew uninstall -vd opencolorio -brew install -vd opencolorio --with-python -''' - -import sys -import os -import array - -import OpenImageIO as oiio - -import process - -# -# Functions used to generate LUTs using CTL transforms -# -def generate1dLUTImage(ramp1dPath, resolution=1024, minValue=0.0, maxValue=1.0): - #print( "Generate 1d LUT image - %s" % ramp1dPath) - - # open image - format = os.path.splitext(ramp1dPath)[1] - ramp = oiio.ImageOutput.create(ramp1dPath) - - # set image specs - spec = oiio.ImageSpec() - spec.set_format( oiio.FLOAT ) - #spec.format.basetype = oiio.FLOAT - spec.width = resolution - spec.height = 1 - spec.nchannels = 3 - - ramp.open (ramp1dPath, spec, oiio.Create) - - data = array.array("f", "\0" * spec.width * spec.height * spec.nchannels * 4) - for i in range(resolution): - value = float(i)/(resolution-1) * (maxValue - minValue) + minValue - data[i*spec.nchannels +0] = value - data[i*spec.nchannels +1] = value - data[i*spec.nchannels +2] = value - - ramp.write_image(spec.format, data) - ramp.close() - -# Credit to Alex Fry for the original single channel version of the spi1d writer -def writeSPI1D(filename, fromMin, fromMax, data, entries, channels): - f = file(filename,'w') - f.write("Version 1\n") - f.write("From %f %f\n" % (fromMin, fromMax)) - f.write("Length %d\n" % entries) - f.write("Components %d\n" % (min(3, channels)) ) - f.write("{\n") - for i in range(0, entries): - entry = "" - for j in range(0, min(3, channels)): - entry = "%s %s" % (entry, data[i*channels + j]) - f.write(" %s\n" % entry) - f.write("}\n") - f.close() - -def generate1dLUTFromImage(ramp1dPath, outputPath=None, minValue=0.0, maxValue=1.0): - if outputPath == None: - outputPath = ramp1dPath + ".spi1d" - - # open image - ramp = oiio.ImageInput.open( ramp1dPath ) - - # get image specs - spec = ramp.spec() - type = spec.format.basetype - width = spec.width - height = spec.height - channels = spec.nchannels - - # get data - # Force data to be read as float. The Python API doesn't handle half-floats well yet. - type = oiio.FLOAT - data = ramp.read_image(type) - - writeSPI1D(outputPath, minValue, maxValue, data, width, channels) - -def generate3dLUTImage(ramp3dPath, resolution=32): - args = ["--generate", "--cubesize", str(resolution), "--maxwidth", str(resolution*resolution), "--output", ramp3dPath] - lutExtract = process.Process(description="generate a 3d LUT image", cmd="ociolutimage", args=args) - lutExtract.execute() - -def generate3dLUTFromImage(ramp3dPath, outputPath=None, resolution=32): - if outputPath == None: - outputPath = ramp3dPath + ".spi3d" - - args = ["--extract", "--cubesize", str(resolution), "--maxwidth", str(resolution*resolution), "--input", ramp3dPath, "--output", outputPath] - lutExtract = process.Process(description="extract a 3d LUT", cmd="ociolutimage", args=args) - lutExtract.execute() - -def applyCTLToImage(inputImage, - outputImage, - ctlPaths=[], - inputScale=1.0, - outputScale=1.0, - globalParams={}, - acesCTLReleaseDir=None): - if len(ctlPaths) > 0: - ctlenv = os.environ - if acesCTLReleaseDir != None: - if os.path.split(acesCTLReleaseDir)[1] != "utilities": - ctlModulePath = "%s/utilities" % acesCTLReleaseDir - else: - ctlModulePath = acesCTLReleaseDir - ctlenv['CTL_MODULE_PATH'] = ctlModulePath - - args = [] - for ctl in ctlPaths: - args += ['-ctl', ctl] - args += ["-force"] - #args += ["-verbose"] - args += ["-input_scale", str(inputScale)] - args += ["-output_scale", str(outputScale)] - args += ["-global_param1", "aIn", "1.0"] - for key, value in globalParams.iteritems(): - args += ["-global_param1", key, str(value)] - args += [inputImage] - args += [outputImage] - - #print( "args : %s" % args ) - - ctlp = process.Process(description="a ctlrender process", cmd="ctlrender", args=args, env=ctlenv ) - - ctlp.execute() - -def convertBitDepth(inputImage, outputImage, depth): - args = [inputImage, "-d", depth, "-o", outputImage] - convert = process.Process(description="convert image bit depth", cmd="oiiotool", args=args) - convert.execute() - -def generate1dLUTFromCTL(lutPath, - ctlPaths, - lutResolution=1024, - identityLutBitDepth='half', - inputScale=1.0, - outputScale=1.0, - globalParams={}, - cleanup=True, - acesCTLReleaseDir=None, - minValue=0.0, - maxValue=1.0): - #print( lutPath ) - #print( ctlPaths ) - - lutPathBase = os.path.splitext(lutPath)[0] - - identityLUTImageFloat = lutPathBase + ".float.tiff" - generate1dLUTImage(identityLUTImageFloat, lutResolution, minValue, maxValue) - - if identityLutBitDepth != 'half': - identityLUTImage = lutPathBase + ".uint16.tiff" - convertBitDepth(identityLUTImageFloat, identityLUTImage, identityLutBitDepth) - else: - identityLUTImage = identityLUTImageFloat - - transformedLUTImage = lutPathBase + ".transformed.exr" - applyCTLToImage(identityLUTImage, transformedLUTImage, ctlPaths, inputScale, outputScale, globalParams, acesCTLReleaseDir) - - generate1dLUTFromImage(transformedLUTImage, lutPath, minValue, maxValue) - - if cleanup: - os.remove(identityLUTImage) - if identityLUTImage != identityLUTImageFloat: - os.remove(identityLUTImageFloat) - os.remove(transformedLUTImage) - -def correctLUTImage(transformedLUTImage, correctedLUTImage, lutResolution): - # open image - transformed = oiio.ImageInput.open( transformedLUTImage ) - - # get image specs - transformedSpec = transformed.spec() - type = transformedSpec.format.basetype - width = transformedSpec.width - height = transformedSpec.height - channels = transformedSpec.nchannels - - # rotate or not - if width != lutResolution * lutResolution or height != lutResolution: - print( "Correcting image as resolution is off. Found %d x %d. Expected %d x %d" % (width, height, lutResolution * lutResolution, lutResolution) ) - print( "Generating %s" % correctedLUTImage) - - # - # We're going to generate a new correct image - # - - # Get the source data - # Force data to be read as float. The Python API doesn't handle half-floats well yet. - type = oiio.FLOAT - sourceData = transformed.read_image(type) - - format = os.path.splitext(correctedLUTImage)[1] - correct = oiio.ImageOutput.create(correctedLUTImage) - - # set image specs - correctSpec = oiio.ImageSpec() - correctSpec.set_format( oiio.FLOAT ) - correctSpec.width = height - correctSpec.height = width - correctSpec.nchannels = channels - - correct.open (correctedLUTImage, correctSpec, oiio.Create) - - destData = array.array("f", "\0" * correctSpec.width * correctSpec.height * correctSpec.nchannels * 4) - for j in range(0, correctSpec.height): - for i in range(0, correctSpec.width): - for c in range(0, correctSpec.nchannels): - #print( i, j, c ) - destData[correctSpec.nchannels*correctSpec.width*j + correctSpec.nchannels*i + c] = sourceData[correctSpec.nchannels*correctSpec.width*j + correctSpec.nchannels*i + c] - - correct.write_image(correctSpec.format, destData) - correct.close() - else: - #shutil.copy(transformedLUTImage, correctedLUTImage) - correctedLUTImage = transformedLUTImage - - transformed.close() - - return correctedLUTImage - -def generate3dLUTFromCTL(lutPath, - ctlPaths, - lutResolution=64, - identityLutBitDepth='half', - inputScale=1.0, - outputScale=1.0, - globalParams={}, - cleanup=True, - acesCTLReleaseDir=None): - #print( lutPath ) - #print( ctlPaths ) - - lutPathBase = os.path.splitext(lutPath)[0] - - identityLUTImageFloat = lutPathBase + ".float.tiff" - generate3dLUTImage(identityLUTImageFloat, lutResolution) - - - if identityLutBitDepth != 'half': - identityLUTImage = lutPathBase + "." + identityLutBitDepth + ".tiff" - convertBitDepth(identityLUTImageFloat, identityLUTImage, identityLutBitDepth) - else: - identityLUTImage = identityLUTImageFloat - - transformedLUTImage = lutPathBase + ".transformed.exr" - applyCTLToImage(identityLUTImage, transformedLUTImage, ctlPaths, inputScale, outputScale, globalParams, acesCTLReleaseDir) - - correctedLUTImage = lutPathBase + ".correct.exr" - correctedLUTImage = correctLUTImage(transformedLUTImage, correctedLUTImage, lutResolution) - - generate3dLUTFromImage(correctedLUTImage, lutPath, lutResolution) - - if cleanup: - os.remove(identityLUTImage) - if identityLUTImage != identityLUTImageFloat: - os.remove(identityLUTImageFloat) - os.remove(transformedLUTImage) - if correctedLUTImage != transformedLUTImage: - os.remove(correctedLUTImage) - #os.remove(correctedLUTImage) - -def main(): - import optparse - - p = optparse.OptionParser(description='A utility to generate LUTs from CTL', - prog='generateLUT', - version='0.01', - usage='%prog [options]') - - p.add_option('--lut', '-l', type="string", default="") - p.add_option('--ctl', '-c', type="string", action="append") - p.add_option('--lutResolution1d', '', type="int", default=1024) - p.add_option('--lutResolution3d', '', type="int", default=33) - p.add_option('--ctlReleasePath', '-r', type="string", default="") - p.add_option('--bitDepth', '-b', type="string", default="float") - p.add_option('--keepTempImages', '', action="store_true") - p.add_option('--minValue', '', type="float", default=0.0) - p.add_option('--maxValue', '', type="float", default=1.0) - p.add_option('--inputScale', '', type="float", default=1.0) - p.add_option('--outputScale', '', type="float", default=1.0) - p.add_option('--ctlRenderParam', '-p', type="string", nargs=2, action="append") - - p.add_option('--generate1d', '', action="store_true") - p.add_option('--generate3d', '', action="store_true") - - options, arguments = p.parse_args() - - # - # Get options - # - lut = options.lut - ctls = options.ctl - lutResolution1d = options.lutResolution1d - lutResolution3d = options.lutResolution3d - minValue = options.minValue - maxValue = options.maxValue - inputScale = options.inputScale - outputScale = options.outputScale - ctlReleasePath = options.ctlReleasePath - generate1d = options.generate1d == True - generate3d = options.generate3d == True - bitDepth = options.bitDepth - cleanup = not options.keepTempImages - - params = {} - if options.ctlRenderParam != None: - for param in options.ctlRenderParam: - params[param[0]] = float(param[1]) - - try: - argsStart = sys.argv.index('--') + 1 - args = sys.argv[argsStart:] - except: - argsStart = len(sys.argv)+1 - args = [] - - #print( "command line : \n%s\n" % " ".join(sys.argv) ) - - # - # Generate LUTs - # - if generate1d: - print( "1D LUT generation options") - else: - print( "3D LUT generation options") - - print( "lut : %s" % lut ) - print( "ctls : %s" % ctls ) - print( "lut res 1d : %s" % lutResolution1d ) - print( "lut res 3d : %s" % lutResolution3d ) - print( "min value : %s" % minValue ) - print( "max value : %s" % maxValue ) - print( "input scale : %s" % inputScale ) - print( "output scale : %s" % outputScale ) - print( "ctl render params : %s" % params ) - print( "ctl release path : %s" % ctlReleasePath ) - print( "bit depth of input : %s" % bitDepth ) - print( "cleanup temp images : %s" % cleanup) - - if generate1d: - generate1dLUTFromCTL( lut, - ctls, - lutResolution1d, - bitDepth, - inputScale, - outputScale, - params, - cleanup, - ctlReleasePath, - minValue, - maxValue) - - elif generate3d: - generate3dLUTFromCTL( lut, - ctls, - lutResolution3d, - bitDepth, - inputScale, - outputScale, - params, - cleanup, - ctlReleasePath) - else: - print( "\n\nNo LUT generated. You must choose either 1D or 3D LUT generation\n\n") -# main - -if __name__ == '__main__': - main() - diff --git a/aces_1.0.0/python/process.py b/aces_1.0.0/python/process.py deleted file mode 100755 index 17347ee..0000000 --- a/aces_1.0.0/python/process.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/python2.6 - -'''A process wrapper class that maintains the text output and execution status of a process -or a list of other process wrappers which carry such data.''' - -import os -import sys -import traceback - -def readText(textFile): - if( textFile != "" ): - fp = open(textFile, 'rb') - # Create a text/plain message - text = (fp.read()) - fp.close() - return text -# readText - -def writeText(text, textFile): - if( textFile != "" ): - fp = open(textFile, 'wb') - # Create a text/plain message - fp.write(text) - fp.close() - return text -# readText - -class Process: - "A process with logged output" - - def __init__(self, description=None, cmd=None, args=[], cwd=None, env=None, batchWrapper=False): - "Initialize the standard class variables" - self.cmd = cmd - if not description: - self.description = cmd - else: - self.description = description - self.status = None - self.args = args - self.start = None - self.end = None - self.log = [] - self.echo = True - self.cwd = cwd - self.env = env - self.batchWrapper = batchWrapper - self.processKeys = [] - # __init__ - - def getElapsedSeconds(self): - import math - if self.end and self.start: - delta = (self.end - self.start) - formatted = "%s.%s" % (delta.days * 86400 + delta.seconds, int(math.floor(delta.microseconds/1e3))) - else: - formatted = None - return formatted - # getElapsedtime - - def writeKey(self, writeDict, key=None, value=None, startStop=None): - "Write a key, value pair in a supported format" - if key != None and (value != None or startStop != None): - indent = '\t'*writeDict['indentationLevel'] - if writeDict['format'] == 'xml': - if startStop == 'start': - writeDict['logHandle'].write( "%s<%s>\n" % (indent, key) ) - elif startStop == 'stop': - writeDict['logHandle'].write( "%s\n" % (indent, key) ) - else: - writeDict['logHandle'].write( "%s<%s>%s\n" % (indent, key, value, key) ) - else: # writeDict['format'] == 'txt': - writeDict['logHandle'].write( "%s%40s : %s\n" % (indent, key, value) ) - - def writeLogHeader(self, writeDict): - import platform - - # Retrieve operating environment information - user = None - try: - user = os.getlogin() - except: - try: - user = os.getenv("USERNAME") - if user == None: - user = os.getenv("USER") - except: - user = "unknown_user" - try: - (sysname, nodename, release, version, machine, processor) = platform.uname() - except: - (sysname, nodename, release, version, machine, processor) = ("unknown_sysname", "unknown_nodename", "unknown_release", "unknown_version", "unknown_machine", "unknown_processor") - try: - hostname = platform.node() - except: - hostname = "unknown_hostname" - - self.writeKey(writeDict, 'process', None, 'start' ) - writeDict['indentationLevel'] += 1 - - self.writeKey(writeDict, 'description', self.description ) - self.writeKey(writeDict, 'cmd', self.cmd ) - if self.args: self.writeKey(writeDict, 'args', ' '.join(self.args) ) - self.writeKey(writeDict, 'start', self.start ) - self.writeKey(writeDict, 'end', self.end ) - self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() ) - - self.writeKey(writeDict, 'user', user ) - self.writeKey(writeDict, 'sysname', sysname ) - self.writeKey(writeDict, 'nodename', nodename ) - self.writeKey(writeDict, 'release', release ) - self.writeKey(writeDict, 'version', version ) - self.writeKey(writeDict, 'machine', machine ) - self.writeKey(writeDict, 'processor', processor ) - - if len(self.processKeys) > 0: - self.writeKey(writeDict, 'processKeys', None, 'start' ) - for pair in self.processKeys: - (key, value) = pair - writeDict['indentationLevel'] += 1 - self.writeKey(writeDict, key, value ) - writeDict['indentationLevel'] -= 1 - self.writeKey(writeDict, 'processKeys', None, 'stop' ) - - self.writeKey(writeDict, 'status', self.status ) - # writeLogHeader - - def writeLogFooter(self, writeDict): - writeDict['indentationLevel'] -= 1 - self.writeKey(writeDict, 'process', None, 'stop' ) - # writeLogFooter - - def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'): - "Write logging information to the specified handle" - - writeDict = {} - writeDict['logHandle'] = logHandle - writeDict['indentationLevel'] = indentationLevel - writeDict['format'] = format - - if logHandle: - self.writeLogHeader(writeDict) - - if self.log: - self.writeKey(writeDict, 'output', None, 'start' ) - if format == 'xml': - logHandle.write( "\n" ) - self.writeKey(writeDict, 'output', None, 'stop' ) - - self.writeLogFooter(writeDict) - # writeLog - - def writeLogToDisk(self, logFilename=None, format='xml', header=None): - if logFilename: - try: - # This also doesn't seem like the best structure... - # 3.1 - try: - logHandle = open( logFilename, mode='wt', encoding="utf-8") - # 2.6 - except: - logHandle = open( logFilename, mode='wt') - except: - print( "Couldn't open log : %s" % logFilename ) - logHandle = None - - if logHandle: - if header: - if format == 'xml': - logHandle.write( "\n" ) - self.writeLog(logHandle) - logHandle.close() - # writeLogToDisk - - def logLine(self, line): - "Add a line of text to the log" - self.log.append( line.rstrip() ) - if self.echo: - print( "%s" % line.rstrip() ) - # logLine - - def execute(self): - "Execute this process" - import re - import datetime - import traceback - - try: - import subprocess as sp - except: - sp = None - - self.start = datetime.datetime.now() - - cmdargs = [self.cmd] - cmdargs.extend(self.args) - - if self.echo: - if sp: - print( "\n%s : %s\n" % (self.__class__, sp.list2cmdline(cmdargs)) ) - else: - print( "\n%s : %s\n" % (self.__class__, " ".join(cmdargs)) ) - - # intialize a few variables that may or may not be set later - process = None - tmpWrapper = None - stdout = None - stdin = None - parentenv = os.environ - parentcwd = os.getcwd() - - try: - # Using subprocess - if sp: - if self.batchWrapper: - cmd = " ".join(cmdargs) - tmpWrapper = os.path.join(self.cwd, "process.bat") - writeText(cmd, tmpWrapper) - print( "%s : Running process through wrapper %s\n" % (self.__class__, tmpWrapper) ) - process = sp.Popen([tmpWrapper], stdout=sp.PIPE, stderr=sp.STDOUT, - cwd=self.cwd, env=self.env) - else: - process = sp.Popen(cmdargs, stdout=sp.PIPE, stderr=sp.STDOUT, - cwd=self.cwd, env=self.env) - - # using os.popen4 - else: - if self.env: - os.environ = self.env - if self.cwd: - os.chdir( self.cwd ) - - stdin, stdout = os.popen4( cmdargs, 'r') - except: - print( "Couldn't execute command : %s" % cmdargs[0] ) - traceback.print_exc() - - # Using subprocess - if sp: - if process != None: - #pid = process.pid - #log.logLine( "process id %s\n" % pid ) - - try: - # This is more proper python, and resolves some issues with a process ending before all - # of its output has been processed, but it also seems to stall when the read buffer - # is near or over it's limit. this happens relatively frequently with processes - # that generate lots of print statements. - # - for line in process.stdout: - self.logLine(line) - # - # So we go with the, um, uglier option below - - # This is now used to ensure that the process has finished - line = "" - while line != None and process.poll() == None: - try: - line = process.stdout.readline() - except: - break - # 3.1 - try: - self.logLine( str(line, encoding="utf-8") ) - # 2.6 - except: - self.logLine( line ) - except: - self.logLine( "Logging error : %s" % sys.exc_info()[0] ) - - self.status = process.returncode - - if self.batchWrapper and tmpWrapper: - try: - os.remove(tmpWrapper) - except: - print( "Couldn't remove temp wrapper : %s" % tmpWrapper ) - traceback.print_exc() - - # Using os.popen4 - else: - exitCode = -1 - try: - #print( "reading stdout lines" ) - stdoutLines = stdout.readlines() - exitCode = stdout.close() - - stdout.close() - stdin.close() - - if self.env: - os.environ = parentenv - if self.cwd: - os.chdir( parentcwd ) - - if len( stdoutLines ) > 0: - for line in stdoutLines: - self.logLine(line) - - if not exitCode: - exitCode = 0 - except: - self.logLine( "Logging error : %s" % sys.exc_info()[0] ) - - self.status = exitCode - - self.end = datetime.datetime.now() - #execute -# Process - -class ProcessList(Process): - "A list of processes with logged output" - - def __init__(self, description, blocking=True, cwd=None, env=None): - Process.__init__(self, description, None, None, cwd, env) - "Initialize the standard class variables" - self.processes = [] - self.blocking = blocking - # __init__ - - def generateReport(self, writeDict): - "Generate a log based on the success of the child processes" - if self.processes: - _status = True - indent = '\t'*(writeDict['indentationLevel']+1) - - self.log = [] - - for child in self.processes: - if isinstance(child, ProcessList): - child.generateReport(writeDict) - - childResult = "" - key = child.description - value = child.status - if writeDict['format'] == 'xml': - childResult = ( "%s%s" % (indent, key, value) ) - else: # writeDict['format'] == 'txt': - childResult = ( "%s%40s : %s" % (indent, key, value) ) - self.log.append( childResult ) - - if child.status != 0: - _status = False - if not _status: - self.status = -1 - else: - self.status = 0 - else: - self.log = ["No child processes available to generate a report"] - self.status = -1 - - def writeLogHeader(self, writeDict): - self.writeKey(writeDict, 'processList', None, 'start' ) - writeDict['indentationLevel'] += 1 - - self.writeKey(writeDict, 'description', self.description ) - self.writeKey(writeDict, 'start', self.start ) - self.writeKey(writeDict, 'end', self.end ) - self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() ) - - self.generateReport(writeDict) - - self.writeKey(writeDict, 'status', self.status ) - # writeLogHeader - - def writeLogFooter(self, writeDict): - writeDict['indentationLevel'] -= 1 - self.writeKey(writeDict, 'processList', None, 'stop' ) - # writeLogFooter - - def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'): - "Write logging information to the specified handle" - - writeDict = {} - writeDict['logHandle'] = logHandle - writeDict['indentationLevel'] = indentationLevel - writeDict['format'] = format - - if logHandle: - self.writeLogHeader(writeDict) - - if self.log: - self.writeKey(writeDict, 'output', None, 'start' ) - for line in self.log: - logHandle.write( '%s%s\n' % ("", line) ) - self.writeKey(writeDict, 'output', None, 'stop' ) - - if self.processes: - self.writeKey(writeDict, 'processes', None, 'start' ) - for child in self.processes: - child.writeLog( logHandle, indentationLevel + 1, format ) - self.writeKey(writeDict, 'processes', None, 'stop' ) - - self.writeLogFooter(writeDict) - # writeLog - - def execute(self): - "Execute this list of processes" - import datetime - - self.start = datetime.datetime.now() - - self.status = 0 - if self.processes: - for child in self.processes: - if child: - try: - child.execute() - except: - print( "%s : caught exception in child class %s" % (self.__class__, child.__class__) ) - traceback.print_exc() - child.status = -1 - - if self.blocking and child.status != 0: - print( "%s : child class %s finished with an error" % (self.__class__, child.__class__) ) - self.status = -1 - break - - self.end = datetime.datetime.now() - # execute -# ProcessList - -def main(): - import optparse - - p = optparse.OptionParser(description='A process logging script', - prog='process', - version='process 0.1', - usage='%prog [options] [options for the logged process]') - p.add_option('--cmd', '-c', default=None) - p.add_option('--log', '-l', default=None) - - options, arguments = p.parse_args() - - # - # Get options - # - cmd = options.cmd - logFilename = options.log - - try: - argsStart = sys.argv.index('--') + 1 - args = sys.argv[argsStart:] - except: - argsStart = len(sys.argv)+1 - args = [] - - if cmd == None: - print( "process: No command specified" ) - - # - # Test regular logging - # - process = Process(description="a process",cmd=cmd, args=args) - - # - # Test report generation and writing a log - # - processList = ProcessList("a process list") - processList.processes.append( process ) - processList.echo = True - processList.execute() - - processList.writeLogToDisk(logFilename) -# main - -if __name__ == '__main__': - main() diff --git a/aces_1.0.0/python/util.py b/aces_1.0.0/python/util.py deleted file mode 100644 index aa891d3..0000000 --- a/aces_1.0.0/python/util.py +++ /dev/null @@ -1,37 +0,0 @@ -import PyOpenColorIO as OCIO - -# -# Utility classes and functions -# -class ColorSpace: - "A container for data needed to define an OCIO 'Color Space' " - - def __init__(self, - name, - description=None, - bitDepth=OCIO.Constants.BIT_DEPTH_F32, - equalityGroup=None, - family=None, - isData=False, - toReferenceTransforms=[], - fromReferenceTransforms=[], - allocationType=OCIO.Constants.ALLOCATION_UNIFORM, - allocationVars=[0.0, 1.0]): - "Initialize the standard class variables" - self.name = name - self.bitDepth=bitDepth - self.description = description - self.equalityGroup=equalityGroup - self.family=family - self.isData=isData - self.toReferenceTransforms=toReferenceTransforms - self.fromReferenceTransforms=fromReferenceTransforms - self.allocationType=allocationType - self.allocationVars=allocationVars - -# Create a 4x4 matrix (list) based on a 3x3 matrix (list) input -def mat44FromMat33(mat33): - return [mat33[0], mat33[1], mat33[2], 0.0, - mat33[3], mat33[4], mat33[5], 0.0, - mat33[6], mat33[7], mat33[8], 0.0, - 0,0,0,1.0] \ No newline at end of file