Edit, resize, crop, filter, and optimize images using code-based image processing
Resize, crop, filter, and optimize images. Pillow for Python, sharp for Node. Clarify intent before starting.
When a user asks to "edit a photo" or "change an image," the request could mean two very different things. Ask before proceeding if it's ambiguous:
When to ask:
Don't ask when it's obvious:
| Tool | Use when | Install |
|---|---|---|
| Pillow | Default: resize, crop, filters, text, format conversion | pip install Pillow |
| OpenCV | Computer vision: face detection, perspective transform, contours | pip install opencv-python |
| sharp (Node) | High-volume pipelines — 4-5x faster than Pillow (libvips-backed) | npm install sharp |
| rembg | AI background removal | pip install rembg |
| ImageMagick | CLI batch ops, 200+ formats | apt install imagemagick |
from PIL import Image, ImageOps
img = Image.open("photo.jpg")
img = ImageOps.exif_transpose(img) # CRITICAL: applies EXIF rotation, then strips tag
# Without this, phone photos appear sideways after processing
from PIL import Image, ImageOps
# --- Fit inside box, keep aspect ratio (shrink only) ---
img.thumbnail((1080, 1080), Image.Resampling.LANCZOS) # modifies in place
# --- Exact size, keep aspect, center-crop overflow (best for thumbnails) ---
thumb = ImageOps.fit(img, (300, 300), Image.Resampling.LANCZOS, centering=(0.5, 0.5))
# --- Exact size, keep aspect, pad with color (letterbox) ---
padded = ImageOps.pad(img, (1920, 1080), color=(0, 0, 0))
# --- Exact size, ignore aspect (will distort) ---
stretched = img.resize((800, 600), Image.Resampling.LANCZOS)
# --- Scale by factor ---
half = img.resize((img.width // 2, img.height // 2), Image.Resampling.LANCZOS)
# --- Manual crop (left, upper, right, lower) — NOT (x, y, w, h) ---
cropped = img.crop((100, 50, 900, 650))
Resampling filters: LANCZOS for photo downscale (best quality), BICUBIC for upscale, NEAREST for pixel art/icons (no smoothing).
from PIL import ImageEnhance, ImageOps
# --- Enhancers: 1.0 = unchanged, <1 less, >1 more ---
img = ImageEnhance.Brightness(img).enhance(1.15)
img = ImageEnhance.Contrast(img).enhance(1.2)
img = ImageEnhance.Color(img).enhance(1.1) # saturation
img = ImageEnhance.Sharpness(img).enhance(1.5)
# --- Quick ops ---
gray = ImageOps.grayscale(img)
inverted = ImageOps.invert(img.convert("RGB"))
auto = ImageOps.autocontrast(img, cutoff=1) # stretch histogram, clip 1% extremes
equalized = ImageOps.equalize(img) # flatten histogram
from PIL import ImageFilter
img.filter(ImageFilter.GaussianBlur(radius=5))
img.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)) # better than SHARPEN
img.filter(ImageFilter.BoxBlur(10))
img.filter(ImageFilter.FIND_EDGES)
img.filter(ImageFilter.MedianFilter(size=3)) # denoise, removes salt-and-pepper
from PIL import Image, ImageDraw, ImageFont
draw = ImageDraw.Draw(img)