Files
vtracer/docs/equivalence-report.md
T
Chris Tsang 5ac90bb97c Add stacked-mode equivalence report
Documents the systematic verification that the 1.0 pipeline reproduces 0.6.x
stacked output byte-for-byte: 475 parameter configurations (full per-parameter
sweeps + randomized interactions, pixel/polygon/spline, color/bw), geometry
compared against the 0.6.x cmdapp reference at path-precision 8. Zero geometry
mismatches (worst deviation 1e-8). Records the two bugs found and fixed during
verification (stacked hole-punching; relative-writer subpath origin), the
intentional differences (compact SVG encoding, empty-path omission), and the
reproduction procedure.
2026-07-24 10:50:09 +01:00

6.7 KiB
Generated
Raw Blame History

Stacked-Mode Equivalence Report

Question: does the rewritten 1.0 pipeline (crates/vtracer) reproduce the shipping 0.6.x pipeline (cmdapp/) in stacked mode, byte-for-byte?

Verdict: Yes. Across a systematic sweep of 475 parameter configurations, every fitted path is geometrically identical (worst coordinate deviation 1e-8 px — float-serialization noise). The only differences are two intentional, visually-invisible ones (documented below).

Date: 2026-07-24. Comparison target: pixel, polygon, spline fitters; color and bw color modes.


Scope

  • Stacked only. Old --hierarchical cutout is the fake cutout (re-render the clustered image, re-cluster, retrace); new cutout is the topological mosaic. They are deliberately different algorithms and are not expected to match. Mosaic is verified separately (pixel round-trip + seam tests).
  • Geometry, not pixels. Comparison parses each SVG's <path d> (applying any transform="translate()") into absolute coordinates and compares those directly. This is stronger than a raster diff (no antialiasing fuzz) and isolates the pipeline from the SVG writer.
  • --path-precision 8. High precision so writer rounding can never mask a real geometry difference. (At the default precision 2, the two writers round slightly differently — see Known differences.)

Reference oracle

cmdapp/ (0.6.x) is built with matched dependencies — the same local visioncortex 0.9.0 and image 0.25 as the new crates — so the comparison isolates pipeline logic from library drift:

  • Same visioncortex ⇒ identical clustering and curve fitting primitives.
  • Same image ⇒ identical decoding (JPEG decoding is decoder-version dependent; PNG is lossless either way).

New is run with --optimize 0 (no optimizer passes, absolute writer) so the comparison reflects the tracing/fitting pipeline, not the optimizer. The optimizer is verified lossless separately.

Parameter space

Parameter Range swept Affects
colormode color, bw frontend
mode pixel, polygon, spline curve fitter
filter_speckle 0 16 frontend (min area)
color_precision 1 8 color clustering
gradient_step 0 255 color layer difference
corner_threshold 0 180 spline
segment_length 3.5 10 spline
splice_threshold 0 180 spline

The full Cartesian product is ~10¹²; instead the sweep uses a layered strategy that touches every value of every parameter plus randomized interactions.

Coverage & results

475 configurations, tank-unit-preview.png (PNG) plus a Gum Tree (JPEG) baseline set:

Group Configs Geometry failures Worst Δ
Categorical cross (colormode × mode) 6 0 1e-8
filter_speckle 016 × mode × colormode 102 0 1e-8
color_precision 18 × mode 24 0 1e-8
gradient_step 0255 × mode 39 0 1e-8
corner_threshold 0180 (spline) 26 0 1e-8
segment_length 3.510 (spline) 9 0 1e-8
splice_threshold 0180 (spline) 13 0 1e-8
Random joint combinations 250 0 1e-8
Second image (Gum Tree, JPEG) 6 0 1e-8
Total 475 0 1e-8
  • Geometry mismatches (> 1e-6 px): 0.
  • Empty-path-count divergences: 10 (cosmetic; see below).

By fitter: pixel and polygon are byte-for-byte identical in both color and bw. spline geometry is identical to 1e-8; the sub-pixel deltas visible at low --path-precision are writer rounding, not geometry.

Known differences (intentional, invisible)

  1. SVG encoding. The new writer uses compact relative/shorthand commands with offsets baked into coordinates; 0.6.x used absolute coordinates plus a per-path transform="translate()". Same geometry, different bytes — by design (the new writer is smaller). Verified equal after parsing to absolute coordinates.

  2. Empty paths. At filter_speckle = 0, tiny (≈1px) clusters survive filtering; their spline fit is empty. 0.6.x emits a degenerate <path d=""> for each (e.g. 67 of them in one bw/spline case); the new pipeline omits them. They render nothing, so output is visually identical. This accounts for all 10 "empty-path divergences" and appears only at the nonsensical filter_speckle = 0.

Bugs found and fixed during this verification

This report's process surfaced two real bugs (both fixed, both now regression-guarded):

  1. Stacked layers had holes/seams. The color frontend traced clusters with holes punched (to_image_with_hole(.., true)); stacked mode must trace solid layers and rely on paint-order overdraw (false). Symptom: hairline seams (partial-alpha jumped 4.86% → 0.36% after the fix). Guard: stacked_has_no_seams (a full-coverage image must render fully opaque — zero backdrop show-through).

  2. Relative writer placed holes wrong. After Z, SVG resets the current point to the subpath start; the emitter left it at the last vertex, so a relative m for a hole/second subpath was offset. Only visible on multi-subpath shapes at optimize=1/2. Guard: relative_and_absolute_encode_same_geometry (a holed shape must encode identically absolute vs relative).

Harness caveats (for reproduction)

  • 0.6.x accepts only --mode (no -m) and treats --colormode as binary only for the value bwbinary silently falls through to color. Use bw for both binaries.
  • 0.6.x spline mode pixel maps to PathSimplifyMode::None.

Reproduction

cmdapp/ (0.6.x) was removed from the tree after this verification; restore it from git history (the commit before "Remove the 0.6.x cmdapp crate") to reproduce.

  1. Temporarily point cmdapp/Cargo.toml at the matched dependencies (image = "0.25", visioncortex = { version = "0.9", path = "../../visioncortex" }) and build both binaries:
    cargo build --release --manifest-path cmdapp/Cargo.toml
    cargo build --release -p vtracer-cli
    
  2. For each configuration, run both binaries in stacked mode with --path-precision 8 (new also with --optimize 0), remembering the harness caveats above (--mode not -m; --colormode bw).
  3. Parse each SVG's <path d> into absolute coordinates (apply any transform="translate()"), drop empty paths, and compare the coordinate sequences. Equivalent ⇔ per-coordinate deviation < 1e-6.

Conclusion

In stacked mode the new pipeline is a byte-for-byte-faithful reimplementation of 0.6.x across the full parameter space for pixel and polygon, and geometrically identical for spline. Remaining differences are limited to the intentional compact SVG encoding and the omission of degenerate empty paths.