Tag Archives: ImageMagick

Padding Thumbnails to Fix Aspect Ratio

Creating thumbnails for images on a website is key to performant and responsive page loads. This is easy to do for things like screenshots that tend to be all the same size; however it can be problematic for images with varying sizes, especially elongated ones. In this article we’ll discuss a technique to generate thumbnails that preserve a consistent aspect ratio, by padding them as needed.

Generating Thumbnails with ImageMagick

A 712×180 image from my upcoming Ravenloft: Strahd’s Possession walkthrough

In “Resizing Images and Creating Thumbnails with ImageMagick“, I demonstrated a simple way to resize images using ImageMagick:

convert input.jpg -resize 175x109 output.jpg
175×44 result

This works, and it preserves the aspect ratio, but disregards the desired size (in this case 175×109). For instance, applying this to a 712×180 image produces a thumbnail of size 175×44, which is undesirable in a page where your other thumbnails will be 175×109.

An alternative is to force the output size by adding an exclamation mark to the size:

convert input.jpg -resize 175x109! output.jpg
175×109 result

This produces an image with the desired size, but throws the aspect ratio out of the window. Elongated images end up with thumbnails that look squished.

Fortunately, ImageMagick already has the ability to pad out thumbnails, and so it is easy to produce a thumbnail that respects both the desired output size and the aspect ratio, by having it pad out the extra space in the thumbnail:

convert input.jpg -thumbnail 175x109 -background cyan -gravity center -extent 175x109 output.jpg
Padded thumbnail

Note that I’ve used cyan only to demonstrate the effect of the padding; in practice you will want to use a colour that blends with the rest of the image, in this case white.

The resulting thumbnail preserves the proportions of the features of the original image, while fitting nicely into the 175×109 dimensions that will be used by all other thumbnails on the page.

Mathematical Foundation

By the time I discovered how to generate padded thumbnails using ImageMagick, I had already worked out the mathematical calculations to do it myself. While this is now unnecessary, it’s an interesting exercise and can be useful if you ever need to do this kind of thing yourself programmatically without the help of ImageMagick.

Elongated images need to be resized into a smaller space (white) and padded (blue) to fit fixed thumbnail dimensions

So, imagine we have an elongated image and we need to generate a thumbnail that fits a specific size and is padded out to keep the original image’s aspect ratio, same as we did with ImageMagick. One way to do this is to pad the original image with extra space to achieve the desired aspect ratio of the thumbnail, and then just resize to the desired thumbnail dimensions. This works out slightly differently depending on whether the image is horizontally or vertically elongated.

Fitting a 600×200 image into a higher space

Let’s start with the first case: say we have an image of original size 600×200, and we want to fit it into a thumbnail of size 175×109. The first thing we need to do is calculate the aspect ratio of the original image and that of the thumbnail.

Calculating aspect ratio

The aspect ratio is calculated simply by dividing width by height. When the aspect ratio of the original image is larger than that of the thumbnail, as in this case, it means that the image is horizontally elongated and needs to be padded vertically. Conversely, if the original image’s aspect ratio were smaller than that of the thumbnail, we would be considering the second case, i.e. a vertically elongated image that needs to be padded horizontally.

Now, we need to figure out the dimensions of the padded image. We already know that our 600×200 image needs to be padded vertically, so the width remains the same at 600, but how do we calculate the new height (new_h)? As it turns out, the Law of Similar Triangles also applies to rectangles, and since we want to keep a constant aspect ratio, then it becomes just a matter of comparing ratios:

Finding the new height after padding

To double-check the result, calculate its aspect ratio again. Dividing 600 by 373.71 does in fact roughly give us 1.6055, the aspect ratio we were hoping to obtain.

Fitting a 300×700 image into wider space

The second case, i.e. when we’re dealing with vertically elongated images, works out similarly. In this case the original image’s aspect ratio is less than that of the thumbnail, and we need to find out the padded image height instead of the width. Assuming we’re dealing with a 300×700 image, then:

Finding the new width after padding

Dividing the new height, 481.65, by 300 roughly gives us the aspect ratio we wanted.

For both cases, once we manage to fit the original image onto a bigger canvas with the right aspect ratio, then it can be resized right down to the thumbnail dimensions without losing quality.

PIL Proof of Concept

To see the above concepts in action, let’s implement them using the Python Image Library (PIL). First, make sure you have it installed:

pip3 install pillow

Then, the following code generates thumbnails for horizontally elongated images:

from PIL import Image

thumb_w = 175
thumb_h = 109

