Chakkhu αž…αž€αŸ’αžαž»: Khmer + English OCR

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.

  • 4.50% character error rate on the full KHOB benchmark (all 4,018 real text lines, never trained on); Netra-OCR 5.87%
  • 2.09% on held-out 1990s–2000s Cambodian government decrees (old print, typewriter, show-through)
  • a confidence (0–1) for every line: the most sure half of lines had 0.7% errors, the least sure 10% had 9%
  • about 140 ms per line on a laptop CPU, ~20 ms on a GPU
  • writes whole Khmer letter clusters (αžŸαŸ’αžαŸ’αžšαžΈ = 1 step), so subscripts and left-drawn vowels come out in typed order

Demo + free API: INGMUYLEANG/chakkhu-demo Β· Made by Ing Muyleang.

Results (KHOB, all 4,018 lines)

Model Character errors (CER) ↓ Lines fully right ↑
Tesseract 5 (khm+eng, best) 20.55% 16.5%
Netra-OCR 5.87% 56.9%
Chakkhu 4.50% 58.8%

By picture kind (CER %):

KHOB task Tesseract 5 Netra-OCR Chakkhu
clean document 12.8 5.02 2.26
sent through Messenger 14.0 6.16 2.85
scanned 13.8 4.55 2.43
phone-scanned 19.1 4.13 3.27
street, day 31.8 5.78 6.41
street, night 35.3 10.64 11.17
short lines (≀ 10 chars) 51.8 22.41 8.73
English / numbers only 40.8 21.95 24.58

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.62% and 4.59%.

Old government documents (12 held-out pages of 1993–2003 royal decrees, laws and sub-decrees, never trained on; labels: Gemini's reading, kept where it agreed with Chakkhu): previous version 3.62% β†’ 2.09% CER, 51.7% β†’ 66.8% lines fully right.

Weaker on: street signs (decorative fonts, night photos), English-only lines. Handwriting: some (2,311 real lines in training), not measured yet.

How it works

Figure 1. Chakkhu architecture Figure 2. Inside the blocks Figure 3. How Chakkhu learns and reads

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).

Use it

pip install torch pillow safetensors huggingface_hub rapidfuzz
from huggingface_hub import snapshot_download
import sys
folder = snapshot_download("INGMUYLEANG/chakkhu-ocr")
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("INGMUYLEANG/chakkhu-demo")
c.predict(handle_file("page.jpg"), "page", api_name="/read")
# β†’ {"text": …, "lines": [{"text", "confidence", "box": [x, y, w, h]}], "ms": …}

Training

About 5 hours on one GPU in total (two runs: from scratch, then a 1-hour continuation with old-document data).

  • Lines drawn by us (about half): Khmer sentences from the Akkhara project and from Khmer Wikipedia (CC BY-SA 4.0), English words, numbers, prices, dates; 35 open fonts; 9 picture styles: document, scan, Messenger-compressed, screenshot, photo, night, tiny blurry print, old print (ink bleed, worn type, patchy ink, yellow paper, show-through from the back of the page) and typewriter (each cluster struck separately: baseline bounce, uneven ribbon ink)
  • Chanrith123333/khmer_english_ocr_image_line (CC BY 4.0)
  • SoyVitou/62k-images-khmer-printed-dataset (MIT)
  • Sokheng/khmer-synthetic-ocr-v1-100k (CC BY 4.0)
  • Thareah/khmer-handwriting (MIT): real handwritten lines
  • 2,228 lines from 89 scanned pages of Cambodian laws and royal decrees, 1993–2003 (Open Development Cambodia), read by Gemini and kept where it agreed with Chakkhu. Laws, decrees and official texts are not protected by copyright in Cambodia (Law on Copyright and Related Rights, 2003, Article 10).
  • Fonts: Google Fonts, SIL Open Font License 1.1
  • Loss: 0.3 Β· CTC + 0.7 Β· cross-entropy; AdamW, bf16, batch 96
  • Test data never used for training: KHOB (MIT), 12 held-out decree pages

Thanks to the authors of these open datasets and fonts, Wikipedia contributors, Open Development Cambodia, and the KHOB benchmark authors.

License

Model weights and code: Apache-2.0. Please credit Ing Muyleang (Chakkhu).

Downloads last month
2
Safetensors
Model size
18.6M params
Tensor type
F32
Β·
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Space using INGMUYLEANG/chakkhu-ocr 1