ThaOCR rec β Khmer + English text-line recogniser (CRNN-CTC, ONNX)
Stage 3 of 5 of the ThaOCR pipeline. Reads one text-line crop at a time. CRNN-CTC, arch "M": conv channels 48-96-192-256,
projection 320, BiLSTM 2 layers x 256, 4.76 M parameters. Input is a grey line crop of height 48 and dynamic width,
[N,1,48,W], float32 in 0..1. The output has one step per time column over 195 symbols (Khmer letters, vowels, signs,
digits, Latin letters, punctuation; blank id 0), decoded with greedy CTC. Text is normalised with normalize_khmer
(canonical ordering of Khmer clusters, visually identical spellings merged).
Every version has an fp32 ONNX, an int8 ONNX (dynamic quantisation of the same checkpoint, 4.8 MB) and its own .vocab.json.
Use the vocab with the same name as the model: the int8 file shares the vocab of its fp32 sibling.
Models
| id | version | file | size | description |
|---|---|---|---|---|
crnn_m_v1 |
v1 | rec/crnn_m_v1.onnx |
19.1 MB | CRNN-CTC m v1 (fp32) |
crnn_m_v1_int8 |
v1 | rec/crnn_m_v1_int8.onnx |
4.8 MB | CRNN-CTC m v1 (int8) |
crnn_m_v2 |
v2 | rec/crnn_m_v2.onnx |
19.1 MB | CRNN-CTC M v2 (print + handwriting, fp32) |
crnn_m_v2_int8 |
v2 | rec/crnn_m_v2_int8.onnx |
4.8 MB | CRNN-CTC M v2 (print + handwriting, int8 4.8 MB) |
crnn_m_v2_hw |
v2.1 | rec/crnn_m_v2_hw.onnx |
19.1 MB | CRNN-CTC M v2.1 (print + REAL handwriting, fp32) |
crnn_m_v2_hw_int8 |
v2.1 | rec/crnn_m_v2_hw_int8.onnx |
4.8 MB | CRNN-CTC M v2.1 (print + REAL handwriting, int8 4.8 MB) |
crnn_m_v2_stamp |
v2.2 | rec/crnn_m_v2_stamp.onnx |
19.1 MB | CRNN-CTC M v2.2 (print + handwriting + text under stamps) |
crnn_m_v2_stamp_int8 |
v2.2 | rec/crnn_m_v2_stamp_int8.onnx |
4.8 MB | CRNN-CTC M v2.2 int8 (phone size) |
crnn_m_v2_stamp2 (default) |
v2.3 | rec/crnn_m_v2_stamp2.onnx |
19.1 MB | CRNN-CTC M v2.3 (print + handwriting + text under stamps, gentle) |
crnn_m_v2_stamp2_int8 |
v2.3 | rec/crnn_m_v2_stamp2_int8.onnx |
4.8 MB | CRNN-CTC M v2.3 int8 (phone size) |
crnn_m_v2_stamp3 |
v2.4 | rec/crnn_m_v2_stamp3.onnx |
19.1 MB | CRNN-CTC M v2.4 (v2.3 + 4 epochs) |
crnn_m_v2_stamp3_int8 |
v2.4 | rec/crnn_m_v2_stamp3_int8.onnx |
4.8 MB | CRNN-CTC M v2.4 int8 (phone size) |
The default is crnn_m_v2_stamp2 (v2.3); use its _int8 file on phones. crnn_m_v2_stamp3 (v2.4) is slightly better on every val set
but has not been made the default yet.
Use
import onnxruntime as ort, json
sess = ort.InferenceSession("rec/crnn_m_v2_stamp2_int8.onnx")
vocab = json.load(open("rec/crnn_m_v2_stamp2.vocab.json"))["itos"]
# grey line crop, height 48, shape [1,1,48,W] float32 in 0..1 -> greedy CTC over the output, blank id 0
Lineage (each version starts from the previous best checkpoint)
| version | model id | epochs / lr | what was added | val CER print / real_dev |
|---|---|---|---|---|
| v1 | crnn_m_v1 |
6 / 1.5e-3 | mix_print_scene: sections S01βS08 + synthetic scene sets | 0.29 % / β |
| expA | (internal) | 2 | + rec_doclaynet real English lines |
0.29 % / 14.8 % |
| v2 | crnn_m_v2 |
6 / 1e-3 | + docgen print + synthetic handwriting (exam sheets, forms) | 0.22 % / 13.6 % |
| v2.1 | crnn_m_v2_hw |
3 / 5e-4 | + real Khmer handwriting (Thareah, SoyVitou) | 0.23 % / 13.1 % |
| v2.2 | crnn_m_v2_stamp |
2 / 1.2e-4 | + stamp augmentation (STAMP_AUG=0.12) |
0.22 % / 12.9 % |
| v2.3 | crnn_m_v2_stamp2 |
4 / 1.2e-4 | same data, longer, from v2.1 | 0.216 % / 12.7 % |
| v2.4 | crnn_m_v2_stamp3 |
4 / 1e-4 | v2.3 + 4 epochs | 0.213 % / 12.6 % |
real_dev is the owner's real Khmer document dev set (character error rate).
How v2.3 (default) was trained
Trainer training/rec/train.py (the Khm-Dataset-Creation CRNN trainer + packed line sets). --aug strong = GPU-side warp, slant, low
resolution, polarity inversion. STAMP_AUG=0.12 = training/rec/stamp_aug.py overlays grey ring arcs and short stamp strokes on 12 % of crops,
so text under Cambodian official red stamps is still read.
STAMP_AUG=0.12 python -m training.rec.train --mix training/rec/configs/final_v2_hand_real.json \
--out work/runs/rec_v2_M_stamp2 --arch M --aug strong --init work/runs/rec_v2_M_ft/best.pt \
--epochs 4 --lr 1.2e-4
Dataset mix final_v2_hand_real (1,041,136 lines per epoch):
- synthetic print sections S01βS08 (200,000 lines sampled): Khmer news corpus rendered with 21 open-licence Khmer fonts + Khmer OS fonts, with print/scan effects
- synthetic scene/signboard sets: dataset_v2 (scene, heavy, medium, + 25,000 clean), dataset_nf_mix, dataset_hard, dataset_nf_hard, dataset_long (10,000)
rec_doclaynet: 90,860 real English lines cut from DocLayNet pages (text-layer transcriptions)rec_docgen: 99,869 printed lines from docgen v2 pages (Khmer + English, many fonts)rec_exam_hand: 69,081 synthetic Khmer handwriting lines from docgen exam answer sheets (handwriting fonts, phone-photo degradation)rec_docgen_hand: 11,905 synthetic handwritten form values (names, numbers, dates)rec_hand_real_thareah: 2,311 real Khmer handwriting lines (Thareah/khmer-handwriting, 147 scanned pages), oversampled x8rec_hand_real_soyvitou: 3,549 real Khmer handwritten words/short lines (SoyVitou/khmer-handwritten-dataset-4.2k), oversampled x4
Validation character error rate (best epoch): synthetic print V01 0.216 %, synthetic scene 2.73 %, real English 0.29 %, docgen print 7.5 %, synthetic exam handwriting 4.5 %, synthetic form handwriting 3.6 %, real handwriting Thareah 21.3 %, SoyVitou 9.8 %, owner's real Khmer document dev set 12.7 %.
Training data and licences
| data | type | licence |
|---|---|---|
| Khmer news corpus (5,601 articles, 257,501 train lines, scraped by the owner for research; source URLs not recorded) rendered with Khmer fonts | synthetic lines | text: research use; fonts OFL 1.1 / LGPL 2.1 (Khmer OS) |
| Synthetic scene sets dataset_v2, nf_mix, hard, nf_hard, long | synthetic lines | generated |
| docgen v2 / exam pages (English from 19 Project Gutenberg books, Faker identities; no real people) | synthetic lines | generated |
| DocLayNet v2 (English lines) | real lines | CDLA-Permissive 1.0 |
| Thareah/khmer-handwriting (2,891 lines) | real lines | MIT |
| SoyVitou/khmer-handwritten-dataset-4.2k (4,202 snippets) | real lines | MIT |
Part of the training text is research-use only, so the licence field is other: check that your use fits those terms. The owner's private exam photos were never used for training.
Part of ThaOCR
ThaOCR is a five-stage Khmer + English document OCR pipeline (layout, text-line detection, line recognition, font classification, page structure). Every model is plain ONNX (opset 17) and runs with onnxruntime on CPU. There is one repo per stage:
| stage | repo |
|---|---|
| layout | Thareah/ThaOCR-layout |
| textdet | Thareah/ThaOCR-textdet |
| rec | Thareah/ThaOCR-rec (this repo) |
| font | Thareah/ThaOCR-font |
| structure | Thareah/ThaOCR-structure |
The index repo Thareah/ThaOCR-models holds the full models.json manifest for all five stages.
models.rec.json in this repo lists only this stage. The file and vocab paths in both are relative to a folder into which
all stage repos are downloaded together, for example hf download Thareah/ThaOCR-rec --local-dir ThaOCR.
The pre/post-processing (letterbox + NMS, DB unclip, width bucketing + CTC, ...) is small numpy/OpenCV code in the
ThaOCR backend (backend/app/services/stages/).