with Image.open('input.jpg') as input_image:
    orig_w, orig_h = input_image.size

    orig_aspect = (orig_w / orig_h)
    thumb_aspect = (thumb_w / thumb_h)

    if orig_aspect > thumb_aspect: # horizontal elongation - pad vertically
        new_w = orig_w
        new_h = int((orig_w * thumb_h) / thumb_w)

        with Image.new( 'RGB', (new_w, new_h), (0, 255, 255)) as output_image: # cyan background
            # y-position of original image over padded image
            orig_y = int((new_h  / 2) - (orig_h / 2))
            # copy original image onto padded image
            output_image.paste(input_image, (0, orig_y))
            # resize padded image to thumbnail size
            output_image = output_image.resize((thumb_w, thumb_h), resample=Image.LANCZOS)
            # save final image to disk
            output_image.save('output.jpg')
        
    else: # vertical elongation - pad horizontally
        pass # ...

Based on the calculations in the previous section, the code compares the aspect ratio of the original image to that of the desired thumbnail dimensions to determine whether it needs to pad vertically or horizontally. For the first case (pad vertically), it calculates the padded image height (new_h) and creates a new image to accommodate it (again, the cyan background is just to demonstrate the effect). It then copies the original image into the middle of the new image. Finally, it resizes the new image to thumbnail size, and saves it to disk:

Vertically padded thumbnail generated with PIL

For the second case (pad horizontally), the code is mostly the same, except that we calculate the padded image width (new_w) instead of the height, and we calculate the x-position (orig_x) when placing the original image in the middle of the new image:

    else: # vertical elongation - pad horizontally
        new_w = int((thumb_w * orig_h) / thumb_h)
        new_h = orig_h

        with Image.new( 'RGB', (new_w, new_h), (0, 255, 255)) as output_image: # cyan background
            # x-position of original image over padded image
            orig_x = int((new_w  / 2) - (orig_w / 2))
            # copy original image onto padded image
            output_image.paste(input_image, (orig_x, 0))
            # resize padded image to thumbnail size
            output_image = output_image.resize((thumb_w, thumb_h), resample=Image.LANCZOS)
            # save final image to disk
            output_image.save('output.jpg')

Applying this to a vertically-elongated image, we get something like this:

Horizontally padded thumbnail generated with PIL

This code is just a quick-and-dirty proof of concept. It can be simplified and may need adjusting to account for off-by-one errors, cases where the aspect ratio already matches, images that aren’t JPG, etc. But it shows that the calculations we’ve seen actually work in practice and produce the desired result.

Conclusion

In this article we’ve discussed the need to produce thumbnails that both conform to a desired size and retain the original image’s aspect ratio. In cases where resizing breaks the aspect ratio, we can pad the original image before resizing in order to maintain the aspect ratio.

We’ve seen how to generate padded image thumbnails using ImageMagick, and then delved into how we could do the same thing ourselves. After demonstrating the mathematical calculations necessary to create the right padding to preserve the aspect ratio, we then applied them in practice using PIL.

I learned the above techniques while trying to find a way to automatically generate decent-looking thumbnails for maps in my upcoming Ravenloft: Strahd’s Possession walkthrough, where the maps come in all shapes and sizes and some of them needed a little more attention due to elongation. Hopefully this will be useful to other people as well.

Resizing Images and Creating Thumbnails with ImageMagick

My first website, Dino’s Ultima Page, turned 18 years old last week. As a website about games, it’s got hundreds of images, mostly screenshots or other images of in-game objects and people. I spent a lot of time manually making thumbnails for most of the larger images, so that pages would load quickly and larger screenshots would be loaded only when requested.

Now, hopefully a little wiser, I know that such things can be automated. I recently showed how to extract regions of an image using ImageMagick, and this software, which I’m growing to love more and more, is also capable of resizing images (among many other operations).

Resizing a Single Image

Many of the games I remember from the early 1990s ran at a resolution of 320×200. Although they would often be scaled up during gameplay, taking a screenshot (in DOSBox, for instance) results in an image that is 320 pixels wide and 200 pixels high.

A screenshot from Dark Sun: Shattered Lands.

For an image that I’d like to display on a website, the size is a little annoying. It’s too small to make out much of the details, but possibly slightly too large to use as a thumbnail. I’d like to scale it down to create a thumbnail, but then link to a scaled up version of the image.

Using ImageMagick, it’s pretty easy to resize an image. Assuming the image above is called dsun_003.png, we can create the smaller and larger version as follows:

convert dsun_003.png -resize 640x400 dsun_003-large.png
convert dsun_003.png -resize 200x125 dsun_003-small.png

We call the convert command, passing the following as arguments:

  • The name of the original image we’d like to resize
  • The -resize parameter, followed by the new size
  • The name of the output file where the resized image will be saved

Note that I intentionally chose the new image sizes such that the aspect ratio is preserved (which is usually what you want). If you need to change the aspect ratio, check the Resizing documentation for more advanced examples.

This leaves us with a nice thumbnail and a larger image where we can savour the graphics of 1993:

