Quality pass: faithful color illustrations + smooth curves + small-feature fidelity (v1.0→v1.3)#43
Merged
Merged
Conversation
Quality pass iteration 1, from testing on real illustrations: - preprocess is now mode-aware (#35): color illustrations are no longer pre-quantized to 16 / over-denoised, so small features (faces, eyes) survive and dark fills (outlines, hoodies) are kept. - solid backgrounds are preserved (#34): remove_background defaults off for color and binary (a solid background is content, not noise). - verify no longer starts color-starved (#36): first pass traces at full color_precision instead of ramping up from 1. - root cause of vanishing dark regions: the illustration preset now uses hierarchical=cutout. postprocess regroups paths by fill, which reordered a stacked SVG and let light layers paint over dark ones. Cutout regions don't overlap, so regrouping is order-safe (raw trace was already faithful at sim~0.98; postprocess was destroying it). Real-image results: shiba 0.92->0.96 (face/hoodie/bg restored), snowman 0.86->0.97 (subject restored), doodle 0.95->0.97. Corpus goldens recalibrated upward to lock the gains; no similarity regressions. Photo path/byte budgets widened (gradients trace richer now) — bloat reduction tracked in #38. Closes #34, closes #35, closes #36 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full-resolution inspection revealed three artifacts the downscaled previews hid: jagged outlines, white cracks between regions, and shattered fills. Root cause of the first two: postprocess grouped paths by fill color and emitted the groups in sorted-hex order, which reorders the tracer's stacked paint order (later = on top). Black/dark fills got painted under lighter ones and were covered — the earlier cutout workaround then traded the vanishing for inter- region gaps and rough edges. Fix: group only *consecutive* same-fill paths (order-preserving) and keep the illustration preset on hierarchical=stacked. Paint order is now exact, matching the raw tracer output (sim ~0.97 on doodle/shiba/snowman, gaps and jaggies gone). Color-variation 'shattering' on painterly inputs (lettering, watercolor) is a separate root cause (color flattening) tracked in #38. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Color-variation-heavy art (painterly, shaded lettering) traced into many small faceted regions — a 'shattered glass' look — and bloated the output. Add a bilateral flatten step (skimage denoise_bilateral, sigma_color=0.04) to the color preprocess: it smooths gradients/texture *within* regions while preserving edges, so those areas trace into clean flat color instead of facets. Mild enough that flat art is untouched (snowman/doodle similarity within ~0.5%) while painterly inputs improve and shrink a lot: - watercolor: 4808->2689 paths, 960KB->402KB, similarity 0.79->0.80 - doodle: 336->26 paths, 45KB->8KB - lettering: smoother letters, no facets Addresses #38 (bloat) and the shattering artifact. Corpus goldens recalibrated; no similarity regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds src/svgsmith/smooth.py: corner- and straight-edge-preserving curve refitting
that removes the pixel-boundary wobble vtracer leaves on contours. Splits each
closed contour at corners, keeps straight edges crisp (stripes/geometry), and
Gaussian-smooths + Catmull-Rom-refits genuinely-curved arcs. Wired into the
pipeline behind ConvertOptions.smooth / --smooth, default OFF.
Visually a clear win on outlines (doodle, snowman body) while scarf stripes stay
crisp. Default off because the Catmull-Rom emit is not yet compact (3-8x larger);
v1.2 will add least-squares Bezier fitting for smooth AND compact output.
Versioned test outputs under z-design/SVGSmith_Convert/output/{v1.0,v1.1}/ with
VERSIONS.md for tracking. (XML parsing uses stdlib xml.etree to match the existing
codebase; input is always our own freshly-generated SVG, not external — defusedxml
hardening tracked separately.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…act) Replaces v1.1's dense per-point Catmull-Rom with Schneider 1990 recursive least-squares cubic fitting (generate-bezier -> Newton-Raphson reparameterize -> split-at-max-error). Node count is governed by a canvas-relative fit-error tolerance, so contours are smooth AND sparse instead of bloated. vs the gap analysis target (perfectvector): doodle 79% curves at 10KB (was v1.0 8KB jagged / v1.1 67KB dense); snowman crisp stripes + smooth body at 31KB. Corner detection now runs on the raw resample (before per-arc denoise) so stripe/rectangle vertices are preserved, not rounded. Smoothing is now DEFAULT ON for color mode, SSIM-guarded: the smoothed result is kept only if it costs < 0.06 SSIM vs un-smoothed (a larger drop means a lost feature -> fall back). Thresholds are canvas-relative (viewBox-diagonal ratios). Corpus goldens recalibrated for the small expected SSIM cost; suite green. Addresses #39 (P0a). Palette reduction (#41) next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drives color illustrations toward a small, clean flat palette like professional output, without the fixed-K risk of crushing genuinely rich art: - postprocess palette consolidation now uses perceptual CIE76 deltaE in LAB (was raw RGB euclidean), so visually near-identical tints (the many close skin tones a tracer emits) collapse while distinct hues are kept. Threshold 14 deltaE. - illustration preset color_precision 8 -> 6: fewer, flatter regions up front. Adaptive result (vs perfectvector's 7 colors on the Shiba): shiba 24->7 colors, 236->95 paths, 50KB->24.5KB; doodle 2 colors; snowman 9 — while rich art stays rich (vector 21, lettering 30, watercolor 25), not crushed. Visual fidelity holds (colors faithful). Corpus goldens recalibrated; suite green, no SSIM regressions. With P0a (Schneider smoothing) this brings the Shiba to perfectvector-comparable quality. Addresses #41. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deep-dive (multi-agent + adversarial verify) proved the small-feature gap to perfectvector was POST-TRACE: raw vtracer already produces clean eyes (7 cubics) and an intact nose+mouth W (29 cubics); our own passes destroyed them. Fixes: - postprocess._is_linear_segment: absolute DP epsilon (~2px) reclassified small features' real curves as straight lines (eyes -> 5-7-sided polygons). Now uses a chord-RELATIVE test so a short cubic with a real bulge stays a curve. Eyes go from 14-29% curve polygons back to 100%-curve smooth ovals. - smooth.py: the nose+mouth W has curvature reversals but no sharp corners, so it was split into 2 arcs and fit straight across the concavities -> bowtie, mouth gone. Now also splits at signed-curvature reversals (inflections), resamples small contours denser, and — decisively — a per-feature AREA guard rejects a smoothing that fills a concavity (the bowtie) and keeps the crisp raw geometry. Nose is a clean rounded shape again and the mouth W is restored. No new deps (numpy only). 6-image set: no bloat/regression; suite green; corpus goldens recalibrated. Addresses the small-feature half of the v1.2->PV gap (outline-uniformity half tracked separately, needs the fill-and-inset stage). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The remaining gap to perfectvector is outline-width uniformity: PV holds an even ~12px band because its outline is a constant inset of one silhouette; ours is the byproduct of two independently-traced contours so the band wobbles (IQR 7-9px). Adds an OPT-IN --uniform-outline / ConvertOptions.uniform_outline (default OFF, color mode only): before tracing, force a constant-width band of the darkest palette color around the silhouette (skimage/scipy morphology — silhouette mask, erode by k, paint the ring). Makes the outer outline more even by construction. Opt-in because it assumes the art already has a dark outline around a solid-bg figure; on line art it would add a wrong border. The morphology re-smoothing means it is an improvement, not perfect parity (full parity needs vector polygon-offset + a geometry dep). Declares scipy as an explicit dep (was transitive via skimage). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #33, closes #34, closes #35, closes #36, closes #38, closes #39, closes #41
End-to-end quality overhaul driven by testing on real illustrations and reverse-engineering perfectvector.com output.
What changed (in order)
--uniform-outline: even outline band for outlined illustrations (default off).Result vs perfectvector (Shiba mascot)
Multi-agent deep-dives with adversarial verification confirmed the gaps were post-trace bugs in our own code (not vtracer/ML limits) and corrected several proposed fixes. Analysis:
~/Projects/docs/ANALYSIS-svgsmith-vs-perfectvector.md.85 tests green, ruff clean, corpus goldens recalibrated, no SSIM regressions. Remaining (tracked): #42 same-color union (fewer paths), #37 honest scoring, #40 defusedxml.
🤖 Generated with Claude Code