MEDIA GUIDES / Bulk Image Resize

Python Image Resize With Pillow and OpenCV

python resize

Key takeaways

  • Pillow’s resize() takes a (width, height) tuple and returns a new image, so it distorts anything whose proportions don’t already match the box you asked for.
  • Use thumbnail() when the proportions have to survive: it fits the image inside the box, does the arithmetic for you, and never enlarges.
  • Pass the filter explicitly. Image.Resampling.LANCZOS suits shrinking photographs, BICUBIC suits enlarging, and Image.ANTIALIAS no longer exists.
  • Resizing locally produces fixed files. Resizing at delivery, from a parameter in the URL, produces every size on demand from one upload.

Python is a popular object-oriented programming language for image-related tasks for webpages, visualizations, or when using Python for machine-learning operations through frameworks like OpenCV and Scikit Learn.

Reducing the size of an image means changing its dimensions by removing its pixels. Scaling up an image increases the number of pixels but lowers quality. Either way, if you set the width and height independently, the image’s aspect ratio changes, which results in distortion.

This article describes how to resize images in bulk with the Pillow library, a popular fork of the Python Imaging Library (PIL); and, to maintain the quality and aspect ratio, in OpenCV, a robust library of programming functions for computer vision, neural networks, and other advanced image processing tasks. Also explained is how to resize and crop Python images with Cloudinary through automation.

Pillow vs OpenCV vs a Hosted Image API

Option Resize call Best for Keeps the aspect ratio for you
Pillow Image.resize(), Image.thumbnail() Files on disk, thumbnails, format conversion, batch jobs Only with thumbnail()
OpenCV cv2.resize() Arrays already inside a computer vision or machine learning pipeline, video frames No
scikit-image skimage.transform.resize() Scientific work that wants float output and explicit anti-aliasing No
Wand, a binding for ImageMagick Image.resize() Formats and effects ImageMagick already handles No
A hosted image API, such as ImageKit or imgix URL parameters Serving many sizes of one image to browsers and apps on demand Depends on the mode you request
Cloudinary build_url(width=…, crop=…) The same delivery case, with upload, transformation and CDN caching behind one API Yes, with crop=”fit” or crop=”limit”

Pillow and OpenCV resize on your machine, which is the right answer for a one-off script, a build step, or anything that has to run offline. A hosted API resizes at request time and caches the result on a CDN, which is the right answer when one source image has to be served at a dozen sizes to devices you cannot predict.

Resize Images in Python With Pillow

Pillow is a fork of the Python Imaging Library (PIL) that supports Python 3 and numerous image formats, including PNG, JPEG, TIFF, and PPM. When you load an image from a file, create a new image, or generate separate instances for images, you create an instance of PIL’s Image class.

To resize an image with Pillow’s resize() method:

  1. Import the PIL image class:
    1. from PIL import Image
  2. Load the image from a file with the open() function:
    1. image = Image.open('myimage.jpg')

    The above command returns an Image object. In case of failure, the command raises an OSError exception.

  3. Call the resize() method on the new image instance, passing a tuple argument with two integers to specify the width and height you desire:
    1. image = Image.open('myimage.jpg')
    2. new_image = image.resize((500, 500))
    3. new_image.save('myimage_500.jpg')

    Note: Instead of modifying the image file, this function returns a separate Image instance with the new dimensions.

The resize() method has two drawbacks, however:

  • Oftentimes, resizing to an exact width and height changes the image’s aspect ratio, leading to distortions.
  • If you set the size of the new instance to be larger than that of the original, resize() “blows up” the instance, reducing its quality.

As a solution, resize the image with the more advanced Pillow method, thumbnail():

  1. Perform steps 1 and 2 of the above procedure.
  2. Call the thumbnail() method on the Image instance, passing a tuple argument with two integers to specify the width and height you desire:
    1. image = Image.open('demo_image.jpg')
    2. image.thumbnail((400, 400))
    3. image.save('image_thumbnail.jpg')
    4. print(image.size) # Output: (400, 350)

