Spaces:
Running on Zero
Running on Zero
|
Download README.md from SharpMeow/scroll-lens: direct link, hf CLI and curl.
- Browser
- Download file 11.4 kB
-
https://huggingface.co/spaces/SharpMeow/scroll-lens/resolve/main/README.md
- Command line
-
hf download hf://spaces/SharpMeow/scroll-lens/README.md
-
curl -L -o README.md https://huggingface.co/spaces/SharpMeow/scroll-lens/resolve/main/README.md
11.4 kB
| title: Scroll Lens | |
| emoji: 🔎 | |
| colorFrom: yellow | |
| colorTo: gray | |
| sdk: gradio | |
| sdk_version: 6.28.0 | |
| python_version: "3.12" | |
| app_file: app.py | |
| pinned: false | |
| license: apache-2.0 | |
| suggested_hardware: zero-a10g | |
| startup_duration_timeout: 1h | |
| short_description: See scroll CT through 3D DINO, ink and surface models | |
| models: | |
| - scrollprize/dinovol_v2_ps8_with_paris4_352500 | |
| - scrollprize/ink_3d_dino_guided | |
| - scrollprize/surface_recto_3dunet | |
| tags: | |
| - vesuvius-challenge | |
| - herculaneum | |
| - ct | |
| - 3d | |
| - dino | |
| - self-supervised | |
| - zerogpu | |
| - mcp-server | |
| # Scroll Lens | |
| Look inside a carbonized Herculaneum scroll with the Vesuvius Challenge's newest 3D models, and click to search. | |
| People use the page, flat or in 3D; scripts and AI agents use the same cubes through tools that are an API and an MCP server. | |
| Scroll Lens reads a small cube (128³ or 256³ voxels, 0.3 or 0.6 mm a side) of a public X-ray CT scan straight from the | |
| [Vesuvius Challenge open data](https://scrollprize.org/data) and runs three models on it, all released by the | |
| Vesuvius Challenge team with their PHerc. Paris 4 work ([Angelotti et al. 2026](https://arxiv.org/abs/2606.29085)): | |
| | Layer | Model | What it shows | | |
| | --- | --- | --- | | |
| | 3D DINO embeddings | [`scrollprize/dinovol_v2_ps8_with_paris4_352500`](https://huggingface.co/scrollprize/dinovol_v2_ps8_with_paris4_352500) | A self-supervised 3D ViT: 864 numbers for every 8 × 8 × 8 voxel patch. Alike patches get alike numbers. Shown as *DINO colors* (the three main directions as red, green, blue) and used by click search. | | |
| | 3D ink | [`scrollprize/ink_3d_dino_guided`](https://huggingface.co/scrollprize/ink_3d_dino_guided) | Ink voxels marked directly in the scan, with no flattened surface. | | |
| | Recto surface | [`scrollprize/surface_recto_3dunet`](https://huggingface.co/scrollprize/surface_recto_3dunet) | The written side of each papyrus sheet. | | |
| **Click to search.** Click a patch with *More like this* and every patch whose embedding is close to it lights up; | |
| *Not this* subtracts a patch. This is the organizers' own recipe: their ink reference (shown in amber) is the mean | |
| embedding of 256 expert clicks on ink. Your concept follows you to other cubes and scans, and downloads as | |
| `concept.npy` (and `concept_not.npy` for the Not this clicks), so you can click ink in one scroll and look for its like | |
| in another. | |
| **Everything shown is model output, not a reading.** A cube is smaller than one letter. | |
| ## Using it | |
| 1. **Gallery** (no GPU): 99 ready-made cubes. Pick one and it opens; *Previous*, *Next* and *Random* step through | |
| them, and you can type in the list to search it. The page opens on the first one. | |
| 2. **Your own point**: pick a scan, type a point (level-0 voxels, z y x) or press *Surprise me*, then *Run the | |
| models*. A 256³ cube uses about 5 seconds of your daily ZeroGPU time (a call takes 7 to 12 s), a 128³ cube under a | |
| second. ZeroGPU starts a call only while about 21 s (256³) or 11 s (128³) of your quota is left. When it refuses, | |
| or no GPU is free, the models run on the Space's CPU on the 128³ around your point instead (about 10 seconds, one | |
| such run at a time). | |
| 3. **Look**: the three views are drawn in your browser, so everything answers at once. Drag a slider, or drag inside a | |
| view to move the crosshair: the other views follow as you move. The wheel or the arrow keys step one slice (Shift: | |
| 8). Ctrl or Cmd with the wheel, or a pinch, zooms; right-drag or Shift-drag pans; double-click resets. The line | |
| under the views reads out what is under the pointer: place in the cube and the scan, CT value, ink and surface | |
| probability, and similarity. | |
| 4. **Search**: a click only moves the crosshair at first (*Move only*). Choose *More like this* and click a patch; | |
| *Not this* subtracts one; *Undo click* (or Ctrl or Cmd with Z) forgets the last click. *Top % shown* sets how many of the most similar patches light up. The organizers' reference (amber) | |
| never lights a patch below their own threshold, cosine 0.5. | |
| 5. **3D view**: turns the three views into one 3D space of the cube, as in Unroll: the three slices as planes that | |
| move as you drag the sliders, ink and surface as points, and the patches like your concept or the organizers' | |
| reference glowing. Drag to rotate, wheel to zoom, right-drag to pan, *Spin* to turn it slowly; click a slice to | |
| search there. | |
| 6. **More**: brightness and contrast, *Center on point*, *Reset zoom*, *Use crosshair as point* (copies the | |
| crosshair's scan position to Your own point), *Copy link* (a link that opens this cube at this crosshair), and | |
| *Save files* (model outputs as `.npz`, the receipt `.json`, your concept). | |
| Links: `?cube=ID` opens a gallery cube; `?v=PHerc0139&z=…&y=…&x=…&size=256` fills in Your own point (press Run the | |
| models); `&s=z,y,x` puts the crosshair at that voxel of the cube. | |
| ## The scans | |
| Only these twelve scans, all at 2.2 to 2.4 µm, the scale the models work at. None is on the First Letters or Grand | |
| Prize eligible lists. PHerc. Paris 4's title is an open prize, so Scroll Lens itself only ever picks the eight Paris 4 | |
| points the organizers listed for training the 3D ink model; you can type any point. | |
| | Id | Scroll | 3D DINO | 3D ink | Recto surface | | |
| | --- | --- | --- | --- | --- | | |
| | PHercParis4 | PHerc. Paris 4 | trained on this scan | made for this scroll | not trained on it | | |
| | PHerc0139 | PHerc. 139 | trained on this scan | its teacher was | trained on it | | |
| | PHerc1667 | PHerc. 1667 (Scroll 4) | **never saw it** | its teacher was | trained on it | | |
| | PHerc0500P2 | PHerc. 500P2 | trained on this scan | its teacher was | trained on it | | |
| | PHerc0343P | PHerc. 343P | trained on this scan | not trained on it | trained on it | | |
| | PHerc0814 | PHerc. 814 | trained on this scan | its teacher was | not trained on it | | |
| | PHerc0009B | PHerc. 9B | trained on this scan | its teacher was | not trained on it | | |
| | PHercMAN5 | PHerc. MAN 5 | trained on this scan | its teacher was | not trained on it | | |
| | PHerc1299 | PHerc. 1299 | trained on this scan | not trained on it | not trained on it | | |
| | PHerc0332 | PHerc. 332 | trained on this scan | not trained on it | not trained on it | | |
| | PHerc1451 | PHerc. 1451 | trained on this scan | not trained on it | not trained on it | | |
| | PHercMANB | PHerc. MAN B | trained on this scan | not trained on it | trained on it (MAN Bp) | | |
| "Its teacher was": the 3D ink model was distilled from a stage-1 teacher trained on that scroll's ink labels. From | |
| the model cards and the checkpoints' own training configs, read 16 October 2026. The page shows this per cube. | |
| ## Checks | |
| - Each cube shows how well the organizers' ink reference picks out the patches the 3D ink model marks as ink (AUC), | |
| beside controls that must fail: the same ink mask moved away from its place. Papyrus is layered, so one move can | |
| line the layers up again; the control is therefore taken over 16 fixed moves, and the AUC means something only | |
| above their range (`auc_control_p95`). Your own concept gets the same check. This is agreement between two models, | |
| not ground truth. | |
| - The 3D DINO code is the organizers' own (dinovol, at a pinned commit) and reproduces their napari inspector's patch | |
| embeddings to a cosine of 0.9999 on a Paris 4 cube. | |
| - Weights are downloaded at pinned revisions and refused unless their SHA-256 matches; the model code is downloaded | |
| at pinned commits. The receipt records all of it, plus the device, timings and the cube's place in the scan. | |
| ## For agents and scripts | |
| Everything a person can do on the page, an agent can do too: each tool below is both an API endpoint | |
| (`gradio_client`) and an MCP tool. Every answer carries `open_in_browser`, a link that opens the same cube on the page, | |
| so an agent can hand its result to a person. | |
| | Tool | What it does | GPU | | |
| | --- | --- | --- | | |
| | `list_scans` | the twelve scans, their sizes, what each model saw of them in training, the Paris 4 training points | no | | |
| | `list_gallery` | the ready-made cubes, with ids | no | | |
| | `random_point` | a point on the papyrus of a scan, fixed by a seed | no | | |
| | `inspect_cube` | three slice views with chosen overlays (one PNG) and the cube's receipt, checks and layer fractions | gallery: no | | |
| | `search_cube` | the patches most like example points, a `concept.npy`, or the organizers' ink reference; with the AUC against the 3D ink model and its control, an image through the best match, and the concept as `concept.npy` | gallery: no | | |
| | `download_cube` | the model outputs as a `.npz` and the receipt | gallery: no | | |
| | `look` | the `.npz` for any point, for batch scripts (`tools/build_gallery.py` uses it) | yes | | |
| A cube is named by a gallery `cube_id`, or by `scan` and a point `z`, `y`, `x` (level-0 voxels) with `size` 128 or | |
| 256. Points for `search_cube` are scan coordinates inside the cube, written `"z,y,x; z,y,x"`. A point outside the | |
| listed scans, outside the cube, or not three whole numbers is refused with a message saying why. | |
| **MCP.** Add `https://sharpmeow-scroll-lens.hf.space/gradio_api/mcp/` to an MCP client (Claude, Cursor and others). | |
| Calls on a new point use the caller's ZeroGPU time, so give the client a Hugging Face token | |
| (`Authorization: Bearer hf_...`); gallery cubes need none. | |
| **Python.** | |
| ```python | |
| from gradio_client import Client | |
| c = Client("SharpMeow/scroll-lens", token="hf_...") # your token: GPU time counts against your quota | |
| cubes = c.predict(api_name="/list_gallery")["cubes"] | |
| image, info = c.predict(cubes[0]["id"], "", 0, 0, 0, 256, "ink,reference", api_name="/inspect_cube") | |
| image, hits, concept = c.predict("", "PHerc1667", 15108, 10100, 5532, 256, # cube_id, scan, z, y, x, size | |
| "15108,10100,5532", "", None, 10, # positive, negative, concept, top_k | |
| api_name="/search_cube") | |
| path = c.predict("PHerc1667", 15108, 10100, 5532, # look: scan, z, y, x | |
| 256, "dino,ink,surface", # cube side, models | |
| False, False, False, # mirror averaging, compact, allow the CPU fallback | |
| api_name="/look") | |
| ``` | |
| Read a `.npz` with `lens.load_npz(path)` from this Space's `lens.py`: `grid` is (Z/8, Y/8, X/8, 864) float16 unit | |
| embeddings (or `grid_reduced` and `grid_basis` when compact), `ink` and `surface` are (Z, Y, X) uint8 probabilities | |
| times 255, and `receipt` is the JSON receipt. The scan itself is not included; read it from the bucket with | |
| `lens.read_cube(volume, receipt["cube_origin_zyx"], receipt["cube_size"])`. | |
| `lens.py` also runs on any computer: `python3 lens.py look PHerc0139 47316 9452 12796 --size 256` (CUDA if present). | |
| ## Credits and licenses | |
| Code: Apache 2.0 (see `LICENSE` and `NOTICE`), by Chase Hendrick. Models and their code: MIT, by the Vesuvius | |
| Challenge (dinovol includes parts under the DINOv2 Apache 2.0 license and the DINOv3 License Agreement). Scan data: | |
| CC BY-NC 4.0, Giorgio Angelotti, Stephen Parsons, Sean Johnson, Elian Rafael Dal Prà, Johannes Rudolph, Paul | |
| Tafforeau, Alessandro Mirone, Paul Henderson, Hendrik Schilling, Forrest McDonald, David Josey, Youssef Nader, | |
| C. Seth Parker, W. Brent Seales, *Vesuvius Challenge: CT Scans of Herculaneum Papyri*. Gallery cubes are model output | |
| derived from that data (CC BY-NC 4.0) and hold no scan voxels. | |
| Source and tests: [github.com/ChaseHendrick/Scrolls](https://github.com/ChaseHendrick/Scrolls), `spaces/scroll-lens/`. | |