Using imagemagick and python to quickly convert images to another format and remove successfully converted images.


Image optimization

Classical image formats used in our games are JPG (lossy compression) and PNG (lossless compression with an alpha (transparency) layer). While JPGs can be fairly small, PNGs are massive. It is slightly unfortunate that we need to use many PNG files. In comes a fairly new image format: WebP. It’s developed by Google and supports both lossy and lossless compression (including the alpha layer). According to their manual, WebP lossy images are 25-35% smaller than JPEGs and lossless images are 26% smaller than PNGs. Since our games deal with lots of large images, these savings can be fairly substantial.

Converting images with Imagemagick

Though Google provides their own conversion tool (see their link above), we’re using a powerful image library tool named Imagemagick. Imagemagick is an incredibly powerful tool that is free for commercial and personal use. It is primarily a command-line utility available for all major platforms and some others. There may be unofficial graphical interfaces for it, but we don’t use them.

We won’t go into installing it or ensuring the path to it is correct, but their documentation and forums are fairly thorough. Here’s the relevant conversion command:

magick convert input_file_name -quality 90 -define webp:lossless=false  output_file.webp

The important arguments to take note of are:

Batch convert images

Now wrap up the imagemagick conversion command into a little python script that can feed folders of images:

import subprocess
import os, fnmatch

# Convert images (jpg, png, etc.) to webp format using imagemagick.

# https://stackoverflow.com/a/2186673
# Find all files that match a specific filepattern recursively through a given directory
def find_files(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            if fnmatch.fnmatch(basename, pattern):
                filename = os.path.join(root, basename)
                yield filename
                
# Convert a given image. If it is a JPG file, then we want to use lossy compression (lossless=false).
# If it's a PNG, then we want lossless compression.
def convert_img(filepath):
    if filepath.endswith('jpg'):
        cmd = "magick convert " + filepath + " -quality 90 -define webp:lossless=false " + filepath.split('.jpg')[0] + ".webp"
        print("Converting jpg...")
        run_cmd = subprocess.call(cmd, shell=True)
        if run_cmd == 0:
            print("Done converting: {}".format(filepath))
            os.remove(filepath)
        else:
            print("FAILED TO CONVERT: {}".format(filepath))
    if filepath.endswith('png'):
        cmd = "magick convert " + filepath + " -quality 90 -define webp:lossless=true " + filepath.split('.png')[0] + ".webp"
        print("Converting png...")
        run_cmd = subprocess.call(cmd, shell=True)
        if run_cmd == 0:
            print("Done converting: {}".format(filepath))
            os.remove(filepath)
        else:
            print("FAILED TO CONVERT: {}".format(filepath))
            
if __name__ == "__main__":
# An example run of the script. We want to convert all the PNGs and JPGs in our Backgrounds, CG, and Sprites folders.
    img_folders = ["game/images/Backgrounds", "game/images/CG", "game/images/Sprites"]
    exts = ["*.png", "*.jpg"]
    for ext in exts:
        for folder in img_folders:
            for filepath in find_files(folder, ext):
                convert_img(filepath)

You don’t necessarily need to use a python wrapper to batch convert all the images, but as our Bash scripting prowess is nonexistent, we couldn’t quickly figure out how to add in the if test statements. It would be bad if we failed to convert an image and then deleted it anyway.

We have a public GitHub repo now where you can find these little tools! We’ll be uploading previous discussed tools (ID validator, etc.) later.