Recrystal
A layout parser for research papers: PDF in, ordered typed blocks out.
PDF ──▶ [detector: fine-tuned YOLO11, 12 classes] ──▶ regions
──▶ [assembler: textreflow] ──▶ typed blocks ──▶ [grain_growth] ──▶ chunks
A YOLO11 layout model fine-tuned on research papers (DocLayNet's 11 classes
plus Authors) finds the regions on each page, run through onnxruntime with no
ultralytics at runtime. PyMuPDF supplies the words, each assigned to the
smallest region containing it; regions the model merged across a column gutter
are split back apart. textreflow
then rebuilds reading order and paragraphs.
Install
pip install recrystal # CPU
pip install recrystal[gpu] # CUDA 12 / cuDNN 9, via onnxruntime-gpu
onnxruntime and onnxruntime-gpu share one package directory, so whichever
is installed last wins. If another package pulls in the CPU build after the GPU
one (chromadb does), reinstall onnxruntime-gpu. A GPU session that silently
falls back to CPU is logged as a warning.
Use
from recrystal import parse_pdf
blocks, dropped = parse_pdf("paper.pdf")
# blocks: [{"type": "title", "page": 0, "text": "..."},
# {"type": "authors", ...}, {"type": "paragraph", ...}, ...]
# dropped: page headers, footers and text inside figures, kept out of the flow
The blocks are the input of
grain-growth-chunking. For
the intermediate layout — regions with boxes, labels and lines, in the
textreflow input contract — use layout_pdf:
from recrystal import LayoutDetector, layout_pdf
detector = LayoutDetector() # load once, reuse across PDFs
layout = layout_pdf("paper.pdf", detector=detector, max_pages=5)
LayoutDetector(model_path=..., providers=["CPUExecutionProvider"]) takes a
local .onnx and an explicit provider list. On a GPU that runs out of memory,
inference drops to CPU instead of failing.
Weights
The weights (38 MB, .onnx) are downloaded on first use from
darkdwine/yolo11-doc-layout-research-papers
into $RECRYSTAL_HOME, default ~/.cache/recrystal. Put the file there
yourself for offline use.
Evaluation
From a pre-registered evaluation over 514 papers and 400 questions, round 2 of Anneal's report:
- Fewest answers lost in parsing: 24 of 400 answer spans missing from the output (near match), against Docling's 48 and PyMuPDF4LLM's 61. A span the parser never emits cannot be retrieved by any chunker.
- Retrieves better under every chunker tested: pooled +0.064 on
span_hit_near@4000chagainst Docling and +0.122 against PyMuPDF4LLM, significant on 3 of 3 chunkers for each. - 2.88× faster than Docling per page, 0.1375 s against 0.3958, over 6,076 pages on a 4 GB consumer GPU.
Licence
AGPL-3.0-or-later, for two independent reasons: it links PyMuPDF (AGPL-3.0), and the weights are fine-tuned from Ultralytics YOLO11 (AGPL-3.0). The assembler, textreflow, and the chunker, grain-growth-chunking, link neither and are Apache-2.0.
Citation
See CITATION.cff.
Metadata
Release files for recrystal 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| recrystal-1.0.0.tar.gz | 35.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| recrystal-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.4 kB
Release files / recrystal-1.0.0.tar.gz
| Download URL | recrystal-1.0.0.tar.gz |
|---|---|
| Size | 35.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e129c8a9b01c203ffe6699395238a5c487c8c3d65adea850c257ce31bd4cae51
|
|
BLAKE2b-256 checksum How to use checksums |
3a97087fd9004467d0be3546a5e7dc2eca304de75ad99b97541884bc9dd81778
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / recrystal-1.0.0-py3-none-any.whl
| Download URL | recrystal-1.0.0-py3-none-any.whl |
|---|---|
| Size | 28.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
286c016fec407a2ccbe6411740dfa441647e6ad0e3e4eda78fc2d1ec0f811bb5
|
|
BLAKE2b-256 checksum How to use checksums |
e4af581af6c1f6d0c1ef225034df66bf0409ccac17f4be3932e2ae173a055993
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log