Image Titler 1.7.2 Features Batch Processing

Image Titler 1.7.2 Features Batch Processing Featured Image

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 -b:

image_titler -b

Without supplying a path, this will open up a folder selection dialog:

Note: this photo was taken using a newer version of image-titler (1.8.4).

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 -o.

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 -b flag.

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 process_image():

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 show() from 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.

Other Changes?

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 -b option
  • 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.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Today, he pursues a PhD in Computer Science in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Content