def write_LUT_ResolveCube(LUT, path, decimals=7): """ Writes given *LUT* to given *Resolve* *.cube* *LUT* file. Parameters ---------- LUT : LUT1D or LUT3x1D or LUT3D or LUTSequence :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. path : unicode *LUT* path. decimals : int, optional Formatting decimals. Returns ------- bool Definition success. References ---------- :cite:`Chamberlain2015` Examples -------- Writing a 3x1D *Resolve* *.cube* *LUT*: >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.1, -0.1], [3.0, 3.0, 3.0]]) >>> LUT = LUT3x1D( ... spow(LUT3x1D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_ResolveCube(LUT, 'My_LUT.cube') # doctest: +SKIP Writing a 3D *Iridas* *.cube* *LUT*: >>> domain = np.array([[-0.1, -0.1, -0.1], [3.0, 3.0, 3.0]]) >>> LUT = LUT3D( ... spow(LUT3D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_ResolveCube(LUT, 'My_LUT.cube') # doctest: +SKIP """ has_3D, has_3x1D = False, False if isinstance(LUT, LUTSequence): assert (len(LUT) == 2 and isinstance(LUT[0], (LUT1D, LUT3x1D)) and isinstance(LUT[1], LUT3D)), ( 'LUTSequence must be 1D + 3D or 3x1D + 3D!') if isinstance(LUT[0], LUT1D): LUT[0] = LUT[0].as_LUT(LUT3x1D) has_3x1D = True has_3D = True name = LUT[1].name elif isinstance(LUT, LUT1D): name = LUT.name LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3x1D): name = LUT.name LUT = LUTSequence(LUT, LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3D): name = LUT.name LUT = LUTSequence(LUT3x1D(), LUT) has_3D = True else: raise ValueError('LUT must be 1D, 3x1D, 3D, 1D + 3D or 3x1D + 3D!') for i in range(2): assert not LUT[i].is_domain_explicit(), ( '"LUT" domain must be implicit!') assert (len(np.unique(LUT[0].domain)) == 2 and len(np.unique(LUT[1].domain)) == 2), 'LUT domain must be 1D!' if has_3x1D: assert 2 <= LUT[0].size <= 65536, ( 'Shaper size must be in domain [2, 65536]!') if has_3D: assert 2 <= LUT[1].size <= 256, 'Cube size must be in domain [2, 256]!' def _format_array(array): """ Formats given array as a *Resolve* *.cube* data row. """ return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) def _format_tuple(array): """ Formats given array as 2 space separated values to *decimals* precision. """ return '{1:0.{0}f} {2:0.{0}f}'.format(decimals, *array) with open(path, 'w') as cube_file: cube_file.write('TITLE "{0}"\n'.format(name)) if LUT[0].comments: for comment in LUT[0].comments: cube_file.write('# {0}\n'.format(comment)) if LUT[1].comments: for comment in LUT[1].comments: cube_file.write('# {0}\n'.format(comment)) default_domain = np.array([[0, 0, 0], [1, 1, 1]]) if has_3x1D: cube_file.write('{0} {1}\n'.format('LUT_1D_SIZE', LUT[0].table.shape[0])) if not np.array_equal(LUT[0].domain, default_domain): cube_file.write('LUT_1D_INPUT_RANGE {0}\n'.format( _format_tuple([LUT[0].domain[0][0], LUT[0].domain[1][0]]))) if has_3D: cube_file.write('{0} {1}\n'.format('LUT_3D_SIZE', LUT[1].table.shape[0])) if not np.array_equal(LUT[1].domain, default_domain): cube_file.write('LUT_3D_INPUT_RANGE {0}\n'.format( _format_tuple([LUT[1].domain[0][0], LUT[1].domain[1][0]]))) if has_3x1D: table = LUT[0].table for row in table: cube_file.write('{0}\n'.format(_format_array(row))) cube_file.write('\n') if has_3D: table = LUT[1].table.reshape([-1, 3], order='F') for row in table: cube_file.write('{0}\n'.format(_format_array(row))) return True
def write_LUT_Cinespace(LUT, path, decimals=7): """ Writes given *LUT* to given *Cinespace* *.csp* *LUT* file. Parameters ---------- LUT : LUT1D or LUT3x1D or LUT3D or LUTSequence :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. path : unicode *LUT* path. decimals : int, optional Formatting decimals. Returns ------- bool Definition success. References ---------- :cite:`RisingSunResearch` Examples -------- Writing a 3x1D *Cinespace* *.csp* *LUT*: >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> LUT = LUT3x1D( ... spow(LUT3x1D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_Cinespace(LUT, 'My_LUT.cube') # doctest: +SKIP Writing a 3D *Cinespace* *.csp* *LUT*: >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> LUT = LUT3D( ... spow(LUT3D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_Cinespace(LUT, 'My_LUT.cube') # doctest: +SKIP """ has_3D, has_3x1D, non_uniform = False, False, False if isinstance(LUT, LUTSequence): assert (len(LUT) == 2 and (isinstance(LUT[0], LUT1D) or isinstance(LUT[0], LUT3x1D)) and isinstance(LUT[1], LUT3D)), 'LUTSequence must be 1D+3D or 3x1D+3D!' has_3x1D = True has_3D = True name = LUT[1].name if isinstance(LUT[0], LUT1D): LUT[0] = LUT[0].as_LUT(LUT3x1D) elif isinstance(LUT, LUT1D): if LUT.is_domain_explicit(): non_uniform = True name = LUT.name LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3x1D): if LUT.is_domain_explicit(): non_uniform = True name = LUT.name LUT = LUTSequence(LUT, LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3D): name = LUT.name LUT = LUTSequence(LUT3x1D(), LUT) has_3D = True else: assert False, 'LUT must be 1D, 3x1D, 3D, 1D+3D or 3x1D+3D!' if has_3x1D: assert 2 <= LUT[0].size <= 65536, ( 'Shaper size must be in domain [2, 65536]!') if has_3D: assert 2 <= LUT[1].size <= 256, 'Cube size must be in domain [2, 256]!' def _ragged_size(table): """ Return the ragged size of given table. """ r, g, b = tsplit(table) r_len = r.shape[-1] - np.sum(np.isnan(r)) g_len = g.shape[-1] - np.sum(np.isnan(g)) b_len = b.shape[-1] - np.sum(np.isnan(b)) return [r_len, g_len, b_len] def _format_array(array): """ Formats given array as a *Cinespace* *.cube* data row. """ return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) def _format_tuple(array): """ Formats given array as 2 space separated values to *decimals* precision. """ return '{1:0.{0}f} {2:0.{0}f}'.format(decimals, *array) with open(path, 'w') as csp_file: csp_file.write('CSPLUTV100\n') if has_3D: csp_file.write('3D\n\n') else: csp_file.write('1D\n\n') csp_file.write('BEGIN METADATA\n') csp_file.write('{0}\n'.format(name)) if LUT[0].comments: for comment in LUT[0].comments: csp_file.write('{0}\n'.format(comment)) if LUT[1].comments: for comment in LUT[1].comments: csp_file.write('{0}\n'.format(comment)) csp_file.write('END METADATA\n\n') if has_3D or non_uniform: if has_3x1D: for i in range(3): if LUT[0].is_domain_explicit(): size = _ragged_size(LUT[0].domain)[i] table_min = np.nanmin(LUT[0].table) table_max = np.nanmax(LUT[0].table) else: size = LUT[0].size csp_file.write('{0}\n'.format(size)) for j in range(size): if LUT[0].is_domain_explicit(): entry = LUT[0].domain[j][i] else: entry = ( LUT[0].domain[0][i] + j * (LUT[0].domain[1][i] - LUT[0].domain[0][i]) / (LUT[0].size - 1)) csp_file.write('{0:.{1}f} '.format(entry, decimals)) csp_file.write('\n') for j in range(size): entry = LUT[0].table[j][i] if non_uniform: entry -= table_min entry /= (table_max - table_min) csp_file.write('{0:.{1}f} '.format(entry, decimals)) csp_file.write('\n') else: for i in range(3): csp_file.write('2\n') csp_file.write('{0}\n'.format( _format_tuple( [LUT[1].domain[0][i], LUT[1].domain[1][i]]))) csp_file.write('{0:.{2}f} {1:.{2}f}\n'.format( 0, 1, decimals)) if non_uniform: csp_file.write('\n{0}\n'.format(2)) row = [table_min, table_min, table_min] csp_file.write('{0}\n'.format(_format_array(row))) row = [table_max, table_max, table_max] csp_file.write('{0}\n'.format(_format_array(row))) else: csp_file.write('\n{0} {1} {2}\n'.format( LUT[1].table.shape[0], LUT[1].table.shape[1], LUT[1].table.shape[2])) table = LUT[1].table.reshape((-1, 3), order='F') for row in table: csp_file.write('{0}\n'.format(_format_array(row))) else: for i in range(3): csp_file.write('2\n') csp_file.write('{0}\n'.format( _format_tuple([LUT[0].domain[0][i], LUT[0].domain[1][i]]))) csp_file.write('0.0 1.0\n') csp_file.write('\n{0}\n'.format(LUT[0].size)) table = LUT[0].table for row in table: csp_file.write('{0}\n'.format(_format_array(row))) return True
def write_LUT_Cinespace(LUT, path, decimals=7): """ Writes given *LUT* to given *Cinespace* *.csp* *LUT* file. Parameters ---------- LUT : LUT1D or LUT3x1D or LUT3D or LUTSequence :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. path : unicode *LUT* path. decimals : int, optional Formatting decimals. Returns ------- bool Definition success. References ---------- :cite:`RisingSunResearch` Examples -------- Writing a 3x1D *Cinespace* *.csp* *LUT*: >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> LUT = LUT3x1D( ... spow(LUT3x1D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_Cinespace(LUT, 'My_LUT.cube') # doctest: +SKIP Writing a 3D *Cinespace* *.csp* *LUT*: >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> LUT = LUT3D( ... spow(LUT3D.linear_table(16, domain), 1 / 2.2), ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) >>> write_LUT_Cinespace(LUT, 'My_LUT.cube') # doctest: +SKIP """ has_3D, has_3x1D, non_uniform = False, False, False if isinstance(LUT, LUTSequence): assert (len(LUT) == 2 and (isinstance(LUT[0], LUT1D) or isinstance(LUT[0], LUT3x1D)) and isinstance(LUT[1], LUT3D)), 'LUTSequence must be 1D+3D or 3x1D+3D!' has_3x1D = True has_3D = True name = LUT[1].name if isinstance(LUT[0], LUT1D): LUT[0] = LUT[0].as_LUT(LUT3x1D) elif isinstance(LUT, LUT1D): if LUT.is_domain_explicit(): non_uniform = True name = LUT.name LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3x1D): if LUT.is_domain_explicit(): non_uniform = True name = LUT.name LUT = LUTSequence(LUT, LUT3D()) has_3x1D = True elif isinstance(LUT, LUT3D): name = LUT.name LUT = LUTSequence(LUT3x1D(), LUT) has_3D = True else: assert False, 'LUT must be 1D, 3x1D, 3D, 1D+3D or 3x1D+3D!' if has_3x1D: assert 2 <= LUT[0].size <= 65536, ( 'Shaper size must be in domain [2, 65536]!') if has_3D: assert 2 <= LUT[1].size <= 256, 'Cube size must be in domain [2, 256]!' def _ragged_size(table): """ Return the ragged size of given table. """ r, g, b = tsplit(table) r_len = r.shape[-1] - np.sum(np.isnan(r)) g_len = g.shape[-1] - np.sum(np.isnan(g)) b_len = b.shape[-1] - np.sum(np.isnan(b)) return [r_len, g_len, b_len] def _format_array(array): """ Formats given array as a *Cinespace* *.cube* data row. """ return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) def _format_tuple(array): """ Formats given array as 2 space separated values to *decimals* precision. """ return '{1:0.{0}f} {2:0.{0}f}'.format(decimals, *array) with open(path, 'w') as csp_file: csp_file.write('CSPLUTV100\n') if has_3D: csp_file.write('3D\n\n') else: csp_file.write('1D\n\n') csp_file.write('BEGIN METADATA\n') csp_file.write('{0}\n'.format(name)) if LUT[0].comments: for comment in LUT[0].comments: csp_file.write('{0}\n'.format(comment)) if LUT[1].comments: for comment in LUT[1].comments: csp_file.write('{0}\n'.format(comment)) csp_file.write('END METADATA\n\n') if has_3D or non_uniform: if has_3x1D: for i in range(3): if LUT[0].is_domain_explicit(): size = _ragged_size(LUT[0].domain)[i] table_min = np.nanmin(LUT[0].table) table_max = np.nanmax(LUT[0].table) else: size = LUT[0].size csp_file.write('{0}\n'.format(size)) for j in range(size): if LUT[0].is_domain_explicit(): entry = LUT[0].domain[j][i] else: entry = ( LUT[0].domain[0][i] + j * (LUT[0].domain[1][i] - LUT[0].domain[0][i]) / (LUT[0].size - 1)) csp_file.write('{0:.{1}f} '.format(entry, decimals)) csp_file.write('\n') for j in range(size): entry = LUT[0].table[j][i] if non_uniform: entry -= table_min entry /= (table_max - table_min) csp_file.write('{0:.{1}f} '.format(entry, decimals)) csp_file.write('\n') else: for i in range(3): csp_file.write('2\n') csp_file.write('{0}\n'.format( _format_tuple( [LUT[1].domain[0][i], LUT[1].domain[1][i]]))) csp_file.write('{0:.{2}f} {1:.{2}f}\n'.format( 0, 1, decimals)) if non_uniform: csp_file.write('\n{0}\n'.format(2)) row = [table_min, table_min, table_min] csp_file.write('{0}\n'.format(_format_array(row))) row = [table_max, table_max, table_max] csp_file.write('{0}\n'.format(_format_array(row))) else: csp_file.write('\n{0} {1} {2}\n'.format( LUT[1].table.shape[0], LUT[1].table.shape[1], LUT[1].table.shape[2])) table = LUT[1].table.reshape([-1, 3], order='F') for row in table: csp_file.write('{0}\n'.format(_format_array(row))) else: for i in range(3): csp_file.write('2\n') csp_file.write('{0}\n'.format( _format_tuple([LUT[0].domain[0][i], LUT[0].domain[1][i]]))) csp_file.write('0.0 1.0\n') csp_file.write('\n{0}\n'.format(LUT[0].size)) table = LUT[0].table for row in table: csp_file.write('{0}\n'.format(_format_array(row))) return True