The original image (top-left), the thumbnail (bottom-left) and the larger image (right).

Resizing all images in a Folder

If we can resize a single image from the terminal, then we should be able to automate this process for a large number of images in a folder, saving a lot of time that would otherwise be spent clicking stuff in an image editor.

For instance, I have a few screenshots from King’s Quest 6 in a folder called kq6. Each image has a size of 640×440 and is of type .png.

Six screenshots of King’s Quest 6.

I’d like to resize all images in this folder regardless of how many there are. I can achieve this using a loop in bash:

for filename in $1/*.png; do
    convert $filename -resize 320x220 "$1/$(basename "$filename" .png)-sm.png"
done

Actually, the trickiest part of this is to extract the filename without the extension. That’s the part where we’re using basename, which takes the filename as the first argument, and the suffix to trim off as the second. This solution works where we know the image type a priori and all images in the folder are the same type.

You’ll also note the use of $1. That’s simply the first argument to the script, so that we can use the same script in different places, just passing the name of the folder as an argument. In fact, after saving the script as resizeall.sh and giving it execute permissions, let’s call it on our kq6 folder:

./resizeall.sh kq6

This has the effect of creating smaller versions of all the images in the folder, which have the same name except for a -sm suffix before the file extension:

We’ve generated smaller versions of each image in the folder.

Conclusion

We’ve seen again that ImageMagick is really handy for image manipulation, and the ability to do things from the terminal makes it really easy to automate operations on large numbers of images. In this case, we found it was really easy to resize game screenshots to create thumbnails and larger images to display on a website.

Extracting a Region of an Image with ImageMagick

Selecting a rectangular region of an image and doing something with it is an extremely common operation. Even the simplest image viewers (e.g. Microsoft Paint) allow you to do this. While this is easy enough to do by hand, there are times when it’s useful to do it via the command-line, for example:

  • The image is too big to load into memory via an image viewer
  • You need to extract the same region for many images

In this article, we’ll see how to use ImageMagick to extract a region in an image. Just to give some context, I’m using Linux Kubuntu 19.10 and ImageMagick 7.0.10-23.

Installing ImageMagick

The first thing to do is make sure you have ImageMagick installed. While you can definitely check out the ImageMagick Download page, I’ve found these instructions particularly helpful on Kubuntu.

Analysing an Image

Before we go ahead and use ImageMagick, we first need to study the image that we want to process. Let’s say, for instance, that we’re going to work with the following screenshot from Warcraft II: Beyond the Dark Portal:

A screenshot from Warcraft 2: Beyond the Dark Portal, showing the opening screen of the last level of the Orc campaign. Deathwing is the currently selected unit, and a cheat was used to reveal the entire map.

There are a few regions of interest that we could potentially extract: the minimap, the selected unit’s portrait, the main view, etc. Let’s say we want to extract the minimap. First, we need to figure out the details of its rectangular region, namely the coordinates of its top-right corner, its width, and its height.

We can do this using most basic image viewers. For instance, Linux distributions using the K Desktop Environment (KDE) have a simple Paint-like tool called KolourPaint. If you use the rectangular selection tool and use it to draw a boundary around the region of interest, the program should tell you the details of interest in the status bar. This is easier to do accurately if you zoom in enough and turn on the grid lines, allowing you to see exactly where your selection fits within the pixel boundaries.

Zooming in on the minimap with KolourPaint. The blue and yellow dotted line shows the selected region around the minimap. At the bottom, the details of the selection can be seen: 22,24 – 153,155 … 132×132.

For this particular example, we can see that the region of interest has its top-left corner at the coordinates (22, 24), and it has a size of 132×132.

Extracting the Minimap

All we need is to pass this information to ImageMagick along with the image file, and use the -extract switch to extract the region of interest to a separate file, like this:

magick -extract 132x132+22+24 deathwing.png deathwing-minimap.png

That means: extract a region of size 132×132 at (22, 24) from deathwing.png and save it to deathwing-minimap.png.

And it works beautifully:

The extracted minimap.

Analysing and Extracting the Unit’s Portrait

To extract Deathwing’s portrait, we just need to repeat the same process for a different region. First, we identify the coordinates and size of the region of interest:

This time, the portrait is selected, and the selected region information is: 6,166 – 57,209 … 52×44.

Then we use ImageMagick to extract the desired region, as before. Obviously, we change the details of the region and the output filename:

magick -extract 52x44+6+166 deathwing.png deathwing-portrait.png

And the result is as we expect:

The extracted portrait of Deathwing.

Conclusion

This is great, especially because you can do the analysis step once and repeat the command on many images that have the same structure. For instance, if I wanted to create a website with information about each unit in Warcraft 2, I would just need a screenshot for each unit. I could then simply run a command on each image, and extract the units’ portraits with ease.