بنقرة واحدة
photo-editor
Edit, resize, crop, filter, and optimize images using code-based image processing
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Edit, resize, crop, filter, and optimize images using code-based image processing
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Design static ad creatives for social media and display advertising campaigns.
Source and evaluate candidates with job analysis, search strategies, specific candidate profiles, and outreach templates.
Draft emails, manage calendar scheduling, prepare meeting agendas, and organize productivity
Create brand identity kits with color palettes, typography, logo concepts, and brand guidelines.
Perform competitive market analysis with feature comparisons, positioning, and strategic recommendations.
Create social media posts, newsletters, and marketing content calibrated to your voice and platform.
| name | photo-editor |
| description | 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)
try:
font = ImageFont.truetype("DejaVuSans-Bold.ttf", 48) # Linux default
except OSError:
font = ImageFont.load_default() # fallback (tiny, ugly)
# --- Text with outline ---
draw.text((50, 50), "Caption", font=font, fill="white",
stroke_width=3, stroke_fill="black")
# --- Centered text ---
bbox = draw.textbbox((0, 0), "Centered", font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
draw.text(((img.width - tw) // 2, (img.height - th) // 2), "Centered", font=font, fill="white")
# --- Watermark (semi-transparent PNG overlay) ---
logo = Image.open("logo.png").convert("RGBA")
logo.thumbnail((img.width // 5, img.height // 5))
# Fade to 40% opacity
alpha = logo.split()[3].point(lambda p: int(p * 0.4))
logo.putalpha(alpha)
pos = (img.width - logo.width - 20, img.height - logo.height - 20)
img.paste(logo, pos, logo) # third arg = alpha mask — REQUIRED for transparency
# --- JPEG ---
img.convert("RGB").save("out.jpg", quality=85, optimize=True, progressive=True)
# convert("RGB") REQUIRED if source has alpha — JPEG can't store transparency
# --- PNG (lossless — quality param does nothing) ---
img.save("out.png", optimize=True, compress_level=9)
# --- WebP (best web format: ~30% smaller than JPEG at same quality) ---
img.save("out.webp", quality=85, method=6) # method 0-6, 6=slowest/best compression
# --- AVIF (smallest files, Pillow 11+, slower encode) ---
img.save("out.avif", quality=75) # 75 ≈ JPEG 85 visually, ~50% smaller
# --- Strip all metadata (privacy) ---
clean = Image.new(img.mode, img.size)
clean.putdata(list(img.getdata()))
clean.save("stripped.jpg", quality=85)
Quality guide: JPEG/WebP 85 = sweet spot. 90+ = diminishing returns. <70 = visible artifacts. Never re-save JPEGs repeatedly — each save degrades (generation loss).
from pathlib import Path
from PIL import Image, ImageOps
out = Path("optimized"); out.mkdir(exist_ok=True)
for p in Path("photos").glob("*.[jJ][pP]*[gG]"): # matches jpg, jpeg, JPG, JPEG
img = ImageOps.exif_transpose(Image.open(p))
img.thumbnail((1920, 1920), Image.Resampling.LANCZOS)
img.convert("RGB").save(out / f"{p.stem}.webp", quality=85, method=6)
const sharp = require('sharp');
// Resize + convert + optimize, streaming (flat memory)
await sharp('in.jpg')
.rotate() // auto-rotate from EXIF (like exif_transpose)
.resize(1080, 1080, { fit: 'cover', position: 'center' }) // = ImageOps.fit
.webp({ quality: 85 })
.toFile('out.webp');
// fit options: 'cover' (crop), 'contain' (letterbox), 'inside' (shrink to fit), 'fill' (stretch)
// Composite watermark
await sharp('photo.jpg')
.composite([{ input: 'logo.png', gravity: 'southeast' }])
.toFile('watermarked.jpg');
sharp strips all metadata by default. Use .withMetadata() to preserve EXIF/ICC.
import cv2
img = cv2.imread("in.jpg") # BGR order, not RGB!
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Face detection
cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imwrite("out.jpg", img)
# Pillow ↔ OpenCV
import numpy as np
cv_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
pil_img = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
| Platform | Size | Ratio |
|---|---|---|
| Instagram post | 1080×1080 | 1:1 |
| Instagram story / TikTok | 1080×1920 | 9:16 |
| Twitter/X | 1200×675 | 16:9 |
| YouTube thumbnail | 1280×720 | 16:9 |
| Open Graph (link preview) | 1200×630 | 1.91:1 |
img.crop() box is (left, top, right, bottom) — absolute coords, NOT (x, y, width, height)thumbnail() mutates in place and returns None — don't do img = img.thumbnail(...)bg.paste(fg, pos, fg)img.convert("RGB") firstImageFont.truetype needs a real font file. Linux: /usr/share/fonts/truetype/dejavu/. Ship a .ttf with your code for portability.