A small closed loop whose fit collapsed to a single cubic (fit_closed),
or a piece returning to its own junction with fewer than two fitted
cubics (fit_piece), kept every positioned point as a straight segment.
A looser Paths collapses more loops, so it could give more segments
than a tighter one.
Such a loop now tries the fewest segments that still hold it, each only
if the result keeps an area (more than 0.25 px^2) and stays within
1.5 * max(tolerance, 0.5 px) of the points both ways: the fit at up to
three somewhat tighter tolerances (what a slightly higher Paths gives);
for a closed loop then two cubics split at its start and its farthest
point, then the outline simplified at the tolerance and finer. The
positioned points stay the last resort, as before.
Test (code-drawn): a_higher_paths_never_gives_fewer_segments sweeps
Paths 0/25/50/75/100 over 32 seeded 48 px images of soft blobs plus
pixel noise in Color and Black and White at Noise 1 and 3. Before, six
runs had fewer segments at a higher Paths (e.g. 125 -> 79 from Paths 0
to 25); after, none. One image (seed 16, Black and White: 140 -> 136
from Paths 0 to 25, with or without this change) has another cause and
is left out of the sweep.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tBcdwNhptqtmX32JCGFzB
- A sharp corner may fall up to 0.75 px short within 3 px of where it should be:
anti-aliasing and blur take that much off a corner (not on a thin bar, which
is all corners).
- A rounded rectangle gets the radius that fits its outline best, searched
around the one its area says, instead of that one alone.
- A rectangle's direction is tried along its longest straight side, the longer
axis of its area and the pixel grid: a blurred one may have no straight side.
- A regular polygon's sides, corners and size are read from the outline's shape
(how its distance from the middle goes round, and its area), not from how many
anchors the fitter gave it.
- An outline without anti-aliasing is known to half a pixel: it gets half as
much room again.
Plain shapes found with Shapes on, all sizes and variants: Default 57% -> 98%,
6 Colors 62% -> 100%. The harness counts false shapes: none on any input but one
thin line (a rectangle 2 px wide) whose trace is 0.70 px off with or without
Shapes.
Snap Curves To Lines made every straight piece within 2.5 degrees of level or
upright exactly so, whatever its length: a 200 px line 2 degrees off had each end
moved 3.5 px. Now a piece is levelled only when neither end moves more than
1.6 px; a longer line that far off is left as the image has it.
Technical Drawing (the preset with the option on) on its inputs: largest
distance from the art 4.21 -> 1.46 px (1.43 before levelling existed), mean
0.178 -> 0.126 px; on the askew inputs 98% -> 70% of the outline comes out level
or upright.
Create: Strokes (the Outline preset, or the option) traced lines along the pixel skeleton: right to a pixel,
with a width counted in pixels, and fitted by a separate, older fitter.
- Each skeleton point is moved to the middle of the line's ink, read from the image across the line: every
pixel counts as much ink as its colour is the line's rather than the paper's, the ink's centroid is the
middle and its sum the width (an anti-aliased or blurred line says both to a fraction of a pixel).
- The stroke width is the median of those widths, no longer a whole number of pixels.
- Each end goes where the ink ends, less the half width a round cap adds.
- Centre lines are fitted as outlines are: a straight line is two anchors, corners stay corners, Snap Curves To
Lines levels them. The old Douglas-Peucker and simplify fitter, and the crate's pathops dependency, go.
Outline on the thin-line inputs, all sizes and variants (docs/image-trace-quality.md), before -> after:
- painted outline 0.310 -> 0.055 px off on average; 81 -> 14 anchors for five lines
- centre line 0.295 -> 0.052 px from the true centre line; stroke width 0.13 -> 0.02 px off; 18.4 -> 2.7
anchors a stroke (blurred, noisy and scanned lines: 2.0 each; aliased: 4.6)
- largest distance 1.84 -> 2.94 px: the square ends of a 10 px line under a round cap
Presets without Create: Strokes are unchanged.
The documented option "replaces slightly curved lines with straight lines and snaps lines near 0 or 90 degrees
to absolute 0 or 90 degrees" (helpx, Image Trace panel options); ours did the first half only.
- With Snap Curves To Lines, a straight piece at least 2 px long and within 2.5 degrees of level or upright is
made exactly so, by moving its ends across it to their mean (the page gives no angle: 2.5 degrees is our
choice). The pieces next to a moved end follow it.
- A junction other regions share stays where it is, so abutting regions still have one curve along their edge;
a line between a junction and a free end is levelled to the junction.
- The harness gains askew inputs (a rectangle, a plus sign and two lines, 1 to 2 degrees off level and upright)
and an "upright %" column: the share of the outline length that is exactly level or upright.
On the clean askew inputs (docs/image-trace-quality.md): Black and White Logo and Technical Drawing, the presets
with the option on, go from 0% to 91% and 95% of their outline exactly level or upright. Their outlines are then
0.49 and 0.59 px from the art as drawn where they were 0.05 (4.2 px at the far end of a 300 px side turned 1.5
degrees): that distance is what the option is for. Presets without the option are unchanged.
Each region's outline was fitted on its own, so the edge between two regions was fitted twice, a little
differently, and left hairline gaps and overlaps.
- An outline is cut at its junctions: the pixel corners where three or more regions meet, where two parts of a
region touch by a corner only, and where an edge reaches the image's border (each side of the image is a
neighbour of its own, so the image's corners stay exact). A junction lies where the edges around it say, the
same for every region around it.
- Each piece between two junctions is the whole edge between two regions. Both get exactly the same points for
it (shifts are rounded to 1/1024 px so that the two sides agree to the last bit; whether a piece is hard is
decided by the piece alone) and it is fitted the same way from either side (from its end further left), so it
is one and the same curve in both paths. A loop without junctions is fitted clockwise from its leftmost point:
a shape's outline and the hole it leaves in the shape around it are one curve too.
- Stacked layers (Overlapping) share no edges and are cut at the image's border only.
On each preset's own inputs, all sizes and variants (docs/image-trace-quality.md), before -> after:
- Default: gaps 22.2 -> 0.8 px, overlaps 22.4 -> 0.5 px
- 3 Colors: gaps 48.6 -> 0.0, overlaps 29.4 -> 0.0; outlines 0.087 -> 0.104 px off on average
- 6 Colors: gaps 39.0 -> 0.2, overlaps 35.7 -> 0.3; 0.137 -> 0.140 px off, largest 1.09 -> 1.34
- 16 Colors: gaps 34.5 -> 0.2, overlaps 26.4 -> 0.2; 0.133 -> 0.141 px off, largest 1.09 -> 1.31
Each crack between an inside and an outside pixel is moved to where the pixels across it say the edge is (the
blend of the two tones in the column of pixels across the crack; a thin line by the centroid and sum of its
tone; hard-edged images are read as staircases), and the outline is fitted as Flat Logo mode fits its own:
corners put back, straight runs as lines, a round outline as a circle, the rest as few curves as Paths allows.
Snap Curves To Lines straightens slightly curved runs and makes arcs. A fit that strays from its outline
gives way to a plainer one, and at last to the outline's own points.
On each preset's own inputs, all sizes and variants (docs/image-trace-quality.md):
- Default: outlines 0.313 -> 0.091 px off on average, largest 1.51 -> 1.02; edge error 17.2 -> 10.1;
gaps between abutting paths 63.5 -> 22.2 px, overlaps 67.0 -> 22.4 px
- Black and White Logo: 0.270 -> 0.064 px off, kinks 15.3 -> 0.3, anchors 3.78 -> 2.73 times the art's
- Technical Drawing: 0.201 -> 0.086 px off; Line Art: 0.158 -> 0.093
- Worse: anchors on blurred and scanned thin lines (Line Art 25 -> 77 on average), High Fidelity Photo
(colour error 2.30 -> 2.50, anchors 46,866 -> 81,597, 1 megapixel photo-like image 0.37 -> 1.98 s)
Advanced › Create: Fills and/or Strokes, with Stroke (the widest line, px).
With Strokes, each colour's 8-connected parts no wider than the stroke width
(chamfer distance transform) trace as open centre lines instead of filled
outlines: the line is thinned to one pixel (Zhang-Suen with each removal
re-checked, so two-pixel lines survive; staircase corners dropped), its
skeleton becomes runs between ends and junctions (junction pixels merged,
spurs pruned, runs through two-way junctions joined, rings closed), and each
run is simplified and fitted with curves. The stroke width is the line's mean
width (pixels over length). Traced lines get round caps and joins in the
document. Without Fills, wider areas become 1 px stroked outlines.
`imageTrace.make` takes fills, strokes and strokeWidth, and refuses neither
fills nor strokes and a non-positive width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>