Skip to main content

python-images

Use for exact text over pictures (posters, titles, captions), grids and contact sheets, picture-book pages, palettes, masks and measurements, or procedural SVG drawings made in wangp_python.

Source facts

Repository
deepbeepmeep/WanGP
Last source activity
November 1, 2026 at 01:03
Detected SKILL.md language
English
Stars
11,407
Forks
1,847

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
python-images
description
Use for exact text over pictures (posters, titles, captions), grids and contact sheets, picture-book pages, palettes, masks and measurements, or procedural SVG drawings made in wangp_python.
# Images and text in Python Models paint the picture; Python places exact text and measured layouts over it. Generate or pick the key art first, then compose. ## Text over a picture ```python # Poster title over the key art from PIL import Image, ImageDraw, ImageFilter img = Image.open(wangp.path("visual:IMAGE")).convert("RGB") W, H = img.size title = "BLUE HOUR JAZZ" font = wangp.font(int(H * 0.11), "display", bold=True) box = ImageDraw.Draw(img).textbbox((0, 0), title, font=font) x, y = (W - (box[2] - box[0])) // 2, int(H * 0.72) shadow = Image.new("RGBA", img.size, (0, 0, 0, 0)) ImageDraw.Draw(shadow).text((x + 6, y + 6), title, font=font, fill=(0, 0, 0, 180)) img = Image.alpha_composite(img.convert("RGBA"), shadow.filter(ImageFilter.GaussianBlur(8))) ImageDraw.Draw(img).text((x, y), title, font=font, fill=(255, 240, 200, 255)) img.convert("RGB").save("out/poster.png") (W, H, x, y) ``` - Fonts only through `wangp.font(size, family, bold, italic)`: families `sans`, `serif`, `mono`, `condensed`, `display`. Never guess font files. - Text inside any box (speech bubble, badge, button, tile, panel): measure it centred, `l, t, r, b = draw.textbbox((0, 0), text, font=font, anchor="mm")`, size the box as `(r - l) + 2 * pad` by `(b - t) + 2 * pad`, then draw the text at the box centre with `anchor="mm"`. Without the anchor, PIL places the top-left of the line box, so the glyphs sit low and touch the bottom edge. - Measure where the text really is: `box = draw.textbbox((x, y), text, font=font)` at the drawing position gives the glyphs' true top and bottom (a box measured at `(0, 0)` starts below 0, so `y + height` lands inside tall letters). Place the next line, rule or panel from `box[3]` plus a gap. `anchor="mm"` centres a line on a point; `textwrap.wrap(text, width=n)` with `draw.multiline_text(..., spacing=...)` for paragraphs. - Margins as fractions of the width (0.06 to 0.1); one display face for titles, one sans for the rest; a translucent panel (`Image.new("RGBA", (w, h), (0, 0, 0, 140))`) behind small text over busy pictures; check contrast with `ImageStat.Stat(crop).mean`. - Then `inspect_media` on the result with pointed questions: "Does any line, shape or shadow cross or touch the letters?", "Read the text exactly", "Is any text cut off?". A general "does it look right" misses a rule drawn through a word. ## Diagrams and infographics Several labels beside drawn shapes (clouds, arrows, a mountain) collide easily, and a visual check of the whole picture often misses light text over a shape. Lay out in code: - Keep every placed element as a box `(x0, y0, x1, y1)`: text boxes from `draw.textbbox((x, y), ...)` at their real position, shapes from the coordinates you drew them with. - Before drawing, test each text box against every shape box (`a[0] < b[2] and b[0] < a[2] and a[1] < b[3] and b[1] < a[3]`); move the text, shrink the shape, or put the text on a panel until nothing intersects. Print the collisions you found and fixed. - Text inside a circle or badge: `draw.text((cx, cy), "3", font=f, anchor="mm")` centres it; without `anchor` the glyph hangs below and to the right of the point. - Inspect crops of the dense regions (`bbox` in `inspect_media`) as well as the whole picture. ## Grids, sheets and pages Open each image, `ImageOps.fit(img, (cell_w, cell_h))`, paste on a canvas with gutters, caption under each cell; round corners by pasting through an `L` mask drawn with `rounded_rectangle`. A picture-book page: picture in the upper two thirds, wrapped text in the lower third on a paper-coloured background. ## Palettes, masks, measurements - Dominant colours: `small = img.convert("RGB").resize((64, 64)); pal = small.quantize(6).getpalette()[:18]`, swatches with hex labels (`"#%02x%02x%02x"`). - To change one object (recolour a product, blur a background, cut out a subject), ask WanGP for its mask first: `m = wangp.toolbox("create_mask", media_id=image, keywords=["the sneaker"])` gives a mask image (its `media_id` in the result), far cleaner than thresholds or OpenCV contours. Then `mask = Image.open(wangp.path(m["media_id"])).convert("L")` and work only inside it: hue shift in HSV with numpy, `Image.composite(edited, original, mask)`. - Masks from `image_channels` are images too; the centroid of a mask tells where the subject sits. - Visual duplicates: a perceptual hash (8x8 or 16x16 grey thumbnail, bits above the mean) groups resized copies; confirm close pairs with a pixel comparison at one size. ## Procedural SVG Build markup in Python when a drawing has many regular elements (rays, grids, stars, waves, gradients, diagrams), then `wangp.svg(markup, W, H)` gives an RGBA image to composite or save. For an animated vector card without Python, `draw_svg` with `duration_s` is the direct tool.
View on GitHub