How to Resize Images Using Python | by Nicholas Ballard | Towards Data Science

It would be a safe bet saying every “person of the computer” has needed to resize an image at one time or another. MacOS’ Preview can do it; so can Windows PowerToys.
If you understand Photoshop or Gimp, then you and I... We are not the same. Command line utilities? Okay, now that is something I can wrap my head around.
Lucky for us, image manipulation, and command line tooling, are two specialties of Python.
This article aims to show you three things:
Basics of what an image is.Python libraries for manipulating images.Code you can take and use in your own projects.
Basics of what an image is.
Python libraries for manipulating images.
Code you can take and use in your own projects.
The command line program we are going to build can resize one or multiple image files at once.
Let’s jump in!
For this example, we are going to create our own image rather than find a real one to manipulate.
Why? Because I have to over-complicate things, that’s why!
Actually, creating our own is a good way to illustrate what an image actually is. This resizing program will work just as well on anything from Instagram.
So, what is an image? In Python data terms, an image is a list of lists of tuples of integers.
image = list[list[tuple[*int, float]]]
A NumPyesque definition would be a two-dimensional array of shape (h, w, 4), with h the number of pixels high (up and down), and w the number of pixels across (left to right).
In other words, an image is a list (whole image) of lists (rows) of pixels (cells). Each pixel is a sequence of 3 integers and 1 optional float: red channel, green channel, blue channel, alpha (float that is optional). The red, green, blue channels — RGB — have a value from 0 to 255.
From here on out we’ll talk about color images without alpha channel to keep it simple. Alpha is the transparency of the pixel. Images can also have only one channel with a value from 0 to 255. This is what greyscale — aka black and white — images are. But this is the real world... we work with color images!
import matplotlib as plt​pixel: tuple = (200, 100, 150)plt.imshow([[list(pixel)]])
Hot.
Vanilla Python is fully capable of creating an image. To display it, I will use the matplotlib library, which you can install with:
pip install matplotlib
Creating a Pixel:
Creating an image:
That is the image rendered. Behind the scenes, this is what the data looks like (for a 90x90):
[[[234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [234, 162, 33],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [255, 0, 0],  [234, 162, 33],  ...
Now that we have an image, let’s get resizing it!
Psych! We will not be writing an algorithm for resizing images in Python.
Why?
TLDR: It’s too much work.
There is so much that goes into image processing algorithms. Some people dedicate their lives to it. Resampling–using one pixel in a scaled down image to stand in for the many around it in the higher resolution–is a huge topic by itself. If you want to see for yourself, check out Image.py in Pillow's source code when you install it at path/to/site-packages/PIL.
Then there are optimizations like antialiasing and gap-reduction... It’s endless. We’re going to stand on the shoulders of giants here, meaning trust the brilliant work of those in the computer vision field, and shamelessly invoke one-liners to solve our problems.
If you are interested in learning more about what is going on behind the curtain when processing images, I encourage you to check out the topic of “machine vision” more! It is definitely a booming field, and there’s more niches to explore than one person has time.
Get good enough, and there’s a legion of companies willing to pay top dollar for your computer vision expertise. Self-driving, IoT, surveillance, you name it; all rely fundamentally on processing pictures (usually in Python or C++).
A great place to start is checking out scikit-image.
OpenCV is the end-all, be-all for image processing. Written in C++ and ported to Python, OpenCV is what runs in the trunk of your homemade self-driving car for lane detection.
The options for the interpolation argument are one of the flags provided in the cv2 package:
INTER_NEAREST – a nearest-neighbor interpolation INTER_LINEAR – a bilinear interpolation (used by default) INTER_AREA – resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method. INTER_CUBIC – a bicubic interpolation over 4×4 pixel neighborhood INTER_LANCZOS4 – a Lanczos interpolation over 8×8 pixel neighborhood
And this returns:
It did what we expected. The image went from 900 pixels high, and 900 pixels across, to 450 X 450 (and still three color channels). The screenshot above doesn’t look so good because of matplotlib colorizations in Jupyter Lab.
The pillow library has a resizing method on the Image class. The arguments it takes are:
size: (width, height)resample: Defaults to BICUBIC. A flag for the resampling algorithm.box: Defaults to None. A 4-tuple defining a rectangle of the image to work on within parameters (0, 0, width, height).reducing_gap: Defaults to None. A resampling optimization algorithm to make the output look better.
Here it is for our purposes in a function:
The function using Pillow is very much the same as OpenCV. The only difference is the PIL.Image.Image class has an attribute size to access the (width, height) of the image.
The result:
Notice how the show method opens the operating system's default program for viewing the image's file type.
Now we have a function for resizing images, it’s time to bring it full circle with an interface for running the resize.
Resizing one image is fine. But if we are going through all this trouble, we want the ability to process images in bulk, too.
The interface we are going to build will be the simplest one possible: a command line utility.
The Pallets Project is the community of geniuses behind Flask, the Jinja template engine, and relevant to us right now: Click.
pip install click
Click is a library for making command line programs. It is way better than using plain argparse or whipping up some if-then logic in the if __name__ == '__main__': statement. So, we're going to use Click to decorate our image resizer.
Here is the full script for resizing images from the command line!
The command line program runs from the entrypoint function main. Arguments are passed to main by click.option decorators:
pattern takes a Python / RE2 style string to locate the image or images relative to the directory where the script is running. --pattern="../catpics/*.png" will go up one level and find the catpics folder, and return any files in that folder with the .png image extension.
scale takes a number, float or integer, and passes this to the resize function. This script is simple, and that means no data validation. Bonus if you add to the code, checking scale is a number between let's say, 5 and 99 (reasonable down-scaling parameters). As it stands right now, you can pass -s "chicken nuggets" and an Exception won't be thrown until Pillow stops the program.
quiet is an option argument if you do not want text printing to standard out while the program is running.
Run the program from the command line:
python resize.py -s 35 -p "./*jpg"
Result:
$ py resize.py -p "checkers.jpg" -s 90resized image saved to checkers_90_810x810.jpg.
Checking the folder:
$ ls -lh checkers*-rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg-rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg
Nice! So the program downsized the image, gave it a descriptive label, and we can see the file size went from ~362KB to ~231KB!
Just to see the program working on multiple files at once, we’ll run it again:
$ py resize.py --pattern="checkers*" --scale=20resized image saved to checkers_20_180x180.jpg.resized image saved to checkers_90_810x810_20_162x162.jpg.
And the file system:
$ ll -h checkers*-rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg-rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_20_180x180.jpg-rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg-rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_90_810x810_20_162x162.jpg
Okay, running the program recursively is only for crazy people, but it shows you can run on any number of images matching the pattern all at once. Go, automation!
Bonus
Click is an amazing tool. It can wrap a function and run in a module “the normal way” from an if __name__ == '__main__': statement. (And actually, it doesn’t even need that; you can just define and decorate the function you want to run.) But where it really shines is installing the script as a package.
That is done with the setuptools library that comes with Python.
Here is my setup.py.
Build the executables / wrap the package with the command:
pip install -e .
Now you can invoke the script without using the python command. Also–as a bonus bonus–if you add the new executable to a folder in your PATH, you can call this program from anywhere on your computer like resize -p *jpg -s 75!
This tutorial bounced around quite a bit: first going over some of the third party Python libraries for image manipulation. Then building an image from scratch using Python to understand a little more about what an image actually is. Then, choosing one of the options and building a script to downscale images while keeping their proportions. And, finally–inhales–putting that all in a command line utility accepting configurable options with click.
A few more sentences and I’m done, I promise.
I just wanted to say: there is a reason as to why I covered all these topics in one go with you. And that reason is not because I have ADHD.
It’s because programming ideas need to close the loop from idea to execution. A one-off concept on how to do something in Python is fine... but at the end of the day, a programming language is a tool. It needs to help us get stuff done to have any value.
When you start to feel tutorials are overwhelming you with “yet another thing you have to learn”–it means it’s time to take a break from reading, and start coding on some small project. Something you can complete in one sitting, and feel you have made progress.
Remember, code can take hours or days to write. Yet it takes only milliseconds to run. The programs you make don’t have to be big. Any single thing you build that can save you time or let you produce more output, can potentially serve you for the rest of your life!