Amidst this pandemic, I have somehow found the time to drastically update my image-titler script. In the latest installment, I’ve added a batch processing feature which allows a user to input a folder of pictures to be processed.
Table of Contents
What Is Batch Processing?
In the simplest terms, batch processing is exactly what I outlined in the intro: it’s a way of processing more than one image at a time. In this case, it’s a feature I’ve added that allows a user to add titles to more than one image at a time.
To do this, I’ve added a command line option
Without supplying a path, this will open up a folder selection dialog:
Once a folder is selected, the files in that folder will be processed and dumped to the usual location.
As always, the usual flags continue to work. For example, it’s possible to select a folder path from the command line using
-p. Likewise, you can continue to select an output folder using
That said, be careful! The title option,
-t, has no effect during batch processing. Instead, the automated title generation process takes over. If for some reason you want to apply the same title to multiple images, you’ll have to manually process those images at this time.
Why Batch Processing?
For me, the rationale behind batch processing came from the need to regenerate all the images on my website. While I could have written a quick batch script to loop, I felt like the feature would be more useful out of the box.
Now, no one had to write a script to iterate over files and populate the
-p field. Instead, we can write a single command which includes the
In addition, batch processing opens the door for other multi-file features like the ability to bulk tag images. Previously, I had talked about tagging file names, but I’d still like to add a version tag to the EXIF data of edited files as well. Being able to batch process files makes this an easy way to update a ton of files.
Speaking of additional features, I’d love for this tool to be able to eventually allow users to filter what gets edited. For now, entire folders are processed. In the future, it would be nice to be able to select a folder and specify editing criteria. Likewise, it would be nice to see a list of which files will be edited, so we can confirm our edits before they happen.
How Does Batch Processing Work?
Surprisingly, this took a lot of work. When I first wrote this script, I was just trying to get something working. As a result, a lot of the functionality was coupled, so I had to go through a process of rewriting functionality.
Eventually, I ended up with a generic
process_image() function with a handful of parameters:
def process_image(input_path: str, tier: str = None, logo_path: str = None, output_path: str = None, title: str = None) -> Image.Image: """ Processes a single image. :param input_path: the path of an image :param tier: the image tier (free or premium) :param logo_path: the path to a logo :param output_path: the output path of the processed image :param title: the title of the processed image :return: the edited image """ if not title: file_name = Path(input_path).resolve().stem title = titlecase(file_name.replace('-', ' ')) img = Image.open(input_path) edited_image = draw_overlay(img, title, tier, logo_path) save_copy(img, edited_image, title, output_path) return edited_image
At that point, it was a simple as writing a wrapper function called
process_batch() which accepts a folder rather than an image. Then, I loop over all the files in that folder while calling
def process_batch(input_path: str, tier: str = None, logo_path: str = None, output_path: str = None): """ Processes a batch of images. :param input_path: the path to a folder of images :param tier: the image tier (free or premium) :param logo_path: the path to a logo :param output_path: the output path of the processed images :return: None """ for path in os.listdir(input_path): absolute_path = os.path.join(input_path, path) process_image(absolute_path, tier, logo_path, output_path)
From there, the process is the same as any image. However, I did have to decouple the call to
process_image(). Otherwise, every image would display to the user which quickly goes out of control with more than 10 images. As a consequence,
process_image() now returns a reference to the edited image, so we can call
show() at a later point in the process.
Ultimately, we discern between a batch run and a solo run if the batch flag is present:
if input_path: if args.batch: process_batch(input_path, tier, logo_path, output_path) else: process_image(input_path, tier, logo_path, output_path, title).show()
Overall, I’m quite pleased with this implementation, and I’m excited to expand it to support other features like batch filtering.
Absolutely! Here’s a quick reference list:
- Help menu now features descriptions of all options
- Images only display during single image processing
- README updated to include
- Samples added to the repo
- Single word titles are given a single bar for their title
- Version tagging was updated to be URL friendly (no dots, only dashes)
In general, these are mainly quality of life updates. Either they fix bugs or they address minor inconveniences.
Of all these changes, my favorite has to be the samples page which now shows you what images would look like under different conditions. For example, you can see what an image is like with and without a logo.
I’m also a big fan of the help page update which now features descriptions of all the possible options. I expect this page to grow and develop over time.
Plans for the Future?
Yep! I have plenty of those. Just check out the list of issues on the GitHub page. By the time this article will be released, I expect 1.8.0 to already be released which features an automatic color detection algorithm.
On my immediate radar, I’m looking to add some of the features I mentioned in this article including batch filtering and EXIF data loading. Eventually, I’d like to wrap the whole system in a GUI, so the tool is more accessible.
In the meantime, help me grow my support by checking out this article. It allows all the different ways you can support the site including hopping on my mailing list and joining me on Patreon.
Otherwise, stick around with the following related articles:
Thanks again for your support! I appreciate it.
Chiropractors get a bad rap, but I find their YouTube content somewhat enjoyable. That said, they absolutely destroyed my YouTube recommendations.
Python is a fun language to learn on its own, but what if we could learn it by doing something even more fun like making Discord bots?