In this edition of the image-titler tool, we’re bringing a new personalization feature that automatically detects the primary color of a logo and uses that color as the base for the title bars. In this article, we’ll take a look at what this feature does in more detail as well as talk about the rationale behind the feature.
Table of Contents
What is Logo Primary Color Detection?
This word soup is basically meaningless. That said, what it’s attempting to describe is a brand new feature of the image-titler script. Now, title bars adjust to changes in logo automatically (more on that below).
Obviously, you can’t really see a difference with my featured images because they already match my logo. However, if I were to swap my logo out for a different logo—say the VirtualFlat logo—we’d see a dramatically different title bar:
Now, title bars will match the primary color of the provided logo. Here, the primary color is defined as the most prominent color in logo.
In the future, I’d like to be able to select for primary and secondary colors, but getting this feature to work is cool enough as-is.
Why Add Logo Primary Color Detection?
As with many additions, this features came out of the constraints of the project. Previously, everything had been hardcoded to my style. In other words, the title bars were fixed and could not be changed.
Ultimately, I wanted to be able to expand the customization of this script, so other folks could make it suit their needs. One way to do that would be to allow users to specify their own color bars. While that works, I wanted something that would be even more simple for people to use. As a result, I decided to implement a way to match title bars to logos.
The simplicity of this change allows a user to experiment with logo choice without having to be acutely aware of the color choice for the bars. Likewise, this sort of automation makes things like batch processing easier because it defers design choices to the software. Later when we add batch filtering, it’ll be nice to be able to apply a logo to only a subset of the data and not have to worry about bar colors.
Now, I think this feature is awesome, but it has some drawbacks. For example, it prefers images with darker primary colors. After all, the text is never changed, so a light background would clash with the white text. In the future, I’ll need to implement some form of automated complementary color system for the text.
How Does Logo Primary Color Detection Work?
Glad you asked! It’s actually pretty simple. As it turns out, Pillow images have a
get_colors() method which returns a list of all colors used in the image and their counts. All I had to do was sort this list (though I suppose I could have just found the max) and return the most popular color.
Now, there’s a bit of a catch with the “most popular color” situation. As it turns out, a lot of logos have a layer of transparency which comes back as the most popular color. Naturally, the tool has to filter that out:
def get_best_top_color(image: Image.Image) -> tuple: """ Computes the most popular non-white color from an image. :param image: an image file :return: the most dominant color as a tuple """ top_colors = sorted(image.getcolors(image.size * image.size), reverse=True) curr_color = iter(top_colors) while (color := next(curr_color)) == WHITE: pass return color
Here, I filter out
WHITE which may not work for every logo. We’ll see. That said, this function grabs the primary color of an image and returns it.
At that point, we replace any instance of the hard coded color with whatever the
get_best_top_color() function returns—if a logo exists:
def process_image(input_path: str, tier: str = "", 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 = convert_file_name_to_title(file_name) img = Image.open(input_path) cropped_img: Image = img.crop((0, 0, IMAGE_WIDTH, IMAGE_HEIGHT)) color = RECTANGLE_FILL if logo_path: logo: Image.Image = Image.open(logo_path) color = get_best_top_color(logo) draw_logo(cropped_img, logo) edited_image = draw_overlay(cropped_img, title, tier, color) save_copy(img, edited_image, title, output_path) return edited_image
Fun fact: I decided to make use of the walrus operator here which actually restricted the script to Python 3.8. Sorry about that! If it becomes an issue, I’ll rework this solution.
As it turns out, there were quite a few changes that got us up to version 1.8.4. Here’s the list:
- Restricted installation to Python 3.8 users only
- Added testing and looped that testing into continuous integration
- Added default behavior description to master README
- Fixed links for PyPI
- Fixed issue where special characters in title cause images to not get saved
- Fixed an issue where the samples page had the wrong command in it
- Added a comment to the title flag which explained that it has no effect during batch processing
- Added titles to the file dialog windows to make them easier to discern
- Fixed an issue which caused batch processing to break the script
As always, I’m sure there are other changes, but these were the ones I documented.
Plans for the Future?
At this point, the biggest change I’m hoping to make is EXIF tagging. I think that’s the plan for version 1.9.0. After that, I might try my hand at adding some batch filtering parameters or even a complementary color mechanic for title text.
Eventually, I’d like to release a 2.0.0 version which features a GUI. For now, though, I don’t have that on my radar. Instead, I’m going to keep grinding out new features.
If you’re interested in seeing what’s planned for future milestones, check out the milestones page on GitHub. In the meantime, check out some of these related articles:
Otherwise, help support the site by checking out this list. It includes ways you can help me grow the site like hopping on my mailing list or joining me on Patreon.
Once again, thanks for stopping by! See you next time.
Indexing arrays is always a confusing topic. After all, the average person starts counting from one, so why don't arrays?
As someone who collects a lot of feedback, I sometimes underestimate how much work goes into processing, so let's try!