You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements the reference report's "B2A1 Roundtrip dEab by quantized L*; Pts vary
from GBD to Neutral" figure, and stops one bad analysis from blanking the page.
New: iccviz::RoundTripByLightness
32 lightness levels × 64 gamut-boundary points, each repeated eroded toward
neutral at chroma ×0.8/×0.5/×0.2, last point of each level replaced by neutral.
8192 points — the reference's exact count. New WASM export
roundTripByLightness; new plot under the histogram in Profile Statistics.
An envelope (min/mean/max per band) was built FIRST and discarded after reading
the actual figure: its entire content is the WITHIN-band shape — error falling
from the gamut surface to the neutral axis — and any summary erases exactly
that. Individual points, dotted band separators, 2 px markers.
Convergence against the published GRACoL2013_CRPC6 figures (0.343 / 0.947 / 2.76):
first cut 0.482 / 1.377 / 8.43
+ reference L* range 0.416 / 1.222 / 8.79
+ L-interpolated GBD 0.386 / 1.149 / 6.25
+ denser boundary sampling (S=36) 0.411 / 1.248 / 3.22
Two findings behind those steps:
- The L* range is INKSET-derived, not gamut-derived: hi = (100 + L_yellow)/2,
lo = (L_blackpoint + L_blue)/2, reusing HueExtrema + WhiteBlackPoints. That
reproduces the reference's 18.82–96.85 exactly. The raw gamut extent drags in
shadow levels the reference excludes, where the gamut is a sliver.
- A cell-maximum gamut descriptor biases OUTWARD: seeds land just outside the
gamut, B2A clamps them, and the ΔE reports our sampling rather than the
profile. The tell was a 13× cliff between the boundary ring and the ×0.8 ring.
Interpolating in L and sampling denser fixed most of it (max 8.4 → 3.2).
Residual ~20% high on mean: our GBD comes from a boundary point cloud, the
reference builds one with a gamut routine we do not have. Ours errs HIGH, the
conservative direction. Shape and scale match; the absolutes are ours.
Fix: the Analysis tab is fault-isolated
ErrorBoundary existed in the tree but was wired NOWHERE (its own comment claimed
otherwise), so any render throw unmounted the whole React tree and left a blank
browser tab with no message. The Analysis tab — several independent analyses over
untrusted profile data through a WASM engine — is now boundaried, with a readable
fallback and resetKey={bytes}.
Caught this because RtByLightness was mounted inside StatsTable, which does not
receive `bytes`/`intent`; they resolved as undefined globals and threw. Now
threaded from ProfileStatsSection. Note the build CANNOT catch that class of bug
(no scope analysis) and `npx eslint` fails here with "couldn't find
eslint.config.js" — the repo has ESLint installed but no config, so nothing
lints. A flat config with no-undef would have caught it; not added here because
it needs plugin deps and would surface pre-existing findings.
Also
PlotlyGraph gains `markerSize` so a dense scatter can ask for small dots.
Removed the superseded per-band aggregation from DeStats / RoundTripResult /
plot-wrapper rather than leave it unread.
i18n: dead band_min/mean/max keys removed (verified unreferenced first), new
analysis_rt_band_pts added across 12 locales; CJK includes the measure word so
it reads after a numeral. Spreadsheets re-synced.
MANUAL: Analysis section documents how to read the plot band by band, and why
its numbers are not comparable with the table above it.
WASM built from a clean iccDEV origin/master worktree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkxVtREhRmqbY71R5KnV7e
Copy file name to clipboardExpand all lines: MANUAL.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,6 +215,14 @@ The four round-trip types:
215
215
-**Reproducibility (RT2)** — ΔE\*ab between the first and second round trips: how stable a repeated round trip is, independent of the first trip's gamut clipping.
216
216
-**PRMG interoperability** — PCS colours inside the Perceptual Reference Medium Gamut round-tripped once; the ΔE distribution indicates cross-profile interoperability.
217
217
218
+
Below the histogram, **Round-trip ΔE by lightness** answers the question the summary figures cannot: *where* in the tone scale the inversion struggles, and *how far into the gamut* the trouble reaches.
219
+
220
+
It samples 32 lightness levels. At each level it takes 64 points **on the gamut boundary**, then repeats them eroded toward the neutral axis at 80 %, 50 % and 20 % of their chroma — 8,192 points in all, each plotted individually, with dotted separators between levels.
221
+
222
+
Read it band by band. Within one band the points run from the gamut surface on the left to neutral on the right, so the error should **fall away** across the band: colours near the boundary are the hardest to invert, colours near neutral the easiest. A band whose error stays high all the way to neutral is a profile in trouble at that lightness. The tallest spikes mark the lightness levels where the gamut boundary itself is least well inverted.
223
+
224
+
Every seed is inside the gamut by construction, so what you see is genuine `B2A`/`A2B` disagreement rather than colours being clipped for being unreachable. Note that this plot has its **own** sampling — deliberately weighted toward the gamut boundary — so its mean and maximum are higher than, and not comparable with, the table above. It follows the rendering intent but not the round-trip type.
225
+
218
226
A profile lacking the device↔PCS transforms a metric needs shows a *not applicable* note; a device space too wide to sample is reported as skipped rather than as an error.
Copy file name to clipboardExpand all lines: frontend/public/help.html
+4Lines changed: 4 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -329,6 +329,10 @@ <h4>Profile Statistics</h4>
329
329
<li><strong>Reproducibility (RT2)</strong> — ΔE*ab between the first and second round trips: how stable a repeated round trip is, independent of the first trip's gamut clipping.</li>
330
330
<li><strong>PRMG interoperability</strong> — PCS colours inside the Perceptual Reference Medium Gamut round-tripped once; the ΔE distribution indicates cross-profile interoperability.</li>
331
331
</ul>
332
+
<p>Below the histogram, <strong>Round-trip ΔE by lightness</strong> answers the question the summary figures cannot: <em>where</em> in the tone scale the inversion struggles, and <em>how far into the gamut</em> the trouble reaches.</p>
333
+
<p>It samples 32 lightness levels. At each level it takes 64 points <strong>on the gamut boundary</strong>, then repeats them eroded toward the neutral axis at 80 %, 50 % and 20 % of their chroma — 8,192 points in all, each plotted individually, with dotted separators between levels.</p>
334
+
<p>Read it band by band. Within one band the points run from the gamut surface on the left to neutral on the right, so the error should <strong>fall away</strong> across the band: colours near the boundary are the hardest to invert, colours near neutral the easiest. A band whose error stays high all the way to neutral is a profile in trouble at that lightness. The tallest spikes mark the lightness levels where the gamut boundary itself is least well inverted.</p>
335
+
<p>Every seed is inside the gamut by construction, so what you see is genuine <code>B2A</code>/<code>A2B</code> disagreement rather than colours being clipped for being unreachable. Note that this plot has its <strong>own</strong> sampling — deliberately weighted toward the gamut boundary — so its mean and maximum are higher than, and not comparable with, the table above. It follows the rendering intent but not the round-trip type.</p>
332
336
<p>A profile lacking the device↔PCS transforms a metric needs shows a <em>not applicable</em> note; a device space too wide to sample is reported as skipped rather than as an error.</p>
333
337
<h4>Extrema Colorimetry</h4>
334
338
<p>The ends of the profile's reproduction range — the numbers a print operator usually reads first.</p>
0 commit comments