# Sequence

Note

The image `sequence-animation.gif` used in this docs has been released into the public domain by its author, C6541 at Wikipedia project. This applies worldwide. (Source)

Added in version 0.3.0.

Some images may actually consist of two or more images. For example, animated image/gif images consist of multiple frames. Some image/ico images have different sizes of icons.

For example, the above image `sequence-animation.gif` consists of the following frames (actually it has 60 frames, but we sample only few frames to show here):

## `sequence` is a `Sequence`

If we open this image, `Image` object has `sequence`. It’s a list-like object that maintain its all frames.

For example, `len()` for this returns the number of frames:

    >>> from wand.image import Image
    >>> with Image(filename='sequence-animation.gif') as image:
    ...     len(image.sequence)
    ...
    60

You can get an item by index from `sequence`:

    >>> with Image(filename='sequence-animation.gif') as image:
    ...     image.sequence[0]
    ...
    <wand.sequence.SingleImage: ed84c1b (256x256)>

Or slice it:

    >>> with Image(filename='sequence-animation.gif') as image:
    ...     image.sequence[5:10]
    ...
    [<wand.sequence.SingleImage: 0f49491 (256x256)>,
     <wand.sequence.SingleImage: 8eba0a5 (256x256)>,
     <wand.sequence.SingleImage: 98c10fa (256x256)>,
     <wand.sequence.SingleImage: b893194 (256x256)>,
     <wand.sequence.SingleImage: 181ce21 (256x256)>]

## `Image` versus `SingleImage`

Note that each item of `sequence` is a `SingleImage` instance, not `Image`.

`Image` is a container that directly represents image files like `sequence-animation.gif`, and `SingleImage` is a single image that represents frames in animations or sizes in image/ico files.

They both inherit `BaseImage`, the common abstract class. They share the most of available operations and properties like `resize()` and `size`, but some are not. For example, `save()` and `mimetype` are only provided by `Image`. `delay` and `index` are only available for `SingleImage`.

In most cases, images don’t have multiple images, so it’s okay if you think that `Image` and `SingleImage` are the same, but be careful when you deal with animated image/gif files or image/ico files that contain multiple icons.

## Manipulating `SingleImage`

When working with `sequence`, it’s important to remember that each instance of `SingleImage` holds a copy of image data from the stack. Altering the copied data will not automatically sync back to the original image-stack.

    >>> with Image(filename='animation.gif') as image:
    ...     # Changes on SingleImage are invisible to `image` container.
    ...     image.sequence[2].negate()
    ...     image.save(filename='output.gif')  # Changes ignored.

If you intended to alter a `SingleImage`, and have changes synchronized back to the parent image-stack, use an additional with-statement context manager.

    >>> with Image(filename='animation.gif') as image:
    ...     # Changes on SingleImage are sync-ed after context manager closes.
    ...     with image.sequence[2] as frame:
    ...         frame.negate()
    ...     image.save(filename='output.gif')  # Changes applied.

## Working directly with Image-Stack Iterators

A faster way to work with images in a sequence is to use the internal stack iterator. This does not create copies, or generate `Sequence` / `SingleImage` instances.

Warning

Users should NOT mix `Image.sequence` code with direct iterator methods.

When reading a image file, the internal iterator is pointing to the last frame read. To iterate over all frames, use `Image.iterator_reset()` and `Image.iterator_next()` methods.

    >>> with Image(filename='link_to_the_past.gif') as img:
    ...     img.iterator_reset()
    ...     print("First frame", img.size)
    ...     while img.iterator_next():
    ...         print("Additional frame", img.size)
    First frame (300, 289)
    Additional frame (172, 128)
    Additional frame (172, 145)
    Additional frame (123, 112)
    Additional frame (144, 182)
    Additional frame (107, 117)
    Additional frame (171, 128)
    Additional frame (123, 107)

You can also iterate backwards with `Image.iterator_last()` and `Image.iterator_previous()` methods.

    >>> with Image(filename='link_to_the_past.gif') as img:
    ...     img.iterator_last()
    ...     print("End frame", img.size)
    ...     while img.iterator_previous():
    ...         print("Previous frame", img.size)
    End frame (123, 107)
    Previous frame (171, 128)
    Previous frame (107, 117)
    Previous frame (144, 182)
    Previous frame (123, 112)
    Previous frame (172, 145)
    Previous frame (172, 128)
    Previous frame (300, 289)

Method `Image.iterator_first()` is like `Image.iterator_reset()`, but allows the next image read to be prepended at the start of the stack.

    >>> with Image(filename='support/link_net.gif') as img:
    ...     img.iterator_first()
    ...     img.pseudo(1, 1, 'xc:gold')
    ...     img.iterator_set(0)
    ...     print(img.size)
    ...     img.iterator_set(1)
    ...     print(img.size)
    (1, 1)
    (300, 289)

Note

The “image-stack” is general term for a linked list of sub-images in a file. The nomenclature varies between industries & users. You may find documents referencing sub-images as:

> - Frame for animated formats (GIF)

> - Page for document based formats (PDF)

> - Layer for publishing formats (PSD, TIFF)