VMware ESXi and vSphere Cluster Management
Resize Images with Pillow in Python
Learn how to resize images with Python Pillow using resize() for exact dimensions and thumbnail() for proportional previews.
Pillow is a Python imaging library commonly imported through the PIL package namespace. It lets you open, inspect, resize, crop, rotate, and save image files.
This lesson focuses on changing image dimensions with Pillow. You will learn how to inspect an image's original size, force an exact output size with resize(), and create a proportional preview with thumbnail().
Open an image with Pillow
Import the Image module from Pillow, then use Image.open() to load an image file. The result is an image object: Pillow's in-memory representation of the opened image.
from PIL import Image
img = Image.open("handsome.jpg")
Loading the image should happen before you inspect or transform it. The filename can be a relative path, such as "handsome.jpg", or a path to a file in another directory.
Inspect the original dimensions
An image's dimensions are its width and height, measured in pixels. Pillow exposes these dimensions through the image object's size attribute.
from PIL import Image
img = Image.open("handsome.jpg")
print(img.size)
The value printed by size is a two-item tuple in width-by-height order. For example, (1200, 1599) means:
- The width is 1200 pixels.
- The height is 1599 pixels.
A tuple is an ordered Python value written with parentheses and commas. Recording the original dimensions makes it easier to compare the result of a resizing operation.
Resize to explicit dimensions with resize()
Use resize() when the output must have a particular width and height. Its size argument is a two-element tuple written as (width, height).
resized_img = img.resize((900, 500))
print(resized_img.size)
resized_img.show()
The outer parentheses call the resize() method. The inner parentheses create the tuple containing the requested width and height:
img.resize(...)is the method call.(900, 500)is the tuple passed as the method's argument.
resize() returns a resized image object. Assign that returned object to a variable such as resized_img unless you intentionally want to replace your existing reference.
show() can open a preview using an image viewer available in your environment. You can also save the returned image:
resized_img = img.resize((900, 500))
resized_img.save("handsome-900x500.jpg")
The result of this operation has exactly the requested dimensions:
print(img.size) # For example: (1200, 1599)
print(resized_img.size) # (900, 500)
Understand aspect ratio consequences
An aspect ratio is the proportional relationship between an image's width and height. A portrait image is taller relative to its width, while a landscape image is wider relative to its height.
When you pass arbitrary width and height values to resize(), Pillow creates that exact rectangle. It does not automatically preserve the source aspect ratio. If the target proportions differ from the original, subjects may look stretched, compressed, overly wide, or overly tall.
Example: forced dimensions
Suppose the original portrait image is 1200 by 1599 pixels. Resizing it directly to 900 by 500 changes a tall image into a wide one. People and objects in the photograph can appear horizontally stretched or vertically compressed.
Exact dimensions can still be appropriate when a fixed-size layout requires a particular canvas and distortion is acceptable or handled separately. For example, a design may require every source image to occupy a 900 by 500 slot. If distortion is not acceptable, consider cropping or placing the image inside that canvas instead of simply forcing both dimensions.
Create proportional thumbnails with thumbnail()
Use thumbnail() when retaining the source image's proportions is more important than reaching exact dimensions. Its argument is a bounding box: maximum width and maximum height limits that the result must fit inside.
from PIL import Image
img = Image.open("handsome.jpg")
img.thumbnail((900, 500))
print(img.size)
img.show()
Unlike resize(), thumbnail() preserves the source aspect ratio. The values (900, 500) mean “fit within 900 pixels wide and 500 pixels tall,” not “be exactly 900 by 500.”
For a 1200 by 1599 portrait image, the height reaches the 500-pixel maximum first. The width remains below 900 pixels, producing a result such as (375, 500). The image fits inside both limits without being warped.
thumbnail() changes the existing image object in place. An in-place operation modifies the current object instead of returning a separate transformed object. It also does not enlarge an image beyond its existing dimensions.
img = Image.open("handsome.jpg")
print(img.size) # For example: (1200, 1599)
img.thumbnail((900, 500))
print(img.size) # For example: (375, 500)
img.save("handsome-thumbnail.jpg")
If you need to keep the full-size image available, reopen the file or create a copy before calling thumbnail(). The variable now refers to the reduced image object.
Compare resize() and thumbnail()
Choose the method based on the requirement:
- Use
resize()when the output must have exact dimensions. - Use
thumbnail()when the image must retain its proportions and fit within maximum dimensions. - Check
image.sizeafter either operation so you know the actual result.
Compare example dimensions
Run a direct comparison
To compare both methods, open the source twice. This prevents the in-place behavior of thumbnail() from affecting the image used for another test.
from PIL import Image
source = Image.open("handsome.jpg")
forced = source.resize((900, 500))
thumbnail = Image.open("handsome.jpg")
thumbnail.thumbnail((900, 500))
print("Source:", source.size)
print("resize():", forced.size)
print("thumbnail():", thumbnail.size)
forced.save("forced-900x500.jpg")
thumbnail.save("thumbnail-900x500.jpg")
The fixed resize has the requested 900 by 500 dimensions but can distort the portrait subject. The thumbnail has smaller dimensions, such as 375 by 500, but preserves the original shape.
Troubleshoot common resizing problems
The resized image looks stretched or squashed
The width and height passed to resize() probably do not match the source image's aspect ratio. Use thumbnail() for a proportional reduction, or calculate a target dimension that preserves the original ratio.
The thumbnail does not have the exact dimensions passed to thumbnail()
thumbnail() treats its values as upper bounds. It keeps the source proportions, so one dimension may be smaller than its limit. Use resize() when exact dimensions are mandatory.
The original image variable changed after thumbnail()
This is expected because thumbnail() operates in place. Reopen the image or copy it before the call when the original-size image must remain available.
The transformed image was not retained
resize() returns a new image object. If you call it without assigning the result, your variable still refers to the original image.
# The resized result is discarded
img.resize((900, 500))
# Keep the resized image
resized_img = img.resize((900, 500))
The double parentheses are confusing
The outer pair belongs to the method invocation, and the inner pair creates the (width, height) tuple. This is why img.resize((900, 500)) contains two pairs of parentheses.
Exam-relevant notes
Image.open()loads a file and returns an image object.image.sizereports dimensions as(width, height).resize((width, height))returns an image with explicit dimensions.resize()can change the aspect ratio and distort content.thumbnail((max_width, max_height))preserves proportions and fits within a bounding box.thumbnail()modifies the current image object in place and does not enlarge it.- Always inspect the final
sizewhen the output dimensions matter.