Ukrainian HTR Model (Puigcerver CRNN)
A Handwritten Text Recognition (HTR) model for 19th–20th century Ukrainian manuscripts and typewritten texts, based on the CNN + BiLSTM + CTC architecture introduced in Puigcerver (2017) and used as the backbone of PyLaia and Transkribus.
This is a clean-room PyTorch reimplementation of that published architecture (PyLaia-inspired). It does not use the PyLaia Python package and is not loadable by it — training and inference run via plain PyTorch (see Usage below).
Model Details
- Architecture: CNN encoder [12, 24, 48, 48 filters] + 3-layer Bidirectional LSTM (256 units) + CTC decoder (Puigcerver 2017)
- Input: Grayscale line images, normalized to 128 px height with aspect ratio preserved
- Line crops: trained on polygon-masked lines (everything outside the line polygon set to white), not on rectangular crops — see Line cropping below
- Output: UTF-8 Ukrainian Cyrillic text
- Vocabulary: 184 symbols (
symbols.txt) - Framework: Pure PyTorch — clean-room reimplementation of the Puigcerver (2017) architecture (PyLaia-inspired); the PyLaia package is not required
Performance
| Metric | Value |
|---|---|
| Validation CER | 4.76% |
| Training epochs | 86 |
| Training lines | 24,706 |
| Training pages | 773 |
| Validation lines | 970 |
| Validation pages | 28 |
Line cropping matters for this model
The training lines were cut along their PAGE XML polygons and masked, so the model expects the area outside the line to be white. Fed rectangular (bounding-box) crops, in which parts of the neighbouring lines are visible, it makes about twice as many errors. In a separate evaluation on the same 970 validation lines, extracted once each way:
| Crop at inference | CER |
|---|---|
| polygon-masked (as in training) | 5.10% |
| bounding box | 10.29% |
Polyscriptor's segmenters (Kraken, HPP, PAGE XML import) currently pass bounding-box crops to the recognizer, so inside Polyscriptor expect the higher figure until polygon cropping is available there. When you call the model directly, mask each line with its polygon first.
Training Data
Trained on Ukrainian handwriting and typewritten text images transcribed and exported from Transkribus (see the corresponding Transkribus model page). The dataset covers Ukrainian manuscripts and typewritten texts from the 19th and 20th centuries.
The training data was drawn from two sources:
- Prozhito Project (with the participation of Misha Melnichenko)
- Foundation of the International Memorial Association (with the participation of Aren Vanyan and Nikita Lomakin)
The Transkribus model was curated and trained by Aleksej Tikhonov (MultiHTR project, University of Freiburg) as an extension of the Ukrainian generic handwriting 1 model. Model curated by Achim Rabus (Slavic Department, University of Freiburg). Funded by the Ministry of Science, Research and the Arts of Baden-Württemberg (funds from the state digitization strategy digital@bw).
Usage
Requirements
The inference code lives in polyscriptor and imports
other modules from it, so run it from a clone of the repository. Install into a fresh virtual
environment in one pip call; requirements-kraken.txt adds the Kraken segmentation used for full
pages below (for a GPU install, see the polyscriptor README):
git clone https://github.com/achimrabus/polyscriptor
cd polyscriptor
python3 -m venv htr_env && source htr_env/bin/activate
pip install -r requirements.txt -r requirements-kraken.txt
hf download achimrabus/crnn-ctc-ukrainian --local-dir models/crnn-ctc-ukrainian
Inference
From the root of the clone:
from inference_pylaia_native import PyLaiaInference
from PIL import Image
# Load model
model = PyLaiaInference(
checkpoint_path="models/crnn-ctc-ukrainian/best_model.pt",
syms_path="models/crnn-ctc-ukrainian/symbols.txt"
)
# Transcribe a line image
image = Image.open("line_image.jpg")
text, confidence = model.transcribe(image)
print(text, f"(confidence {confidence:.2f})")
Note: Input should be a single text line image, not a full page. Preprocessing (grayscale conversion, height normalization, aspect ratio preservation) is handled automatically by
inference_pylaia_native.py.
For full-page inference with automatic line segmentation, use batch_processing.py. The neural
Kraken segmenter (kraken-blla) is strongly recommended; the default projection-based segmenter
(hpp) is fast but can miss most lines on complex pages:
python batch_processing.py \
--engine crnn-ctc \
--model-path models/crnn-ctc-ukrainian/best_model.pt \
--segmentation-method kraken-blla \
--input-folder images/ \
--output-folder output/
Web Interface (recommended)
polyscriptor's main interface runs in the browser: upload page images or PDFs, segment them automatically (Kraken) or reuse existing PAGE XML, transcribe, correct lines inline and export TXT, CSV or PAGE XML. It runs on a laptop or on a remote server (via SSH tunnel), and CRNN-CTC models also work without a GPU.
The web interface picks up any folder under models/ that contains best_model.pt and
symbols.txt, so after the download above it only needs to be started:
uvicorn web.polyscriptor_server:app --host 0.0.0.0 --port 8765
# open http://localhost:8765 and choose the model under CRNN-CTC
Desktop GUI
polyscriptor also ships PyQt6 desktop interfaces: transcription_gui_plugin.py for interactive
single pages (automatic line segmentation, PAGE XML export) and polyscriptor_batch_gui.py for
whole folders (uses existing PAGE XML files, e.g. from Transkribus, when available).
Intended Use
- Transcription of 19th–20th century Ukrainian handwritten documents and typewritten texts
- Ukrainian historical document digitization
- Digital humanities research on modern Ukrainian texts
Limitations
- Optimized for 19th–20th century Ukrainian script; may underperform on earlier periods or other variants
- Expects polygon-masked line crops; bounding-box crops roughly double the CER (see Line cropping)
- Full-page segmentation quality depends on the segmentation method used upstream
Citation
If you use this model in your research, please cite the architecture paper, the publication describing the model and datasets, and this model:
@article{tikhonov2024ukrainian,
title = {Handwritten Text Recognition of Ukrainian Manuscripts in the 21st Century:
Possibilities, Challenges, and the Future of the First Generic AI-based Model},
author = {Tikhonov, Aleksej and Rabus, Achim},
journal = {Kyiv-Mohyla Humanities Journal},
volume = {11},
pages = {226--247},
year = {2024},
doi = {10.18523/2313-4895.11.2024.226-247},
url = {https://doi.org/10.18523/2313-4895.11.2024.226-247}
}
@article{puigcerver2017multidimensional,
title = {Are Multidimensional Recurrent Layers Really Necessary for Handwritten Text Recognition?},
author = {Puigcerver, Joan},
journal = {Proceedings of the 14th IAPR International Conference on Document Analysis and Recognition (ICDAR)},
year = {2017},
url = {https://www.jpuigcerver.net/pubs/jpuigcerver_icdar2017.pdf}
}
% TODO: add publication for Prozhito dataset/model
@misc{prozhito,
title = {Prozhito: {PLACEHOLDER}},
author = {},
year = {},
url = {}
}
% TODO: add publication for Memorial dataset/model
@misc{memorial,
title = {Memorial: {PLACEHOLDER}},
author = {},
year = {},
url = {}
}
@misc{rabus2026polyscriptor,
title = {Polyscriptor: Multi-Engine HTR Training \& Comparison Tool},
author = {Rabus, Achim},
year = {2026},
url = {https://github.com/achimrabus/polyscriptor}
}