Implements support for *ACES* colorspaces conversions and transfer functions.
"""
-import array
import math
import numpy
import os
import pprint
+import string
+import shutil
import PyOpenColorIO as ocio
-import aces_ocio.generate_lut as genlut
from aces_ocio.generate_lut import (
generate_1d_LUT_from_CTL,
generate_3d_LUT_from_CTL,
write_SPI_1d)
-from aces_ocio.utilities import ColorSpace, mat44_from_mat33, sanitize_path, compact
+from aces_ocio.utilities import (
+ ColorSpace,
+ mat44_from_mat33,
+ sanitize,
+ compact)
__author__ = 'ACES Developers'
__email__ = 'aces@oscars.org'
__status__ = 'Production'
-__all__ = ['create_ACEScc',
+__all__ = ['ACES_AP1_TO_AP0',
+ 'ACES_AP0_TO_XYZ',
+ 'create_ACEScc',
'create_ACESproxy',
'create_ACEScg',
'create_ADX',
'create_ACES_RRT_plus_ODT',
'create_odts',
'create_aces',
+ 'get_transform_info',
+ 'get_ODT_info',
+ 'get_LMT_info',
'create_colorspaces']
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Matrix converting *ACES AP1* primaries to *AP0*.
-ACES_AP1_to_AP0 = [0.6954522414, 0.1406786965, 0.1638690622,
+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,
+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(aces_CTL_directory,
- lut_directory,
+ lut_directory,
lut_resolution_1d,
cleanup,
name='ACEScc',
min_value=0.0,
max_value=1.0,
input_scale=1.0):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace(name)
cs.description = 'The %s color space' % name
cs.aliases = ["acescc_ap1"]
'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
lut = '%s_to_ACES.spi1d' % name
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_1d_LUT_from_CTL(
os.path.join(lut_directory, lut),
# *AP1* primaries to *AP0* primaries.
cs.to_reference_transforms.append({
'type': 'matrix',
- 'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
+ 'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
'direction': 'forward'})
cs.from_reference_transforms = []
# *ACESproxy*
# -------------------------------------------------------------------------
def create_ACESproxy(aces_CTL_directory,
- lut_directory,
+ lut_directory,
lut_resolution_1d,
cleanup,
name='ACESproxy'):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace(name)
cs.description = 'The %s color space' % name
cs.aliases = ["acesproxy_ap1"]
'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')]
lut = '%s_to_aces.spi1d' % name
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_1d_LUT_from_CTL(
os.path.join(lut_directory, lut),
# *AP1* primaries to *AP0* primaries.
cs.to_reference_transforms.append({
'type': 'matrix',
- 'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
+ 'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
'direction': 'forward'
})
cs.from_reference_transforms = []
return cs
+
# -------------------------------------------------------------------------
# *ACEScg*
# -------------------------------------------------------------------------
def create_ACEScg(aces_CTL_directory,
- lut_directory,
+ lut_directory,
lut_resolution_1d,
cleanup,
name='ACEScg'):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace(name)
cs.description = 'The %s color space' % name
cs.aliases = ["lin_ap1"]
# *AP1* primaries to *AP0* primaries.
cs.to_reference_transforms.append({
'type': 'matrix',
- 'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
- 'direction': 'forward'
- })
+ 'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
+ 'direction': 'forward'})
cs.from_reference_transforms = []
return cs
+
# -------------------------------------------------------------------------
# *ADX*
# -------------------------------------------------------------------------
-def create_ADX(lut_directory,
+def create_ADX(lut_directory,
lut_resolution_1d,
- bit_depth=10,
+ bit_depth=10,
name='ADX'):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
name = '%s%s' % (name, bit_depth)
cs = ColorSpace(name)
cs.description = '%s color space - used for film scans' % name
cs.from_reference_transforms = []
return cs
+
# -------------------------------------------------------------------------
# *Generic Log Transform*
# -------------------------------------------------------------------------
middle_grey=0.18,
min_exposure=-6.0,
max_exposure=6.5):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace(name)
cs.description = 'The %s color space' % name
cs.aliases = aliases
'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl')]
lut = '%s_to_aces.spi1d' % name
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_1d_LUT_from_CTL(
os.path.join(lut_directory, lut),
lut_resolution_3d=64,
cleanup=True,
aliases=[]):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace('%s' % lmt_name)
cs.description = 'The ACES Look Transform: %s' % lmt_name
cs.aliases = aliases
if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
ctls = [shaper_to_ACES_CTL % aces_CTL_directory]
- shaper_lut = sanitize_path(shaper_lut)
+ shaper_lut = sanitize(shaper_lut)
generate_1d_LUT_from_CTL(
os.path.join(lut_directory, shaper_lut),
lmt_values['transformCTL'])]
lut = '%s.%s.spi3d' % (shaper_name, lmt_name)
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_3d_LUT_from_CTL(
os.path.join(lut_directory, lut),
if 'transformCTLInverse' in lmt_values:
ctls = [os.path.join(aces_CTL_directory,
+ # TODO: Investigate "odt_values" undeclared
+ # variable.
odt_values['transformCTLInverse']),
shaper_from_ACES_CTL % aces_CTL_directory]
lut = 'Inverse.%s.%s.spi3d' % (odt_name, shaper_name)
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_3d_LUT_from_CTL(
os.path.join(lut_directory, lut),
return cs
+
# -------------------------------------------------------------------------
# *LMTs*
# -------------------------------------------------------------------------
def create_lmts(aces_CTL_directory,
- lut_directory,
+ lut_directory,
lut_resolution_1d,
lut_resolution_3d,
lmt_info,
shaper_name,
cleanup):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
colorspaces = []
return colorspaces
+
# -------------------------------------------------------------------------
# *ACES RRT* with supplied *ODT*.
# -------------------------------------------------------------------------
lut_resolution_3d=64,
cleanup=True,
aliases=[]):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
cs = ColorSpace('%s' % odt_name)
cs.description = '%s - %s Output Transform' % (
odt_values['transformUserNamePrefix'], odt_name)
if not os.path.exists(os.path.join(lut_directory, shaper_lut)):
ctls = [shaper_to_ACES_CTL % aces_CTL_directory]
- shaper_lut = sanitize_path(shaper_lut)
+ shaper_lut = sanitize(shaper_lut)
generate_1d_LUT_from_CTL(
os.path.join(lut_directory, shaper_lut),
odt_values['transformCTL'])]
lut = '%s.RRT.a1.0.0.%s.spi3d' % (shaper_name, odt_name)
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_3d_LUT_from_CTL(
os.path.join(lut_directory, lut),
shaper_from_ACES_CTL % aces_CTL_directory]
lut = 'InvRRT.a1.0.0.%s.%s.spi3d' % (odt_name, shaper_name)
- lut = sanitize_path(lut)
+ lut = sanitize(lut)
generate_3d_LUT_from_CTL(
os.path.join(lut_directory, lut),
return cs
+
# -------------------------------------------------------------------------
# *ODTs*
# -------------------------------------------------------------------------
def create_odts(aces_CTL_directory,
- lut_directory,
+ lut_directory,
lut_resolution_1d,
lut_resolution_3d,
odt_info,
cleanup,
linear_display_space,
log_display_space):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
colorspaces = []
displays = {}
# *AP1* primaries to *AP0* primaries.
log2_shaper_AP1.to_reference_transforms.append({
'type': 'matrix',
- 'matrix': mat44_from_mat33(ACES_AP1_to_AP0),
+ 'matrix': mat44_from_mat33(ACES_AP1_TO_AP0),
'direction': 'forward'
})
colorspaces.append(log2_shaper_AP1)
for odt in sorted_odts:
(odt_name, odt_values) = odt
- # Handling *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']:
+ # Generating legal range transform for *ODTs* that can generate
+ # either *legal* or *full* output.
+ if odt_values['transformHasFullLegalSwitch']:
odt_name_legal = '%s - Legal' % odt_values['transformUserName']
else:
odt_name_legal = odt_values['transformUserName']
'Log': log_display_space,
'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']:
+
+ # Generating full range transform for *ODTs* that can generate
+ # either *legal* or *full* output.
+ if odt_values['transformHasFullLegalSwitch']:
print('Generating full range ODT for %s' % odt_name)
odt_name_full = '%s - Full' % odt_values['transformUserName']
return (colorspaces, displays)
+
def create_aces():
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
# Defining the reference colorspace.
ACES = ColorSpace('ACES2065-1')
ACES.description = (
return ACES
-def create_colorspaces(aces_CTL_directory,
- lut_directory,
- lut_resolution_1d,
+
+def get_transform_info(ctl_transform):
+ """
+ Object description.
+
+ Parameters
+ ----------
+ parameter : type
+ Parameter description.
+
+ Returns
+ -------
+ type
+ Return value description.
+ """
+
+ with open(ctl_transform, 'rb') as fp:
+ lines = fp.readlines()
+
+ # Retrieving the *transform ID* and *User Name*.
+ transform_id = lines[1][3:].split('<')[1].split('>')[1].strip()
+ 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())
+
+ # Figuring out if this transform has options for processing full and legal range
+ transform_full_legal_switch = False
+ for line in lines:
+ if line.strip() == "input varying int legalRange = 0":
+ # print( "%s has legal range flag" % transform_user_name)
+ transform_full_legal_switch = True
+ break
+
+ return (transform_id, transform_user_name, transform_user_name_prefix,
+ transform_full_legal_switch)
+
+
+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] != '.')]
+
+ odts = {}
+
+ for odt_CTL in odt_CTLs:
+ odt_tokens = os.path.split(odt_CTL)
+
+ # Handling 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)
+
+ # Building full name,
+ transform_CTL = odt_tokens[-1]
+ odt_name = string.join(transform_CTL.split('.')[1:-1], '.')
+
+ # Finding id, user name and user name prefix.
+ (transform_ID,
+ transform_user_name,
+ transform_user_name_prefix,
+ transform_full_legal_switch) = get_transform_info(
+ os.path.join(aces_CTL_directory, 'odt', odt_dir, transform_CTL))
+
+ # Finding 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
+
+ # Add to list of ODTs
+ odts[odt_name] = {}
+ odts[odt_name]['transformCTL'] = os.path.join(odt_dir, transform_CTL)
+ if transform_CTL_inverse is not 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
+ odts[odt_name][
+ 'transformHasFullLegalSwitch'] = transform_full_legal_switch
+
+ forward_CTL = odts[odt_name]['transformCTL']
+
+ 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(
+ '\tHas Full / Legal Switch : %s' % transform_full_legal_switch)
+ print('\tForward ctl : %s' % forward_CTL)
+ if 'transformCTLInverse' in odts[odt_name]:
+ inverse_CTL = odts[odt_name]['transformCTLInverse']
+ print('\tInverse ctl : %s' % inverse_CTL)
+ 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] != '.')]
+
+ lmts = {}
+
+ for lmt_CTL in lmt_CTLs:
+ lmt_tokens = os.path.split(lmt_CTL)
+
+ # Handlimg 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)
+
+ # Building full name.
+ transform_CTL = lmt_tokens[-1]
+ lmt_name = string.join(transform_CTL.split('.')[1:-1], '.')
+
+ # Finding id, user name and user name prefix.
+ (transform_ID,
+ transform_user_name,
+ transform_user_name_prefix,
+ transform_full_legal_switch) = get_transform_info(
+ os.path.join(aces_CTL_directory, lmt_dir, transform_CTL))
+
+ # Finding 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
+
+ lmts[lmt_name] = {}
+ lmts[lmt_name]['transformCTL'] = os.path.join(lmt_dir, transform_CTL)
+ if transform_CTL_inverse is not None:
+ lmts[lmt_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
+
+ forward_CTL = lmts[lmt_name]['transformCTL']
+
+ 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' % forward_CTL)
+ if 'transformCTLInverse' in lmts[lmt_name]:
+ inverse_CTL = lmts[lmt_name]['transformCTLInverse']
+ print('\t Inverse ctl : %s' % inverse_CTL)
+ else:
+ print('\t Inverse ctl : %s' % 'None')
+
+ print('\n')
+
+ return lmts
+
+
+def create_colorspaces(aces_CTL_directory,
+ lut_directory,
+ lut_resolution_1d,
lut_resolution_3d,
lmt_info,
odt_info,
ACES = create_aces()
- ACEScc = create_ACEScc(aces_CTL_directory, lut_directory, lut_resolution_1d, cleanup)
+ ACEScc = create_ACEScc(aces_CTL_directory, lut_directory,
+ lut_resolution_1d, cleanup)
colorspaces.append(ACEScc)
- ACESproxy = create_ACESproxy(aces_CTL_directory, lut_directory, lut_resolution_1d, cleanup)
+ ACESproxy = create_ACESproxy(aces_CTL_directory, lut_directory,
+ lut_resolution_1d, cleanup)
colorspaces.append(ACESproxy)
- ACEScg = create_ACEScg(aces_CTL_directory, lut_directory, lut_resolution_1d, cleanup)
+ ACEScg = create_ACEScg(aces_CTL_directory, lut_directory,
+ lut_resolution_1d, cleanup)
colorspaces.append(ACEScg)
ADX10 = create_ADX(lut_directory, lut_resolution_1d, bit_depth=10)
ADX16 = create_ADX(lut_directory, lut_resolution_1d, bit_depth=16)
colorspaces.append(ADX16)
- lmts = create_lmts(aces_CTL_directory,
- lut_directory,
- lut_resolution_1d,
+ lmts = create_lmts(aces_CTL_directory,
+ lut_directory,
+ lut_resolution_1d,
lut_resolution_3d,
lmt_info,
shaper_name,
cleanup)
colorspaces.extend(lmts)
- (odts, displays) = create_odts(aces_CTL_directory,
- lut_directory,
- lut_resolution_1d,
- lut_resolution_3d,
- odt_info,
- shaper_name,
- cleanup,
- ACES,
- ACEScc)
+ odts, displays = create_odts(aces_CTL_directory,
+ lut_directory,
+ lut_resolution_1d,
+ lut_resolution_3d,
+ odt_info,
+ shaper_name,
+ cleanup,
+ ACES,
+ ACEScc)
colorspaces.extend(odts)
- return (ACES, colorspaces, displays, ACEScc)
+ return ACES, colorspaces, displays, ACEScc