As shown under print, the size of the new instance is 400×350 pixels. The aspect ratio of the original image remains unchanged. In addition, if the dimensions of the original are smaller than that specified for the new instance, instead of “blowing up” the image, thumbnail() returns an instance of the same size.

Automatically resize and crop image with AI

How Do You Resize an Image and Keep the Aspect Ratio?

This is the common case: a layout fixes one dimension and the other has to follow. Compute the scale factor from the dimension you control.

from PIL import Image

TARGET_WIDTH = 800

with Image.open("input.jpg") as img:
    width, height = img.size
    new_height = round(height * (TARGET_WIDTH / width))
    resized = img.resize((TARGET_WIDTH, new_height), Image.Resampling.LANCZOS)
    resized.save("input_800w.jpg", quality=85, optimize=True)

Use round() rather than int(). Truncating is what leaves the output one pixel short of the size you expected on roughly half your inputs.

thumbnail() does the same arithmetic for you, with two details worth knowing: it changes the image in place and returns None, and it only ever shrinks. Call copy() first if you still need the full-resolution image afterwards.

Which Pillow Resampling Filter Should You Use?

Filters live on the Image.Resampling enum, reachable as Image.Resampling.LANCZOS. When you leave the resample out, Pillow uses BICUBIC, which is a compromise rather than the best choice in either direction.

Filter Speed Use it for
NEAREST Fastest Pixel art, masks and label maps, where an invented intermediate value would be wrong
BOX Fast Shrinking by an exact integer factor
BILINEAR Fast Quick previews and mild shrinking
HAMMING Moderate Shrinking with less ringing than bicubic
BICUBIC Moderate Enlarging. This is the default for both resize() and thumbnail()
LANCZOS Slowest Shrinking photographs where sharpness matters most

Note: Image.ANTIALIAS was deprecated in Pillow 9.1.0 and removed in 10.0.0. Image.LANCZOS and Image.Resampling.LANCZOS are its replacements, and the short form is not deprecated.

How Do You Resize a Whole Folder of Images?

For a directory, pathlib and thumbnail() cover it. The script below writes to a separate output directory, keeps each file’s format, and reports what it skipped instead of stopping on one bad file.

from pathlib import Path
from PIL import Image

SOURCE = Path("photos")
DEST = Path("photos_resized")
MAX_SIZE = (1200, 1200)
SUFFIXES = {".jpg", ".jpeg", ".png", ".webp", ".tif", ".tiff"}

DEST.mkdir(parents=True, exist_ok=True)

for path in sorted(SOURCE.iterdir()):
    if path.suffix.lower() not in SUFFIXES:
        continue
    try:
        with Image.open(path) as img:
            img.thumbnail(MAX_SIZE)
            img.save(DEST / path.name)
            print(f"{path.name} -> {img.size[0]}x{img.size[1]}")
    except OSError as err:
        print(f"skipped {path.name}: {err}")

Resize Images in Python With OpenCV

OpenCV is an open-source computer-vision library with thousands of machine-learning and deep-learning algorithms for face detection, object recognition, and many other computer-vision tasks. Given that numerous computer-vision models require a certain size and quality level for their images, resizing is critical. To determine which image variation performs best, experiment with different sizes or resolutions.

Here is the full syntax for the resize() method in OpenCV:

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])

The parameters are as follows:

src The input image, as the array returned by cv2.imread(), not a file path.
dsize The size of the output image, which adheres to the syntax (width, height).
fx The scale factor for the X axis.
fy The scale factor for the Y axis.
interpolation The technique for adding or removing pixels during the resizing process. The default is cv2.INTER_LINEAR.

Note: Apply either dsize or fx and fy, or all three.

To perform a simple resizing task with OpenCV:

  1. Import the OpenCV library:
    import cv2
    import matplotlib.pyplot as plt
  2. Acquire a sample image and specify its current size:
    img = cv2.imread("myimage.jpg")
    print('Image Width is', img.shape[1])
    print('Image Height is', img.shape[0])
  3. Resize the image to 300x300px:
    cv2.resize(img, (300,300))

