# TileMosaic engine 1.2.0 TileMosaic assembles a bounded rectangular grid of overlapping materials microscopy tiles into an 8-bit grayscale PNG mosaic. It produces real placement records, overlap decisions, coverage images, decoded source images and a multilevel PNG tile pyramid. Processing is local; the engine makes no network calls. This version supports **integer translations at fixed magnification**. It is unsuitable for diagnosis, pathology, measurement correction, camera control, rotation, perspective, z-stacks, subpixel reconstruction or general microscope format conversion. It does not produce TIFF, OME-TIFF or an HTML viewer. Keep the original image files: RGB colors and original PNG metadata/compressed bytes are not preserved after grayscale conversion. No microscope compatibility or demand is established by synthetic tests. ## Dependencies and execution Native validation used Python 3.12.3, NumPy 2.4.1 and Pillow 12.1.0, which were already installed. `requirements.txt` pins those native dependency versions. The parent browser build uses Pyodide 314.0.6 (Python 3.14.2), NumPy 2.4.6 and Pillow 12.2.0; actual browser CPU acceptance is separate, not inferred from native tests. No dependency installation or model API call was made by this continuation. ```sh python3 -m unittest discover -s . -p test_product.py -v bash SMOKE.sh python3 product.py samples/valid.json /tmp/tilemosaic-new-output ``` The CLI accepts an absent or empty ordinary output directory. It refuses symlink paths or ancestors and nonempty directories, so a stale ZIP cannot survive an invalid rerun. It writes `report.json`, then `result.zip`, using atomic replacement of each file. The pair is not a filesystem transaction: interruption can leave only the report, which is not a successful CLI exit. PASS exits 0, invalid input exits 2, unresolved observations exit 3. Invalid JSON can fail before an output directory is created. `input.json` is JSON data, never executable code. ## Direct JSON schema `schema.json` describes structural validation. Runtime checks also enforce complete grids, unique coordinates, equal dimensions, aggregate size bounds and supported geometry. | Field | Type and meaning | | --- | --- | | `schema` | Required exact string `tilemosaic/v1`. | | `grid` | Required object containing integer `rows` and `cols`, each 1..64, product at most 64. | | `overlap` | Required number 0.15..0.75: expected fraction of tile width/height shared with the next grid cell. A search hint, not a measured alignment. | | `tiles` | Required list of exactly rows × cols records, one per grid cell; input ordering does not affect placements or mosaic. | | `intensity_mode` | Optional exact string `grayscale`; that is also the default. | | `pixel_scale_um` | Optional finite isotropic micrometers per pixel, 1e-9..1e9, or null. User supplied, not independently calibrated. Omission/null yields `calibration_status: UNKNOWN`, not zero scale. | | `search_radius_px` | Optional integer 1..32 around each expected displacement on both axes. Default: `max(2, min(16, ceil(min(tile_height,tile_width)*0.1)))`. | | `note` | Optional text up to 500 characters, suitable for identifying synthetic examples. | Every tile contains integer `row`/`col`, a `pixels` rectangular array of **integer** values 0..255, and optional text `label` up to 120 characters. Labels never become filesystem names. A tile can also contain integer `stage_x_px` and `stage_y_px` in -100000..100000. Both coordinates must appear on every tile or none. Their pairwise differences replace overlap-derived search centers. Absolute stage coordinates do not create a verified physical origin. Fractional stage values are rejected. All tiles have the same height/width, with each dimension 16..2048. The entire job is at most 64 tiles, 16,777,216 input pixels and 32,000,000 rendered canvas pixels. A full native 2×2 grid of 2048px PNGs was measured against an independent exact-pixel oracle under a 2 GiB address-space ceiling. This does not establish browser capacity. Browser/device practical limits may be lower; a browser failure is not an engine PASS. These are the current fixed-version limits, not a promise of terabyte or unlimited jobs. Unexpected fields, booleans as numbers, floats as pixels, nonfinite values, duplicate JSON keys, malformed JSON and missing/duplicate grid cells fail. JSON is limited to 96 MiB, 12 nesting levels, and 16,800,000 visited values. General Python objects are not accepted. Input labels and notes are data, not instructions. ## Actual file ingestion `files_to_payload(files)` accepts an object mapping filename to `{encoding, content}`. It never opens a filename on disk. There are exactly two alternatives: 1. A sole `input.json`, with `encoding: "utf8"` and the direct payload as text. 2. `layout.json` as UTF-8 JSON, plus all referenced `.png` files encoded with strict standard base64. Layout uses the same top-level fields as the direct payload; each tile uses a required `file` basename in place of `pixels`. Every PNG reference must be distinct and present. Extra files, mixed input methods, unknown encoding values and layout properties are rejected. Filenames use ASCII letters/numbers, `_`, `-` and `.`, start with a letter/number, contain no `..`, and have at most 120 characters. Paths, backslashes and absolute filenames are refused. Files in `samples/file-input/` form a complete actual upload example equivalent to `samples/valid.json`. Supported PNG modes are one-frame `L` (8-bit grayscale) and `RGB` (8-bit per channel). RGB becomes grayscale with the exact integer rule: `gray = floor((299*R + 587*G + 114*B + 500) / 1000)` RGBA, palette, bilevel, 16-bit, animated PNG, TIFF, corrupt/truncated PNG and data after the PNG IEND are rejected. Size is checked before raster decoding. Each PNG and layout.json is at most 16 MiB; decoded binary upload total is at most 64 MiB, with the same 16,777,216 decoded pixel limit. Original PNG textual metadata is not used for registration or calibration. ## Registration and reliability Grid neighbors are right/down pairs. For each pair, the expected displacement is the rounded tile dimension × (1 − overlap) on its grid axis, or the supplied stage-coordinate difference. Every integer displacement inside the two-axis search window is evaluated. The implementation computes cross-products with power-of-two zero-padded NumPy FFT, reuses the cross-spectrum for its phase diagnostic and releases integral images before allocating FFT buffers and computes means/variances of the **actual overlapping rectangles** using integral images. Padding is not mistaken for specimen pixels. A normalized raw-intensity phase-correlation value is retained as a diagnostic; the acceptance score is full-window spatial normalized cross-correlation (NCC). A candidate needs at least max(64, ceil(0.04 × tile area)) overlapping pixels and overlap standard deviation at least 1.0. The best NCC must be at least 0.985 and exceed the runner-up by at least 0.015. A best candidate on the search-window boundary is unresolved. Exact globally repeated axis texture is rejected even if the narrow search window hides alternative repeats. Blank, periodic, ambiguous, poorly matching, or unobservable neighbors produce UNKNOWN. Wider periods, near-periodic texture and arbitrary geometric distortions cannot be perfectly recognized; acceptance establishes consistency within the declared model and search, not physical truth. **Every declared neighbor must pass.** Accepted translations feed an anchored least-squares graph. Disconnection, any cycle residual above 1e-6 pixels or a noninteger global solution produces UNKNOWN. There is no silent outlier deletion, guessed placement or generative fill. A single tile is copied exactly with `registration_status: COPY_ONLY`; no alignment was needed or proved. The implementation deliberately has a high rejection threshold. Smooth or noisy real imagery may be unresolved even when a human could align it. Half-pixel shifts, resizing and rotation are tested adversarial cases; this is not subpixel image reconstruction. Caller-declared unsupported fields fail rather than being ignored. ## Outputs and deterministic replay `run(payload)` returns the contract report with PASS/FAIL/UNKNOWN, summary, CSV row tables, findings and provenance. `run_json(text)` strictly parses and returns JSON, including a FAIL report for malformed input. `bundle(payload)` raises ValueError for invalid input and returns deterministic ZIP bytes otherwise. The report never hides full image arrays in private fields. PASS ZIPs contain: - `report.json`, exact supplied JSON values in `input.json`, `README.txt` and SHA256 `manifest.json` covering every other member. - `transforms.csv` with grid-stable placements, anchored positions and labels; `alignment-residuals.csv` with all tested neighbor decisions, candidate count, scores and cycle residuals. - `mosaic.png`, `coverage-mask.png` (255 observed, 0 unobserved) and `coverage-count.png` (actual number of contributing tiles, 0..64). - `calibration.json` with optional user-declared scale, physical extents and canvas offset relative to tile (0,0). Pixel positions are top-left coordinates; no verified specimen-world origin is inferred. - `sources/rN-cN.png` preserving each exact decoded grayscale raster and `source-manifest.json` with pixel and output-file hashes. - `pyramid.json` and actual `pyramid/level-N/X-Y.png` tiles. Level 0 is the full mosaic split into up to 256×256 tiles. Each next level averages each actual 2×2 group with half-up integer rounding, including partial odd edges, until a 1×1 level. Scale doubles by level. This is a documented custom PNG pyramid, not OME, Deep Zoom or another interoperable proprietary format. Full-resolution overlaps are arithmetic means of supplied grayscale pixels with half-up integer rounding; no exposure correction or invented detail is applied. Uncovered pixels are black and explicitly masked. Pyramid means include those black pixels; use full-resolution coverage for observation boundaries. UNKNOWN ZIPs contain only input, report, available alignment evidence and explanatory metadata. They contain no final mosaic, transforms, calibration or pyramid. Formula-shaped strings are neutralized in CSV exports; numeric values remain numeric. JSON preserves the original label text. ZIP ordering, timestamps and PNG settings are fixed. Bytes repeat within the recorded dependency versions. Insufficient allocation during computation returns UNKNOWN and no final images; inability to package an archive is an unsuccessful operation, never a successful export. The CLI returns 3 if memory is unavailable before an output can be produced. Dependency upgrades may change ZIP/PNG or numeric diagnostic bytes; no cross-version byte identity is promised. Acceptance compares placements, pixel arrays, masks and units independently as well as deterministic same-version ZIP hashes.