def create_Dolby_PQ_scaled(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name='pq', aliases=None, min_value=0.0, max_value=1.0, input_scale=1.0, middle_grey=0.18, min_exposure=-6.0, max_exposure=6.5): if aliases is None: aliases = [] cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = aliases cs.equality_group = name cs.family = 'Utility' cs.is_data = False ctls = [os.path.join( aces_ctl_directory, 'utilities', 'ACESlib.OCIOShaper_to_lin_param.a1.0.0.ctl')] lut = '%s_to_linear.spi1d' % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(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
def create_dolbypq_scaled( aces_CTL_directory, lut_directory, lut_resolution_1d, cleanup, name="pq", aliases=[], min_value=0.0, max_value=1.0, input_scale=1.0, middle_grey=0.18, min_exposure=-6.0, max_exposure=6.5, ): cs = ColorSpace(name) cs.description = "The %s color space" % name cs.aliases = aliases cs.equality_group = name cs.family = "Utility" cs.is_data = False ctls = [os.path.join(aces_CTL_directory, "utilities", "ACESlib.DolbyPQ_to_lin_param.a1.0.0.ctl")] lut = "%s_to_linear.spi1d" % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(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
def create_dolbypq(aces_CTL_directory, lut_directory, lut_resolution_1d, cleanup, name='pq', aliases=[], min_value=0.0, max_value=1.0, input_scale=1.0): cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = aliases cs.equality_group = name cs.family = 'Utility' cs.is_data = False ctls = [os.path.join( aces_CTL_directory, 'utilities', 'ACESlib.OCIO_shaper_dolbypq_to_lin.a1.0.0.ctl')] lut = '%s_to_linear.spi1d' % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(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'}) cs.from_reference_transforms = [] return cs
def create_ACES_RRT_plus_ODT(odt_name, odt_values, shaper_info, aces_ctl_directory, lut_directory, lut_resolution_3d=64, cleanup=True, aliases=None): """ Object description. Parameters ---------- parameter : type Parameter description. Returns ------- type Return value description. """ if aliases is None: aliases = [] cs = ColorSpace('%s' % odt_name) cs.description = '%s - %s Output Transform' % ( odt_values['transformUserNamePrefix'], odt_name) cs.aliases = aliases cs.equality_group = '' cs.family = 'Output' cs.is_data = False cs.aces_transform_id = odt_values['transformID'] pprint.pprint(odt_values) # Generating the *shaper* transform. (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_linear.spi1d' % shaper_name shaper_lut = sanitize(shaper_lut) shaper_ocio_transform = { 'type': 'lutFile', 'path': shaper_lut, 'interpolation': 'linear', 'direction': 'inverse'} # Generating the *forward* transform. cs.from_reference_transforms = [] if 'transformLUT' in odt_values: transform_lut_file_name = os.path.basename( odt_values['transformLUT']) lut = os.path.join(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: ctls = [ shaper_to_aces_ctl % aces_ctl_directory, os.path.join(aces_ctl_directory, 'rrt', 'RRT.a1.0.0.ctl'), os.path.join(aces_ctl_directory, 'odt', odt_values['transformCTL'])] lut = '%s.RRT.a1.0.0.%s.spi3d' % (shaper_name, odt_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, 'float', 1 / shaper_input_scale, 1, 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'}) # Generating the *inverse* transform. cs.to_reference_transforms = [] if 'transformLUTInverse' in odt_values: transform_lut_inverse_file_name = os.path.basename( odt_values['transformLUTInverse']) lut = os.path.join(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 = [os.path.join(aces_ctl_directory, 'odt', odt_values['transformCTLInverse']), os.path.join(aces_ctl_directory, 'rrt', 'InvRRT.a1.0.0.ctl'), shaper_from_aces_ctl % aces_ctl_directory] lut = 'InvRRT.a1.0.0.%s.%s.spi3d' % (odt_name, shaper_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, 'half', 1, 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
def create_ACES_LMT(lmt_name, lmt_values, shaper_info, aces_ctl_directory, lut_directory, lut_resolution_3d=64, cleanup=True, aliases=None): """ Creates the *ACES LMT* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACES LMT* colorspace. """ if aliases is None: aliases = [] cs = ColorSpace('%s' % lmt_name) cs.description = 'The ACES Look Transform: %s' % lmt_name cs.aliases = aliases cs.equality_group = '' cs.family = 'Look' cs.is_data = False cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] cs.aces_transform_id = lmt_values['transformID'] pprint.pprint(lmt_values) # Generating the *shaper* transform. (shaper_name, shaper_to_aces_ctl, shaper_from_aces_ctl, shaper_input_scale, shaper_params) = shaper_info shaper_lut = '%s_to_linear.spi1d' % shaper_name shaper_lut = sanitize(shaper_lut) shaper_ocio_transform = { 'type': 'lutFile', 'path': shaper_lut, 'interpolation': 'linear', 'direction': 'inverse'} # Generating the forward transform. cs.from_reference_transforms = [] if 'transformCTL' in lmt_values: ctls = [shaper_to_aces_ctl % aces_ctl_directory, os.path.join(aces_ctl_directory, lmt_values['transformCTL'])] lut = '%s.%s.spi3d' % (shaper_name, lmt_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, 'float', 1 / shaper_input_scale, 1, 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'}) # Generating the inverse transform. cs.to_reference_transforms = [] if 'transformCTLInverse' in lmt_values: ctls = [os.path.join(aces_ctl_directory, lmt_values['transformCTLInverse']), shaper_from_aces_ctl % aces_ctl_directory] lut = 'Inverse.%s.%s.spi3d' % (lmt_name, shaper_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, 'half', 1, shaper_input_scale, shaper_params, cleanup, aces_ctl_directory, 0) 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
def create_ACESproxy(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name='ACESproxy'): """ Creates the *ACESproxy* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACESproxy* colorspace. """ cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = ['acesproxy', 'acesproxy_ap1'] cs.equality_group = '' cs.family = 'ACES' cs.is_data = False cs.aces_transform_id = 'ACEScsc.ACESproxy10i_to_ACES.a1.0.0' ctls = [os.path.join(aces_ctl_directory, 'ACESproxy', 'ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl'), # 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: os.path.join(aces_ctl_directory, 'ACEScg', 'ACEScsc.ACES_to_ACEScg.a1.0.0.ctl')] lut = '%s_to_linear.spi1d' % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_1d, 'float', 1, 1, {}, cleanup, aces_ctl_directory, 0, 1, 1) 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
def create_log_c(gamut, transfer_function, exposure_index, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from LogC to ACES, with various transfer functions and encoding gamuts covered Parameters ---------- gamut : str The name of the encoding gamut to use. transfer_function : str The name of the transfer function to use exposure_index : str The exposure index to use lut_directory : str or unicode The directory to use when generating LUTs lut_resolution_1d : int The resolution of generated 1D LUTs aliases : list of str Aliases for this colorspace Returns ------- ColorSpace A ColorSpace container class referencing the LUTs, matrices and identifying information for the requested colorspace. """ name = '%s (EI%s) - %s' % (transfer_function, exposure_index, gamut) if transfer_function == '': name = 'Linear - ARRI %s' % gamut if gamut == '': name = 'Curve - %s (EI%s)' % (transfer_function, exposure_index) cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/ARRI' cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = ( 'IDT.ARRI.Alexa-v3-logC-EI%s.a1.v1' % exposure_index) # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] IDT_maker_version = '0.08' nominal_EI = 400 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 / 9 slope = 1 / (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 / 1023 - enc_offset) / enc_gain - offset) / slope enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain a = 1 / gray b = nz - black_signal / gray e = slope * a * enc_gain f = enc_gain * (slope * b + offset) + enc_offset # Ensuring 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 normalized_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 - p['d']) / p['c']) - p['b']) / p['a']) else: linear = (code_value - p['f']) / p['e'] 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] = normalized_log_c_to_linear(c / (lut_resolution_1d - 1), int(exposure_index)) lut = '%s_to_linear.spi1d' % ( '%s_%s' % (transfer_function, exposure_index)) lut = sanitize(lut) genlut.write_SPI_1d( os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) 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_protune(gamut, transfer_function, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from ProTune to ACES, with various transfer functions and encoding gamuts covered Parameters ---------- gamut : str The name of the encoding gamut to use. transfer_function : str The name of the transfer function to use lut_directory : str or unicode The directory to use when generating LUTs lut_resolution_1d : int The resolution of generated 1D LUTs aliases : list of str Aliases for this colorspace Returns ------- ColorSpace A ColorSpace container class referencing the LUTs, matrices and identifying information for the requested colorspace. """ # The gamut should be marked as experimental until matrices are fully # verified. name = '%s - %s - Experimental' % (transfer_function, gamut) if transfer_function == '': name = 'Linear - %s - Experimental' % gamut if gamut == '': name = 'Curve - %s' % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/GoPro' cs.is_data = False # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] def protune_to_linear(normalized_code_value): c1 = 113.0 c2 = 1.0 c3 = 112.0 linear = ((pow(c1, normalized_code_value) - c2) / c3) return linear cs.to_reference_transforms = [] if transfer_function == 'Protune Flat': data = array.array('f', '\0' * lut_resolution_1d * 4) for c in range(lut_resolution_1d): data[c] = protune_to_linear(float(c) / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function lut = sanitize(lut) genlut.write_SPI_1d(os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) cs.to_reference_transforms.append({ 'type': 'lutFile', 'path': lut, 'interpolation': 'linear', 'direction': 'forward' }) if gamut == 'Protune Native': cs.to_reference_transforms.append({ 'type': 'matrix', 'matrix': [ 0.533448429, 0.32413911, 0.142412421, 0, -0.050729924, 1.07572006, -0.024990416, 0, 0.071419661, -0.290521962, 1.219102381, 0, 0, 0, 0, 1 ], 'direction': 'forward' }) cs.from_reference_transforms = [] return cs
def create_log_c(gamut, transfer_function, exposure_index, lut_directory, lut_resolution_1d, aliases): """ Creates a colorspace covering the conversion from LogC to ACES, with various transfer functions and encoding gamuts covered. Parameters ---------- gamut : str The name of the encoding gamut to use. transfer_function : str The name of the transfer function to use. exposure_index : str The exposure index to use. lut_directory : str or unicode The directory to use when generating LUTs. lut_resolution_1d : int The resolution of generated 1D LUTs. aliases : list of str Aliases for this colorspace. Returns ------- ColorSpace A ColorSpace container class referencing the LUTs, matrices and identifying information for the requested colorspace. """ name = '%s (EI%s) - %s' % (transfer_function, exposure_index, gamut) if transfer_function == '': name = 'Linear - ALEXA %s' % gamut if gamut == '': name = 'Curve - %s (EI%s)' % (transfer_function, exposure_index) cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/ARRI' cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = ('IDT.ARRI.Alexa-v3-logC-EI%s.a1.v1' % exposure_index) # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] IDT_maker_version = '0.09' nominal_exposure_index = 400 black_signal = 16 / 4095 # 0.003907 mid_gray_signal = 0.01 encoding_gain = 500 / 1023 * 0.525 # 0.256598 encoding_offset = 400 / 1023 # 0.391007 def gain_for_EI(ei): return (math.log(ei / nominal_exposure_index) / math.log(2) * (0.89 - 1) / 3 + 1) * encoding_gain def hermite_weights(x, x1, x2): d = x2 - x1 s = (x - x1) / d s2 = 1 - s return [(1 + 2 * s) * s2 * s2, (3 - 2 * s) * s * s, d * s * s2 * s2, -d * s * s * s2] def normalized_sensor_to_relative_exposure(ns, ei): return (ns - black_signal) * ( 0.18 / (mid_gray_signal * nominal_exposure_index / ei)) def normalized_log_c_to_linear(code_value, exposure_index): cut = 1 / 9 slope = 1 / (cut * math.log(10)) offset = math.log10(cut) - slope * cut gain = exposure_index / nominal_exposure_index gray = mid_gray_signal / gain # The higher the EI, the lower the gamma. enc_gain = (math.log(gain) / math.log(2) * (0.89 - 1) / 3 + 1) * encoding_gain enc_offset = encoding_offset for i in range(0, 3): nz = ((95 / 1023 - enc_offset) / enc_gain - offset) / slope enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain # see if we need to bring the hermite spline into play xm = math.log10((1 - black_signal) / gray + nz) * enc_gain + enc_offset if xm > 1.0: if code_value > 0.8: hw = hermite_weights(code_value, 0.8, 1) d = 0.2 / (xm - 0.8) v = [0.8, xm, 1.0, 1 / (d * d)] # reconstruct code value from spline code_value = 0 for i in range(0, 4): code_value += (hw[i] * v[i]) code_value = (code_value - enc_offset) / enc_gain # compute normalized sensor value ns = pow(10, code_value) if (code_value - offset) / slope > cut else ( code_value - offset) / slope ns = (ns - nz) * gray + black_signal return normalized_sensor_to_relative_exposure(ns, exposure_index) 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] = normalized_log_c_to_linear(c / (lut_resolution_1d - 1), int(exposure_index)) lut = '%s_to_linear.spi1d' % ('%s_%s' % (transfer_function, exposure_index)) lut = sanitize(lut) genlut.write_SPI_1d(os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) 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_ACES_RRT_plus_ODT( odt_name, odt_values, shaper_info, aces_ctl_directory, lut_directory, lut_resolution_1d=1024, lut_resolution_3d=64, cleanup=True, aliases=None, ): """ Object description. Parameters ---------- parameter : type Parameter description. Returns ------- type Return value description. """ if aliases is None: aliases = [] cs = ColorSpace("%s" % odt_name) cs.description = "%s - %s Output Transform" % (odt_values["transformUserNamePrefix"], odt_name) cs.aliases = aliases cs.equality_group = "" cs.family = "Output" cs.is_data = False cs.aces_transform_id = odt_values["transformID"] pprint.pprint(odt_values) # Generating the *shaper* transform. (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 # Add the shaper transform shaper_lut = "%s_to_linear.spi1d" % shaper_name shaper_lut = sanitize(shaper_lut) shaper_OCIO_transform = {"type": "lutFile", "path": shaper_lut, "interpolation": "linear", "direction": "inverse"} # Generating the *forward* transform. cs.from_reference_transforms = [] if "transformLUT" in odt_values: transform_LUT_file_name = os.path.basename(odt_values["transformLUT"]) lut = os.path.join(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: ctls = [ shaper_to_ACES_CTL % aces_ctl_directory, os.path.join(aces_ctl_directory, "rrt", "RRT.a1.0.0.ctl"), os.path.join(aces_ctl_directory, "odt", odt_values["transformCTL"]), ] lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaper_name, odt_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), # shaperLUT, ctls, lut_resolution_3d, "float", 1 / shaper_input_scale, 1, 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"} ) # Generating the *inverse* transform. cs.to_reference_transforms = [] if "transformLUTInverse" in odt_values: transform_LUT_inverse_file_name = os.path.basename(odt_values["transformLUTInverse"]) lut = os.path.join(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 = [ os.path.join(aces_ctl_directory, "odt", odt_values["transformCTLInverse"]), os.path.join(aces_ctl_directory, "rrt", "InvRRT.a1.0.0.ctl"), shaper_from_ACES_CTL % aces_ctl_directory, ] lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odt_name, shaper_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), # None, ctls, lut_resolution_3d, "half", 1, 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
def create_ACES_LMT( lmt_name, lmt_values, shaper_info, aces_ctl_directory, lut_directory, lut_resolution_1d=1024, lut_resolution_3d=64, cleanup=True, aliases=None, ): """ Creates the *ACES LMT* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACES LMT* colorspace. """ if aliases is None: aliases = [] cs = ColorSpace("%s" % lmt_name) cs.description = "The ACES Look Transform: %s" % lmt_name cs.aliases = aliases cs.equality_group = "" cs.family = "Look" cs.is_data = False cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] cs.aces_transform_id = lmt_values["transformID"] pprint.pprint(lmt_values) # Generating the *shaper* transform. (shaper_name, shaper_to_ACES_CTL, shaper_from_ACES_CTL, shaper_input_scale, shaper_params) = shaper_info # Add the shaper transform shaper_lut = "%s_to_linear.spi1d" % shaper_name shaper_lut = sanitize(shaper_lut) shaper_OCIO_transform = {"type": "lutFile", "path": shaper_lut, "interpolation": "linear", "direction": "inverse"} # Generating the forward transform. cs.from_reference_transforms = [] if "transformCTL" in lmt_values: ctls = [shaper_to_ACES_CTL % aces_ctl_directory, os.path.join(aces_ctl_directory, lmt_values["transformCTL"])] lut = "%s.%s.spi3d" % (shaper_name, lmt_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, "float", 1 / shaper_input_scale, 1, 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"} ) # Generating the inverse transform. cs.to_reference_transforms = [] if "transformCTLInverse" in lmt_values: ctls = [ os.path.join(aces_ctl_directory, lmt_values["transformCTLInverse"]), shaper_from_ACES_CTL % aces_ctl_directory, ] lut = "Inverse.%s.%s.spi3d" % (odt_name, shaper_name) lut = sanitize(lut) generate_3d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_3d, "half", 1, shaper_input_scale, shaper_params, cleanup, aces_ctl_directory, 0, 1, 1, ) 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
def create_generic_log( aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name="log", aliases=[], min_value=0, max_value=1, input_scale=1, middle_grey=0.18, min_exposure=-6, max_exposure=6.5, ): """ Creates the *Generic Log* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *Generic Log* colorspace. """ cs = ColorSpace(name) cs.description = "The %s color space" % name cs.aliases = aliases cs.equality_group = name cs.family = "Utility" cs.is_data = False ctls = [os.path.join(aces_ctl_directory, "utilities", "ACESlib.Log2_to_Lin_param.a1.0.0.ctl")] lut = "%s_to_linear.spi1d" % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_1d, "float", input_scale, 1, {"middleGrey": middle_grey, "minExposure": min_exposure, "maxExposure": max_exposure}, cleanup, aces_ctl_directory, min_value, max_value, 1, ) cs.to_reference_transforms = [] cs.to_reference_transforms.append( {"type": "lutFile", "path": lut, "interpolation": "linear", "direction": "forward"} ) cs.from_reference_transforms = [] return cs
def create_ACESproxy(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name="ACESproxy"): """ Creates the *ACESproxy* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACESproxy* colorspace. """ cs = ColorSpace(name) cs.description = "The %s color space" % name cs.aliases = ["acesproxy", "acesproxy_ap1"] cs.equality_group = "" cs.family = "ACES" cs.is_data = False cs.aces_transform_id = "ACEScsc.ACESproxy10i_to_ACES.a1.0.0" ctls = [ os.path.join(aces_ctl_directory, "ACESproxy", "ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl"), # 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: os.path.join(aces_ctl_directory, "ACEScg", "ACEScsc.ACES_to_ACEScg.a1.0.0.ctl"), ] lut = "%s_to_linear.spi1d" % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_1d, "float", 1, 1, {}, cleanup, aces_ctl_directory, 0, 1, 1, ) 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
def create_log_c(gamut, transfer_function, exposure_index, lut_directory, lut_resolution_1d, aliases): """ 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 - ARRI %s" % gamut if gamut == "": name = "Curve - %s (EI%s)" % (transfer_function, exposure_index) cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = "" cs.family = "Input/ARRI" cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = "IDT.ARRI.Alexa-v3-logC-EI%s.a1.v1" % exposure_index # A linear space needs allocation variables. if transfer_function == "": cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] IDT_maker_version = "0.08" nominal_EI = 400 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 / 9 slope = 1 / (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 / 1023 - enc_offset) / enc_gain - offset) / slope enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain a = 1 / gray b = nz - black_signal / gray e = slope * a * enc_gain f = enc_gain * (slope * b + offset) + enc_offset # Ensuring 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 normalized_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 - p["d"]) / p["c"]) - p["b"]) / p["a"] else: linear = (code_value - p["f"]) / p["e"] 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] = normalized_log_c_to_linear(c / (lut_resolution_1d - 1), int(exposure_index)) lut = "%s_to_linear.spi1d" % ("%s_%s" % (transfer_function, exposure_index)) lut = sanitize(lut) genlut.write_SPI_1d(os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) 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_protune(gamut, transfer_function, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from ProTune to ACES, with various transfer functions and encoding gamuts covered Parameters ---------- gamut : str The name of the encoding gamut to use. transfer_function : str The name of the transfer function to use lut_directory : str or unicode The directory to use when generating LUTs lut_resolution_1d : int The resolution of generated 1D LUTs aliases : list of str Aliases for this colorspace Returns ------- ColorSpace A ColorSpace container class referencing the LUTs, matrices and identifying information for the requested colorspace. """ # The gamut should be marked as experimental until matrices are fully # verified. name = '%s - %s - Experimental' % (transfer_function, gamut) if transfer_function == '': name = 'Linear - %s - Experimental' % gamut if gamut == '': name = 'Curve - %s' % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/GoPro' cs.is_data = False # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] def protune_to_linear(normalized_code_value): c1 = 113.0 c2 = 1.0 c3 = 112.0 linear = ((pow(c1, normalized_code_value) - c2) / c3) return linear cs.to_reference_transforms = [] if transfer_function == 'Protune Flat': data = array.array('f', '\0' * lut_resolution_1d * 4) for c in range(lut_resolution_1d): data[c] = protune_to_linear(float(c) / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function lut = sanitize(lut) genlut.write_SPI_1d( os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) cs.to_reference_transforms.append({ 'type': 'lutFile', 'path': lut, 'interpolation': 'linear', 'direction': 'forward'}) if gamut == 'Protune Native': cs.to_reference_transforms.append({ 'type': 'matrix', 'matrix': [0.533448429, 0.32413911, 0.142412421, 0, -0.050729924, 1.07572006, -0.024990416, 0, 0.071419661, -0.290521962, 1.219102381, 0, 0, 0, 0, 1], 'direction': 'forward'}) cs.from_reference_transforms = [] return cs
def create_ACEScc(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name='ACEScc', min_value=0, max_value=1, input_scale=1): """ Creates the *ACEScc* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACEScc* colorspace. """ cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = ["acescc_ap1"] cs.equality_group = '' cs.family = 'ACES' cs.is_data = False cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM cs.allocation_vars = [min_value, max_value] ctls = [os.path.join(aces_ctl_directory, 'ACEScc', 'ACEScsc.ACEScc_to_ACES.a1.0.0.ctl')] lut = '%s_to_linear.spi1d' % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_1d, 'float', input_scale, 1, {'transferFunctionOnly':1}, cleanup, aces_ctl_directory, min_value, max_value, 1) 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
def create_log_c(gamut, transfer_function, exposure_index, name, lut_directory, lut_resolution_1d, aliases): """ 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 - ARRI %s' % gamut if gamut == '': name = '%s (EI%s)' % (transfer_function, exposure_index) cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/ARRI' cs.is_data = False # A linear space needs allocation variables if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] # Globals. IDT_maker_version = '0.08' nominal_EI = 400 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 / 9 slope = 1 / (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 / 1023 - enc_offset) / enc_gain - offset) / slope enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain a = 1 / gray b = nz - black_signal / gray e = slope * a * enc_gain f = enc_gain * (slope * b + offset) + enc_offset # Ensuring 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 - p['d']) / p['c']) - p['b']) / p['a']) else: linear = (code_value / 1023 - p['f']) / p['e'] 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 * c / (lut_resolution_1d - 1), int(exposure_index)) lut = '%s_to_linear.spi1d' % ( '%s_%s' % (transfer_function, exposure_index)) lut = sanitize(lut) genlut.write_SPI_1d( os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) 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_generic_log(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name='log', aliases=[], min_value=0, max_value=1, input_scale=1, middle_grey=0.18, min_exposure=-6, max_exposure=6.5): """ Creates the *Generic Log* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *Generic Log* colorspace. """ cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = aliases cs.equality_group = name cs.family = 'Utility' cs.is_data = False ctls = [os.path.join( aces_ctl_directory, 'utilities', 'ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl')] lut = '%s_to_linear.spi1d' % name lut = sanitize(lut) generate_1d_LUT_from_CTL( os.path.join(lut_directory, lut), ctls, lut_resolution_1d, 'float', input_scale, 1, {'middleGrey': middle_grey, 'minExposure': min_exposure, 'maxExposure': max_exposure}, cleanup, aces_ctl_directory, min_value, max_value, 1) cs.to_reference_transforms = [] cs.to_reference_transforms.append({ 'type': 'lutFile', 'path': lut, 'interpolation': 'linear', 'direction': 'forward'}) cs.from_reference_transforms = [] return cs
def create_protune(gamut, transfer_function, lut_directory, lut_resolution_1d, aliases): """ Object description. Protune to ACES. Parameters ---------- parameter : type Parameter description. Returns ------- type Return value description. """ # The gamut should be marked as experimental until matrices are fully # verified. name = '%s - %s - Experimental' % (transfer_function, gamut) if transfer_function == '': name = 'Linear - %s - Experimental' % gamut if gamut == '': name = 'Curve - %s' % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/GoPro' cs.is_data = False # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] def protune_to_linear(normalized_code_value): c1 = 113.0 c2 = 1.0 c3 = 112.0 linear = ((pow(c1, normalized_code_value) - c2) / c3) return linear cs.to_reference_transforms = [] if transfer_function == 'Protune Flat': data = array.array('f', '\0' * lut_resolution_1d * 4) for c in range(lut_resolution_1d): data[c] = protune_to_linear(float(c) / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function lut = sanitize(lut) genlut.write_SPI_1d( os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) cs.to_reference_transforms.append({ 'type': 'lutFile', 'path': lut, 'interpolation': 'linear', 'direction': 'forward'}) if gamut == 'Protune Native': cs.to_reference_transforms.append({ 'type': 'matrix', 'matrix': [0.533448429, 0.32413911, 0.142412421, 0, -0.050729924, 1.07572006, -0.024990416, 0, 0.071419661, -0.290521962, 1.219102381, 0, 0, 0, 0, 1], 'direction': 'forward'}) cs.from_reference_transforms = [] return cs
def create_log_c(gamut, transfer_function, exposure_index, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from LogC to ACES, with various transfer functions and encoding gamuts covered Parameters ---------- gamut : str The name of the encoding gamut to use. transfer_function : str The name of the transfer function to use exposure_index : str The exposure index to use lut_directory : str or unicode The directory to use when generating LUTs lut_resolution_1d : int The resolution of generated 1D LUTs aliases : list of str Aliases for this colorspace Returns ------- ColorSpace A ColorSpace container class referencing the LUTs, matrices and identifying information for the requested colorspace. """ name = '%s (EI%s) - %s' % (transfer_function, exposure_index, gamut) if transfer_function == '': name = 'Linear - ARRI %s' % gamut if gamut == '': name = 'Curve - %s (EI%s)' % (transfer_function, exposure_index) cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/ARRI' cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = ('IDT.ARRI.Alexa-v3-logC-EI%s.a1.v1' % exposure_index) # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] IDT_maker_version = '0.08' nominal_EI = 400 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 / 9 slope = 1 / (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 / 1023 - enc_offset) / enc_gain - offset) / slope enc_offset = encoding_offset - math.log10(1 + nz) * enc_gain a = 1 / gray b = nz - black_signal / gray e = slope * a * enc_gain f = enc_gain * (slope * b + offset) + enc_offset # Ensuring 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 normalized_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 - p['d']) / p['c']) - p['b']) / p['a']) else: linear = (code_value - p['f']) / p['e'] 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] = normalized_log_c_to_linear(c / (lut_resolution_1d - 1), int(exposure_index)) lut = '%s_to_linear.spi1d' % ('%s_%s' % (transfer_function, exposure_index)) lut = sanitize(lut) genlut.write_SPI_1d(os.path.join(lut_directory, lut), 0, 1, data, lut_resolution_1d, 1) 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