Rotate an Image in Python with Pillow, OpenCV, and NumPy

Quick answer: To rotate an image in Python, choose a library based on the data flow: Pillow is straightforward for image files, OpenCV fits computer-vision pipelines, and NumPy handles right-angle array rotations. For arbitrary angles, set canvas expansion and interpolation deliberately so the output is not clipped or unexpectedly blurred.

Python Pool infographic showing Python image rotation Pillow OpenCV expand interpolation and clipping choices
Rotation choices affect canvas size, clipping, interpolation, color channels, and whether the output is an image object or an array.

To rotate an image in Python, choose the tool that matches the job. Use Pillow when you are opening and saving image files, OpenCV when the image is already part of a computer-vision pipeline, NumPy for fast 90-degree array rotations, and SciPy when you need an arbitrary-angle rotation on numeric arrays. The best method depends on whether you care about canvas size, interpolation, transparency, EXIF orientation, and output format.

This guide replaces the old screenshot-based approach with copyable examples. Pillow is the maintained library behind the old PIL name. If your import fails, see PythonPool’s guide to fixing No module named PIL before debugging the rotation code itself.

Rotate an image with Pillow

Pillow’s Image.rotate() method is the simplest option for common file-based work. Angles are measured counterclockwise in degrees. Use expand=True when you want the output canvas to grow enough to keep the entire rotated image instead of clipping the corners.

from PIL import Image

image = Image.open("input.jpg")
rotated = image.rotate(45, expand=True, resample=Image.Resampling.BICUBIC)
rotated.save("rotated-45.jpg")

If the background corners matter, pass fillcolor. This is useful when rotating icons, thumbnails, product images, or charts that should not have black corner triangles.

from PIL import Image

image = Image.open("input.png").convert("RGBA")
rotated = image.rotate(
    30,
    expand=True,
    resample=Image.Resampling.BICUBIC,
    fillcolor=(255, 255, 255, 0),
)
rotated.save("rotated-transparent.png")

Rotate 90, 180, or 270 degrees with Pillow

For right-angle turns, use transpose() instead of arbitrary-angle rotation. It is direct, avoids interpolation blur, and is easier to read. This works well for orientation fixes and simple batch processing.

from PIL import Image

image = Image.open("input.jpg")
rotated_90 = image.transpose(Image.Transpose.ROTATE_90)
rotated_180 = image.transpose(Image.Transpose.ROTATE_180)
rotated_270 = image.transpose(Image.Transpose.ROTATE_270)

rotated_90.save("rotated-90.jpg")

Many camera images also include EXIF orientation metadata. Before rotating manually, normalize that metadata with ImageOps.exif_transpose() so the pixel data matches what viewers display.

from PIL import Image, ImageOps

image = Image.open("camera-photo.jpg")
image = ImageOps.exif_transpose(image)
image.save("orientation-fixed.jpg")

Rotate an image with OpenCV

Use OpenCV when the image is already a NumPy array or when rotation is part of a larger computer-vision workflow. OpenCV uses getRotationMatrix2D() to build an affine transform and warpAffine() to apply it. The example below keeps the original canvas size, which means corners can be clipped after rotation.

import cv2

image = cv2.imread("input.jpg")
height, width = image.shape[:2]
center = (width / 2, height / 2)

matrix = cv2.getRotationMatrix2D(center, 45, 1.0)
rotated = cv2.warpAffine(image, matrix, (width, height))
cv2.imwrite("opencv-rotated.jpg", rotated)

If you display the result locally, PythonPool’s cv2.imshow guide explains common display issues. If Matplotlib reports image dtype errors after conversion, see the fix for image data of dtype object cannot be converted to float.

Python Pool infographic showing an image, Pillow rotate, angle, expand, and output
Pillow rotates an image and can expand the canvas to fit the transformed result.

Rotate without clipping in OpenCV

To keep the whole image after an arbitrary OpenCV rotation, compute the new bounding box and shift the transformation matrix. This takes more code than Pillow’s expand=True, but it is useful in computer-vision pipelines where staying in OpenCV arrays avoids extra conversions.

import cv2

image = cv2.imread("input.jpg")
height, width = image.shape[:2]
center = (width / 2, height / 2)
angle = 45

matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
cos = abs(matrix[0, 0])
sin = abs(matrix[0, 1])
new_width = int((height * sin) + (width * cos))
new_height = int((height * cos) + (width * sin))

matrix[0, 2] += (new_width / 2) - center[0]
matrix[1, 2] += (new_height / 2) - center[1]

rotated = cv2.warpAffine(image, matrix, (new_width, new_height))
cv2.imwrite("opencv-rotated-expanded.jpg", rotated)

Rotate an image array with NumPy

