Wand Documentation

repository·master·Indexed 23 days ago

https://github.com/emcconville/wand

A ctypes-based Python binding for the MagickWand API, providing access to ImageMagick functionality for Python 3.8+ and PyPy. The library allows for image manipulation including colorspace adjustments, pixel channel evaluation, complex distortions (such as perspective, polar, and polynomial), and drawing operations via the Drawing object.

Tokens
34.9K
Snippets
138
Records
164
Agent score
81%

What's inside Wand

  1. Handle empty regions with Virtual Pixels

    master

    When distorting images, new regions may be created outside the original bounding box. These are called 'virtual pixels'. You can control how these regions are filled by setting the Image.virtual_pixel attribute using values from wand.image.VIRTUAL_PIXEL_METHOD.

    Common values include:

    • 'transparent'
    • 'black'
    • 'white'
    • 'background' (uses the existing background color)
    • 'dither'
    • 'edge'
    • 'mirror'
    • 'random'
    • 'tile'
    with Image(filename='rose:') as img:
        img.resize(140, 92)
        img.background_color = img[70, 46]
        img.virtual_pixel = 'background'
        img.distort('arc', (60, ))
  2. Emulate PIL using Wand's compatibility layer

    master

    Wand provides a PIL compatibility layer to allow existing Python projects that depend on PIL to use Wand instead. There are two ways to implement this:

    1. Module-level compatibility: Manually change your imports to use wand.pilcompat.Image instead of PIL.Image.
    2. Global monkeypatcher: Use the patch() function from wand.pilcompat.monkey to modify sys.modules. This allows existing code that calls import PIL.Image to transparently use Wand's implementation.
    # Method 1: Module-level compatibility
    try:
        from wand.pilcompat import Image
    except ImportError:
        from PIL import Image
    
    # Method 2: Global monkeypatcher
    from wand.pilcompat.monkey import patch; patch()
    import PIL.Image  # it imports wand.pilcompat.Image module
  3. Why you should not rely on automatic garbage collection for Wand resources

    master

    While Wand implements destructors that invoke Resource.destroy(), relying on automatic deallocation via Python's garbage collector is discouraged.

    On CPython, reference counting often handles deallocation automatically, but this is an implementation detail. On other implementations like PyPy, which uses a non-deterministic garbage collector, the timing of destructor invocation is not guaranteed. Relying on automatic cleanup in these environments can lead to broken programs or resource leaks. Always use with statements or explicit .destroy()/.close() calls.

  4. Handle HDRI and color underflow in ImageMagick-7

    master

    ImageMagick-7 enables High Dynamic Range Imaging (HDRI) by default. This can lead to color-value underflow or overflow during arithmetic operations.

    To ensure consistent results, do not hard-code maximum color values; instead, use Image.quantum_range. To prevent underflow/overflow when using methods like Image.evaluate(), Image.function(), or Image.composite_channel(), use the Image.clamp() method.

    with Image(width=1, height=1, background=Color("gray5")) as canvas:
        canvas.evaluate("subtract", canvas.quantum_range * 0.07)
        canvas.clamp()
        print(canvas[0, 0]) #=> srgb(0,0,0)
  5. Use built-in kernels for morphology

    master

    Wand provides access to approximately three dozen pre-built kernels via ImageMagick. To use a built-in kernel, provide a string in the following format:

    label[:arg1,arg2,arg3,..]

    • label: A string defined in wand.image.KERNEL_INFO_TYPES (e.g., disk, square, cross).
    • arg1, arg2, ...: Optional arguments defined as a comma-separated list of doubles.

    Common Kernel Examples:

    • Cross: kernel='cross:3'
    • Diamond: kernel='diamond:3'
    • Disk: kernel='disk:5'
    • Octagon: kernel='octagon:5'
    • Plus: kernel='plus:3'
    • Ring: kernel='ring:5,4'
    • Square: kernel='square:3'
    img.morphology(method='dilate', kernel='disk:5')
  6. Handle sequential images like GIFs and ICOs

    master

    Wand supports sequential images (e.g., animated GIFs or multi-icon .ico files) via the sequence attribute on an Image object. The sequence attribute is a list-like object implementing the collections.MutableSequence protocol, allowing you to iterate over it, index it, slice it, or use len().

    Each item within the sequence is an instance of wand.sequence.SingleImage. Both wand.image.Image and wand.sequence.SingleImage share a common superclass, wand.image.BaseImage, meaning most operations and properties are available for both.

    from wand.image import Image
    import urllib2
    with Image(file=urllib2.urlopen('https://github.com/favicon.ico')) as ico:
        max(ico.sequence, key=lambda i: i.width * i.height)
  7. How the Drawing object works

    master
    The wand.drawing.Drawing object acts as a buffer for drawing instructions. Instead of drawing directly to an image, you record shapes and styles (like stroke_color, stroke_width, and fill_color) onto the Drawing object. Once your instructions are complete, you can apply them to one or more wand.image.Image objects by either calling the Drawing object as a callable or using the .draw(image) method.
  8. Difference between `wand.image.Image` and `wand.sequence.SingleImage`

    master

    While both inherit from wand.image.BaseImage and share methods like resize() and the size property, they serve different purposes:

    • wand.image.Image: A container representing the entire image file (e.g., the whole GIF). It provides file-level operations like save() and the mimetype attribute.
    • wand.sequence.SingleImage: Represents an individual frame or icon size within a sequence. It provides frame-specific attributes like delay and index.

    Warning: When dealing with animated GIFs or multi-size ICO files, ensure you are using the correct type for the operation you intend to perform.

  9. Manage Image Profiles (EXIF, ICC, XMP)

    master

    Wand provides access to image profiles (like EXIF, ICC, or XMP) via the Image.profiles dictionary. Because ImageMagick is not a tag editor, you should treat profiles as binary blobs (byte-arrays). To modify a profile, you must export the payload, modify it externally, and then import it back.

    Important: Every write operation on a profile requires the raster image data to be re-encoded. For lossy formats, this can result in generation loss.

    with Image(filename='wandtests/assets/beach.jpg') as image:
        # Extract EXIF payload
        if 'EXIF' in image.profiles:
            exif_binary = image.profiles['EXIF']
    
        # Import/replace ICC payload
        with open('color_profile.icc', 'rb') as icc:
            image.profiles['ICC'] = icc.read()
    
        # Remove XMP payload
        del image.profiles['XMP']
  10. Manage drawing styles with Push & Pop

    master

    For complex vector graphics, you can use the graphic-context stack to manage different styles and operations without affecting the global state of the Drawing object.

    • push(): Marks the beginning of a sub-routine and grows the stack.
    • pop(): Marks the end of a sub-routine and restores the graphical context to its previous state.

    Other stack methods include push_clip_path(), push_defs(), push_pattern(), and their corresponding pop counterparts (pop_clip_path(), pop_defs(), pop_pattern()).

    from wand.color import Color
    from wand.image import Image
    from wand.drawing import Drawing
    from math import cos, pi, sin
    
    with Color('lightblue') as bg, Color('transparent') as fg, Drawing() as draw:
        draw.stroke_width = 3
        draw.fill_color = fg
        for degree in range(0, 360, 15):
            draw.push()  # Grow stack
            draw.stroke_color = Color('hsl({0}%, 100%, 50%)'.format(degree * 100 / 360))
            t = degree / 180.0 * pi
            x = 35 * cos(t) + 50
            y = 35 * sin(t) + 50
            draw.line((50, 50), (x, y))
            draw.pop()  # Restore stack
        with Image(width=100, height=100, background=Color('lightblue')) as img:
            draw(img)
  11. Run Wand tests with pytest

    master

    Wand includes unit and regression tests. You can run them using the setup.py script, which will automatically install pytest if it is missing, or you can use pytest directly after installing it manually.

    To run all tests via setup.py:

    $ python setup.py test

    To run tests manually using pytest:

    $ pip install pytest
    $ pytest