Using PIL’s Image for image transformation
By: Karan Bhanot
Tags: Image Processing, Towards Data Science, Data Science, Technology, Image

I was recently working on a project that involved manipulating images so we could run an unsupervised machine learning algorithm on it. Being a Python fanboy, rather than going the Photoshop route, I decided to use Python and its libraries to do the manipulation for me.

In this article, I highlight how I used the Image sub module in PIL to create the images I needed for my project.

Gather images

I took two images for this article, both from Unsplash. I particularly used different sized images so we can access how different sized images might interact with one other.

Image by Ivars Utināns

Image by sergio souza

Note that the first image is wider while the second image is longer in height. We’ll have a lot of fun combining them.

Image 1: https://unsplash.com/photos/DujBHPRqAKk

Image 2: https://unsplash.com/photos/JSye5r1eAVY

I usually use unsplash.com as it has a humongous variety of high quality images all freely accessible. Obviously, don’t skip on giving proper credit to the artists. It’s good ethics!!

Install libraries

I am using Python 3.6 here but all versions of Python 3.5 and above should ideally work.

pip install Pillow

pip install matplotlib

The Pillow is the PIL package containing our sub module Image . I’ve also decided to install matplotlib because it just makes it easier to display images within a Jupyter notebook irrespective of the size of the image. For example, when I tried to directly display the image using Image , it opened a new window with the image all full blown up.

If you want to do this in Jupyter notebooks, install it using pip install jupyter , start the Jupyter server and create a new notebook.

Load libraries

We’ll now load in the installed libraries. We need the Image sub module from PIL and the pyplot sub module from matplotlib which we will load in by the name plt . You can really name it anything or nothing at all, it’s just common practice to use plt .

from PIL import Image

import matplotlib.pyplot as plt

Load images

Using Image.open() , we can simply load the image in the notebook (or Python file if you’re using that). We’ll save the first image in the variable img1 and the second image in the variable img2 .

img1 = Image.open("image.jpg")

img2 = Image.open("image2.jpg")

And to view, say the first image, we simply use the command:

plt.imshow(img1)

The imshow() method displays the image within the Jupyter notebook and it will look like the image below:

Result of plt.imshow(img1)

We can repeat the same to view the second image as well just by replacing the name of the variable in the command above.

Resize image

It's very very easy to resize an image using Image . We just use the resize() method and define a tuple for the (width, height) of the final resized image. In the code below, I resize the image 1 to 1200x800, then print the size of the old and the new image, and finally display the resultant image.

img1_resized = img1.resize((1200, 800))

print("Original image size: {}".format(img1.size))

print("Resized image size: {}".format(img1_resized.size))

plt.imshow(img1_resized) ## Output

# Original image size: (5464, 3640)

# Resized image size: (1200, 800)

Resized image

Look at the horizontal and vertical axis and you’ll see that the new image is now resized to the size 1200x800.

Horizontal concatenation

In my project, I had to combine several images one after the other horizontally. The idea was to have images with the same height but with increasing width. Once I started looking for possible solutions, I was able to write a code that worked. However, unlike square images in my project, I was curious if I could combine different sized images and generate usable images. After careful experimentation, I found a solution that would work for images with varying sizes.

result_img = Image.new('RGB', (img1.width + img2.width, min(img1.height, img2.height)))

result_img.paste(img1, (0, 0))

result_img.paste(img2, (img1.width, 0))

plt.imshow(result_img)

I start by creating a new image using Image.new() . The first argument states that I’m creating an RGB image and the second argument is the width and height of the final image. It’s quite obvious to see that when two images will be placed side by side, their width would simply add up. However, as the heights of the two would be different, using a min() method would shrink the combined image to the appropriate height so there is no blank space.

I then paste the first image starting at (0,0) and the second image where the first image ends by (img1.width, 0) . The resultant image is below:

Combining two images horizontally

In case, we replaced the min() method with the max() method, we’ll get black color at places where there were no pixels originally. In the above code, if we used max() , the resultant image would have been:

Combining two images horizontally (using max())

Vertical concatenation

Just like horizontal concatenation, we can also combine images vertically. For that the total height would be the sum of the heights of the two images and the width would be the minimum of the two.

result_img = Image.new('RGB', (min(img1.width, img2.width), img1.height + img2.height))

result_img.paste(img1, (0, 0))

result_img.paste(img2, (0, img1.height))

plt.imshow(result_img)

Combining images vertically

Here, we’ll paste image 1 again at (0,0) but the second image will be placed at (0, img1.height) . Again, we used the min() method here in place of the max() method.

Save image

Finally, once all transformations are done, we can save the image. If we have the image as result_img , the command would be:

result_img.save("new_image.jpg")