Fork me on GitHub

Project Notes

Image Resize

Simple image resizing with python and the Pillow library and a little yak shaving with EXIF tags.

Notes

The original Python Imaging Library (PIL) has been superseded by the Pillow fork as perhaps the most popular image manipulation library for python. The history is described in this wikipedia article.

Installation

This script uses Pillow as described in requirements.txt. Install with pip:

$ python --version
Python 3.7.3
$ pip install -r requirements.txt
Collecting pillow
  Using cached Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl (3.3 MB)
Installing collected packages: pillow
Successfully installed pillow-9.3.0

Example

The example.py script takes any input image and resizes it.

The Image module provides two functions that could be used:

  • resize - resize to requested size ignoring the aspect ratio
  • thumbnail - resize to best fit while retaining the aspect ratio

I’m using resize in the example.

$ ./example.py -h
usage: example.py [-h] [-x X] [-y Y] filename

Resize an image

positional arguments:
  filename    source file

optional arguments:
  -h, --help  show this help message and exit
  -x X        resize to width in pixels (default 420)
  -y Y        resize to height in pixels (default 420)

As an example, I’m using this photograph:

source

$ ./example.py data/source.jpg
Source: data/source.jpg (2992w x 2992h)
Resized: data/source-resized-420x420.jpg (420w x 420h)

Producing this output file:

source-resized-420x420

Custom resize dimensions may be provided:

$ ./example.py data/source.jpg -x 80 -y 60
Source: data/source.jpg (2992w x 2992h)
Resized: data/source-resized-80x60.jpg (80w x 60h)

Producing this very low resolution output file:

source-resized-80x60

A Little Yak Shaving - EXIF Orientation and Bugs

My first attempt at this script resized the image correctly, but with a -90˚ rotation.

  • stackoverflow to the rescue: this was because the original image had an EXIF orientation tag that was being lost in the resize

My second attempt applied the exif_transpose to retain the orientation indicated by the EXIF tags. But this failed with a TypeError: object of type 'int' has no len() in the PIL/TiffImagePlugin module.

After a bit of digging around, it turns out that EXIF tags have been a source of many issues e.g. LensSpecification.

In my case, it was the GPS Info field 0x8825 causing problems:

0x8825: 656 type:<class 'int'>

As a result, I’ve added a remove_orientation function that blows this EXIF field away before performing the exif_transpose:

def remove_orientation(image):
    exif = image.getexif()
    for tag in exif.keys():
        # print(f'0x{tag:04x}: {exif[tag]} type:{type(exif[tag])}')
        if tag in [0x8825]:
            # remove the GPS exif fields because exif_transpose can't handle the tags I have in the source image
            del exif[tag]
    image.info['exif'] = exif.tobytes()
    return ImageOps.exif_transpose(image)

NB: to be super-safe, I could alternatively throw away all tags except orientation (0x0112)

Credits and References

About LCK#227 python
Project Source on GitHub Return to the Project Catalog

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.