PythonのPILで書き出したGIF画像をつなげて動画GIFを作る



参考URL
[http://pastebin.com/Rr7dAxbF]
[http://www.tohoho-web.com/wwwgif.htm]
[http://www.marguerite.jp/Nihongo/Labo/Image/GIF.html]


```python:agif.py

# -*- coding: utf-8 -*-
import struct
import math
from cStringIO import StringIO
from PIL import Image, GifImagePlugin


class AGIF(object):
    """
    >>> from PIL import ImageDraw, ImageFont
    >>> agif = AGIF(4)
    >>> size = (80, 15)
    >>> img = Image.new("RGB", size, "white")
    >>> draw = ImageDraw.Draw(img)
    >>> text = "Hello, world!"
    >>> for i in range(len(text)):
    ...     draw.rectangle((0, 0) + size, fill="white")
    ...     draw.text((0, 0), text[:i], fill="black")
    ...     agif.paste(img, duration=0.2)
    >>> agif.save("example1.gif")


    >>> import urllib
    >>> from cStringIO import StringIO
    >>> from PIL import ImageOps
    >>> url = "http://www.python.org/community/logos/python-powered-h-140x182.png"
    >>> istream = StringIO(urllib.urlopen(url).read())
    >>> image = Image.open(istream)
    >>> square = ImageOps.fit(image, (240, 240))

    >>> agif = AGIF(128)
    >>> for i in range(0, 360, 5):
    ...     agif.paste(square.rotate(i), 0.03)
    >>> agif.save("example2.gif")
    """

    def __init__(self, colors, loop=0):
        assert colors in [2, 4, 8, 16, 32, 64, 128, 256]
        self.colors = colors
        self.loop   = loop
        self.buf    = StringIO()
        self.pasted = False


    def paste(self, image, duration=0.1):
        image = self._convert_to_gif(image)
        if not self.pasted:
            self._write_header(image)
        self._write_image(image, duration)
        self.pasted = True


    def save(self, file):
        if isinstance(file, basestring):
            file = open(file, "w")
        self.buf.seek(0)
        file.writelines(self.buf)
        file.write("\x3b")


    def _convert_to_gif(self, image):
        """半透明なPNG画像を白色の背景とマージする"""
        if image.mode == "RGBA":
            merged = Image.new("RGBA", image.size, (255, 255, 255, 0))
            merged.paste(image, (0, 0) + image.size, image)
            image = merged.convert("RGB")
        return image.convert("P", palette=Image.ADAPTIVE, colors=self.colors)


    def _write_header(self, image):
        buf = self.buf
        buf.write("GIF89a")
        width, height = image.size
        buf.write(struct.pack("< H", width))
        buf.write(struct.pack("< H", height))
        buf.write(chr(0xe0 | int(math.log(self.colors, 2) - 1)))
        buf.write("\x00\x00")

        buf.write(GifImagePlugin.getheader(image)[1][:self.colors*3])
        buf.write("\x21\xff\x0bNETSCAPE2.0")
        if self.loop == 0:
            buf.write("\x03\x01\x00\x00")
        elif self.loop != 1:
            buf.write("\x03\x01")
            buf.write(struct.pack("< H", self.loop-1))
        buf.write("\x00")


    def _write_image(self, image, duration):
        buf = self.buf
        buf.write("\x21\xf9\x04\x08")
        buf.write(struct.pack("< H", duration * 100))
        buf.write("\x00\x00")

        if self.pasted:
            data = GifImagePlugin.getdata(image)
            header = list(data[0])
            header[-2] = chr(128 | int(math.log(self.colors, 2) - 1))
            buf.write("".join(header)[:-1])
            buf.write(GifImagePlugin.getheader(image)[1][:self.colors*3])
            buf.write(header[-1])
            for d in data[1:]:
                buf.write(d)
        else:
            for d in GifImagePlugin.getdata(image):
                buf.write(d)

if __name__ == '__main__':
    import doctest
    doctest.testmod()
```