CoolFace
Modelpublic

onnx-community/rt-detrv2-heron-101-rukopys-ONNX

sourceHugging Faceapache-2.0updated 1mo agoView on Hugging Face
0likes80downloads
Model Card

rt-detrv2-heron-101-rukopys (ONNX)

This is an ONNX version of Hukyl/rt-detrv2-heron-101-rukopys. It was automatically converted and uploaded using this Hugging Face Space.

Usage with Transformers.js

See the pipeline documentation for object-detection: https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ObjectDetectionPipeline


rt-detrv2-heron-101-rukopys

An RT-DETRv2-R101 (RTDetrV2ForObjectDetection, ~76.7M params) document-layout detector for handwritten Ukrainian pages, fine-tuned on the Rukopys dataset with a 7-class head: 0 handwritten · 1 printed · 2 formula · 3 table · 4 annotation · 5 image · 6 graph.

TL;DR

value
ArchitectureRT-DETRv2, ResNet-101 backbone, RTDetrV2ForObjectDetection
Parameters~76.7M
Init from`docling-project/docling-layout-heron-101` (17-class document-layout checkpoint)
Classeshandwritten, printed, formula, table, annotation, image, graph
Decodingset prediction, 300 object queries, no NMS
Gold-val mAP@50 / mAP@50-950.7685 / 0.4819 (macro over 7 classes)
Inputa document page image, 640×640
Outputclass-labeled region bounding boxes

Intended use

Detecting and classifying page regions on handwritten Ukrainian documents, upstream of region recognizers (e.g. `Hukyl/trocr-large-rukopys` for text, `Hukyl/trocr-base-rukopys-formula` for formulas). For a stronger single detector on the same task, see `Hukyl/doclayout-yolov10-rukopys`.

How to use

python
import torch
from PIL import Image
from transformers import AutoImageProcessor, AutoModelForObjectDetection

repo = "Hukyl/rt-detrv2-heron-101-rukopys"
model = AutoModelForObjectDetection.from_pretrained(repo).eval()
processor = AutoImageProcessor.from_pretrained(repo)

image = Image.open("page.jpg").convert("RGB")
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
    outputs = model(**inputs)

# threshold low — the scores are diffuse; max-F1 is at ~0.075 (see Limitations)
target_sizes = torch.tensor([image.size[::-1]])  # (height, width)
result = processor.post_process_object_detection(
    outputs, threshold=0.1, target_sizes=target_sizes
)[0]
for score, label, box in zip(result["scores"], result["labels"], result["boxes"]):
    print(model.config.id2label[label.item()], round(score.item(), 3), box.tolist())

Files

model.safetensors, config.json,   the shipped checkpoint — loads directly
preprocessor_config.json          with from_pretrained
training_meta.json                recorded recipe + the shipped checkpoint's metrics
metrics.json                      val metrics, aggregate + per-class
selection_comparison.json         the three checkpoint-selection axes side by side
training_log.jsonl                per-epoch loss / mAP / P / R curves

Head re-initialisation (17 → 7)

The backbone, encoder, decoder, and the class-agnostic box-regression head load from the base checkpoint as-is; the classification head is re-initialised to the 7 Rukopys classes, with id2label/label2id baked into the config.

Training

Fine-tuned on the human-labeled Rukopys train split via the 🤗 Trainer. The image processor applies a deterministic 640×640 resize with ImageNet normalisation.

Hyperparameters (as launched)

hyperparametervalue
epochs60 (early stopping, patience 15 on val loss)
batch / image size32 / 640
optimizer / scheduleAdamW, linear decay
learning rate1e-4
warmup ratio0.1
weight decay1e-4
freezenone
augmentationnone
eval / checkpoint cadenceevery epoch
checkpointbest val mAP@50 (epoch 20)
seed42

The full recorded recipe ships in training_meta.json; per-epoch curves in training_log.jsonl.

Checkpoint selection

The run tracked three selection axes; val loss and val mAP@50 are anti-correlated across this run (Pearson ≈ −0.78). This repo ships the mAP@50-selected checkpoint.

selected onmAP@50mAP@50-95precisionrecall
val loss0.74440.47310.95830.0175
val mAP@50 (shipped)0.76850.48190.97300.0137
val recall0.10550.05900.79290.4690

Class distribution (region counts)

classtrainval
handwritten19,4202,157
printed26642
formula2,545347
table12814
annotation49460
image1154
graph545
total23,0222,629

Results

Held-out val split of the Rukopys train data: 133 pages / 2,629 regions (stratified holdout, seed 42). mAP is threshold-free with box matching at IoU 0.50 (mAP@50–95 averages IoU 0.50:0.95); precision and recall are reported at a fixed confidence ≥ 0.50 operating point (class-aware greedy matching at IoU ≥ 0.50). The model's per-class sigmoid scores are diffuse and few boxes clear 0.50, so recall at this operating point collapses to ~0.01 — an artifact of the threshold, not the miss rate; the threshold-free mAP is the signal. Per-class precision/recall are not recorded.

Aggregate (macro over 7 classes)

metricvalue
mAP@500.7685
mAP@50-950.4819
precision0.9730
recall0.0137

Per class

classnmAP@50mAP@50-95
handwritten2,1570.84780.5042
printed420.82130.4453
formula3470.79730.5242
table140.85530.4179
annotation600.32460.1567
image40.95050.7693
graph50.78300.5555

We also acknowledge that printed/table/annotation/image/graph n is quite small, so measuring detection metrics against them is quite noisy.

Limitations

  • —Confidence is uncalibrated: the empirical max-F1 confidence is ≈0.075, far below a typical 0.25–0.50 first guess, and F1 collapses by conf 0.30. Tune the inference threshold against your own target metric.
  • —annotation (mAP@50 0.32) is a weak class.
  • —Trained and evaluated at 640×640; very small or dense regions on high-resolution scans may benefit from a higher inference resolution.
  • —Handwritten Ukrainian school/archival-style pages only; behaviour on other document types is untested.
  • —Single seed and validation split — no across-run variance estimate.

Training data & attribution

datasetsourcelicenserole
Rukopys`UkrainianCatholicUniversity/rukopys`CC BY 4.0gold fine-tune

Model weights are Apache-2.0, inherited from RT-DETRv2 and the base checkpoint.