Updated to ACES 1.0.1
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / generate_lut.py
diff --git a/aces_1.0.0/python/aces_ocio/generate_lut.py b/aces_1.0.0/python/aces_ocio/generate_lut.py
deleted file mode 100755 (executable)
index 94bd735..0000000
+++ /dev/null
@@ -1,1048 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""
-Defines objects to generate various kind of 1D and 3D LUTs in various file
-formats.
-"""
-
-from __future__ import division
-
-import array
-import os
-
-import OpenImageIO as oiio
-
-from aces_ocio.process import Process
-
-__author__ = 'ACES Developers'
-__copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
-__license__ = ''
-__maintainer__ = 'ACES Developers'
-__email__ = 'aces@oscars.org'
-__status__ = 'Production'
-
-__all__ = ['generate_1d_LUT_image',
-           'write_SPI_1d',
-           'write_CSP_1d',
-           'write_CTL_1d',
-           'write_1d',
-           'generate_1d_LUT_from_image',
-           'generate_3d_LUT_image',
-           'generate_3d_LUT_from_image',
-           'apply_CTL_to_image',
-           'convert_bit_depth',
-           'generate_1d_LUT_from_CTL',
-           'correct_LUT_image',
-           'generate_3d_LUT_from_CTL',
-           'main']
-
-
-def generate_1d_LUT_image(ramp_1d_path,
-                          resolution=1024,
-                          min_value=0,
-                          max_value=1):
-    """
-    Generates a 1D LUT image, i.e. a simple ramp, going from the min_value to 
-    the max_value.
-
-    Parameters
-    ----------
-    ramp_1d_path : str or unicode
-        The path of the 1D ramp image to be written
-    resolution : int, optional
-        The resolution of the 1D ramp image to be written
-    min_value : float, optional
-        The lowest value in the 1D ramp
-    max_value : float, optional
-        The highest value in the 1D ramp
-
-    Returns
-    -------
-    None
-    """
-
-    ramp = oiio.ImageOutput.create(ramp_1d_path)
-
-    spec = oiio.ImageSpec()
-    spec.set_format(oiio.FLOAT)
-    # spec.format.basetype = oiio.FLOAT
-    spec.width = resolution
-    spec.height = 1
-    spec.nchannels = 3
-
-    ramp.open(ramp_1d_path, spec, oiio.Create)
-
-    data = array.array('f',
-                       '\0' * spec.width * spec.height * spec.nchannels * 4)
-    for i in range(resolution):
-        value = float(i) / (resolution - 1) * (
-            max_value - min_value) + min_value
-        data[i * spec.nchannels + 0] = value
-        data[i * spec.nchannels + 1] = value
-        data[i * spec.nchannels + 2] = value
-
-    ramp.write_image(spec.format, data)
-    ramp.close()
-
-
-def write_SPI_1d(filename,
-                 from_min,
-                 from_max,
-                 data,
-                 entries,
-                 channels,
-                 components=3):
-    """
-    Writes a 1D LUT in the Sony Pictures Imageworks .spi1d format.
-
-    Credit to *Alex Fry* for the original single channel version of the spi1d
-    writer.
-
-    Parameters
-    ----------
-    filename : str or unicode
-        The path of the 1D LUT to be written
-    from_min : float
-        The lowest value in the 1D ramp
-    from_max : float
-        The highest value in the 1D ramp
-    data : array of floats
-        The entries in the LUT
-    entries : int
-        The resolution of the LUT, i.e. number of entries in the data set
-    channels : int
-        The number of channels in the data
-    components : int, optional
-        The number of channels in the data to actually write
-
-    Returns
-    -------
-    None
-    """
-
-    # May want to use fewer components than there are channels in the data
-    # Most commonly used for single channel LUTs
-    components = min(3, components, channels)
-
-    with open(filename, 'w') as fp:
-        fp.write('Version 1\n')
-        fp.write('From %f %f\n' % (from_min, from_max))
-        fp.write('Length %d\n' % entries)
-        fp.write('Components %d\n' % components)
-        fp.write('{\n')
-        for i in range(0, entries):
-            entry = ''
-            for j in range(0, components):
-                entry = '%s %s' % (entry, data[i * channels + j])
-            fp.write('        %s\n' % entry)
-        fp.write('}\n')
-
-
-def write_CSP_1d(filename,
-                 from_min,
-                 from_max,
-                 data,
-                 entries,
-                 channels,
-                 components=3):
-    """
-    Writes a 1D LUT in the Rising Sun Research Cinespace .csp format.
-
-    Parameters
-    ----------
-    filename : str or unicode
-        The path of the 1D LUT to be written
-    from_min : float
-        The lowest value in the 1D ramp
-    from_max : float
-        The highest value in the 1D ramp
-    data : array of floats
-        The entries in the LUT
-    entries : int
-        The resolution of the LUT, i.e. number of entries in the data set
-    channels : int
-        The number of channels in the data
-    components : int, optional
-        The number of channels in the data to actually write
-
-    Returns
-    -------
-    None
-    """
-
-    # May want to use fewer components than there are channels in the data
-    # Most commonly used for single channel LUTs
-    components = min(3, components, channels)
-
-    with open(filename, 'w') as fp:
-        fp.write('CSPLUTV100\n')
-        fp.write('1D\n')
-        fp.write('\n')
-        fp.write('BEGIN METADATA\n')
-        fp.write('END METADATA\n')
-
-        fp.write('\n')
-
-        fp.write('2\n')
-        fp.write('%f %f\n' % (from_min, from_max))
-        fp.write('0.0 1.0\n')
-        fp.write('2\n')
-        fp.write('%f %f\n' % (from_min, from_max))
-        fp.write('0.0 1.0\n')
-        fp.write('2\n')
-        fp.write('%f %f\n' % (from_min, from_max))
-        fp.write('0.0 1.0\n')
-
-        fp.write('\n')
-
-        fp.write('%d\n' % entries)
-        if components == 1:
-            for i in range(0, entries):
-                entry = ''
-                for j in range(3):
-                    entry = '%s %s' % (entry, data[i * channels])
-                fp.write('%s\n' % entry)
-        else:
-            for i in range(entries):
-                entry = ''
-                for j in range(components):
-                    entry = '%s %s' % (entry, data[i * channels + j])
-                fp.write('%s\n' % entry)
-        fp.write('\n')
-
-
-def write_CTL_1d(filename,
-                 from_min,
-                 from_max,
-                 data,
-                 entries,
-                 channels,
-                 components=3):
-    """
-    Writes a 1D LUT in the Academy Color Transformation Language .ctl format.
-
-    Parameters
-    ----------
-    filename : str or unicode
-        The path of the 1D LUT to be written
-    from_min : float
-        The lowest value in the 1D ramp
-    from_max : float
-        The highest value in the 1D ramp
-    data : array of floats
-        The entries in the LUT
-    entries : int
-        The resolution of the LUT, i.e. number of entries in the data set
-    channels : int
-        The number of channels in the data
-    components : int, optional
-        The number of channels in the data to actually write
-
-    Returns
-    -------
-    None
-    """
-
-    # May want to use fewer components than there are channels in the data
-    # Most commonly used for single channel LUTs
-    components = min(3, components, channels)
-
-    with open(filename, 'w') as fp:
-        fp.write('// %d x %d LUT generated by "generate_lut"\n' % (
-            entries, components))
-        fp.write('\n')
-        fp.write('const float min1d = %3.9f;\n' % from_min)
-        fp.write('const float max1d = %3.9f;\n' % from_max)
-        fp.write('\n')
-
-        # Write LUT
-        if components == 1:
-            fp.write('const float lut[] = {\n')
-            for i in range(0, entries):
-                fp.write('%s' % data[i * channels])
-                if i != (entries - 1):
-                    fp.write(',')
-                fp.write('\n')
-            fp.write('};\n')
-            fp.write('\n')
-        else:
-            for j in range(components):
-                fp.write('const float lut%d[] = {\n' % j)
-                for i in range(0, entries):
-                    fp.write('%s' % data[i * channels])
-                    if i != (entries - 1):
-                        fp.write(',')
-                    fp.write('\n')
-                fp.write('};\n')
-                fp.write('\n')
-
-        fp.write('void main\n')
-        fp.write('(\n')
-        fp.write('  input varying float rIn,\n')
-        fp.write('  input varying float gIn,\n')
-        fp.write('  input varying float bIn,\n')
-        fp.write('  input varying float aIn,\n')
-        fp.write('  output varying float rOut,\n')
-        fp.write('  output varying float gOut,\n')
-        fp.write('  output varying float bOut,\n')
-        fp.write('  output varying float aOut\n')
-        fp.write(')\n')
-        fp.write('{\n')
-        fp.write('  float r = rIn;\n')
-        fp.write('  float g = gIn;\n')
-        fp.write('  float b = bIn;\n')
-        fp.write('\n')
-        fp.write('  // Apply LUT\n')
-        if components == 1:
-            fp.write('  r = lookup1D(lut, min1d, max1d, r);\n')
-            fp.write('  g = lookup1D(lut, min1d, max1d, g);\n')
-            fp.write('  b = lookup1D(lut, min1d, max1d, b);\n')
-        elif components == 3:
-            fp.write('  r = lookup1D(lut0, min1d, max1d, r);\n')
-            fp.write('  g = lookup1D(lut1, min1d, max1d, g);\n')
-            fp.write('  b = lookup1D(lut2, min1d, max1d, b);\n')
-        fp.write('\n')
-        fp.write('  rOut = r;\n')
-        fp.write('  gOut = g;\n')
-        fp.write('  bOut = b;\n')
-        fp.write('  aOut = aIn;\n')
-        fp.write('}\n')
-
-
-def write_1d(filename,
-             from_min,
-             from_max,
-             data,
-             data_entries,
-             data_channels,
-             lut_components=3,
-             format='spi1d'):
-    """
-    Writes a 1D LUT in the specified format.
-
-    Parameters
-    ----------
-    filename : str or unicode
-        The path of the 1D LUT to be written
-    from_min : float
-        The lowest value in the 1D ramp
-    from_max : float
-        The highest value in the 1D ramp
-    data : array of floats
-        The entries in the LUT
-    data_entries : int
-        The resolution of the LUT, i.e. number of entries in the data set
-    data_channels : int
-        The number of channels in the data
-    lut_components : int, optional
-        The number of channels in the data to actually use when writing
-    format : str or unicode, optional
-        The format of the the 1D LUT that will be written
-
-    Returns
-    -------
-    None
-    """
-
-    ocio_formats_to_extensions = {'cinespace': 'csp',
-                                  'flame': '3dl',
-                                  'icc': 'icc',
-                                  'houdini': 'lut',
-                                  'lustre': '3dl',
-                                  'ctl': 'ctl'}
-
-    if format in ocio_formats_to_extensions:
-        if ocio_formats_to_extensions[format] == 'csp':
-            write_CSP_1d(filename,
-                         from_min,
-                         from_max,
-                         data,
-                         data_entries,
-                         data_channels,
-                         lut_components)
-        elif ocio_formats_to_extensions[format] == 'ctl':
-            write_CTL_1d(filename,
-                         from_min,
-                         from_max,
-                         data,
-                         data_entries,
-                         data_channels,
-                         lut_components)
-    else:
-        write_SPI_1d(filename,
-                     from_min,
-                     from_max,
-                     data,
-                     data_entries,
-                     data_channels,
-                     lut_components)
-
-
-def generate_1d_LUT_from_image(ramp_1d_path,
-                               output_path=None,
-                               min_value=0,
-                               max_value=1,
-                               channels=3,
-                               format='spi1d'):
-    """
-    Reads a 1D LUT image and writes a 1D LUT in the specified format.
-
-    Parameters
-    ----------
-    ramp_1d_path : str or unicode
-        The path of the 1D ramp image to be read
-    output_path : str or unicode, optional
-        The path of the 1D LUT to be written
-    min_value : float, optional
-        The lowest value in the 1D ramp
-    max_value : float, optional
-        The highest value in the 1D ramp
-    channels : int, optional
-        The number of channels in the data
-    format : str or unicode, optional
-        The format of the the 1D LUT that will be written
-
-    Returns
-    -------
-    None
-    """
-
-    if output_path is None:
-        output_path = '%s.%s' % (ramp_1d_path, 'spi1d')
-
-    ramp = oiio.ImageInput.open(ramp_1d_path)
-
-    ramp_spec = ramp.spec()
-    ramp_width = ramp_spec.width
-    ramp_channels = ramp_spec.nchannels
-
-    # Forcibly read data as float, the Python API doesn't handle half-float
-    # well yet.
-    type = oiio.FLOAT
-    ramp_data = ramp.read_image(type)
-
-    write_1d(output_path, min_value, max_value,
-             ramp_data, ramp_width, ramp_channels, channels, format)
-
-
-def generate_3d_LUT_image(ramp_3d_path, resolution=32):
-    """
-    Generates a 3D LUT image covering the specified resolution
-    Relies on OCIO's ociolutimage command
-
-    Parameters
-    ----------
-    ramp_3d_path : str or unicode
-        The path of the 3D ramp image to be written
-    resolution : int, optional
-        The resolution of the 3D ramp image to be written
-
-    Returns
-    -------
-    None
-    """
-
-    args = ['--generate',
-            '--cubesize',
-            str(resolution),
-            '--maxwidth',
-            str(resolution * resolution),
-            '--output',
-            ramp_3d_path]
-    lut_extract = Process(description='generate a 3d LUT image',
-                          cmd='ociolutimage',
-                          args=args)
-    lut_extract.execute()
-
-
-def generate_3d_LUT_from_image(ramp_3d_path,
-                               output_path=None,
-                               resolution=32,
-                               format='spi3d'):
-    """
-    Reads a 3D LUT image and writes a 3D LUT in the specified format.
-    Relies on OCIO's ociolutimage command
-
-    Parameters
-    ----------
-    ramp_3d_path : str or unicode
-        The path of the 3D ramp image to be read
-    output_path : str or unicode, optional
-        The path of the 1D LUT to be written
-    resolution : int, optional
-        The resolution of the 3D LUT represented in the image
-    format : str or unicode, optional
-        The format of the the 3D LUT that will be written
-
-    Returns
-    -------
-    None
-    """
-
-    if output_path is None:
-        output_path = '%s.%s' % (ramp_3d_path, 'spi3d')
-
-    ocio_formats_to_extensions = {'cinespace': 'csp',
-                                  'flame': '3dl',
-                                  'icc': 'icc',
-                                  'houdini': 'lut',
-                                  'lustre': '3dl'}
-
-    if format == 'spi3d' or not (format in ocio_formats_to_extensions):
-        # Extract a spi3d LUT
-        args = ['--extract',
-                '--cubesize',
-                str(resolution),
-                '--maxwidth',
-                str(resolution * resolution),
-                '--input',
-                ramp_3d_path,
-                '--output',
-                output_path]
-        lut_extract = Process(description='extract a 3d LUT',
-                              cmd='ociolutimage',
-                              args=args)
-        lut_extract.execute()
-
-    else:
-        output_path_spi3d = '%s.%s' % (output_path, 'spi3d')
-
-        # Extract a spi3d LUT
-        args = ['--extract',
-                '--cubesize',
-                str(resolution),
-                '--maxwidth',
-                str(resolution * resolution),
-                '--input',
-                ramp_3d_path,
-                '--output',
-                output_path_spi3d]
-        lut_extract = Process(description='extract a 3d LUT',
-                              cmd='ociolutimage',
-                              args=args)
-        lut_extract.execute()
-
-        # Convert to a different format
-        args = ['--lut',
-                output_path_spi3d,
-                '--format',
-                format,
-                output_path]
-        lut_convert = Process(description='convert a 3d LUT',
-                              cmd='ociobakelut',
-                              args=args)
-        lut_convert.execute()
-
-
-def apply_CTL_to_image(input_image,
-                       output_image,
-                       ctl_paths=None,
-                       input_scale=1,
-                       output_scale=1,
-                       global_params=None,
-                       aces_ctl_directory=None):
-    """
-    Applies a set of Academy Color Transformation Language .ctl files to 
-    an input image and writes a new image.
-    Relies on the ACES ctlrender command
-
-    Parameters
-    ----------
-    input_image : str or unicode
-        The path to the image to transform using the CTL files
-    output_image : str or unicode
-        The path to write the result of the transforms
-    ctl_paths : array of str or unicode, optional
-        The path to write the result of the transforms
-    input_scale : float, optional
-        The argument to the ctlrender -input_scale parameter
-        For images with integer bit depths, this divides image code values 
-        before they are sent to the ctl commands
-        For images with float or half bit depths, this multiplies image code 
-        values before they are sent to the ctl commands
-    output_scale : float, optional
-        The argument to the ctlrender -output_scale parameter
-        For images with integer bit depths, this multiplies image code values 
-        before they are written to a file.
-        For images with float or half bit depths, this divides image code values 
-        before they are sent to the ctl commands
-    global_params : dict of key value pairs, optional
-        The set of parameter names and values to pass to the ctlrender 
-        -global_param1 parameter
-    aces_ctl_directory : str or unicode, optional
-        The path to the aces 'transforms/ctl/utilities'
-
-    Returns
-    -------
-    None
-    """
-
-    if ctl_paths is None:
-        ctl_paths = []
-    if global_params is None:
-        global_params = {}
-
-    if len(ctl_paths) > 0:
-        ctlenv = os.environ
-
-        if "/usr/local/bin" not in ctlenv['PATH'].split(':'):
-            ctlenv['PATH'] = "%s:/usr/local/bin" % ctlenv['PATH']
-
-        if aces_ctl_directory is not None:
-            if os.path.split(aces_ctl_directory)[1] != 'utilities':
-                ctl_module_path = os.path.join(aces_ctl_directory, 'utilities')
-            else:
-                ctl_module_path = aces_ctl_directory
-            ctlenv['CTL_MODULE_PATH'] = ctl_module_path
-
-        args = []
-        for ctl in ctl_paths:
-            args += ['-ctl', ctl]
-        args += ['-force']
-        args += ['-input_scale', str(input_scale)]
-        args += ['-output_scale', str(output_scale)]
-        args += ['-global_param1', 'aIn', '1.0']
-        for key, value in global_params.iteritems():
-            args += ['-global_param1', key, str(value)]
-        args += [input_image]
-        args += [output_image]
-
-        ctlp = Process(description='a ctlrender process',
-                       cmd='ctlrender',
-                       args=args, env=ctlenv)
-
-        ctlp.execute()
-
-
-def convert_bit_depth(input_image, output_image, depth):
-    """
-    Convert the input image to the specified bit depth and write a new image
-    Relies on the OIIO oiiotool command
-
-    Parameters
-    ----------
-    input_image : str or unicode
-        The path to the image to transform using the CTL files
-    output_image : str or unicode
-        The path to write the result of the transforms
-    depth : str or unicode
-        The bit depth of the output image
-        Data types include: uint8, sint8, uint10, uint12, uint16, sint16, half, float, double
-
-    Returns
-    -------
-    None
-    """
-
-    args = [input_image,
-            '-d',
-            depth,
-            '-o',
-            output_image]
-    convert = Process(description='convert image bit depth',
-                      cmd='oiiotool',
-                      args=args)
-    convert.execute()
-
-
-def generate_1d_LUT_from_CTL(lut_path,
-                             ctl_paths,
-                             lut_resolution=1024,
-                             identity_lut_bit_depth='half',
-                             input_scale=1,
-                             output_scale=1,
-                             global_params=None,
-                             cleanup=True,
-                             aces_ctl_directory=None,
-                             min_value=0,
-                             max_value=1,
-                             channels=3,
-                             format='spi1d'):
-    """
-    Creates a 1D LUT from the specified CTL files by creating a 1D LUT image,
-    applying the CTL files and then extracting and writing a LUT based on the
-    resulting image
-
-    Parameters
-    ----------
-    lut_path : str or unicode
-        The path to write the 1D LUT
-    ctl_paths : array of str or unicode
-        The CTL files to apply
-    lut_resolution : int, optional
-        The resolution of the 1D LUT to generate
-    identity_lut_bit_depth : string, optional
-        The bit depth to use for the intermediate 1D LUT image
-    input_scale : float, optional
-        The argument to the ctlrender -input_scale parameter
-        For images with integer bit depths, this divides image code values 
-        before they are sent to the ctl commands
-        For images with float or half bit depths, this multiplies image code 
-        values before they are sent to the ctl commands
-    output_scale : float, optional
-        The argument to the ctlrender -output_scale parameter
-        For images with integer bit depths, this multiplies image code values 
-        before they are written to a file.
-        For images with float or half bit depths, this divides image code values 
-        before they are sent to the ctl commands
-    global_params : dict of key, value pairs, optional
-        The set of parameter names and values to pass to the ctlrender 
-        -global_param1 parameter
-    cleanup : bool, optional
-        Whether or not to clean up the intermediate images 
-    aces_ctl_directory : str or unicode, optional
-        The path to the aces 'transforms/ctl/utilities'
-    min_value : float, optional
-        The minimum value to consider as input to the LUT
-    max_value : float, optional
-        The maximum value to consider as input to the LUT
-    channels : int, optional
-        The number of channels to use for the LUT. 1 or 3 are valid.
-    format : str or unicode, optional
-        The format to use when writing the LUT
-
-    Returns
-    -------
-    None
-    """
-
-    if global_params is None:
-        global_params = {}
-
-    lut_path_base = os.path.splitext(lut_path)[0]
-
-    identity_lut_image_float = '%s.%s.%s' % (lut_path_base, 'float', 'tiff')
-    generate_1d_LUT_image(identity_lut_image_float,
-                          lut_resolution,
-                          min_value,
-                          max_value)
-
-    if identity_lut_bit_depth not in ['half', 'float']:
-        identity_lut_image = '%s.%s.%s' % (lut_path_base, 'uint16', 'tiff')
-        convert_bit_depth(identity_lut_image_float,
-                          identity_lut_image,
-                          identity_lut_bit_depth)
-    else:
-        identity_lut_image = identity_lut_image_float
-
-    transformed_lut_image = '%s.%s.%s' % (lut_path_base, 'transformed', 'exr')
-    apply_CTL_to_image(identity_lut_image,
-                       transformed_lut_image,
-                       ctl_paths,
-                       input_scale,
-                       output_scale,
-                       global_params,
-                       aces_ctl_directory)
-
-    generate_1d_LUT_from_image(transformed_lut_image,
-                               lut_path,
-                               min_value,
-                               max_value,
-                               channels,
-                               format)
-
-    if cleanup:
-        os.remove(identity_lut_image)
-        if identity_lut_image != identity_lut_image_float:
-            os.remove(identity_lut_image_float)
-        os.remove(transformed_lut_image)
-
-
-def correct_LUT_image(transformed_lut_image,
-                      corrected_lut_image,
-                      lut_resolution):
-    """
-    For some combinations of resolution and bit depth, ctlrender would generate
-    images with the right number of pixels but with the values for width and 
-    height transposed. This function generating a new, corrected image based on
-    the original. The function acts as a pass through if the problem is not
-    detected.
-
-    Parameters
-    ----------
-    transformed_lut_image : str or unicode
-        The path to an image generated by cltrender
-    corrected_lut_image : str or unicode
-        The path to an 'corrected' image to be generated
-    lut_resolution : int
-        The resolution of the 3D LUT that should be contained in 
-        transformed_lut_image
-
-    Returns
-    -------
-    str or unicode
-        The path to the corrected image, or the original, if no correction was
-        needed.
-    """
-
-    transformed = oiio.ImageInput.open(transformed_lut_image)
-
-    transformed_spec = transformed.spec()
-    width = transformed_spec.width
-    height = transformed_spec.height
-    channels = transformed_spec.nchannels
-
-    if width != lut_resolution * lut_resolution or height != lut_resolution:
-        print(('Correcting image as resolution is off. '
-               'Found %d x %d. Expected %d x %d') % (
-                  width,
-                  height,
-                  lut_resolution * lut_resolution,
-                  lut_resolution))
-        print('Generating %s' % corrected_lut_image)
-
-        # Forcibly read data as float, the Python API doesn't handle half-float
-        # well yet.
-        type = oiio.FLOAT
-        source_data = transformed.read_image(type)
-
-        correct = oiio.ImageOutput.create(corrected_lut_image)
-
-        correct_spec = oiio.ImageSpec()
-        correct_spec.set_format(oiio.FLOAT)
-        correct_spec.width = height
-        correct_spec.height = width
-        correct_spec.nchannels = channels
-
-        correct.open(corrected_lut_image, correct_spec, oiio.Create)
-
-        dest_data = array.array('f',
-                                ('\0' * correct_spec.width *
-                                 correct_spec.height *
-                                 correct_spec.nchannels * 4))
-        for j in range(0, correct_spec.height):
-            for i in range(0, correct_spec.width):
-                for c in range(0, correct_spec.nchannels):
-                    dest_data[(correct_spec.nchannels *
-                               correct_spec.width * j +
-                               correct_spec.nchannels * i + c)] = (
-                        source_data[correct_spec.nchannels *
-                                    correct_spec.width * j +
-                                    correct_spec.nchannels * i + c])
-
-        correct.write_image(correct_spec.format, dest_data)
-        correct.close()
-    else:
-        # shutil.copy(transformedLUTImage, correctedLUTImage)
-        corrected_lut_image = transformed_lut_image
-
-    transformed.close()
-
-    return corrected_lut_image
-
-
-def generate_3d_LUT_from_CTL(lut_path,
-                             ctl_paths,
-                             lut_resolution=64,
-                             identity_lut_bit_depth='half',
-                             input_scale=1,
-                             output_scale=1,
-                             global_params=None,
-                             cleanup=True,
-                             aces_ctl_directory=None,
-                             format='spi3d'):
-    """
-    Creates a 3D LUT from the specified CTL files by creating a 3D LUT image,
-    applying the CTL files and then extracting and writing a LUT based on the
-    resulting image
-
-    Parameters
-    ----------
-    lut_path : str or unicode
-        The path to write the 1D LUT
-    ctl_paths : array of str or unicode
-        The CTL files to apply
-    lut_resolution : int, optional
-        The resolution of the 1D LUT to generate
-    identity_lut_bit_depth : string, optional
-        The bit depth to use for the intermediate 1D LUT image
-    input_scale : float, optional
-        The argument to the ctlrender -input_scale parameter
-        For images with integer bit depths, this divides image code values 
-        before they are sent to the ctl commands
-        For images with float or half bit depths, this multiplies image code 
-        values before they are sent to the ctl commands
-    output_scale : float, optional
-        The argument to the ctlrender -output_scale parameter
-        For images with integer bit depths, this multiplies image code values 
-        before they are written to a file.
-        For images with float or half bit depths, this divides image code values 
-        before they are sent to the ctl commands
-    global_params : dict of key, value pairs, optional
-        The set of parameter names and values to pass to the ctlrender 
-        -global_param1 parameter
-    cleanup : bool, optional
-        Whether or not to clean up the intermediate images 
-    aces_ctl_directory : str or unicode, optional
-        The path to the aces 'transforms/ctl/utilities'
-    format : str or unicode, optional
-        The format to use when writing the LUT
-
-    Returns
-    -------
-    None
-    """
-
-    if global_params is None:
-        global_params = {}
-
-    lut_path_base = os.path.splitext(lut_path)[0]
-
-    identity_lut_image_float = '%s.%s.%s' % (lut_path_base, 'float', 'tiff')
-    generate_3d_LUT_image(identity_lut_image_float, lut_resolution)
-
-    if identity_lut_bit_depth not in ['half', 'float']:
-        identity_lut_image = '%s.%s.%s' % (lut_path_base,
-                                           identity_lut_bit_depth,
-                                           'tiff')
-        convert_bit_depth(identity_lut_image_float,
-                          identity_lut_image,
-                          identity_lut_bit_depth)
-    else:
-        identity_lut_image = identity_lut_image_float
-
-    transformed_lut_image = '%s.%s.%s' % (lut_path_base, 'transformed', 'exr')
-    apply_CTL_to_image(identity_lut_image,
-                       transformed_lut_image,
-                       ctl_paths,
-                       input_scale,
-                       output_scale,
-                       global_params,
-                       aces_ctl_directory)
-
-    corrected_lut_image = '%s.%s.%s' % (lut_path_base, 'correct', 'exr')
-    corrected_lut_image = correct_LUT_image(transformed_lut_image,
-                                            corrected_lut_image,
-                                            lut_resolution)
-
-    generate_3d_LUT_from_image(corrected_lut_image,
-                               lut_path,
-                               lut_resolution,
-                               format)
-
-    if cleanup:
-        os.remove(identity_lut_image)
-        if identity_lut_image != identity_lut_image_float:
-            os.remove(identity_lut_image_float)
-        os.remove(transformed_lut_image)
-        if corrected_lut_image != transformed_lut_image:
-            os.remove(corrected_lut_image)
-        if format != 'spi3d':
-            lut_path_spi3d = '%s.%s' % (lut_path, 'spi3d')
-            os.remove(lut_path_spi3d)
-
-
-def main():
-    """
-    A simple main that allows the user to exercise the various functions
-    defined in this file
-
-    Parameters
-    ----------
-    None
-
-    Returns
-    -------
-    None
-    """
-
-    import optparse
-
-    p = optparse.OptionParser(
-        description='A utility to generate LUTs from CTL',
-        prog='generateLUT',
-        version='0.01',
-        usage='%prog [options]')
-
-    p.add_option('--lut', '-l', type='string', default='')
-    p.add_option('--format', '-f', type='string', default='')
-    p.add_option('--ctl', '-c', type='string', action='append')
-    p.add_option('--lutResolution1d', '', type='int', default=1024)
-    p.add_option('--lutResolution3d', '', type='int', default=33)
-    p.add_option('--ctlReleasePath', '-r', type='string', default='')
-    p.add_option('--bitDepth', '-b', type='string', default='float')
-    p.add_option('--keepTempImages', '', action='store_true')
-    p.add_option('--minValue', '', type='float', default=0)
-    p.add_option('--maxValue', '', type='float', default=1)
-    p.add_option('--inputScale', '', type='float', default=1)
-    p.add_option('--outputScale', '', type='float', default=1)
-    p.add_option('--ctlRenderParam', '-p', type='string', nargs=2,
-                 action='append')
-
-    p.add_option('--generate1d', '', action='store_true')
-    p.add_option('--generate3d', '', action='store_true')
-
-    options, arguments = p.parse_args()
-
-    lut = options.lut
-    format = options.format
-    ctls = options.ctl
-    lut_resolution_1d = options.lutResolution1d
-    lut_resolution_3d = options.lutResolution3d
-    min_value = options.minValue
-    max_value = options.maxValue
-    input_scale = options.inputScale
-    output_scale = options.outputScale
-    ctl_release_path = options.ctlReleasePath
-    generate_1d = options.generate1d is True
-    generate_3d = options.generate3d is True
-    bit_depth = options.bitDepth
-    cleanup = not options.keepTempImages
-
-    params = {}
-    if options.ctlRenderParam is not None:
-        for param in options.ctlRenderParam:
-            params[param[0]] = float(param[1])
-
-    if generate_1d:
-        print('1D LUT generation options')
-    else:
-        print('3D LUT generation options')
-
-    print('LUT                 : %s' % lut)
-    print('Format              : %s' % format)
-    print('CTLs                : %s' % ctls)
-    print('LUT Res 1d          : %s' % lut_resolution_1d)
-    print('LUT Res 3d          : %s' % lut_resolution_3d)
-    print('Min Value           : %s' % min_value)
-    print('Max Value           : %s' % max_value)
-    print('Input Scale         : %s' % input_scale)
-    print('Output Scale        : %s' % output_scale)
-    print('CTL Render Params   : %s' % params)
-    print('CTL Release Path    : %s' % ctl_release_path)
-    print('Input Bit Depth     : %s' % bit_depth)
-    print('Cleanup Temp Images : %s' % cleanup)
-
-    if generate_1d:
-        generate_1d_LUT_from_CTL(lut,
-                                 ctls,
-                                 lut_resolution_1d,
-                                 bit_depth,
-                                 input_scale,
-                                 output_scale,
-                                 params,
-                                 cleanup,
-                                 ctl_release_path,
-                                 min_value,
-                                 max_value,
-                                 format=format)
-
-    elif generate_3d:
-        generate_3d_LUT_from_CTL(lut,
-                                 ctls,
-                                 lut_resolution_3d,
-                                 bit_depth,
-                                 input_scale,
-                                 output_scale,
-                                 params,
-                                 cleanup,
-                                 ctl_release_path,
-                                 format=format)
-    else:
-        print(('\n\nNo LUT generated! '
-               'You must choose either 1D or 3D LUT generation\n\n'))
-
-
-if __name__ == '__main__':
-    main()