Automate repetitive tasks for digital artists with Python

Artists and designers need to know how to code. There, I said it.

Now I could get into a good in-depth exposition on how the artist’s mindset and the developer/engineer’s mindset aren’t that different, or how the image of “l ‘non-technical artist’ is a relatively recent phenomenon. These are subjects for another article. For the purposes of this article, suffice it to say that knowing a bit of code will not only give you a better understanding of how your digital tools work, but that knowledge will help you create your work more efficiently.

It doesn’t matter if you’re working on a large-scale collaborative project for a paying client or using digital art as a free-form, free-wheeling engine of catharsis. We all want to make the most of our time. For my part, if I find that I am doing some sort of monotonous task more than three times in a row, I am already thinking about how I could automate it and let my computer do the work for me.

Automation means tinkering with a script. Coding. And when it comes to scripting tasks related to digital media, you’d be hard-pressed to find a more suitable language than Python. Python is an extremely popular open source scripting language which is used to write all kinds of open source software.

For digital creatives, there are other benefits to learning Python. The syntax of the language is relatively easy to learn and Python is available on all major platforms. This means that your scripts can travel with you, whether you use Linux, BSD or one of these proprietary operating systems. Not only that, Python is well integrated into a number of digital content creation tools like Blender and GIMP. Even some proprietary tools have Python embedded in them. This means you can use Python to automate tasks in these programs, or even add new functionality specific to the way you work.

“That’s great and all,” you might say, “but be specific. What can I actually To do with Python?”

I’m glad you asked.

This series of articles is meant to answer that question, to allow you to dive into the flow and get a sense of what’s possible with just a bit of scripting knowledge. Fair warning: if these articles whet your appetite for scripting, I strongly encourage you to take one of the many Python courses available online, including the tutorial that is part of the official Python documentation.

I should also mention that the examples I give might make a “real” Python developer cringe. And it’s good. In the worst case, we still have a script that does exactly what we need. Best case scenario, we get suggestions for improvement in the comments section of this article.

So let’s start with something easy. We all know that command line tools like FFmpeg and ImageMagick are usually a faster way to perform one-time operations on videos or images. Let’s say all you want to do is encode a video or desaturate a PNG and convert it to JPEG. It’s much faster to run a single command than to run a program with a full GUI, load your source media, and then perform this little operation.

The problem, however, is that powerful command line tools like FFmpeg and ImageMagick have a ground options and flexibility. They do so many things, and they’re all controlled by a dizzying array of flags and options that you need to enter correctly. It’s hard to remember the exact magic incantation you need for one or two specific tasks you need to complete.

For example, suppose you have a directory full of PNG images generated by your favorite animation software. There are hundreds, if not thousands of individual images in there. Now suppose you realize that not all of these images need to be saved in 16-bit RGBA PNG format. You could save a ton of disk space and have faster load times if these images were all converted to 1-bit black and white PNG images. Sure, you can re-render, but the frames are already there. With FFmpeg, you could transform those images and convert them in much less time… if only you could remember the exact command to do it.

Luckily, you’ve had to do this many times before, so you took some time and created a little Python script called make_1bit.py. It is way easier to remember make_1bit.py that ffmpeg -i something -flipflop -blah blah -please -o somethingelse or whatever. Here is the content of your script (with line breaks to accommodate this column width; see below for how to properly format your script):

import subprocess

subprocess.call(['ffmpeg', '-f', 'image2', '-i',
'%04d.png', '-pix_fmt', 'monob', '-threads', '0',
'%04d.png'])

Let’s take a moment to walk through the script and describe what it does. The first line, import subprocessimport a Python module called subprocess. Think of modules as pieces of code that someone else has already written. You just need to make your script aware of this code. It’s made with the import statement. In this case, you import the subprocess module, a Python module specially designed to launch commands as if you were typing them on the command line.

In the following block of code (subprocess.call(['ffmpeg',...])), you’re actually using the code in that module to run FFmpeg for you. The entire FFmpeg command is split into a list of strings. In Python, a listing is simply a collection of data. It is indicated by braces ([]) and each data bit is separated by a comma. In this case, each data bit is a rope, or a piece of text surrounded by single quotes. Each string in the list is an argument (flag) of the FFmpeg command you want to run, in the correct order.

In this particular example, we’re assuming the script is in the same directory as your images, and the images have numbered filenames like 0001.png, 0002.png, 0003.png, etc.

For organizational reasons, you can place each of these pairs of arguments on their own lines. That way it’s a little easier to see what’s going on. (Note: Python is very particular about “white space” (spaces and tabs) in your code. So be sure to use spaces or tabs do not mix!and that you use the same number when indenting.) If you do this, your script might look like this:

import subprocess

subprocess.call(['ffmpeg',
                 '-f', 'image2',
                 '-i', '%04d.png',
                 '-pix_fmt', 'monob',
                 '-threads', '0',
                 '%04d.png'])

Now all you have to do is run python make_1bit.py from this directory and, BOOM, the converted files! No need to remember all those different flags for FFmpeg. The script remembers it for you. Of course, if you want to run FFmpeg directly (or perhaps some variation of this command), it’s easy to open your script file and have those flags available and clearly organized for reference. You can even add comments to your script to make it easier to know what each indicator does:

import subprocess

subprocess.call(['ffmpeg',
                 '-f', 'image2',      # Read input as images
                 '-i', '%04d.png',    # Input files (sequential PNGs)
                 '-pix_fmt', 'monob', # Output colorspace is 1-bit
                 '-threads', '0',     # Use all available CPU cores
                 '%04d.png'])         # Output files (overwrite input)

So that’s where we start. We do one small script at a time. The goal is to save us time and focus on getting the job done by simplifying and automating repetitive or tedious tasks.

There will be more in this series… but in the meantime, if you happen to use Python scripting to simplify tasks (whether for creative work or not), you may be able to use the comments section to explain what you To do.

Marilyn M. Davis