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:
- input_file_name: The file you want to convert. Should be the path to the file, such as f.jpg, game/f.jpg, f.png
- -quality: This flag tells imagemagick how much you want to compress the file. In our case, we’re just using
90
, but depending on your images, it’s possible to compress by quite a lot without any obvious artifacts - webp:lossless=false: Here, we want to use lossless file compression. The input file is most likely a PNG
- output_file.webp: Name of the output file. Use Webp extension so that other programs are happy and know how to read the file
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.