# Retail challenge extension — 2026-10-06

Production-release evaluation only. No training, tuning, optimization or casual benchmarks. Historical exposure persists; this is not an untouched holdout. [Policy](AGENTS.md).

The current public comparison is reserved for the user-authorized complete ZXing WASM 3.1.1 run on October 6. Other scanner cells are blank. Earlier measurements remain retained in the older frozen benchmark; they are not mixed into this comparison. Full run status and protocol are recorded in results/zxing-3.1.1-20261006/run.json.

## Compact design

Each of 546 sources receives one lighting transform and exactly one additional transform. Sources are ordered by SHA256(challenge-v1:sourceSHA256), then assigned round-robin to perspective, blur or low resolution: 182 each. Two strengths/directions are distributed within each of these sections. This means sections use different subsets; scores must not be compared as paired difficulty effects without each subset's corresponding baseline.

- Perspective: top or bottom corners inset 14% of image width, projective inverse mapping, bicubic interpolation, white background; all source corners inside output.
- Blur: Pillow GaussianBlur radius 0.8 or 1.2 source pixels; defocus only.
- Low resolution: width/height factor 0.5 or 0.7 with Lanczos interpolation.
- Lighting: one of two linear shadows (gain 0.35–1) or vignette (0.4–1), multiplied in sRGB channel space. Does not simulate camera sensor noise or physical exposure accurately.
- Collage: 12 2×2 arrangements of full positive photographs, no cropping or rescaling, 32px white margins. Hash-selected sources with maximum dimension ≤1800px. Geometry for each tile is recorded, but complete physical-symbol annotation is absent.
- Large input: three hash-selected photos, each upsampled to approximately 12, 24 and 48 MP. These are input-size stress cases, not optical super-resolution. Record API failures rather than quietly resizing. Current Tapirscan 1.2.2 has a 32MP input limit documented by prior runs.

Total: 1,113 new cases. No Cartesian product of transformations. Whole images are EXIF-transposed and quarter-turn normalized using frozen source alignment, leaving natural tilt. Lossless RGB PNG benchmark images live in `datasets/derived/release-only-retail-challenges-20261006/`. JPEG previews are display-only.

## Readability and labels

Transformations inherit the source's expected payloads and human-review provenance. They do not establish post-transform readability. Relative downscaling and absolute blur radii have not been calibrated to narrow module widths. Do not describe these as hardest-possible readable cases. Future geometry/manual review can establish measured module widths and independent readable subsets; preserve original membership and report selection criteria.

The two acquisition exclusions in `retail500-review-20260918/excluded-or-uncertain.json` were excluded for isolated/rendered content, not invalid payloads. They are not new negatives. Five human-confirmed negative photos already remain included. Other acquisition failures require independent review before scoring.

## Reproduction

Run `build.py` with Pillow 12.2.0 and NumPy 2.4.6 from the dataset Python environment. It verifies original hashes and refuses changed frozen manifests or pixels. It never runs scanners. `provenance.json` pins generator and source hashes; `manifest.jsonl` pins all output PNG hashes, recipes, dimensions, source groups and expected payloads. Source aliases and all derivatives remain excluded from training.

## Public website

Intended domain: barcode-benchmark.f-kleinicke.de. Static report; scanners execute only after an explicit device-run action. Hosting should match the user's existing sites. Source image attribution: Open Food Facts contributors, CC BY-SA 3.0; database metadata ODbL. Per-source acquisition attribution must accompany any public image bundle. All originals and lossless prepared challenge inputs are hosted with the website; previews are separate derivatives. Publication status is recorded separately and not implied by preparing this directory.

## Public device reproduction

The public site includes `dataset.html` (paginated complete manifest, downloads and per-source attribution), `run.html` (explicit user-triggered local runs) and `download_dataset.py` (section downloads, file resume, checksums). The worker offers native BarcodeDetector without fallback, ZXing WASM 3.1.1 defaults, ZBar WASM 0.11.0 and unmodified npm Tapirscan 1.2.2 Medium. The earlier pinned Turbo4 observation is retained historically and is not an available engine.

Runs execute in a browser worker, three warmup calls on the first selected input and three measured calls per case. Only scan API plus result materialization is timed; initialization/download/decoding/transforms/hashing and ZBar scanner creation are excluded. Export contains exact case selection, manifest/runner/artifact identity, raw per-repeat results, pixel hashes, user-agent details, optional device name and supported native formats. No automatic upload. Page hiding stops the run to avoid background timing; worker termination permits stop even during a synchronous WASM scan. Whole-case timeouts include downloading and are explicitly not scanner timings.

Original rotation cases must match the frozen reference RGBA hash before scanning; browser/platform pixel differences are input mismatches, not scanner misses. New lossless prepared images have encoded-file hashes checked before decoding and decoded pixel hashes recorded. Cases with any scan error retain error status and do not receive a successful-case median. Collages score distinct expected payload coverage, not spatial instance recall. Quick samples are explicitly separate from full sections and never update published results automatically.

