--- tags: Blue 的學習紀錄, Python --- # 用 Python PIL 做 GIF Resize :::info 此篇主要目的是為「這件事該怎麼做?」留下紀錄,而不是學習心得 以下程式碼來自於 Stack Overflow,但有經過個人合併修改。我並不熟悉其運作,只知道如何使用 連結:<https://stackoverflow.com/a/41827681/14498782> ::: ## 緣由 不知為何線上 Resize GIF 的工具,例如 [ezgif.com](https://ezgif.com/resize),resize(縮小)後的 GIF 會嚴重失真,所以嘗試用 Python 來做 resize ## 程式碼 ```python= import os from PIL import Image def resize_gif(path, save_as=None, resize_to=None): """ Resizes the GIF to a given length: Args: path: the path to the GIF file save_as (optional): Path of the resized gif. If not set, the original gif will be overwritten. resize_to (optional): new size of the gif. Format: (int, int). If not set, the original GIF will be resized to half of its size. """ all_frames = extract_and_resize_frames(path, resize_to) if not save_as: save_as = path if len(all_frames) == 1: print("Warning: only 1 frame found") all_frames[0].save(save_as, optimize=True) else: all_frames[0].save(save_as, optimize=True, save_all=True, append_images=all_frames[1:], loop=1000) def analyseImage(path): """ Pre-process pass over the image to determine the mode (full or additive). Necessary as assessing single frames isn't reliable. Need to know the mode before processing all frames. """ im = Image.open(path) results = { 'size': im.size, 'mode': 'full', } try: while True: if im.tile: tile = im.tile[0] update_region = tile[1] update_region_dimensions = update_region[2:] if update_region_dimensions != im.size: results['mode'] = 'partial' break im.seek(im.tell() + 1) except EOFError: pass return results def extract_and_resize_frames(path, resize_to=None): """ Iterate the GIF, extracting each frame and resizing them Returns: An array of all frames """ mode = analyseImage(path)['mode'] im = Image.open(path) if not resize_to: resize_to = (im.size[0] // 2, im.size[1] // 2) i = 0 p = im.getpalette() last_frame = im.convert('RGBA') all_frames = [] try: while True: # print("saving %s (%s) frame %d, %s %s" % (path, mode, i, im.size, im.tile)) ''' If the GIF uses local colour tables, each frame will have its own palette. If not, we need to apply the global palette to the new frame. ''' if not im.getpalette(): im.putpalette(p) new_frame = Image.new('RGBA', im.size) ''' Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image? If so, we need to construct the new frame by pasting it on top of the preceding frames. ''' if mode == 'partial': new_frame.paste(last_frame) new_frame.paste(im, (0, 0), im.convert('RGBA')) new_frame.thumbnail(resize_to, Image.ANTIALIAS) all_frames.append(new_frame) i += 1 last_frame = new_frame im.seek(im.tell() + 1) except EOFError: pass return all_frames # 於這裡進行輸入輸出的設定 resize_gif("input.gif", "output.gif", (rows, columns)) ```