Python GIF Processing

天气站点需要往微博推送气象信息,原始的基本反射率数据都是单帧的,为了好一点的效果计划自己合成GIF,但找了一下发现Python下没有现成好的GIF处理库,PIL能支持读存操作但不支持合并,没办法只好找些资料自己写。下面列些相关资料的链接和一个GIF合并处理的例子,基本上就是按照标准对数据封装,其它格式对GIF的转换可以同理处理。

Standards and References

Application Extension Spec: NETSCAPE2.0

1
byte   1       : 33 (hex 0x21) GIF Extension code
byte   2       : 255 (hex 0xFF) Application Extension Label
byte   3       : 11 (hex 0x0B) Length of Application Block
                 (eleven bytes of data to follow)
bytes  4 to 11 : "NETSCAPE"
bytes 12 to 14 : "2.0"
byte  15       : 3 (hex 0x03) Length of Data Sub-Block
                 (three bytes of data to follow)
byte  16       : 1 (hex 0x01)
bytes 17 to 18 : 0 to 65535, an unsigned integer in
                 lo-hi byte format. This indicate the
                 number of iterations the loop should
                 be executed.
byte  19       : 0 (hex 0x00) a Data Sub-Block Terminator.
GIF Merge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from PIL import Image


def o16(i):
return chr(i&255) + chr(i>>8&255)


def merge(fp, images, durations, loops=0):
_im = images[0]
_durations = [durations for im in images] if isinstance(durations, int) else durations

_bits = 8
_size = _im.size
try:
assert len(_durations) == len(images)
for im in images:
im.load()
assert _size == im.size
except AssertionError:
raise

# Header
_h = [
'GIF89a',
o16(_size[0]), # width
o16(_size[1]), # height
chr(128 + (_bits - 1)), # flags: palette + bits
chr(0), # background
chr(0), # reserved/aspect
]
fp.write(''.join(_h))

# Palette
_mode = 'P' # palette/grayscale (P/L)
_colors = 256
_palette = _im.im.getpalette('RGB')[:_colors * 3]
fp.write(_palette)

# Ext: application extension
_loop = loops
_ext = [
'\x21\xFF\x0B',
'NETSCAPE2.0',
'\x03\x01',
o16(_loop),
'\x00',
]
fp.write(''.join(_ext))

# Images
for idx, im in enumerate(images):
# Ext: graphics control extension
_cext = [
'\x21\xF9\x04',
'\x08', # flag: transparency: \x08 | \x09
o16(_durations[idx]), # druation
'\x00', # transparency
'\x00',
]
fp.write(''.join(_cext))

# Image
_h = [
'\x2C', # ',' sperator
o16(0), o16(0), # offset
o16(im.size[0]), o16(im.size[1]), # size
chr(0), # flag: no local palette
chr(_bits), # color bits
]
fp.write(''.join(_h))

_encoder = Image._getencoder(im.mode, "gif", im.mode)
_encoder.setimage(im.im, (0, 0) + im.size)
_bufsize = 4096
while True:
l, s, d = _encoder.encode(_bufsize)
fp.write(d)
if s: break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
fp.write('\x00')

# End
fp.write('\x3b') # ';' trailer
fp.flush()