All image assets use same-origin, content-addressed parts of at most 4 MiB. Browsers/scripts reassemble the original encoded bytes and verify part and whole-file SHA-256. This avoids serving very large individual files from the website CDN without introducing recompression. Use of multipart transport does not change any image pixels or encoded-file hashes. Total input payload includes approximately 4.6 GB of originals plus lossless challenge derivatives.

## Published website

Live: https://barcode-benchmark-f-kleinicke.netlify.app/

Dataset browser: /dataset.html. Device runner: /run.html. The requested custom domain is associated with the Netlify site, but public DNS is authoritative at Strato. It requires a CNAME for `barcode-benchmark` pointing to `barcode-benchmark-f-kleinicke.netlify.app`; Netlify-only DNS records do not update Strato. See deployment.json for deployment identity and verification. No Android hardware run has been performed. The current ZXing comparison has its own retained run record and full observations.

## Gallery and image delivery

The gallery groups 546 source photographs into individual cards, with collages separate. Each card offers its available variants; global filters link from the section tables. Rotation previews render the selected orientation; full-resolution inspection and exact downloads use frozen inputs. Blur cards state the exact Pillow GaussianBlur radius (0.8 or 1.2 source pixels). These absolute radii are not scaled to module width or image dimensions, and thumbnail resizing can hide their effect.

No runtime request to Open Food Facts is needed: 546 originals and 1,113 prepared PNG derivatives (about 4.6 GB combined) are served by the site. Only alignment and 0°–90° rotation images are generated in the browser from hosted originals. Other modifications are already generated, hash-pinned PNGs.

## Color extension (v2)

Adds 30 distinct positive source photographs selected by SHA256(color-v1:sourceSHA256), independent of scanner outcomes: ten dark-tone recolorings, ten multiplicative RGB background tints and ten channel permutations. All transformations act on the complete aligned image, preserve dimensions and use lossless PNG. Exact formulas and channel values are in color-manifest.jsonl; build_color.py and color-provenance.json pin reproduction. No scanner measurements were run on this extension. The earlier 4,935-case v1 manifest and ZXing observations are retained under results/zxing-3.1.1-20261006/. Public v2 has 4,965 cases. All existing source exclusions cover these new derivatives.