As in the previous example on resizing images with Pillow’s resize() method, this procedure changes the aspect ratio, causing distortions. To maintain that ratio, run the following command to resize the image to 75% of its width and height:

img_75 = cv2.resize(img, None, fx = 0.75, fy = 0.75)

In addition, for a resized instance that is larger than the original, you can customize the interpolation of the resize operation. Even though doing that causes quality loss, it might be the right choice for certain computer-vision applications.

Here are the values for the interpolation argument:

cv2.INTER_LINEAR The standard bilinear interpolation, ideal for clearer and more detailed enlarged images.
cv2.INTER_NEAREST The nearest neighbor interpolation, which, though fast to run, creates blocky images.
cv2.INTER_AREA The interpolation for the pixel area, which scales down images.
cv2.INTER_CUBIC The bicubic interpolation with 4×4-pixel neighborhoods, which, though slow to run, generates high-quality instances.
cv2.INTER_LANCZOS4 The Lanczos interpolation with an 8×8-pixel neighborhood, which generates images of the highest quality but is the slowest to run.

Resize and Crop Python Images With Cloudinary Through Automation

A cloud-based service for managing images and videos, Cloudinary offers a generous free-forever subscription plan. While on that platform, you can upload images and apply built-in effects, filters, and modifications.

You can also resize images through automation, focusing on the most important elements with AI, or adapt them to your website design by, for example, specifying the width, height, and aspect ratio as qualifiers for the new image instances. Cloudinary then automatically performs the resizing and cropping tasks to meet the criteria. No manual efforts are required.

Take this 1,200×1,200-pixel image:
Model

Resizing it to 200×200 pixels with crop, scale, fill, and pad results in the following images:

Original image

Original Image

Focus on the model in a portrait crop

Focus on the model in a portrait crop

Detect the face for a thumbnail crop

Detect the face for a thumbnail crop

Auto-cropped wide image

Automatically determine what to keep in a banner crop

To automate image resizing and cropping on Cloudinary:

  1. Sign up for a free Cloudinary account.
  2. Install the Python SDK.
  3. Set the transformation criteria. These calls produce a portrait crop, a face thumbnail and a banner crop:
# Focus on the model in a portrait crop.

CloudinaryImage("docs/model.jpg").image(gravity="person", height=600, width=450, crop="fill")

# Detect the face for a thumbnail crop.

CloudinaryImage("docs/model.jpg").image(gravity="face", height=250, width=250, crop="thumb")

# Crop to a banner, automatically focusing on a region of interest.

CloudinaryImage("docs/model.jpg").image(gravity="auto", height=150, width=600, crop="fill")

Which Crop Mode and Gravity Should You Use?

The crop parameter decides what happens when the image and the box you asked for have different proportions, and gravity automatically decides where the image should be centered before cropping.

crop value Behavior
scale Resizes to the dimensions you give, without necessarily keeping the aspect ratio
fit Fits the whole image inside the box and keeps the aspect ratio
limit Like fit, but only scales down
pad Fits the whole image inside the box and pads the remaining area
fill Fills the box exactly without distortion, cropping whatever does not fit
lfill Like fill, but only scales down
thumb Builds a thumbnail based on the gravity you set
crop Extracts a region at the requested size without resizing first

gravity="auto" intelligently picks the most interesting region for you, and gravity="face" targets the largest detected face. imagga_crop and imagga_scale are crop modes rather than gravity values, and they need the Imagga Crop and Scale add-on.

Simplify Your Image Resizing with Cloudinary

Python offers numerous modules and libraries, such as Pillow and OpenCV, to resize images. While these tools are powerful and versatile, they require significant coding effort to handle various use cases, such as different image formats, sizes, and quality requirements. This can be a daunting task for developers looking to streamline their workflow rather than managing image processing code.

The Cloudinary API not only simplifies the process of resizing images but also provides a comprehensive suite of image management functionalities. With Cloudinary, you can easily handle image uploads, transformations, optimizations, and even advanced features like automatic format selection, watermarking, and responsive image delivery.

Ready to take your image processing to the next level? Sign up for a free Cloudinary account today and experience the ease and power of Cloudinary’s image management solutions. Start simplifying your workflow and focus on what truly matters—building great applications.

