Goal: to reduce the size of an imported image to $m \times n$ without simply cropping it: image resampling.
We'll be using the Python Imaging Library, or rather the forked, updated, but backwards-compatible version Pillow
Use matplotlib
/pylab
inline with static images:
In [1]:
%pylab inline
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
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)
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]:
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 mode
“1
” 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.
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.