Chakkhu (ចក្ខុ, "eye") reads Khmer and English text lines from pictures: documents, old printed and typewritten pages, scans, phone photos of paper, screenshots, chat pictures and signs. It is small (18.6M parameters), runs on a CPU, and tells you how sure it is about every line.
Demo + free API: muyleanging/Chakkhu-OCR-Demo · ONNX (no PyTorch, int8, 39 MB): muyleanging/Chakkhu-OCR-Khmer-ONNX · Made by Ing Muyleang.
| Model | Training data | Character errors (CER) ↓ | Lines fully right ↑ |
|---|---|---|---|
| Tesseract 5 (khm+eng, best) | 20.55% | 16.5% | |
| Netra-OCR | includes research-only data | 5.87% | 56.9% |
| Chakkhu, previous version | clean licenses only | 4.50% | 58.9% |
| Chakkhu | clean licenses only | 4.28% | 57.7% |
| Anaksor-OCR (rec-v4) | includes unlicensed / research-only data | 2.39% | 72.1% |
By picture kind (CER %):
| KHOB task | Netra-OCR | Chakkhu, previous | Chakkhu | Anaksor-OCR |
|---|---|---|---|---|
| clean document | 5.02 | 2.27 | 2.26 | 1.13 |
| sent through Messenger | 6.16 | 2.87 | 2.87 | 1.38 |
| scanned | 4.55 | 2.44 | 2.43 | 1.32 |
| phone-scanned | 4.13 | 3.27 | 3.11 | 1.45 |
| street, day | 5.78 | 6.36 | 5.64 | 2.15 |
| street, night | 10.64 | 11.18 | 10.70 | 7.95 |
| short lines (≤ 10 chars) | 22.41 | 8.49 | 9.52 | 4.33 |
| English / numbers only | 21.95 | 24.70 | 20.00 | 13.68 |
Anaksor-OCR (SVTRv2-B, fine-tuned from a large pretrained model) is the most accurate Khmer recognizer we know of; its training data includes KhmerST, khmer-hanuman-100k and KhmerSynthetic1M, which have no license or are research-only. Chakkhu is trained only on data whose license allows any use.
All models were scored by us with the same script, the same line crops and the same Khmer text normalization
(chakkhu/khnormal.py: one fixed order inside every cluster, split vowels joined, invisible characters removed —
NFC alone does not do this for Khmer). KHOB level-1 tasks 2–4 use the task-1 boxes aligned to each picture; two
pages that show a different letter were left out. The reader's CTC weight (0.5) was set before scoring; 0.3 and
0.7 give 4.37% and 4.33%. Tesseract was scored on 2026-11-10, the others on 2026-11-15/16 (KHOB release of 2026-01-31).
Old government documents: the previous version read 12 held-out pages of 1993–2003 decrees at 2.09% CER. Those test pages were not available for this version, so it was not measured again; on KHOB's government documents its error rate is the same as before.
Weaker on: night street photos, curved and vertical text (round seals, logos), very short lines; on documents
it makes a few more small slips than the previous version (a missing space, – for -). Handwriting: some (2,311
real lines in training), not measured yet.
line picture (grey, 64 px high)
→ CNN ResNet blocks: 64 × W → 2 rows × W/8 columns
→ Transformer 6 layers, d 320, sees the whole line
├→ CTC head per column: a cluster or <blank> (fast draft, letter count)
└→ decoder 3 layers, writes one Khmer cluster per step; first trained as a language model on Khmer text
Reading with both heads: the decoder writes its 4 best guesses (beam search), the CTC draft is added, and
each guess is scored by both heads (0.5 · CTC + 0.5 · decoder). A guess that repeats or skips words gets a bad
CTC score; a guess with a missing vowel gets a bad decoder score.
Confidence: agreement of the two heads × how sure the CTC columns are. Use it to decide which lines a person
(or a bigger model) should check.
Vocabulary: 3,402 tokens (Khmer clusters + Latin, digits, punctuation; rare clusters fall back to single characters, so any text can be written).
pip install torch pillow safetensors huggingface_hub rapidfuzz
from huggingface_hub import snapshot_download
import sys
folder = snapshot_download("muyleanging/Chakkhu-OCR-Khmer")
sys.path.insert(0, folder)
from chakkhu import load, read
model, tok = load(folder) # CPU; load(folder, device="cuda") for a GPU
print(read(model, tok, ["line.png"])) # one picture = one text line
print(read(model, tok, ["line.png"], confidence=True)) # [("…", 0.98)]
# read(..., beam=0) → faster, CTC head only
Whole pages: the demo has a line finder, and a free API:
from gradio_client import Client, handle_file
c = Client("muyleanging/Chakkhu-OCR-Demo")
c.predict(handle_file("page.jpg"), "page", api_name="/read")
# → {"text": …, "lines": [{"text", "confidence", "box": [x, y, w, h]}], "ms": …}
Three runs: from scratch (4 h on a rented GPU), a 1-hour continuation with old-document data, and a 6-hour continuation on an Apple M4 Pro laptop with a new sign picture style.
| Hardware | one 24 GB GPU (rented cloud machine) for runs 1–2; an Apple M4 Pro laptop (48 GB, Metal) for run 3 |
| Run 1 (from scratch) | about 4 hours · bf16 · batch 96 |
| Run 2 (continuation, + old-document lines) | 1 hour · learning rate 2e-4, warm-up 500 steps |
| Run 3 (continuation, + sign style) | 6 hours · 54k steps · 1.6M lines · learning rate 4e-4 · fp32 · batch ≤ 48 (wide lines in smaller batches) · 76 lines/s |
| Total | about 5 GPU-hours + 6 laptop-hours |
| Speed | reading ~20 ms per line on a GPU, ~140 ms on a laptop CPU |
| Parameters | 18.6M (weights 74 MB) |
Thanks to the authors of these open datasets and fonts, Wikipedia contributors, Open Development Cambodia, and the KHOB benchmark authors.
If you use Chakkhu, please cite it:
@misc{ing2026chakkhu,
author = {Ing, Muyleang},
title = {Chakkhu: A Small Khmer and English Text-Line OCR Model with Line Confidence},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/muyleanging/Chakkhu-OCR-Khmer}},
note = {Model and demo; CER 4.28\% on the KHOB benchmark}
}
Benchmark and baselines used in the results:
Model weights and code: Apache-2.0. Made by Ing Muyleang. Please credit "Chakkhu by Ing Muyleang" and cite it (above) when you use it.