VMware ESXi and vSphere Cluster Management
How to Rotate and Flip Images with Pillow
Learn how to rotate images by arbitrary or right angles and flip them horizontally or vertically with Python Pillow.
Pillow is a Python imaging library that provides an Image object and methods for changing images. In this lesson, you will rotate images by arbitrary angles, perform exact quarter-turn rotations, and flip images across horizontal or vertical axes.
You should already know basic Python variables, method calls, file paths, and installing packages with pip.
Install Pillow and open an image
Install Pillow with:
pip install Pillow
Import the Image module from Pillow, then use Image.open() to load a file. The returned image should be stored in a source variable. Transformation methods return new image objects, so keeping the source separate makes it easy to create several results from the same original.
from PIL import Image
image = Image.open("input.jpg")
Image.open() loads an image from a file path. Pillow commonly supports JPEG, PNG, GIF, WebP, and other formats. The file is opened when needed, so keep the source file available until you finish loading or processing it.
Rotate an image by an arbitrary angle
Image.rotate() creates a rotated copy of an image. Its angle argument is measured in degrees. Positive angles rotate counter-clockwise; use a negative angle for clockwise rotation.
from PIL import Image
image = Image.open("input.jpg")
rotated = image.rotate(45)
rotated.show()
In this example, rotated receives the returned image. The original image remains unchanged. The show() method opens a temporary local preview using an image viewer available on your system.
Rotate without cropping
By default, rotate() keeps the original canvas dimensions. When an image is rotated by an angle such as 45 degrees, its corners can extend beyond that canvas. Those parts are clipped.
Pass expand=True to enlarge the output canvas enough to contain the full rotated image:
from PIL import Image
image = Image.open("input.jpg")
rotated = image.rotate(45, expand=True)
rotated.save("rotated-expanded.jpg")
The newly exposed areas of the expanded canvas are filled with a background color. For many image modes, this defaults to black or another mode-dependent fill. If you need a particular background, Pillow also supports fill-color options in versions that provide them; check the installed Pillow version when exact fill behavior matters.
Rotate in 90-degree increments
For exact quarter-turn operations, use Image.transpose(). This method applies fixed orientation transformations. The constants Image.ROTATE_90, Image.ROTATE_180, and Image.ROTATE_270 represent rotations of 90, 180, and 270 degrees.
from PIL import Image
image = Image.open("input.jpg")
rotated_90 = image.transpose(Image.ROTATE_90)
rotated_180 = image.transpose(Image.ROTATE_180)
rotated_270 = image.transpose(Image.ROTATE_270)
rotated_90.show()
rotated_180.show()
rotated_270.show()
These operations are useful when an image must be turned by an exact right angle. A 90-degree or 270-degree result swaps the image's width and height. A 180-degree result keeps the same dimensions.
Flip or mirror an image
A flip reflects an image rather than turning it around by an angle. Pillow also performs flips with Image.transpose().
Image.FLIP_LEFT_RIGHT mirrors across the vertical axis, the imaginary line running from top to bottom. It swaps the left and right sides.
from PIL import Image
image = Image.open("input.jpg")
mirrored = image.transpose(Image.FLIP_LEFT_RIGHT)
mirrored.save("mirrored.jpg")
Image.FLIP_TOP_BOTTOM flips across the horizontal axis, the imaginary line running from left to right. It swaps the top and bottom sides.
flipped = image.transpose(Image.FLIP_TOP_BOTTOM)
flipped.show()
For example, text in a left-to-right mirror appears backward from left to right. A top-to-bottom flip places the top of the image at the bottom. Neither operation is the same as rotating by 90, 180, or 270 degrees.
Pillow rotation and flip operations
| Goal | Pillow method | Argument or constant | Result |
|---|---|---|---|
| Arbitrary-angle rotation | rotate() | An angle in degrees | Rotates counter-clockwise for positive angles |
| 90-degree rotation | transpose() | Image.ROTATE_90 | Turns the image by one quarter-turn |
| 180-degree rotation | transpose() | Image.ROTATE_180 | Turns the image upside down |
| 270-degree rotation | transpose() | Image.ROTATE_270 | Turns the image by three quarter-turns |
| Left-to-right mirror | transpose() | Image.FLIP_LEFT_RIGHT | Swaps the left and right sides across the vertical axis |
| Top-to-bottom flip | transpose() | Image.FLIP_TOP_BOTTOM | Swaps the top and bottom across the horizontal axis |
Display and save transformed images
Use show() for a quick local preview:
rotated.show()
A preview is not a durable output file. Use save() when you need to keep the result:
rotated.save("rotated.jpg")
mirrored.save("mirrored.png")
Choose an output extension that matches the intended format, such as .jpg or .png. You can also explicitly provide a format when the filename does not identify one:
rotated.save("rotated-output", format="PNG")
Save to a new filename unless replacing the source is intentional. Keeping the original file separate allows you to retry a transformation and compare the result.
Rotation behavior comparison
| Approach | Best use case | Canvas behavior | Potential issue |
|---|---|---|---|
rotate(angle) | Any angle when the original canvas is acceptable | Retains the original dimensions | Content can be cropped at the corners |
rotate(angle, expand=True) | Arbitrary angles when the entire image must remain visible | Expands to fit the rotated image | Uncovered areas receive a background fill |
transpose(rotation constant) | Exact 90-degree, 180-degree, or 270-degree turns | Uses fixed orientation geometry | It is not intended for arbitrary angles |
Complete examples
Rotate 45 degrees counter-clockwise
from PIL import Image
source = Image.open("input.jpg")
rotated_45 = source.rotate(45)
rotated_45.show()
Rotate 45 degrees and preserve the full image
from PIL import Image
source = Image.open("input.jpg")
rotated_45 = source.rotate(45, expand=True)
rotated_45.save("input-rotated-45.png")
Create 90, 180, and 270-degree outputs
from PIL import Image
source = Image.open("input.jpg")
quarter_turn = source.transpose(Image.ROTATE_90)
half_turn = source.transpose(Image.ROTATE_180)
three_quarter_turn = source.transpose(Image.ROTATE_270)
quarter_turn.save("input-90.jpg")
half_turn.save("input-180.jpg")
three_quarter_turn.save("input-270.jpg")
Mirror and flip
from PIL import Image
source = Image.open("input.jpg")
horizontal_mirror = source.transpose(Image.FLIP_LEFT_RIGHT)
vertical_flip = source.transpose(Image.FLIP_TOP_BOTTOM)
horizontal_mirror.save("input-mirror.jpg")
vertical_flip.save("input-flip.jpg")
Troubleshooting
Parts disappear after a 45-degree rotation
The rotated result is larger than the original canvas, so its corners were clipped. Use source.rotate(45, expand=True).
The rotation goes in the wrong direction
Pillow treats positive angles as counter-clockwise. Use a negative angle, such as source.rotate(-45), for clockwise rotation.
The result is mirrored instead of rotated
A flip constant was probably used. Use Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270 for fixed right-angle rotations.
The output does not appear where expected
show() only previews the image and does not save it. Call save() with an explicit destination filename.
Saving raises a format-related error
Check that the destination has a recognized extension or pass an explicit format. Also check the image mode: some formats do not support every mode, so you may need to convert the image before saving.
Summary
- Use
Image.open()to load an image and keep the source in its own variable. - Use
rotate(angle)for arbitrary angles; positive angles rotate counter-clockwise. - Use
expand=Truewhen an arbitrary rotation must retain the full image. - Use
transpose()with rotation constants for exact quarter-turns. - Use
Image.FLIP_LEFT_RIGHTto mirror left to right andImage.FLIP_TOP_BOTTOMto flip top to bottom. - Use
show()for previews andsave()for persistent output.
For related Pillow image operations, continue with image rotation and transformation techniques.