def create_ACEScg(): """ Creates the *ACEScg* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACEScg* colorspace. """ name = 'ACEScg' cs = ColorSpace(name) cs.description = 'The %s color space' % name cs.aliases = ['acescg', 'lin_ap1'] cs.equality_group = '' cs.family = 'ACES' cs.is_data = False cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] cs.aces_transform_id = 'ACEScsc.ACEScg_to_ACES.a1.0.0' 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 = [] # *AP1* primaries to *AP0* primaries cs.from_reference_transforms.append({ 'type': 'matrix', 'matrix': mat44_from_mat33(ACES_AP0_TO_AP1), 'direction': 'forward'}) return cs
def create_ACEScg(aces_ctl_directory, lut_directory, lut_resolution_1d, cleanup, name="ACEScg"): """ Creates the *ACEScg* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ACEScg* colorspace. """ cs = ColorSpace(name) cs.description = "The %s color space" % name cs.aliases = ["acescg", "lin_ap1"] cs.equality_group = "" cs.family = "ACES" cs.is_data = False cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] cs.aces_transform_id = "ACEScsc.ACEScg_to_ACES.a1.0.0" 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 = [] # *AP1* primaries to *AP0* primaries. cs.from_reference_transforms.append( {"type": "matrix", "matrix": mat44_from_mat33(ACES_AP0_TO_AP1), "direction": "forward"} ) return cs
def create_s_log(gamut, transfer_function, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from Sony spaces 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. """ name = '%s - %s' % (transfer_function, gamut) if transfer_function == '': name = 'Linear - %s' % gamut if gamut == '': name = 'Curve - %s' % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/Sony' cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = 'IDT.Sony.%s_%s_10i.a1.v1' % ( transfer_function.replace('-', ''), gamut.replace('-', '').replace(' ', '_')) # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] 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, ((code_value - 420) / 261.5)) * (0.18 + 0.01) - 0.01) else: linear = (code_value - 95) * 0.01125000 / (171.2102946929 - 95) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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'}) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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'}) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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 == '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_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_ADX(lut_directory, bit_depth=10, name='ADX'): """ Creates the *ADX* colorspace. Parameters ---------- parameter : type Parameter description. Returns ------- Colorspace *ADX* colorspace. """ name = '%s%s' % (name, bit_depth) cs = ColorSpace(name) cs.description = '%s color space - used for film scans' % name cs.aliases = ['adx%s' % str(bit_depth)] cs.equality_group = '' cs.family = 'ADX' cs.is_data = False if bit_depth == 10: cs.aces_transform_id = 'ACEScsc.ADX10_to_ACES.a1.0.0' cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT10 ADX_to_CDD = [1023 / 500, 0, 0, 0, 0, 1023 / 500, 0, 0, 0, 0, 1023 / 500, 0, 0, 0, 0, 1] offset = [-95 / 500, -95 / 500, -95 / 500, 0] elif bit_depth == 16: cs.aces_transform_id = 'ACEScsc.ADX16_to_ACES.a1.0.0' cs.bit_depth = ocio.Constants.BIT_DEPTH_UINT16 ADX_to_CDD = [65535 / 8000, 0, 0, 0, 0, 65535 / 8000, 0, 0, 0, 0, 65535 / 8000, 0, 0, 0, 0, 1] offset = [-1520 / 8000, -1520 / 8000, -1520 / 8000, 0] cs.to_reference_transforms = [] # Converting from *ADX* to *Channel-Dependent Density*. cs.to_reference_transforms.append({ 'type': 'matrix', 'matrix': ADX_to_CDD, 'offset': offset, 'direction': 'forward'}) # Converting 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, 1], '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 - 1520) / 8000 * (100 / 55) - math.log(0.18, 10)) def cid_to_rle(x): if x <= 0.6: return interpolate_1d(x, LUT_1D_XP, LUT_1D_FP) return (100 / 55) * 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 domain = (-0.19, 3) data = [] for i in xrange(num_samples): x = i / (num_samples - 1) x = fit(x, 0, 1, domain[0], domain[1]) data.append(cid_to_rle(x)) lut = 'ADX_CID_to_RLE.spi1d' write_SPI_1d(os.path.join(lut_directory, lut), domain[0], domain[1], data, num_samples, 1) return lut # Converting *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'}) # Converting *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, 1], '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): """ 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_s_log(gamut, transfer_function, lut_directory, lut_resolution_1d, aliases): """ Creates colorspace covering the conversion from Sony spaces 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. """ name = '%s - %s' % (transfer_function, gamut) if transfer_function == '': name = 'Linear - %s' % gamut if gamut == '': name = 'Curve - %s' % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = '' cs.family = 'Input/Sony' cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = 'IDT.Sony.%s_%s_10i.a1.v1' % ( transfer_function.replace('-', ''), gamut.replace('-', '').replace( ' ', '_')) # A linear space needs allocation variables. if transfer_function == '': cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] 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, ((code_value - 420) / 261.5)) * (0.18 + 0.01) - 0.01) else: linear = (code_value - 95) * 0.01125000 / (171.2102946929 - 95) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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' }) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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' }) 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 * c / (lut_resolution_1d - 1)) lut = '%s_to_linear.spi1d' % transfer_function 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 == '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_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_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_s_log(gamut, transfer_function, name, lut_directory, lut_resolution_1d, aliases): """ 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 = "Curve - %s" % transfer_function cs = ColorSpace(name) cs.description = name cs.aliases = aliases cs.equality_group = "" cs.family = "Input/Sony" cs.is_data = False if gamut and transfer_function: cs.aces_transform_id = "IDT.Sony.%s_%s_10i.a1.v1" % ( transfer_function.replace("-", ""), gamut.replace("-", "").replace(" ", "_"), ) # A linear space needs allocation variables if transfer_function == "": cs.allocation_type = ocio.Constants.ALLOCATION_LG2 cs.allocation_vars = [-8, 5, 0.00390625] def s_log1_to_linear(s_log): b = 64.0 ab = 90.0 w = 940.0 if s_log >= ab: linear = (pow(10.0, (((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) * 0.9 return linear def s_log2_to_linear(s_log): b = 64.0 ab = 90.0 w = 940.0 if s_log >= ab: linear = ( 219.0 * (pow(10.0, (((s_log - b) / (w - b) - 0.616596 - 0.03) / 0.432699)) - 0.037584) / 155.0 ) * 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, ((code_value - 420) / 261.5)) * (0.18 + 0.01) - 0.01 else: linear = (code_value - 95) * 0.01125000 / (171.2102946929 - 95) 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 * c / (lut_resolution_1d - 1)) lut = "%s_to_linear.spi1d" % transfer_function 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"} ) 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 * c / (lut_resolution_1d - 1)) lut = "%s_to_linear.spi1d" % transfer_function 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"} ) 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 * c / (lut_resolution_1d - 1)) lut = "%s_to_linear.spi1d" % transfer_function 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 == "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_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 = '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] # 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 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_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_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