Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transparent gifs #16

Open
AKuederle opened this issue May 30, 2020 · 3 comments
Open

Transparent gifs #16

AKuederle opened this issue May 30, 2020 · 3 comments

Comments

@AKuederle
Copy link

First of all thank you for this nice package!
I was trying to export an animation as a gif with a transparent background. Unfortunately, this was not possible using imageio (at least based on my googeling). There is a solution using PIL (see below).

However, this requires implementing a custom save function. Would you be interested in adding alternative support for PIL to save gifs to allow this solution natively? I would be willing to implement this properly. If not, I hope this issue serves as potential information for others who ran into the same issue I did.

import drawSvg as draw
from PIL import Image
from drawSvg import Drawing, render_svg_frames


def convert_to_transparent_frame(im):
    """Modified from https://stackoverflow.com/questions/46850318/transparent-background-in-gif-using-python-imageio"""
    alpha = im.getchannel("A")

    # Convert the image into P mode but only use 255 colors in the palette out of 256
    im = im.convert("RGB").convert("P", palette=Image.ADAPTIVE, colors=255)

    # Mark all pixels with alpha=0 as transparent
    mask = Image.eval(alpha, lambda a: 255 if a == 0 else 0)

    # Paste the color of index 255 and use alpha as a mask
    im.paste(255, mask)

    # The transparency index is 255
    im.info["transparency"] = 255

    return im


def save_video(frames, file, **kwargs):
    """
    Save a series of drawings as a GIF or video.

    Arguments:
        frames: A list of `Drawing`s or a list of `numpy.array`s.
        file: File name or file like object to write the video to.  The
            extension determines the output format.
        align_bottom: If frames are different sizes, align the bottoms of each
            frame in the video.
        align_right: If frames are different sizes, align the right edge of each
            frame in the video.
        bg: If frames are different sizes, fill the background with this color.
            (default is white: (255, 255, 255, 255))
        duration: If writing a GIF, sets the duration of each frame.
        fps: If writing a video, sets the frame rate in FPS.
        **kwargs: Other arguments to imageio.mimsave().

    """
    if isinstance(frames[0], Drawing):
        frames = render_svg_frames(frames, **kwargs)
    kwargs.pop("align_bottom", None)
    kwargs.pop("align_right", None)
    kwargs.pop("bg", None)
    frames = [
        convert_to_transparent_frame(Image.fromarray(frame, "RGBA")) for frame in frames
    ]
    frames[0].save(
        file,
        save_all=True,
        append_images=frames[1:],
        disposal=2,  # This "cleans" the canvas after every draw
        optimize=False,  # This is critical for transparent background
        **kwargs,
    )


# Draw a frame of the animation
def draw_frame(t):
    d = draw.Drawing(2, 6.05, origin=(-1, -1.05))
    d.setRenderSize(h=300)
    # d.append(draw.Rectangle(-2, -2, 4, 8, fill="white")) # Commented out, as we want to have transparent background
    d.append(draw.Rectangle(-1, -1.05, 2, 0.05, fill="brown"))
    t = (t + 1) % 2 - 1
    y = 4 - t ** 2 * 4
    d.append(draw.Circle(0, y, 1, fill="lime"))
    return d


with draw.animate_video(None, draw_frame) as anim:
    # Add each frame to the animation
    for i in range(20):
        anim.draw_frame(i / 10)
    for i in range(20):
        anim.draw_frame(i / 10)
    for i in range(20):
        anim.draw_frame(i / 10)

    save_video(anim.frames, "test.gif", duration=0.05)
@cduck
Copy link
Owner

cduck commented May 30, 2020

This looks great. I think it would be a nice addition to drawSvg.

My suggestion would be to make code like this work:

with draw.animate_video('example6.gif', draw_frame, duration=0.05,
                        gif_transparency=True, gif_alpha_threshold=128
                       ) as anim:
    # Add each frame to the animation
    ...

Or maybe a separate function like with draw.animate_gif('test.gif', duration=0.05, alpha_threshold=0) as anim:.

Let me know if you have questions implementing this or making a pull request.

@AKuederle
Copy link
Author

Sry for the silence. Unfortunately, live came in the way of the project I was using this for. At the moment, I don't know when I will have time to look into this again.

@cduck
Copy link
Owner

cduck commented Jul 5, 2020

No problem. Your code example should be a good starting point if anyone else wants to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants