Parting v1: a contact judge for touching papyrus layers
What it is
Where two papyrus layers in a CT scan of a Herculaneum scroll touch, a tracer can hop from one layer to the next.
Parting takes a point a on one layer, with its sheet normal, and a candidate point, and scores how likely the
candidate is to lie on a's layer; a higher score means the same layer. This file is its classifier: a soft vote
(mean probability) of scikit-learn's HistGradientBoostingClassifier (600 iterations, 31 leaves) and a 64-unit MLP,
over 669 features of the two points. The features come from the Vesuvius Challenge's base dinovol encoder
(scrollprize/dinovol_ps8_9.6um_101626, teacher_305k.pt) at both points and along the line between them, CT
profiles along each point's normal and along that line, and base dinovol's own token cosine.
The code that makes the features, scores them and reruns the published test is scripts/parting/ in
ChaseHendrick/Scrolls (Apache-2.0), with its page
docs/tools/parting.md.
Parting reads model features of the scan; it does not read ink or text. Its scores are model output, not a reading.
The file
| File | parting-v1.joblib |
| Size | 4,110,013 bytes |
| sha256 | f0684ab2ec5725ad9050e08bcdf73df22983960ba906d222cb9c8068a0fdc601 |
| Format | a joblib pickle of a dict: model (the fitted VotingClassifier), choice, features (968), columns (a boolean mask with 669 true: the columns the model reads, in the order scripts/parting/partmodel.py makes them), groups and one more checkpoint hash. The last two record the feature layout of the run the model was fit on, which also held feature groups of another encoder; this model reads none of them |
| Needs | scikit-learn 1.9.1 and joblib (other scikit-learn versions may refuse or change the pickle) |
| Safety | loading a pickle runs code. Check the sha256 first; parting.py score refuses any other file unless given --trust-model |
It holds no site list, site ids, coordinates or training rows: only the fitted estimators (tree splits, bin edges, scaler means and variances, MLP weights and their training curves).
Use it
git clone https://github.com/ChaseHendrick/Scrolls && cd Scrolls
pip install numpy scipy scikit-learn==1.9.1 joblib torch timm einops huggingface_hub
python3 -c "from huggingface_hub import hf_hub_download as d; print(d('SharpMeow/parting-v1', 'parting-v1.joblib'))"
python3 scripts/parting/parting.py score --run RUN --model PATH_PRINTED_ABOVE --sites SITES.json --verdict --out READOUT.json
RUN is a feature run made by parting.py features on your own contact sites (parting.py sites); the tool page
lists every command.
Training data
Contact sites built from the published hand-traced windings of PHerc0139 and PHerc0172 (Vesuvius Challenge open data, CC BY-NC 4.0); touching sites only. Neither scroll is on the eligible lists of the 2027 Grand Prize or the First Letters prize on scrollprize.org/prizes (read 30 October 2026; Sourced fact).
Evaluation (Model output)
One test, fixed before any CT value of the object was read (frozen 29 October 2026 about 20:50 UTC, read once about 21:10 UTC), on PHercParis1Fr34 (Frag3 in the open data), a fragment of a scroll Parting never trained on, against base dinovol's raw token cosine on the same sites (record):
| Value | |
|---|---|
| Parting, touching AUC (2.5 % bound) | 0.8952 (0.8452) |
| Base dinovol token cosine, touching AUC | 0.8392 |
| Gain (2.5 % bound) | +0.0560 (+0.0368) |
| Touching sites, layer pairs | 150, 5 |
| Shuffled-label control, mean of 1,000 | 0.500 |
| Pipeline check, base dinovol on 75 apart sites | 0.944 |
| Verdict (bar: gain at least +0.05, bound above 0) | PASS |
The same file, rule and bar missed on two other fragments: PHerc1667Cr1Fr3 (Frag5), gain +0.045 (bound +0.020), and PHercParis2Fr143 (Frag2), gain +0.010, where base dinovol alone scored 0.965. One pass in three fragments tried.
Rerun it from public data: python3 scripts/parting/parting.py frag3 --model parting-v1.joblib --out OUT
(add --device cuda on a GPU).
Limits
- Fused layers (under 3 voxels apart) are not solved: 0.633 against 0.611 for base dinovol on 38 such sites.
- One pass in three fragments; five layer pairs is the floor the rule allows, so the bound is coarse.
- Built for 7.5 to 10 um voxels; Frag3 was a 3.24 um scan box-averaged to 7.91 um, not a native scan.
- Trained on two scrolls; scores on sites of PHerc0139 or PHerc0172 are not a test.
- The gain can only show where base dinovol leaves room for it.
License
CC BY-NC 4.0 for the weights, like the scan data they were trained on; the code is Apache-2.0. Base dinovol, which makes the features at run time, states no license on its Hugging Face page (checked 30 October 2026); it is not part of this file and is never copied, only downloaded from its own host by the code.
Credits
Scans and layer segments: Vesuvius Challenge open data (CC BY-NC 4.0). Base dinovol: the Vesuvius Challenge. The dinovol backbone code: villa (MIT), in the copy vendored by jimmylomro/hercUNet (MIT). Training windings: the published hand-traced windings of PHerc0139 and PHerc0172.