from PIL import Image, ImageDraw
# Create a new image with RGB mode
image = Image.new("RGB", (200, 200), "blue")
# Draw a red rectangle on the image
draw = ImageDraw.Draw(image)
draw.rectangle([(50, 50), (150, 150)], fill="red")
# Show the image
imagePillow opens, edits and saves images from Python. It reads and writes
PNG, JPEG, GIF, WebP and other formats, and it can resize, crop, rotate,
filter and draw on pictures. People use it to make thumbnails, convert
files and put text on images. It is a fork of the older PIL library,
which is why you import it as PIL. This page is an online Pillow compiler: the code runs in your browser,
so you can try it without installing anything.
Run the example first, then paste any snippet below into a new cell to try it.
Image.new("RGB", (200, 200), "blue") creates a 200 by 200 pixel image
filled with blue. Sizes are always (width, height), and a colour can be a
name, a hex string such as "#ff8800" or an RGB tuple.
ImageDraw.Draw(image) returns an object that draws directly onto that
image. draw.rectangle() fills the area between two corners. Coordinates
start at the top-left corner, y grows downwards, and both corners are
included, so the red square is 101 pixels wide. Ending the cell with
image makes the notebook show it.
ImageDraw also draws ellipses, polygons, lines and text, and
ImageFont.load_default(size=36) gives a font at any size without a font
file. This snippet saves card.png for the next snippets:
from PIL import Image, ImageDraw, ImageFont
card = Image.new("RGB", (400, 200), "white")
draw = ImageDraw.Draw(card)
draw.ellipse([(20, 20), (180, 180)], fill="gold", outline="black", width=4)
draw.polygon([(60, 130), (100, 60), (140, 130)], fill="tomato")
draw.line([(200, 140), (380, 140)], fill="gray", width=3)
font = ImageFont.load_default(size=36)
draw.text((200, 80), "Hello", font=font, fill="black")
card.save("card.png")
card
The snippets below open card.png, so run this one first.
crop() takes a box as (left, top, right, bottom). rotate() turns the
image counterclockwise and keeps its original size, cutting off the
corners, unless you pass expand=True. These methods return a new image
and leave the original alone. thumbnail() is the exception: it shrinks
the image in place to fit a box and keeps its proportions.
from PIL import Image
card = Image.open("card.png")
print(card.format, card.size, card.mode)
small = card.resize((200, 100))
left = card.crop((0, 0, 200, 200))
turned = card.rotate(30, expand=True, fillcolor="white")
print(small.size, left.size, turned.size)
card.thumbnail((100, 100))
print(card.size)
turned
To work on your own photo, click Mount folder in the sidebar's Data
tab (Chrome or Edge on a computer), choose the folder it is in, and open
it by name, as in Image.open("photo.jpg"). Add files does not take
images.
ImageOps, ImageFilter and ImageEnhance hold the common edits.
This makes four versions of the card and pastes them onto one sheet:
from PIL import Image, ImageEnhance, ImageFilter, ImageOps
card = Image.open("card.png")
versions = [
ImageOps.grayscale(card),
card.filter(ImageFilter.GaussianBlur(4)),
ImageEnhance.Contrast(card).enhance(0.4),
ImageOps.mirror(card),
]
sheet = Image.new("RGB", (400, 200), "white")
for i, version in enumerate(versions):
sheet.paste(version.resize((200, 100)), ((i % 2) * 200, (i // 2) * 100))
sheet
save() picks the format from the file extension. JPEG and WebP take a
quality setting: lower values give smaller files with less detail.
import os
from PIL import Image
card = Image.open("card.png")
card.save("card.jpg", quality=85)
card.save("card.webp", quality=80)
for name in ["card.png", "card.jpg", "card.webp"]:
with Image.open(name) as im:
print(name, im.format, im.size, os.path.getsize(name), "bytes")
For a flat graphic like the card, the PNG is still the smallest of the three.
OSError: cannot write mode RGBA as JPEG, so call
image.convert("RGB") first.image.size is (width, height), but np.array(image).shape is
(height, width, 3), because NumPy arrays list rows
first. Image.fromarray() turns an array back into an image.ImageFont.truetype("arial.ttf")
raises OSError. Use load_default(size=...), or put a .ttf file in a
folder you mount and pass its name to truetype().