Almost everything in Pillow starts with an Image object. This page covers the functions that make one, the attributes worth knowing, and the methods you will actually reach for, as they behave today.
Opens an image file and returns an Image object. It reads the header and stops, so the size and mode are known straight away but the pixel data is not loaded until something needs it. That laziness matters: if you open from a file object, do not close that object before you touch the data.
from PIL import Image
im = Image.open("photo.jpg")
print(im.format, im.size, im.mode)
Creates a blank image. The colour can be a number, a tuple matching the mode, or a name that ImageColor understands.
blank = Image.new("RGB", (400, 300), "white")
clear = Image.new("RGBA", (400, 300), (0, 0, 0, 0))
Builds an image from raw pixel bytes. This is the way in from numpy, from a camera buffer or from another library. It was called fromstring in PIL.
Puts single band images back together. The usual partner to split().
| Attribute | What it gives you |
|---|---|
im.size | A (width, height) tuple in pixels |
im.width, im.height | The same two numbers separately |
im.mode | The pixel format, such as RGB, L, RGBA, P, 1, CMYK, I, F |
im.format | The file format it was read from, or None for images you made yourself |
im.info | A dictionary of format specific extras, such as DPI or transparency |
The mode viewer shows what those modes do to a picture and what each costs in memory.
Returns a copy in another mode. Going to L gives you greyscale by the usual luminance weighting. Going to 1 gives black and white, with Floyd and Steinberg dithering unless you turn it off. Going to P reduces to a palette of at most 256 colours.
grey = im.convert("L")
flat = im.convert("1", dither=Image.Dither.NONE)
Returns a new image at the size you ask for, ignoring the original aspect ratio. Which filter you pass matters more than people expect, and the comparison tool shows the difference on your own image.
small = im.resize((320, 240), Image.Resampling.LANCZOS)
Image.ANTIALIAS appears. It was removed in Pillow 10 and always meant Lanczos.Shrinks the image in place, keeping the aspect ratio, so the result fits inside the box you give. It never enlarges. Note that it changes the image rather than returning a new one, which is the opposite of resize and a common source of confusion.
Takes a (left, upper, right, lower) box and returns that rectangle. The origin is the top left corner, and the right and lower edges are not included.
Pastes another image in place. Pass the same image as the mask to respect its alpha channel, which is the trick behind most compositing questions.
base.paste(badge, (10, 10), badge)
Flips and right angle rotations, using the Image.Transpose enum. These are exact and cheap, unlike rotate with an arbitrary angle.
Rotates counter clockwise about the centre. Without expand=True the corners are cut off, which surprises people every time.
split() gives you a tuple of single band images, one per channel. getbands() tells you what those channels are called.
Maps every pixel through a function or lookup table. It is the fast way to do levels, thresholds and curves without a Python loop over pixels.
mask = grey.point(lambda v: 255 if v > 128 else 0)
Applies a filter from ImageFilter, such as GaussianBlur, UnsharpMask, MedianFilter or one of the fixed kernels.
Writes the image out. The format is taken from the file name unless you say otherwise, and the extra options are format specific: quality and optimize for JPEG, compress_level for PNG, save_all and append_images for animated GIF.
im.save("out.jpg", quality=88, optimize=True)
RGBA image as JPEG raises an error, because JPEG has no alpha channel. Convert to RGB first.Returns the Exif data as a dictionary. For photographs from phones and cameras, ImageOps.exif_transpose(im) is usually what you actually want, since it applies the orientation tag so the picture is the way up the photographer saw it.
from PIL import Image, ImageOps, ImageFilter
im = Image.open("photo.jpg")
im = ImageOps.exif_transpose(im)
im = im.convert("RGB")
im.thumbnail((1200, 1200), Image.Resampling.LANCZOS)
im = im.filter(ImageFilter.UnsharpMask(radius=1.2, percent=110))
im.save("web.jpg", quality=85, optimize=True)
That handles the four things that go wrong with photographs on the web, in order: sideways pictures, stray alpha or CMYK, images far larger than needed, and the softness that shrinking introduces.