Dark blue/green/brown bars and light/reddish backgrounds follow GS1 color guidance (https://www.gs1.org/standards/barcodes/10-steps-to-barcode-your-product/english). Digital RGB values cannot certify printed spectral reflectance, contrast or symbol quality. Channel swaps preserve neutral black/white and may show little effect on neutral barcode regions; they are whole-image color robustness cases, not guaranteed recoloring of every bar.

## Barcode-scale blur and low-resolution revision (v3)

User requested stronger, barcode-size-dependent degradation and bottom gallery pagination. Same 182-source membership per section, with new case IDs and separate frozen scale-v3-manifest.jsonl. Estimate barcode length from existing scanner-assisted source polygons: median top/bottom edge length per detection, then median within engine and across engines (ZBar extent fallback). EAN-8 uses 67 encoded modules and UPC-E 51, excluding quiet zones. No new scanner calls or performance-based severity selection. Geometry is not independently reviewed.

Gaussian blur radius = 0.65 × estimated pixels per module. Low-resolution scale = min(0.5, 1.5 / estimated pixels per module), Lanczos. This deliberately caps dimensions at half size even for initially small symbols, so achieved sampling may be below 1.5 pixels/module; theoretical/readable decoding is not guaranteed. Two negative photos have no barcode: blur radius = 0.003 × longest image side, downsample scale = 0.25. Exact settings and fallbacks are recorded per image.

The original v1 cases and measurements are retained; old blur/resolution scores are not displayed against v3 inputs. Public v3 replaces only these 364 active cases, retaining 4,965 total. Gallery paired close-ups are display-only nearest-neighbor views at equal displayed size, not inputs or verified barcode annotations. All source exclusions continue to apply.

## Color correction (v4)

The 10 ink cases now use visually inspected black-ink sources and manually reviewed full-photo rectangles. Inside each region, recolor dark neutral pixels to dark blue RGB(8,50,165), dark green RGB(0,115,55), or dark brown RGB(112,55,18), blending edges with a per-region luminance threshold recorded in the recipe. Pixels outside the region and above the ink threshold are byte-identical to the aligned original. No rescaling, barcode rebuilding or scanner-based selection. Paired previews show original and changed regions.

Already colored originals, including green Photo 358, are in the 10 background-tint cases; the 10 channel swaps remain unchanged. Total 30 Color cases, all unmeasured. Old manifests and images remain retained. Manually reviewed rectangles are image-editing regions, not exhaustive barcode annotations or print-quality certification.

## v5 milder blur and compact collages

At user request, blur radius changes from 0.65 to 0.40 estimated narrow modules (38.46% less), with the negative fallback reduced proportionally. Membership remains 182 cases. Fixed before the explicitly authorized ZXing and Apple Vision/Chrome measurements; no parameter search. Exact observations, native supported formats and API/backend limitations are under results/{zxing,native}-blur-v5-20261006/.

The 12 collages retain the same 48 source photos and each protected barcode region at native RGB resolution. Stored source polygons supply proposed bounds with 25%-of-width padding; all 48 boxed originals were visually reviewed and Photo 122 expanded to the full image. Additional 40px context can overlap; protected regions with quiet-zone margins are pasted last with 24px gaps and verified byte-identical to aligned source crops. No resizing/interpolation. Median canvas area is 15.1% of the original collage area. Source-level clipping/damage remains. Earlier collage recall was 2/12 complete sets, with 24/48 distinct payloads recovered; earlier source pixels were also native resolution. New compact collages are unmeasured.

## Human label reconciliation — 2026-10-06

`human-review-20261006.json` records the user-confirmed EAN-8 20250041 for source 4babb0a90e60f7bb, previously marked barcode-free. Public v6 applies this to all nine cases derived from that source. The website summaries and inspection filters are rescored from existing reads, without new scanner calls or changed image pixels/timings. Historical run manifests, observations and summaries remain original; the previous presentation summary is retained in `results/pre-human-review-20261006-zxing-results.js`. Other unexpected values remain flagged. UPC-E expansion already participates in canonical matching.

## Individual prediction totals

Wrong predictions and duplicate reads count returned items from the first measured repetition per case. A wrong item has no canonical match in the reviewed labels. Every occurrence of a canonical value after its first occurrence counts as a duplicate; a repeated wrong value contributes to both totals. Case counts remain available through inspection links. `update-read-counts.mjs` recounts retained observations only and never scans images.

## Physical instance review — v7

The user identified 12 source photos containing two physical copies of the same barcode. `human-review-20261006.json` records their source IDs; v7 carries two instance labels into rotations and other whole-image derivatives. Cropped collages retain their independent labels. Duplicate totals now count only returns beyond the annotated multiplicity, using Scan 1. This is a count-based allowance, not spatial matching: verified instance polygons are unavailable. Existing recall remains distinct-payload recall. Historical raw observations and labels are preserved; no scanner runs or image changes occurred.

## Generated inputs (generator `retail-generator-1`) — 2026-10-07

Test images are no longer hosted as files. `generator/generate.mjs` renders every case on the fly from the 546 hosted source JPEGs, in browsers, workers and Node alike, and the output is byte-identical everywhere:

- JPEGs are decoded by vendored pure-JS jpeg-js 0.4.4 (integer IDCT, nearest chroma upsampling), never by the browser's decoder; EXIF orientation and the frozen quarter-turn alignment are exact pixel re-indexing.
- Pixel math uses only IEEE-754 `+ - * /`, which ECMAScript requires to be exact, plus floor/ceil/round/min/max. `Math.sin`/`exp` are implementation-approximated, so Gaussian and Lanczos weights use fixed series and rotation uses literal cos/sin values for multiples of 15°.
- Operations: rotation and perspective with Catmull-Rom bicubic sampling on white; separable Gaussian blur with a 3-sigma kernel; separable Lanczos-3 resizing (low resolution, large input); lighting gains; ink recoloring, tints and channel swaps; native-pixel collages. Parameters (angles, sigma, target sizes, regions, placements) are unchanged from the file-based versions.
- Each case's `referenceRgbaSha256` in `public-manifest.json` is recorded by `generator/build.mjs` during packaging and frozen in `generated-inputs-v1.json`. Runners verify it before scanning and report mismatches as input errors. Any intentional generator change needs a new generator ID.

Pixels differ slightly from the earlier Pillow PNGs (decoder and resampling details), so published results were measured on different files. Those PNGs stay hosted only while published results reference them (`KEEP_HISTORICAL_INPUTS` in `package_site.py`). The next complete run will use the generated inputs, after which hosting shrinks from about 4.6 GB to the 475 MB of originals. `download_dataset.py` now downloads originals and the generator; `node generate_inputs.mjs` writes verified PNGs.

## Source revision — 2026-10-07

The user reviewed every barcode-free photo on full images (`datasets/metadata/barcode-free-review-20261007/`). The October 5 review gallery had shown only a scanner crop, so three cut-off barcodes were missed. `build_additions_20261007.py` records the result:

- Photos 139, 265 and 311 are relabeled positives (EAN-8 80720577, UPC-E 03037001, EAN-8 56024487); photo 311's blur case becomes `…-blur-v5-relabeled`, scaled to its barcode. Photo 191 (user unsure) is excluded.
- Ten user-confirmed barcode-free Open Food Facts photos (547–556), chosen by SHA256(`barcode-free-v1:`+hash) order, and the Aquafina photo 14 (557, EAN-13 6111252421568, barcode outline measured visually) are added with seven rotations, lighting and one of perspective, blur or low resolution each.
- Totals: 556 photos (522 EAN-8, 23 UPC-E, 1 EAN-13, 10 barcode-free), 5,055 cases. New photos join the release-only exclusions in `training-exclusions-additions-20261007.json`.

Published scores are unchanged placeholders until the next complete run; they do not cover the revised labels or added photos.