NumPy is best for exact 90-degree turns. The np.rot90() function rotates an array in 90-degree steps and works on image arrays without interpolation. This is useful for masks, labels, grids, and images that should not be blurred by resampling.

import numpy as np
from PIL import Image

image = Image.open("input.png")
array = np.asarray(image)
rotated_array = np.rot90(array, k=1)
rotated = Image.fromarray(rotated_array)
rotated.save("numpy-rot90.png")

When moving between Pillow and NumPy, keep array shape and dtype in mind. The PythonPool guide on converting PIL images to NumPy arrays covers that workflow in more detail. If your work involves angles and radians, the NumPy angle guide is a useful reference.

Python Pool infographic comparing image matrix, OpenCV transform, center, angle, and output
OpenCV uses matrix-based geometric transforms for image rotation workflows.

Rotate arrays with SciPy

SciPy’s ndimage.rotate() is useful when your image is already numeric data and you need an arbitrary angle. It can reshape the result to fit the rotated image, similar to Pillow’s expanded canvas behavior.

import numpy as np
from PIL import Image
from scipy import ndimage

image = Image.open("input.png")
array = np.asarray(image)
rotated_array = ndimage.rotate(array, 30, reshape=True)
rotated = Image.fromarray(rotated_array.astype(np.uint8))
rotated.save("scipy-rotated.png")

For command-line or server-side image transformations, PythonPool also has an ImageMagick in Python with Wand guide. That can be a better fit when you already use ImageMagick for resizing, conversion, or batch processing.

Which method should you use?

  • Pillow: best for opening, rotating, and saving normal image files.
  • Pillow transpose: best for 90, 180, and 270-degree turns without interpolation blur.
  • OpenCV: best when rotation is part of detection, tracking, or image-processing code.
  • NumPy rot90: best for exact quarter-turns on arrays, masks, and labels.
  • SciPy ndimage.rotate: best for arbitrary-angle numeric array rotation.

Common rotation mistakes

  • Clipped corners: use Pillow’s expand=True or calculate a larger OpenCV canvas.
  • Wrong direction: Pillow angles are counterclockwise; OpenCV uses the angle in its affine rotation matrix.
  • EXIF surprises: normalize camera photos with ImageOps.exif_transpose().
  • Blurred right-angle turns: use transpose or np.rot90() for exact 90-degree rotations.
  • Color confusion: OpenCV reads color images as BGR, while Pillow and Matplotlib commonly use RGB.
Python Pool infographic mapping an image through NumPy array axes, rotation, and pixels
Array rotation is useful for right-angle transforms when the image representation is already numeric.

Official references

Choose The Representation

Pillow returns Image objects and handles common file formats, metadata, and resampling. OpenCV commonly uses NumPy arrays with BGR channel order, while a raw NumPy array is best for direct array operations such as 90-degree rotations. Convert between representations at explicit boundaries.

Avoid Clipping

An arbitrary-angle rotation can require a larger canvas than the original rectangle. Pillow’s expand=True or an OpenCV rotation matrix with computed bounds can preserve the full image. Check the output dimensions and inspect transparent or background corners after the operation.

Python Pool infographic testing interpolation, crop, alpha, dimensions, and validation
Check crop behavior, interpolation, alpha channels, dimensions, orientation metadata, and output format.

Choose Interpolation And Fill

Nearest-neighbor preserves hard edges but can look jagged; bilinear or bicubic methods are smoother but change pixels. OpenCV also needs a border mode and fill value. Select these based on whether the image is a photo, mask, label map, or diagram.

Handle Right Angles Efficiently

For exact 90, 180, or 270-degree turns, use a library operation designed for those angles or np.rot90. It avoids unnecessary interpolation and makes the expected shape transformation easy to test.

Test Channels, Metadata, And Output

Test RGB, RGBA, grayscale, and array inputs as applicable. Verify width and height, channel order, alpha behavior, file format, and save/reload fidelity. A visually plausible result can still have swapped channels or lost transparency.

The official Pillow rotate reference documents angle, resampling, expand, center, and fill behavior. Related references include OpenCV geometric transforms, numpy.rot90, and image tests.

For related image pipelines, compare Pillow-to-NumPy conversion, array conversion, and image tests when preserving channels and shape.

Frequently Asked Questions

What is the easiest way to rotate an image in Python?

Pillow’s Image.rotate() is a practical choice for common image workflows; use expand=True when the rotated image should not be cropped.

Why is my rotated image clipped?

The original canvas may be too small for the rotated corners. Expand the canvas or calculate the target bounds before saving.

Should I use Pillow or OpenCV?

Use Pillow for straightforward image-file workflows and OpenCV when the project already depends on computer-vision operations and array-based processing.

How do I rotate a NumPy image array?

Use a shape-aware operation such as np.rot90 for right-angle rotations or an image library for arbitrary angles and interpolation.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted