Resampling an image to resize it

Goal: to reduce the size of an imported image to $m \times n$ without simply cropping it: image resampling.

Dependencies

We'll be using the Python Imaging Library, or rather the forked, updated, but backwards-compatible version Pillow

Imports

Use matplotlib/pylab inline with static images:


In [1]:
%pylab inline


Populating the interactive namespace from numpy and matplotlib

Import Pillow to work with images, and use the iPython display() function.


In [2]:
from PIL import Image

from IPython.display import Image as show_image

Displaying PIL images inline

iPython doesn't currently handle PIL images directly, so we use a convenience function, adapted slightly from http://nbviewer.ipython.org/gist/deeplook/5162445:


In [3]:
from io import BytesIO
from IPython.core import display

def display_pil_image(im):
   """Displayhook function for PIL Images, rendered as PNG."""

   b = BytesIO()
   im.save(b, format='png')
   data = b.getvalue()

   ip_img = display.Image(data=data, format='png', embed=True)
   return ip_img._repr_png_()

# register display func with PNG formatter:
png_formatter = get_ipython().display_formatter.formatters['image/png']
dpi = png_formatter.for_type(Image.Image, display_pil_image)

Loading the image

We'll be loading the image molecule.jpg from the data subdirectory. We'll get the image size, so we can decide what we want to resize it to.


In [4]:
image_filename = 'data/molecule.jpg'  # Input image filename
img = Image.open(image_filename)
img.size, img.mode


Out[4]:
((1280, 800), 'RGB')

Resizing the image

The image is 1280x800 pixels in size. Let's reduce this to one quarter of the area by halving both dimensions, to 640x400. We can do this with Pillow's .resize() method. This method has an optional argument resample that can be used to apply resampling filters, and takes filters from the PIL.Image module.

  • Image.NEAREST (nearest neighbour)
  • Image.BILINEAR (linear interpolation)
  • Image.BICUBIC (cubic spline interpolation)
  • Image.LANCZOS (a high-quality downsampling filter)

If no filter is specified, or if the image has mode1” or “P”, it is set Image.NEAREST.

We'll examine the result of all four filters, below.


In [5]:
filters = [Image.NEAREST, Image.BILINEAR, Image.BICUBIC, Image.LANCZOS]
downsized = [img.resize((640, 400), f) for f in filters]

First, let's look at the original:


In [6]:
img


Out[6]:

With nearest neighbour interpolation:


In [7]:
downsized[0]


Out[7]:

With bilinear interpolation:


In [8]:
downsized[1]


Out[8]:

With cubic spline interpolation:


In [9]:
downsized[2]


Out[9]:

And, finally, with Lanczos kernel resampling:


In [10]:
downsized[3]


Out[10]:

All of these images look to be pretty acceptable, though the nearest-neighbour and Lanczos resampling seems sharpest to my eyes.

Making images even smaller

Reducing the size farther:


In [11]:
smaller = [img.resize((128, 80), f) for f in filters]

In [12]:
smaller[0] # Nearest neighbour


Out[12]:

In [13]:
smaller[1] # Linear


Out[13]:

In [14]:
smaller[2] # Cubic


Out[14]:

In [15]:
smaller[3] # Lanczos


Out[15]:

Again, all the filters give acceptable output.