Django's ImageField depends on Pillow for image validation. For more advanced tasks like generating thumbnails or watermarking on upload, use Django signals or a dedicated library like django-imagekit.
Django: Process Upload
# models.pyfrom django.db import models
class Photo(models.Model):
image = models.ImageField(upload_to="photos/")
# signals.py — auto-generate thumbnail on uploadfrom django.db.models.signals import post_save
from PIL import Image
import os
defcreate_thumbnail(sender, instance, **kwargs):
if instance.image:
with Image.open(instance.image.path) as img:
img.thumbnail((300, 300), Image.Resampling.LANCZOS)
thumb_path = instance.image.path.replace("/photos/", "/thumbs/")
img.save(thumb_path)
post_save.connect(create_thumbnail, sender=Photo)
FastAPI
FastAPI's file upload handling works seamlessly with Pillow via UploadFile → BytesIO → Image.open().
Pillow and NumPy arrays can be converted freely. This allows you to apply arbitrary mathematical operations on pixel data and then render back to an image.
PyTorch uses Pillow as its default image loader. torchvision.transforms natively operates directly on PIL Images for data augmentation and neural network preprocessing pipelines.
Tesseract OCR (via pytesseract): the OCR engine that extracts text and per-word bounding boxes from a Pillow image. To try OCR on a sample image without installing anything, there is a browser demo at tesseractocr.org (a sister site).
Pillow: High-fidelity TrueType text overlays, bounding box annotations (ImageDraw), and final multi-format export.
Pillow + OpenCV + Tesseract OCR Pipeline
import cv2
import numpy as np
import pytesseract
from PIL import Image, ImageDraw, ImageFont
# Step 1: Pre-process with OpenCV (Grayscale + Adaptive Otsu Thresholding)
cv_img = cv2.imread("receipt.png")
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# Step 2: Extract text and bounding boxes with PyTesseract
ocr_data = pytesseract.image_to_data(thresh, output_type=pytesseract.Output.DICT)
# Step 3: Convert OpenCV array back to Pillow Image for annotation
pil_img = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_img)
for i in range(len(ocr_data["text"])):
confidence = int(ocr_data["conf"][i])
text = ocr_data["text"][i].strip()
if confidence > 50and text:
x, y, w, h = ocr_data["left"][i], ocr_data["top"][i], ocr_data["width"][i], ocr_data["height"][i]
# Draw bounding box
draw.rectangle([x, y, x + w, y + h], outline="lime", width=2)
# Overlay detected label
draw.text((x, max(0, y - 14)), text, fill="lime")
pil_img.save("ocr_annotated.png")
Tkinter
Tkinter's native PhotoImage only supports GIF and PGM. Use ImageTk.PhotoImage from Pillow to display any image format in a Tkinter window.