Skip to content

SimpleHTR

Handwritten word recognition model — reimplementation of Harald Scheidl's SimpleHTR. Architecture: 5 CNN layers + 2 bidirectional LSTM layers + CTC output. Takes a single word image and predicts the text.

Source: xournalpp_htr/training/simple_htr/

File structure

File Purpose Deps
config.py Hydra structured config base
network.py SimpleHTRNet (CNN+BiLSTM+CTC) training
dataset.py IAM word-level dataset loader training
train.py Training entrypoint (@hydra.main) training
infer.py Local torch inference from .pth training
demo.py Local Gradio demo (ADR 007) training
export.py ONNX export + HF Hub upload training
utils.py Device, git hash, JSON encoder training
test_best_model.ipynb ONNX validation notebook training
run_training.sh Hyperparameter sweep script shell

GPU training setup

1. Clone and install base

git clone https://github.com/PellelNitram/xournalpp_htr.git
cd xournalpp_htr
bash INSTALL_LINUX.sh

2. Install training extra with CUDA PyTorch

uv sync --extra training-simple-htr

3. Verify GPU

uv run python -c "import torch; print(torch.cuda.is_available())"

4. Authenticate with HuggingFace

uv run huggingface-cli login

5. Download dataset

The IAM-DB dataset is downloaded automatically on first training run. It includes pre-cropped word images (data/words/) and ground truth (data/ascii/words.txt).

Training

Single run

uv run python -m xournalpp_htr.training.simple_htr.train \
    training.learning_rate=0.001 \
    training.batch_size=64 \
    training.epoch_max=100 \
    output_path=outputs

Hyperparameter sweep

bash xournalpp_htr/training/simple_htr/run_training.sh

Evaluation

Find the best model from a sweep:

find experiments/ -name "best_model.json" -exec sh -c \
    'echo "--- $1 ---"; cat "$1"' _ {} \;

Inspection

Gradio demo

uv run python -m xournalpp_htr.training.simple_htr.demo \
    --model-path <path>/best_model.pth --device auto

Export

ONNX export

uv run python -m xournalpp_htr.training.simple_htr.export \
    --checkpoint <path>/best_model.pth --output-dir exports/

Validate ONNX

Use the test_best_model.ipynb notebook to compare PyTorch and ONNX outputs.

Upload to HF Hub

uv run python -m xournalpp_htr.training.simple_htr.export \
    --checkpoint <path>/best_model.pth --output-dir exports/ --upload

Inference

from xournalpp_htr.inference_models import SimpleHTRModel

model = SimpleHTRModel.from_pretrained()
text = model.recognize(word_image_grayscale)
print(text)

Best model

Experiment 3, dropout=0.5, augmentation on — CER 0.056, word accuracy 84.2%. Checkpoint: experiments/experiment3/do0.5_augtrue/best_model.pth. Configuration: lr=0.0005, bs=64, dropout=0.5, augmentation enabled, epoch_max=200 (early stopping with patience 25).

Experiments

2026-06-03 — Experiment 1: learning rate and batch size sweep

  • Hypothesis: Find the best learning rate and batch size combination for the default SimpleHTR architecture on IAM words.
  • Setup: IAM word-level dataset, 95/5 train/val split, no augmentation, early stopping with patience 25, max 100 epochs. Grid: LR ∈ {0.0005, 0.001, 0.002} × BS ∈ {32, 64, 128}. NVIDIA A100 40GB.
  • Command: bash xournalpp_htr/training/simple_htr/run_training.sh
  • Code revision: 89971fe
  • Results:
LR BS Best Epoch CER Word Acc Path
0.0005 64 54 0.070 79.2% experiments/experiment1/lr0.0005_bs64/
0.0005 32 27 0.070 79.3% experiments/experiment1/lr0.0005_bs32/
0.0005 128 83 0.073 78.8% experiments/experiment1/lr0.0005_bs128/
0.001 64 33 0.074 78.8% experiments/experiment1/lr0.001_bs64/
0.001 128 41 0.074 77.8% experiments/experiment1/lr0.001_bs128/
0.002 128 56 0.075 77.8% experiments/experiment1/lr0.002_bs128/
0.001 32 59 0.076 77.3% experiments/experiment1/lr0.001_bs32/
0.002 32 20 0.086 74.9% experiments/experiment1/lr0.002_bs32/
0.002 64 29 0.089 75.1% experiments/experiment1/lr0.002_bs64/
  • Conclusion: LR=0.0005 with BS=64 achieves the best CER (0.070) and word accuracy (79.2%). The top-3 configs all use LR=0.0005, showing that lower learning rates consistently outperform. LR=0.002 clearly underperforms. Recommended defaults: LR=0.0005, BS=64.

2026-06-05 — Experiment 2: augmentation

  • Hypothesis: Data augmentation (Gaussian blur, geometric transforms, morphological ops, contrast/noise) improves generalisation.
  • Setup: LR=0.0005 (best from exp1), BS ∈ {32, 64, 128} × augmentation {on, off}. Max 200 epochs, patience 25. NVIDIA A100 40GB.
  • Command: bash xournalpp_htr/training/simple_htr/run_training.sh
  • Code revision: f5f8c60
  • Results:
Aug BS Best Epoch CER Word Acc Path
true 128 142 0.060 82.9% experiments/experiment2/augtrue_bs128/
true 32 91 0.061 82.7% experiments/experiment2/augtrue_bs32/
true 64 62 0.062 81.8% experiments/experiment2/augtrue_bs64/
false 32 27 0.070 79.3% experiments/experiment2/augfalse_bs32/
false 64 54 0.070 79.2% experiments/experiment2/augfalse_bs64/
false 128 83 0.073 78.8% experiments/experiment2/augfalse_bs128/
  • Conclusion: Augmentation consistently improves results across all batch sizes, reducing CER from ~0.070 to ~0.060 and lifting word accuracy from ~79% to ~83%. Augmented runs need more epochs to converge (62–142 vs 27–83). Batch size has minimal effect with augmentation enabled.

2026-06-05 — Experiment 3: dropout

  • Hypothesis: Dropout between RNN layers provides additional regularisation, especially when combined with augmentation.
  • Setup: LR=0.0005, BS=64. Dropout ∈ {0.0, 0.2, 0.5} × augmentation {on, off}. Max 200 epochs, patience 25. NVIDIA A100 40GB.
  • Command: bash xournalpp_htr/training/simple_htr/run_training.sh
  • Code revision: f5f8c60
  • Results:
Dropout Aug Best Epoch CER Word Acc Path
0.5 true 67 0.056 84.2% experiments/experiment3/do0.5_augtrue/
0.2 true 79 0.058 83.2% experiments/experiment3/do0.2_augtrue/
0.0 true 62 0.062 81.8% experiments/experiment3/do0.0_augtrue/
0.5 false 64 0.067 80.4% experiments/experiment3/do0.5_augfalse/
0.2 false 22 0.070 78.8% experiments/experiment3/do0.2_augfalse/
0.0 false 54 0.070 79.2% experiments/experiment3/do0.0_augfalse/
  • Conclusion: Dropout and augmentation are complementary. The best config (dropout=0.5, augmentation on) achieves CER 0.056 and 84.2% word accuracy — a major improvement over the experiment 1 baseline (CER 0.070, 79.2%). Higher dropout consistently helps; the effect is strongest when combined with augmentation. Recommended defaults: dropout=0.5, augmentation enabled.

Current status

  • [x] Network architecture (CNN + BiLSTM + CTC)
  • [x] IAM word-level dataset loader with caching
  • [x] Training loop with CER/word accuracy validation
  • [x] ONNX export and HF Hub upload
  • [x] Inference model (SimpleHTRModel)
  • [x] Local Gradio demo
  • [x] Hyperparameter sweep script
  • [x] First training run and experiment log
  • [x] ONNX validation notebook
  • [x] Integrated into end-to-end pipeline with WordDetectorNet (2026-06-07_local_pipeline, issue #121)

Outlook

  • Add beam search decoding (issue #120)