.. _Advanced/ExtractEXRLayersToImages:

Extract EXR Layers to Images
============================

Problem
-------

You rendered a sequence of multi-layer EXR and you need to work with each layer separately. You want to extract each layer and save each of them as a separate EXR image file. In addition, you would like Draft to identify each layer based on your multi-layer EXR's channel names and to write each layer to file automatically with a meaningful filename.

Solution
--------

::

    import Draft
    from DraftParamParser import *  

    startFrame = 1
    endFrame = 100
    inFilePattern = "//path/to/multi-layer####.exr"
    outFilePattern = "//path/to/output.exr"

    # Define a dictionary holding channel name equivalences
    channelMap = { 'red': 'R', 'green': 'G', 'blue': 'B', 'alpha': 'A', 
                   'r': 'R', 'g': 'G', 'b': 'B', 'a': 'A' }

    # Get the first frame's channel names 
    currFile = ReplaceFilenameHashesWithNumber( inFilePattern, 1 )
    firstFrame = Draft.Image.ReadFromFile( currFile )
    channelNames = firstFrame.GetChannelNames()

    # Create a dictionary of layers,
    # with the layer name as the key, and the value as the list of channels
    layers = {}
    for name in channelNames:
        # Specific layer
        if string.rfind( name, '.' ) >= 0:
            ( layer, channel ) = string.rsplit( name, '.', 1 )
        # Basic RGB(A) layer
        else:
            ( layer, channel ) = ( None, name )
        if channel.lower() in channelMap:
            if layer in layers:
                layers[layer].append( channel )
            else:
                layers[layer] = [channel]
        else:
            print "Ignoring the following layer.channel: ", name, " (channel not recognized as RGBA)"


    # Process each of the frames in the list of frames
    for currFrame in range ( startFrame, endFrame + 1 ):
        # Read in the frame.
        currFile = ReplaceFilenameHashesWithNumber( inFilePattern, currFrame )
        frame = Draft.Image.ReadFromFile( currFile )
        
        # Extract the layers from the image, saving each as a separate image.
        for ( layer, channels ) in layers.items():
            prefix = ''
            if layer is not None:
                prefix = layer + '.'
            channelList = [ prefix + channel for channel in channels ]
            imgLayer = Draft.Image.CreateImage( frame.width, frame.height, channelList )
            imgLayer.Copy( frame, channels = channelList )
            
            # Rename the channels to RGB(A).
            for channel in channels:
                if prefix + channel != channelMap[channel.lower()]:
                    imgLayer.RenameChannel( prefix + channel, channelMap[channel.lower()] )
            
            # Add the layer name to the filename.
            currOutFile = ReplaceFilenameHashesWithNumber( outFilePattern, currFrame )
            if layer is not None:
                # Specific layer
                if string.rfind( currOutFile, '_' ) >= 0:
                    ( first, second ) = string.rsplit( currOutFile, '_', 1 )
                    currOutFile = first + '_' + layer + '_' + second
                # Basic RGB(A) layer
                else:
                    ( root, ext ) = os.path.splitext( currOutFile )
                    currOutFile = root + '_' + layer + ext

            # Write the layer to file as an image
            imgLayer.WriteToFile( currOutFile )
            
Discussion
----------

In order to be able to extract layers from a wide variety of multi-layer EXR, it's convenient to first define a dictionary of channel name equivalence::

    channelMap = { 'red': 'R', 'green': 'G', 'blue': 'B', 'alpha': 'A', 
                   'r': 'R', 'g': 'G', 'b': 'B', 'a': 'A' }

In that case, 'R', 'red' and 'r' will be treated as being equivalent. This dictionary can be customized to fit your specific needs.

Then, we can create a dictionary of layers based on the channel names of the first frame. This code snippet assumes that all images in the sequence have the same layers as the first frame but this assumption is not mandatory. You can also create a dictionary of layers for each frame by moving its creation in the main loop.

Once the dictionary of layers is created, we are ready to extract the layers from each frame, saving each one as a separate image. For each layer, we get the corresponding ``channelList`` from the dictionary of layers and we use it to extract the layer in the following lines of code::  

    imgLayer = Draft.Image.CreateImage( frame.width, frame.height, channelList )
    imgLayer.Copy( frame, channels = channelList ) 

Note that the method :meth:`~Draft.Image.Copy` only copy over to ``imgLayer`` the channels in ``channelList``. This is where the actual layer extraction takes place. 

Then, the ``imgLayer``'s channel names are renamed to RGB(A) and the output filename is renamed to include the current layer's name. Finally, ``imgLayer`` is written to file. 

To summarize, a set of images, one image per EXR layer, is written to file for each frame. For instance, let's suppose the list of channel names in your multi-layer EXR image is:: 

    {Reflect.R, Reflect.G, Reflect.B, Refract.R, Refract.G, Refract.B, Shadow.R, Shadow.G, Shadow.B, R, G, B}

Then, this code snippet will create the following EXR image files associated with the sequence's first frame::

    output_Reflect_0001.exr, output_Refract_0001.exr, output_Shadow_0001.exr, output_0001.exr

where ``output_0001.exr`` contains the basic RGB(A) layer. A similar set of images will be written to file for each frame in the sequence. 
