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