Add script to stitch tiles into a mosaic, with gutter/padding support
- scripts/stitch_mosaic_from_tiles.py: grid layout from metadata.json, flip-x/y, tile gap (gutters), compare to server mosaic.jpg - tests/test_stitch_mosaic.py, Pillow in requirements, docs/mosaic_reconstruction_report.md
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# RootView SPRUCE server: pre-stitched mosaic vs tile imagery
|
||||
|
||||
Observations from the **RootView** deployment used by the **SPRUCE** minirhizotron experiment (web UI and image service). They concern what the server actually delivers for scan previews versus per-tile imagery—not any particular client or archive layout.
|
||||
|
||||
## Summary
|
||||
|
||||
### How the RootView web app presents the data
|
||||
|
||||
**Tiles** — A *scan* is a rectangular grid of fixed positions along a buried minirhizotron tube. Each cell is a **JPEG image** fetched by coordinates: position along the tube circumference (**X**, millimetres) and along the tube length / depth (**Y**, millimetres). The camera steps a regular grid from a **start** to an **end** position in each axis, with spacing **dx** and **dy**. A scale parameter (**s**) on the tile request selects rendering resolution. In the UI, users open individual tiles (for example from a popup or drill-down) to inspect roots at full zoom; tiles are the **authoritative** image product for a single field of view on the tube.
|
||||
|
||||
**Mosaic** — The **scan view** page shows one large **pre-composited** image, **`mosaic.jpg`**, that depicts the **entire grid** in a single picture: the whole scanned region at a glance. It is used for **orientation and navigation**—seeing how the tube was covered, spotting patterns, and correlating with the grid—rather than as the per-tile download path. The page embeds that image from the separate **image service** (static path per scan ID).
|
||||
|
||||
**Metadata** — The same **scan view** page carries **structured scan metadata**: identifiers (e.g. scan ID, name), **when** the scan ran (date/time, duration), **who** operated it, **how** it was acquired (scan mode, line orientation), and the **imaging grid**: **nx** and **ny** (counts), **start_x** / **start_y** and **end_x** / **end_y** (mm), **dx** / **dy** (step in mm), and derived **total_tiles**. Together, those fields define which **(x, y)** pairs correspond to each tile and how the mosaic is aligned to the physical tube.
|
||||
|
||||
### Sample data
|
||||
|
||||
Hostnames and ports match the **SPRUCE** RootView deployment documented for this service: web app **`http://205.149.147.131:8010`**, image service **`http://205.149.147.131:8011`**.
|
||||
|
||||
**Note:** `index.php` URLs usually require an **authenticated browser session** (login on port 8010). The `mosaic.jpg` URL is served from port **8011**; whether it is reachable without cookies depends on server configuration.
|
||||
|
||||
For each scan below:
|
||||
|
||||
- **Scan view (HTML metadata / grid parameters):**
|
||||
`http://205.149.147.131:8010/index.php?cmd=scan&mode=view&id=<scan_id>`
|
||||
- **Pre-stitched mosaic JPEG:**
|
||||
`http://205.149.147.131:8011/RootView_Database/<scan_id>/mosaic.jpg`
|
||||
- **Example tile (`s=1`, grid corner `row=0`, `col=0` → `x=start_x`, `y=start_y` mm):**
|
||||
`http://205.149.147.131:8010/index.php?cmd=image&mode=image_scan&id=<scan_id>&s=1&x=<start_x>&y=<start_y>`
|
||||
|
||||
| Scan ID | Name (from scan record) | View scan | `mosaic.jpg` | Example tile (`x`, `y` mm) |
|
||||
|--------:|-------------------------|-----------|--------------|---------------------------|
|
||||
| 156875 | Plot 6 AMR19 13458 | [view](http://205.149.147.131:8010/index.php?cmd=scan&mode=view&id=156875) | [mosaic.jpg](http://205.149.147.131:8011/RootView_Database/156875/mosaic.jpg) | [tile s=1](http://205.149.147.131:8010/index.php?cmd=image&mode=image_scan&id=156875&s=1&x=42.14&y=273.46) |
|
||||
| 146368 | Plot 10 AMR22 14350 | [view](http://205.149.147.131:8010/index.php?cmd=scan&mode=view&id=146368) | [mosaic.jpg](http://205.149.147.131:8011/RootView_Database/146368/mosaic.jpg) | [tile s=1](http://205.149.147.131:8010/index.php?cmd=image&mode=image_scan&id=146368&s=1&x=150.5&y=442.96) |
|
||||
| 160022 | Plot 11 AMR23 14309 | [view](http://205.149.147.131:8010/index.php?cmd=scan&mode=view&id=160022) | [mosaic.jpg](http://205.149.147.131:8011/RootView_Database/160022/mosaic.jpg) | [tile s=1](http://205.149.147.131:8010/index.php?cmd=image&mode=image_scan&id=160022&s=1&x=27.09&y=131.08) |
|
||||
| 156957 | Plot 13 AMR 24 17454 | [view](http://205.149.147.131:8010/index.php?cmd=scan&mode=view&id=156957) | [mosaic.jpg](http://205.149.147.131:8011/RootView_Database/156957/mosaic.jpg) | [tile s=1](http://205.149.147.131:8010/index.php?cmd=image&mode=image_scan&id=156957&s=1&x=276.92&y=2.26) |
|
||||
|
||||
The tile coordinates are the scan’s **`start_x`** and **`start_y`** (first grid position). Other tiles use the same `id` and `s`, with `x` and `y` stepped by **`dx`** and **`dy`** from the scan view metadata.
|
||||
|
||||
### Findings
|
||||
|
||||
| Topic | Finding |
|
||||
|--------|---------|
|
||||
| **`mosaic.jpg` resolution** | The image at `http://<image-host>:8011/RootView_Database/<scan_id>/mosaic.jpg` is a **JPEG whose pixel dimensions are far smaller** than assembling the same scan from full-resolution tile requests (e.g. typical tile sizes on the order of 640×480 at scale 1). It behaves like a **downsampled preview**, not a pixel-for-pixel merge of those tiles. |
|
||||
| **“Full resolution”** | The file still represents the **full spatial extent** of the scan (whole tube region as one picture). **Full extent** should not be confused with **full sensor / tile pixel resolution**. |
|
||||
| **Other mosaic URLs** | The scan **view** page embeds this **single** `mosaic.jpg` URL (no query string in the `src` observed in page markup). There is **no** documented or obvious alternate HTTP path in that UI for a higher-resolution pre-stitched mosaic. A different filename or PHP endpoint could exist server-side but would require checking the live service (network log, server code). |
|
||||
| **Circumference (X) direction** | Compared side-by-side with a mosaic built by placing tiles in **grid column order** matching the tile API (`x` / column index increasing in one direction), the server `mosaic.jpg` matches **after mirroring left–right** (column order reversed in the image). |
|
||||
| **Depth (Y) direction** | For the scans checked, **vertical** orientation did **not** require a global flip to align with that tile ordering; a vertical mirror **worsened** agreement. |
|
||||
| **Between-tile appearance** | Server mosaics show **narrow light (typically white) separation** between tile cells—visible borders or gutters—not a seamless paste of tile pixels edge-to-edge. |
|
||||
|
||||
## Endpoints (as exposed by this RootView instance)
|
||||
|
||||
- **Web application** (login, scan list, scan detail): served from the main host (e.g. port **8010** in the SPRUCE deployment).
|
||||
- **Pre-stitched mosaic:** `GET http://<image-host>:8011/RootView_Database/<scan_id>/mosaic.jpg` — static path under the image service; **not** the same request pattern as tiles.
|
||||
- **Individual tiles:** `GET .../index.php?cmd=image&mode=image_scan&id=<scan_id>&s=<scale>&x=<x_mm>&y=<y_mm>` on the web host, with millimetre coordinates from the scan grid.
|
||||
|
||||
Tile **`s`** (scale) controls tile resolution; **`mosaic.jpg`** exposes **no** analogous parameter in the URL seen in the UI.
|
||||
|
||||
## Empirical resolution comparison
|
||||
|
||||
When the same scan is available both as:
|
||||
|
||||
- the downloaded **`mosaic.jpg`**, and
|
||||
- the full set of **tile JPEGs** at a fixed scale,
|
||||
|
||||
the **intrinsic size** of `mosaic.jpg` is on the order of **hundreds** of pixels per side, while an assembly of those tiles spans **thousands** of pixels per side (depending on `nx`, `ny`, and tile size). So the server preview is **not** bit-equivalent to “all tiles merged at native tile resolution.”
|
||||
|
||||
Any numeric comparison after **resizing** one image to match the other is only useful for **relative** checks (e.g. mirroring, gutter width), not for claiming identity, because of scaling, JPEG compression, and residual layout differences.
|
||||
|
||||
## Implications
|
||||
|
||||
1. **`mosaic.jpg`** is appropriate for **quick visual review** and light workflows; **quantitative or publication-grade** work that needs tile-level pixels should use the **tile API** (or derivatives built from those tiles).
|
||||
2. Claims that the server mosaic is “full resolution of the available tile data” should be read carefully: **full field of view** is plausible; **full pixel resolution matching tiles** is **not** supported by measurements on this service.
|
||||
3. To confirm whether **any** higher-resolution pre-stitched asset exists, inspect **browser network requests** on a scan page or **server-side** RootView sources—this cannot be settled from the public HTML alone.
|
||||
|
||||
---
|
||||
|
||||
*Observations reflect the RootView SPRUCE image service behavior as exercised through its web UI and HTTP endpoints; server software may change in future deployments.*
|
||||
Reference in New Issue
Block a user