-
-
Save y3nr1ng/64888d2c4fad64a5cd5088b6b8b0513d to your computer and use it in GitHub Desktop.
Extract frames from an animated GIF, correctly handling palettes and frame update modes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ''' | |
| A fork of this gist https://gist.github.com/BigglesZX/4016539 . | |
| Ported to Python3 and verify it with Pillow. | |
| ''' | |
| import argparse | |
| import os | |
| from PIL import Image | |
| def find_dir(path): | |
| ''' | |
| Create target directory if it does not exist. | |
| ''' | |
| if not os.path.exists(path): | |
| os.makedirs(path) | |
| def analyze_image(path): | |
| ''' | |
| Pre-process the image to determine the mode (full or additive). | |
| ''' | |
| image = Image.open(path) | |
| results = {'size': image.size, 'mode': 'full'} | |
| try: | |
| while True: | |
| if image.tile: | |
| tile = image.tile[0] | |
| update_region = tile[1] | |
| update_region_dimensions = update_region[2:] | |
| if update_region_dimensions != image.size: | |
| results['mode'] = 'partial' | |
| break | |
| image.seek(image.tell()+1) | |
| except EOFError: | |
| pass | |
| return results | |
| def process_image(path): | |
| ''' | |
| Iterate through the frames in GIF, extracting each frame. | |
| ''' | |
| mode = analyze_image(path)['mode'] | |
| image = Image.open(path) | |
| i = 0 | |
| palette = image.getpalette() | |
| last_frame = image.convert('RGBA') | |
| new_dir = ''.join(os.path.basename(path).split('.')[:-1]) | |
| find_dir(new_dir) | |
| try: | |
| while True: | |
| print('Saving {:} ({:}) frame {:}, {:} {:}' | |
| .format(path, mode, i, image.size, image.tile)) | |
| if not image.getpalette(): | |
| image.putpalette(palette) | |
| new_frame = Image.new('RGBA', image.size) | |
| if mode == 'partial': | |
| new_frame.paste(last_frame) | |
| new_frame.paste(image, (0, 0), image.convert('RGBA')) | |
| new_path = '{:}/{:}.png'.format(new_dir, i) | |
| new_frame.save(new_path, 'PNG') | |
| i += 1 | |
| last_frame = new_frame | |
| image.seek(image.tell()+1) | |
| except EOFError: | |
| pass | |
| def get_file_path(): | |
| ''' | |
| Get the file path from command line. | |
| ''' | |
| # Setup the parser. | |
| parser = argparse.ArgumentParser(description='Extract .gif into frames.') | |
| parser.add_argument('filePath', help='Path to the file.') | |
| # Retrieve the filenames. | |
| args = parser.parse_args() | |
| return args.filePath | |
| def main(): | |
| ''' | |
| The main function, wrapped to avoid global variable issue. | |
| ''' | |
| path = get_file_path() | |
| process_image(path) | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment