svg-portrait-mode/SKILL.md
--- name: svg-portrait-mode description: "Portrait Mode" for SVGs — foveated vectorization with 4-zone selective detail. Combines Claude vision annotations, MediaPipe segmentation/landmarks, and optional saliency. Like phone portrait mode, but vectorized. metadata: version: 0.6.1 --- # SVG Portrait Mode Selective simplification: one pipeline pass at high K → zone-aware contour simplification → optional per-zone style transforms. Like phone portrait mode, but vectorized — not blur, but stylis
npx skillsauth add oaustegard/claude-skills svg-portrait-modeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Selective simplification: one pipeline pass at high K → zone-aware contour simplification → optional per-zone style transforms. Like phone portrait mode, but vectorized — not blur, but stylistic separation of foreground and background.
The agent looks at the image first, identifies important regions with rough bounding boxes, then calls:
from portrait_mode import portrait_mode
svg, stats = portrait_mode("photo.jpg",
focus_targets=[
{'bbox': (215, 125, 295, 195), 'label': 'face'},
],
focus_edges=[
{'bbox': (214, 170, 310, 290), 'label': 'beard'},
{'bbox': (210, 415, 300, 505), 'label': 'hands'},
{'bbox': (195, 95, 330, 140), 'label': 'hat'},
])
svg, stats = portrait_mode("photo.jpg",
focus_targets=[{'bbox': (215, 125, 295, 195), 'label': 'face'}],
focus_edges=[{'bbox': (214, 170, 310, 290), 'label': 'beard'}],
style_transforms={
'background': 'desaturate:0.7',
'periphery': 'desaturate:0.3',
})
Without annotations, falls back to MediaPipe face detection:
svg, stats = portrait_mode("photo.jpg")
image-to-svg pipeline (K=96, unified palette)
→ zone detection (agent bboxes + optional MediaPipe landmarks)
→ assign contours to zones (by centroid)
→ per-zone simplification (epsilon, min_area)
→ optional per-zone style transforms
→ single SVG output (zone-labeled <g> groups, no clipPaths)
Subtractive, not additive. One pipeline pass produces full detail everywhere. Zones that don't need detail get simplified by coarsening contour approximation and raising minimum area thresholds. Subject stays sharp; background gets abstract.
v0.5.0 ran four independent pipelines (one per zone) with different K-means palettes, then composited via clipPaths. This produced tonal discontinuities at zone boundaries and was 4-7x slower. v0.6.0 uses a single palette so colors harmonize naturally, and simplification is a cheap post-process on contours the pipeline already extracted.
(x1, y1, x2, y2) pixel coordinates
focus_targets for where the eye goes first (face, eyes)focus_edges for compositionally important areas (beard, hands, hat, props)portrait_mode() — skill handles zone detection, extraction, and assembly| Zone | Purpose | Epsilon | Min Area | Examples | |------|---------|---------|----------|----------| | Target | Where the eye goes first | 0.5× (tight) | 15 px² | Face, eyes, key subject | | Edge | Compositionally important | 1.0× (default) | 40 px² | Beard, hands, hat, props | | Periphery | Context, not focal | 2.5× (loose) | 100 px² | Torso, clothing, limbs | | Background | Atmosphere | 5.0× (very loose) | 200 px² | Sky, walls, landscape |
Epsilon multiplies the base simplification factor (0.002 × perimeter). Higher = fewer vertices = more abstract. Min area filters out small shapes entirely.
Each contour is assigned to the highest-priority zone covering >30% of its area. This prevents focal shapes that straddle a zone boundary from getting simplified. Small contours (<500 px²) use centroid lookup for speed.
When focus targets or edges are specified, periphery is automatically generated as a buffer zone around the foreground (dilated union of target + edge zones). This creates a smooth detail gradient from subject to background.
With zone-tagged shapes sharing a unified palette, backgrounds can be independently styled without affecting subject colors:
style_transforms={
'background': 'desaturate:0.7', # 70% desaturated
'periphery': 'mute:0.3', # 30% toward mid-gray
}
Available transforms:
| Transform | Effect |
|-----------|--------|
| desaturate:N | Shift toward gray (0=none, 1=grayscale) |
| grayscale | Full grayscale |
| mute:N | Shift toward mid-gray (0=none, 1=flat gray) |
| warm:N | Warmer color temperature |
| cool:N | Cooler color temperature |
| opacity:N | Group opacity (0=invisible, 1=full) |
portrait_mode(image_path,
# Zone annotations
focus_targets=None, # [{'bbox': (x1,y1,x2,y2), 'label': str}, ...]
focus_edges=None, # [{'bbox': (x1,y1,x2,y2), 'label': str}, ...]
# Pipeline settings
K=96, # Color clusters (higher = more tonal detail)
smooth=None, # ImageMagick preprocessing ("oilpaint", "kuwahara:N")
svg_width=800,
# MediaPipe options
use_landmarks=True, # Try MP face landmarks for precise face geometry
# Per-zone simplification overrides
zone_simplification=None, # {ZONE_TARGET: {'epsilon_mult': 0.3, 'min_area': 10}}
# Per-zone style transforms
style_transforms=None, # {'background': 'desaturate:0.7'}
)
Single pipeline pass (~8-12s for a typical photo at K=96) vs v0.5.0's four independent passes (~40-60s). Zone detection and contour assignment add <1s. Style transforms are string operations with zero computational cost.
Cross-skill dependencies:
image-to-svg pipeline (/mnt/skills/user/image-to-svg/)flowing DAG runner (/mnt/skills/user/flowing/)seeing-images (/mnt/skills/user/seeing-images/) — for agent's visual inspectionOptional MediaPipe models (auto-downloaded on first use):
blaze_face_short_range.tflite — face detection fallbackface_landmarker.task — precise face oval (478 mesh points)Note: MediaPipe selfie segmenter is NOT used in v0.6.0. Zone detection comes from agent bboxes, with MP used only for face landmark refinement.
pip install opencv-python-headless scikit-image scipy scikit-learn --break-system-packages -q
apt-get install -y librsvg2-bin -qq
After EVERY run, render and visually compare side-by-side. Same as image-to-svg.
import subprocess
from PIL import Image
subprocess.run(['rsvg-convert', '-w', '1400', 'output.svg', '-o', 'output.png'])
orig = Image.open('source.jpg')
rendered = Image.open('output.png')
target_h = 800
orig_r = orig.resize((int(orig.width * target_h / orig.height), target_h))
rend_r = rendered.resize((int(rendered.width * target_h / rendered.height), target_h))
gap = 20
comp = Image.new('RGB', (orig_r.width + rend_r.width + gap, target_h), (255,255,255))
comp.paste(orig_r, (0, 0))
comp.paste(rend_r, (orig_r.width + gap, 0))
comp.save('comparison.png')
development
--- name: verifying-claims description: Check that a document's claims about code are actually true by reading the prose, the code, and the tests and reporting (or fixing) where they disagree. Use whenever the user wants to verify a README, guide, spec, or docstring still matches the code; whenever they mention documentation drift, doc-code sync, "is this still accurate", stale docs, or keeping docs/tests/code consistent; before publishing or merging a docs change; or as a periodic doc-accuracy
tools
Query, filter, and transform Markdown structurally with mq — a jq-like CLI for Markdown. Use to extract headings/sections/code-blocks/links from .md files, build a table of contents, pull code blocks of a given language, slice or reshape LLM prompt/output Markdown, or batch-transform docs. Triggers on "extract sections from this markdown", "get all the code blocks", "jq for markdown", "mq", or any structural query over Markdown that grep/Read can't do cleanly.
development
Composes single-file HTML artifacts (PR review writeups, status reports, incident postmortems, slide decks, design systems, prototypes, flowcharts, module maps, feature explainers, kanban boards, prompt tuners) from a small JSON spec instead of hand-written HTML/CSS/JS. Use when the user asks to "compare options side-by-side", requests an HTML version of a report or review or deck, asks for a flowchart, status update, postmortem, design system reference, interactive prototype, custom editor — or explicitly says "HTML artifact", "single HTML file", "self-contained HTML". Skip for ad-hoc HTML snippets (forms, emails, embedded widgets) where there's no template fit.
development
DAG workflow runner that encodes control flow in code, not prose. Use when a procedure has 3+ steps with branching, retries, or validation that must be enforced — gates as `when=`, edge contracts as `validate=`, predicate loops as `retry_until=`. The runner owns the graph; the LLM provides leaves. Also covers parallel execution, checkpoint resume, detached side-effects.