Frequently Asked Questions

How do you resize an image in Python without losing quality?

Shrink in one step with Image.Resampling.LANCZOS in Pillow or cv2.INTER_AREA in OpenCV, and save JPEG with quality=85, optimize=True. Shrinking always discards information, so the goal is to avoid compounding it: resize from the original every time rather than resizing a file that has already been resized.

Should you use Pillow or OpenCV to resize images?

Pillow when you are working with files and want thumbnails, format conversion and a smaller dependency. OpenCV when the image is already a NumPy array inside a computer vision or machine learning pipeline, or when you are processing video frames.

How do you resize an image to a specific file size in KB?

Neither Pillow nor OpenCV has a target-file-size parameter. Reduce the dimensions first, then search for the quality setting that lands under your budget by saving into a BytesIO buffer in a loop and checking its length before you write to disk.

How do you resize an image that is already a NumPy array?

Pass it straight to cv2.resize(), which operates on arrays. For Pillow, wrap it with Image.fromarray(array), resize, then convert back with numpy.asarray(image).

QUICK TIPS
Tamas Piros
Cloudinary Logo Tamas Piros

In my experience, here are tips that can help you better resize images using Python with Pillow and OpenCV:

  1. Maintain aspect ratio with thumbnail() in Pillow
    Use thumbnail() in Pillow instead of resize() to maintain the image’s original aspect ratio. This method automatically scales the image down to fit within the specified dimensions, avoiding distortion while ensuring the image is not enlarged beyond its original size.
  2. Use ANTIALIAS filter for high-quality resizing in Pillow
    When resizing images with Pillow’s resize(), apply the Image.ANTIALIAS filter to reduce aliasing and achieve smoother results. This is particularly useful when downscaling images, as it improves quality by reducing jagged edges.
  3. Utilize OpenCV’s INTER_AREA for downscaling
    When resizing images to a smaller size with OpenCV, use cv2.INTER_AREA for interpolation. This method is designed for downscaling, as it produces high-quality results by averaging pixel values, making it ideal for reducing image size while maintaining clarity.
  4. Apply INTER_CUBIC for upscaling in OpenCV
    For enlarging images in OpenCV, use cv2.INTER_CUBIC interpolation. Though slower, it produces higher-quality results for upscaling compared to INTER_LINEAR by considering a larger neighborhood of pixels.
  5. Precompute scaling factors for faster resizing
    If you are consistently resizing images by a specific percentage, calculate the scaling factors (fx and fy) ahead of time instead of manually setting new dimensions each time. This approach is faster and more efficient for bulk image processing in OpenCV.
  6. Handle transparency with convert('RGBA') in Pillow
    When resizing images with transparency (e.g., PNGs) in Pillow, ensure you use image.convert('RGBA') before resizing. This ensures that the alpha (transparency) channel is preserved during the resize operation.
  7. Batch process images with Pillow and OpenCV
    To resize multiple images at once, use Python’s os module to iterate through files in a directory and apply the resize operation. This automation is especially useful for processing entire folders of images for web optimization or machine learning.
  8. Apply resize() for consistent target size with cropping
    When you need to resize an image to exact dimensions, use resize() in both Pillow and OpenCV. If maintaining the aspect ratio is not required, this method allows for cropping to match specific target dimensions.
  9. Use imutils for easier OpenCV image manipulation
    Use the imutils Python library, a wrapper for OpenCV, to simplify common image manipulations like resizing. It provides a convenient imutils.resize() function that automatically calculates the necessary dimensions to maintain the aspect ratio.
  10. Offload resizing tasks to Cloudinary for scalability
    For large-scale image processing, integrate Cloudinary into your workflow. Cloudinary handles image resizing, format conversion, and optimizations via URL-based transformations, freeing your Python code from dealing with manual image processing.

These tips will help you efficiently resize images in Python, ensuring better quality, performance, and scalability in your image-processing workflows.

Last updated: Nov 5, 2026
★★★★★
4.8 (29 reviews)
Talk to Sales Talk to Sales