finebooks/bhl-impact-gt
FineBooks BHL IMPACT Ground Truth 2,165 page scans from six historical natural-history books, each paired with an expert, ~99.95%-accurate transcription and full page-layout ground truth. A benchmark for OCR, text recognition, and document layout analysis on real historical print. This dataset is the basis of the BHL OCR Leaderboard, where open OCR models are scored against these transcriptions. As new OCR models are released, they are run through the same evaluation pipeline… See the full description on the dataset page: https://huggingface.co/datasets/finebooks/bhl-impact-gt.
FineBooks BHL IMPACT Ground Truth
*2,165 page scans from six historical natural-history books, each paired with an expert, ~99.95%-accurate transcription and full page-layout ground truth.* A benchmark for OCR, text recognition, and document layout analysis on real historical print.
This dataset is the basis of the [BHL OCR Leaderboard](https://huggingface.co/spaces/finebooks/bhl-ocr-leaderboard), where open OCR models are scored against these transcriptions. As new OCR models are released, they are run through the same evaluation pipeline and added to the leaderboard.
Part of FineBooks. The ground truth is the **IMPACT-BHL** corpus — manually corrected OCR distributed as PRImA PAGE XML — produced in the 2011–2012 collaboration between the EU IMPACT digitisation project and BHL-Europe, and released under CC-BY 3.0. The page images come from the Biodiversity Heritage Library (BHL).
*Text and layout, in one file. Every region polygon in the PAGE XML lives in the same pixel space as the shipped image — the ground truth was traced directly on the exact access scan we ship, so the boxes overlay the page with no transform (verified pixel-perfect across all six books). That makes it a benchmark for OCR / text recognition and* for layout / region detection at the same time.
Multilingual historical print. English, French, German, and early-18th-century Latin across running text, tabular matter, and illustrated plate pages. One volume, a Russian entomological journal, contains occasional Cyrillic in references and footnotes; its body text is French and German. All six volumes are set in antiqua type; the corpus contains no Gothic blackletter typefaces (such as German Fraktur).
At a glance
Quick start
from datasets import load_dataset
ds = load_dataset("finebooks/bhl-impact-gt", split="train")
row = ds[0]
row["image"] # PIL.Image — the page scan (WebP)
row["text"] # full-page transcription, in reading order
row["markdown"] # the same content, as Markdown
row["xml"] # verbatim PAGE XML — region polygons overlay `image` exactlyBrowse every page interactively in the Dataset Viewer above. Richer recipes — drawing the region boxes, rebuilding the structured document, joining to catalog metadata — are in Recipes.
A sample page
One page of Conchologia Iconica (conchologiaiconi05reev, PageID 8967091) — a chapter opener — has this text:
MONOGRAPH
OF THE GENUS
ACHATINA.
“Each shell, each crawling insect, holds a rank
Important in the scale of Him who framed
This scale of beings; holds a rank, which lost,
Would break the chain, and leave a gap behind,
Which Nature's self would rue!”—Stillingfleet.and this markdown, where the three display lines are tagged as headings:
## MONOGRAPH
## OF THE GENUS
## ACHATINA.
“Each shell, each crawling insect, holds a rank
...Text is transcribed exactly as printed — note the fl ligature in Stillingfleet and the curly quotation marks. Nothing is normalized; see Limitations.
Source books
Titles as recorded in the BHL / Internet Archive catalogue metadata:
The upstream corpus also ships a 100-page mixed "random set" drawn from many books with no single clean image source. It is excluded here.
Dataset structure
One row per page. Page images are WebP files under `images/` (not embedded in Parquet). metadata.parquet links each row via a file_name column that datasets resolves into the decoded image column — so load_dataset(...) returns image as the first column.
A few things worth knowing:
- `text`, `markdown`, and `docling` share one reading order. They are three renderings of a single pass over the page, so their region sequence is identical by construction — see Reading order.
- No separate "regions" column, by design. For per-region geometry and labels, read the authoritative
xml, or the bounding boxes insidedocling. - `markdown` keeps page furniture (Docling's furniture layer). A plain body-only
export_to_markdown()drops it if you'd rather — see Recipes. - Tables render as sequential text blocks, not grids — the ground truth stores table columns as separate regions, with no cell structure.
- Join-ready.
PageIDandBarCodeuse the exact column names and types from `finebooks/bhl-metadata`, so the two datasets join with no renaming.
Reading order
Scanned-page XML lists regions in document order, which often is not the order a human reads (it scrambles ~39% of pages). We reconstruct the true reading order, so text, markdown, and docling all flow the way you'd actually read the page — running heads on top, footnotes at the bottom, body text in sequence.
<details> <summary><strong>How the reading order is reconstructed</strong></summary>
The GT provides an explicit <ReadingOrder> that sequences only the main text flow. Over the text regions, we:
- Follow `<ReadingOrder>` for the body, then re-thread those ordered regions back through the document-order slots they occupy.
- Place each un-ordered region (running heads, page numbers, signature-marks, captions, footnotes) by its vertical position: above the ordered body → top margin, below it → bottom margin, otherwise left inline. No region-type rules are used — page numbers sit at the head in some books and the foot in others, so coordinates decide.
- Merge drop-capital initials into the region that follows them (
O+NE specimen…→ONE specimen…).
About 19% of pages have no <ReadingOrder> and fall back to pure document order. The source PAGE XML (xml / xml_path) is always the authoritative per-region order and geometry.
</details>
How the data was built
The link from a ground-truth file to its page image is not obvious. We reconstruct it deterministically and bundle every input under `source/` so the whole chain is auditable:
- Ground truth — the IMPACT-BHL PAGE XML (GitHub, CC-BY 3.0). Initial OCR was ABBYY FineReader, then manually re-keyed and QA'd to ~99.95% character accuracy.
- Book = Internet Archive item. Each GT directory name is the book's IA identifier, which for these six items is also the BHL
BarCode. - Page = scan leaf. GT files sorted by id map 1:1, in order, to the IA scan leaves in
scandata.xml(leafNum0…N-1). The build assertslen(GT) == len(leaves)per book. - Image. Only the access leaves (
addToAccessFormats=true) ship. Their running 1-based access index addresses BHL Open Data's WebP rendition:…/web/{ia_id}/{ia_id}_{access_index:04d}_full.webp. - PageID. The current BHL
PageIDfor each access page is read from the IA METS ({ia_id}_bhlmets.xml):<div ORDER=k>→PageID, whereORDER == access index.
Inputs are grouped by origin under source/: groundtruth-bhl/ (GitHub), bhl-open-data/ (S3 scandata + the bucket's own docs), ia/ (archive.org METS), and bhl-eu/ (Wayback-restored BHL-Europe ⇄ IMPACT documents, including the OCR report and the random-set spreadsheet).
Reproducible. The self-contained uv script that produced this dataset ships alongside the data, so the whole repo rebuilds itself: uv run bhl_impact_gt.py.
Limitations & caveats
- Access pages only. These books have 2,318 GT pages; the 2,165 access pages are included. The other 153 are non-access leaves (blank versos, tissue guards, covers, re-shot "Delete" duplicates) and are excluded — along with the 100-page random set.
- `pcGtsId` is not a BHL PageID. The
pcGtsIdinside each PAGE XML (pc-…) is an IMPACT-internal sequential number that collides with unrelated current BHL pages. Always join via the `PageID` column, never `pcGtsId`. - `�` (U+FFFD) means _illegible_, not mojibake. The ground truth uses the Unicode replacement character to mark a glyph the transcribers couldn't determine. It is part of the source XML (not introduced here) and appears on ~194 pages (1,366 times), mostly in plate captions and tables. Treat it as an explicit "unknown character" token — e.g. a wildcard in scoring — rather than stripping it or counting it as an error.
- Historical transcription, verbatim. Text is kept as printed: archaic orthography, ligatures, long-s, Cyrillic, accents, and hyphenation at line breaks are all preserved, not normalized.
- Image path is a newer, undocumented derivative. The WebP images come from BHL Open Data's
web/prefix, which (unlikeimages/,ocr/,scandata/) is not described in BHL's published bucket README. The content is the standard access rendition, but the path is not contractually documented. - One book has two BHL copies.
histoirenaturell10cuviexists as BHL items 25923 (the GT's scan, used here) and…cuvis/45272 (a different copy that owns the JP2images/). We use the GT's scan viaweb/.
Recipes
Draw the ground-truth region boxes on the page (no transform needed — the polygons already match the image):
from lxml import etree
from PIL import ImageDraw
ns = {"pc": "http://schema.primaresearch.org/PAGE/gts/pagecontent/2010-03-19"}
img = row["image"].copy()
draw = ImageDraw.Draw(img)
for coords in etree.fromstring(row["xml"].encode()).iterfind(".//pc:Coords", ns):
pts = [(int(p.get("x")), int(p.get("y"))) for p in coords.iterfind("pc:Point", ns)]
if len(pts) > 1:
draw.polygon(pts, outline="red")Rebuild the structured document via docling-core (labels + bbox provenance):
from docling_core.types.doc import DoclingDocument
from docling_core.types.doc.document import ContentLayer
doc = DoclingDocument.model_validate(row["docling"]) # `docling` decodes to a dict
doc.export_to_markdown( # == row["markdown"]
included_content_layers={ContentLayer.BODY, ContentLayer.FURNITURE})
doc.export_to_markdown() # body only — drops page furnitureJoin to BHL catalog metadata (via PageID / BarCode):
SELECT x.PageID, x.BarCode, t.FullTitle, p.SequenceOrder, p.PageNumber
FROM 'hf://datasets/finebooks/bhl-impact-gt/metadata.parquet' x
JOIN 'hf://datasets/finebooks/bhl-metadata/data/page/*.parquet' p USING (PageID)
JOIN 'hf://datasets/finebooks/bhl-metadata/data/item/*.parquet' i USING (ItemID)
JOIN 'hf://datasets/finebooks/bhl-metadata/data/title/*.parquet' t USING (TitleID)
LIMIT 10;Sources & license
License: [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/). The IMPACT-BHL ground-truth PAGE XML is released by the IMPACT project under CC-BY 3.0 — attribute IMPACT / Biodiversity Heritage Library. The page images are BHL Open Data; consult per-item rights in `finebooks/bhl-metadata` (item config) before reusing individual images.
Citation
IMPACT Centre of Competence & Biodiversity Heritage Library (2012). IMPACT-BHL ground truth (PRImA PAGE XML, CC-BY 3.0). Produced in the IMPACT ⇄ BHL-Europe collaboration. <https://github.com/impactcentre/groundtruth-bhl>
