Nau OCR 1.0 (ނައޫ)
Reads Dhivehi (Thaana) text from scanned and born-digital PDFs and images, on an ordinary CPU, and says where every line and word is on the page. Pages that arrive sideways, upside down or tilted are put right before reading.
Update, 11 October 2026: a new line cut, and seal removal
The reader now cuts lines so that every mark stays with its own line. On single-spaced pages the fili above one line sit in the same rows as the bottom of the line above. The old line finder split two such lines at a single row, so a line's marks often ended up in the strip above or in a band of their own, and the recogniser saw consonants with nothing over them. The new cut gives each connected piece of ink to the line whose letters it belongs to. It also folds a band of marks back into its line and paints each neighbour's ink out of a strip. Lines that read with all their marks wrong ("collapsed fili") fell by half on the pages below. On the full 300-page independent benchmark, its maintainers measured round 11's median error falling from 1.3% to 0.7% with the new cut.
nau_ocr.py --remove-seal takes a coloured seal out of a page before its lines are found. It
removes only a hollow, round, coloured ring 120–500 px across at 200 dpi, and the coloured ink
inside it. It leaves the ring alone if type overlaps it, and never removes black ink. It is off
unless asked for. The weights are unchanged; all figures below are with the new reader.
Update, 9 October 2026: new weights
The first weights published here (until 9 October 2026) read born-digital documents (Word and PDF exports) worse than the recogniser they were built from. They dropped full stops at the ends of lines, numbers at the start of list items, brackets, commas and some spaces. Their training lines were cut from the text layer with padding, so many line images showed marks their labels did not have. The model learned to leave out what it could see. The "printed documents" figure published with them was measured on lines made the same way, so the problem did not show.
The current weights are trained again from the original recogniser. Every born-digital training line
is cut from the whole page by the reader's own line finder. Its label is the page's own text for
exactly that strip, and a line is used only when all of its ink is covered by that text. Pages whose
text layer is damaged are left out. All scores below are on whole pages or on lines labelled that
way. The first weights stay in this repository's history, at commit 238ba8f.
Results
Whole born-digital pages, read as a user would read them, scored against each page's own text (character error rate over the page; a known line counts as found when a read line is within a quarter of its length):
| Median character error | Mean | Lines found (median) | |
|---|---|---|---|
| 210 held-out pages (documents never trained on) | |||
| Base recogniser | 0.99% | 5.6% | 100% |
| First weights | 8.95% | 12.7% | 87% |
| Current weights | 0.29% | 3.4% | 100% |
| 36 pages of an independent benchmark (chosen by others, by where each PDF came from) | |||
| Base recogniser | 5.05% | 12.1% | 86% |
| First weights | 14.3% | 19.5% | 78% |
| Current weights | 3.08% | 8.8% | 91% |
The worst pages in both sets are dense tables, where some lines are missed.
Held-out lines (character error; lines read exactly in brackets):
| Base recogniser | First weights | Current weights | |
|---|---|---|---|
| Born-digital lines (3,000) | 1.80% (83%) | 8.47% (61%) | 1.05% (88%) |
| Typed old scans (712 lines; truth: two agreeing Gemini readings) | 41.5% (4%) | 13.2% (68%) | 12.5% (67%) |
| Synthetic lines in 183 Thaana fonts (1,200) | 2.31% (68%) | 1.58% (74%) | 1.35% (77%) |
Whole scanned pages, turned and tilted at random (218 pages from every decade, each given a random quarter turn and up to ±5° tilt). Score: the share of words read that are real Dhivehi words.
| Decades | First weights | Current weights | Orientation right (first / current) |
|---|---|---|---|
| 1980s–1990s | 0.61–0.73 | 0.65–0.76 | 96–100% / 96–100% |
| 2000s–2020s | 0.90–0.97 | 0.95–0.98 | 96–100% / 96–100% |
| All | 0.72 | 0.80 | 84% / 85% |
nau_ocr.py now keeps a quarter turn only when the recogniser reads the turned page clearly better.
The ink-profile test alone turned some upright pages. Given each of four turns, it puts 116 of 120
born-digital pages and 154 of 160 scanned pages (1980s–2020s) the right way up.
What it does not read well yet
- Handwriting. Many 1960s–1980s documents are handwritten; the model has not been trained on handwriting.
- Arabic script and calligraphy (the 1930s–1940s records).
- Dense tables: lines are missed where cells crowd together.
- Text crossing table borders, and italic faces: read, but with more dropped letters than plain text.
- Output from old documents should be proofread before it is quoted.
Running it on CPU
| Model size | 6.7 MB (1.6M parameters) |
| Memory | about 1–1.5 GB per process |
| Speed, 1 CPU core | about 3.8 s per page (≈ 940 pages an hour); one process per core scales almost linearly |
| GPU | not needed |
pip install torch pymupdf pillow numpy
python nau_ocr.py document.pdf # writes document.txt and document.json
python nau_ocr.py scan.png --pages 1-3
python nau_ocr.py sealed.pdf --remove-seal # take a coloured seal out before reading
document.json holds, per page: the turn and deskew applied, the text, and every line and word with its
box as fractions of the prepared page (origin top-left). Word boxes are placed from where the model read
each character; line boxes are exact.
from nau_ocr import NauOCR, pages_of
ocr = NauOCR("recogniser.pt")
for n, page in pages_of(Path("document.pdf"), None):
result = ocr.read(page) # {"text", "lines": [{"text", "box", "words": [...]}], ...}
recogniser.pt holds the recogniser (checkpoint["model"], checkpoint["charset"]) and, beside it, the
upside-down head (checkpoint["updown"]).
Licence and attribution
CC BY-NC-SA 4.0, with a commercial licence on request.
- Non-commercial use (research, archives, teaching, public-interest projects) is free. Credit Nau OCR, and share anything you build from it under the same licence.
- Commercial use needs a commercial licence from the Nau authors; its terms include publishing the improvements you make. Ask by opening a discussion titled "Commercial licence" on this repository.
See NOTICE.
Access requires accepting these terms in the form at the top of this page.
Related: Nau 1.0 (Dhivehi chat model) and Nau Embed 1.0 (Dhivehi search embeddings).