Skip to content

Quality pass: faithful color illustrations + smooth curves + small-feature fidelity (v1.0→v1.3)#43

Merged
realproject7 merged 8 commits into
mainfrom
task/quality-pass-v1
Jun 19, 2026
Merged

Quality pass: faithful color illustrations + smooth curves + small-feature fidelity (v1.0→v1.3)#43
realproject7 merged 8 commits into
mainfrom
task/quality-pass-v1

Conversation

@realproject7

Copy link
Copy Markdown
Owner

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)

  • Faithful color illustrations: mode-aware preprocess (no pre-quantize-to-16/over-denoise on color), backgrounds preserved, color-precision ramp no longer starts near-monochrome.
  • Postprocess z-order fix: grouping by sorted-hex reordered the stacked paint order so dark fills vanished; now groups only consecutive same-fill runs (order-preserving). Fixed dark-vanishing + white gaps + jagged edges.
  • Edge-preserving color flattening (bilateral) kills "shattered" facets on painterly art.
  • Schneider least-squares Bézier fitting (smooth.py): smooth AND compact contours (node count by fit tolerance), default-on for color, SSIM-guarded.
  • Perceptual LAB ΔE palette merge + color_precision 8→6: small clean palette, adaptive (shiba 24→7 colors, rich art stays rich).
  • Small-feature restore (v1.3): chord-relative linear test (eyes → smooth ovals); curvature-reversal split + per-feature area guard (nose clean + mouth W restored).
  • Opt-in --uniform-outline: even outline band for outlined illustrations (default off).

Result vs perfectvector (Shiba mascot)

PV ours v1.3 was v1.0
colors 7 7 24
paths 50 95 236
contours smooth smooth faceted
eyes/nose/mouth clean clean polygon/bowtie/missing

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

realproject7 and others added 8 commits June 18, 2026 15:04
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>
@realproject7 realproject7 merged commit 5533234 into main Jun 19, 2026
0 of 4 checks passed
@realproject7 realproject7 deleted the task/quality-pass-v1 branch June 19, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment