ParisaOCR (پریسا اوسیآر)
Open-source OCR for printed Persian. It reads scanned book pages about as well as Google's Gemini Flash models, runs on your own machine (CPU or GPU, no internet needed), and costs a few cents per thousand pages to run.
Status: preview. It works well on printed Persian books and documents; see Known limitations. Feedback with example pages is very welcome.
فارسی · Model on Hugging Face · PyPI
Install
pip install parisaocr
Python 3.10 to 3.13. The two models (text-line detector and recognizer, 22 MB)
are included. PyTorch is installed as a dependency of Kraken; a GPU is used
when one is available, otherwise the CPU (about 5 to 10 seconds per page).
For PDF input, Poppler's command-line tools must be installed
(poppler-utils on Debian and Ubuntu, poppler on Arch, Homebrew and conda-forge).
Use
parisaocr ocr page.png # print the text
parisaocr ocr scans/ --out out # out/txt/*.txt and out/hocr/*.hocr
parisaocr ocr book.pdf --out out --format txt,hocr,jsonl
parisaocr ocr book.pdf --out out --first 10 --last 20
parisaocr ocr book.pdf --out out --format pdf # out/pdf/book.pdf: searchable
parisaocr epub book.pdf --out out # out/book.epub (experimental)
parisaocr epub book.pdf --out out --llm # the structure decided by Gemini (API key)
parisaocr epub book.pdf --out out --review # then check and fix the book in the browser
parisaocr app # all of the above on a local page in the browser
Inputs are images, directories of images, or a PDF (scanned pages are extracted losslessly — a scan set inside a larger page, or under a site's name typed over it, is drawn from the embedded image where the page places it, without the stamp — and born-digital pages rendered). Outputs: plain text (one line per printed line, columns right to left), hOCR with line and word boxes on the original page, and JSONL with text, boxes and confidences.
In the browser: parisaocr app
parisaocr app opens a local page that does all of this without the command
line. Drop a book or scans on it (or give their paths), set the options, follow
each step's progress, then preview the EPUB, read its report, fix it in the
review panel and download the results. For OCR jobs, each page's image is shown next to its
text. Every option of ocr, epub and pages is on the page, and the page
shows the equivalent command. When a language model decides a book's structure,
the app stops after the OCR, shows what that book will cost, and sends nothing
until you agree. The server listens on 127.0.0.1 only. Jobs and their files are
kept in ~/ParisaOCR (--home changes it): a job can be run again with other
options, resumed after a cancel, or deleted. The page is in Persian and English.
Searchable PDFs
--format pdf writes PDFs you can search, select and copy text in, with the
page images exactly as they were:
- PDF input:
out/pdf/NAME.pdfis the original file with an invisible text layer over each page that was read. The scans are not re-encoded; page rotation is respected. Pages that already have a text layer (born-digital pages, or scans someone OCRed before) are left as they are;--pdf-text addadds ours to them too, e.g. over a poor earlier OCR layer, and--pdf-text replaceputs ours in place of the text of every scan page. A text layer of glyph codes in another script than the book's (a broken OCR, found in many shared scans) is replaced in any case. A site's name stamped as text over every page is removed, from the page images as well as from this PDF. - Image input: one PDF per image in
out/pdf/, or all of them in one file with--merge-pdf NAME. The page size follows the image's dpi.
The text layer is stored the way word processors store Persian text (one run
per line in visual order), so PDF viewers find and copy it in reading order.
Tested with Poppler (pdftotext, used by Okular and Evince) and pdf.js
(Firefox): words and their order come out right; pdftotext sometimes moves
punctuation at the edge of a word, and pdf.js drops half-spaces when copying.
Tools that do not reorder right-to-left text (such as pdfminer) return each
line reversed, as they do for any Persian PDF.
E-books (EPUB), experimental
parisaocr epub book.pdf --out out --title "…" --author "…"
turns a scanned Persian book into an EPUB 3 e-book, out/book.epub, that
reflows on phones and e-readers. Besides reading the pages, it works out the
book from the page layout and the printed table of contents, helped by small
line-role models (gradient-boosted trees over layout features, bundled; see
MODEL_CARD.md). By default no language model is used and
nothing leaves your machine; with a Gemini API key, --llm (below) does
much better. What it builds:
- chapters, with their titles and the book's table of contents; section headings, paragraphs joined across pages, block quotes;
- footnotes as popup notes linked to their markers (numbered per page or through the book, or with asterisks);
- verse: two-hemistich lines, and poems set line by line with their stanzas;
- figures and tables as images cut from the page (
--tables htmlfor HTML tables, but OCR of table numbers is not reliable enough to trust); - the printed page numbers as the e-book's page list; pages put in book order by their printed numbers (reversed runs, duplicate scans and missing pages are found and reported);
- the book's title, author, publisher and ISBN from
--title,--authoretc. or a--metaJSON file; with--llmthey, and the translators and editors, are read from the cover, title and copyright pages.
Footnote numbers the OCR lost or cut are re-read from the page image
(PARISAOCR_NOTENUM=0 turns this off). out/book.report.md lists what was
decided and where to look first: pages
read with low confidence, notes whose marker was not found, contents entries
not found as chapters. The OCR is kept in out/book.work, so a second run
takes seconds. Set EPUBCHECK_JAR to an epubcheck jar to have the result
validated; --roles none uses the layout rules without the line-role models.
With a Gemini API key, --llm gives the best structure. Every page
image goes to Google's Gemini with its OCR lines numbered, and Gemini says
what each line is: a chapter or section heading, a footnote or endnote, a
running header, a byline, and so on. It then decides the book's outline from
all the headings found and reads the title, authors, translators and publisher
from the cover, title and copyright pages. On 10 test books that played no
part in the development (measured with 0.4.0):
| chapters found | of those right | footnotes found | of those right | |
|---|---|---|---|---|
| layout rules + line roles | 48% | 86% | 65% | 70% |
--llm |
86% | 99.5% | 89% | 99% |
Most chapters it misses are found as section headings instead: books where
the line between a chapter and a section is a judgement call. Put the key in
GEMINI_API_KEY or ~/.config/gemini/api_key. Gemini 3.8 Flash costs about
$0.004 to $0.005 a page (more for dense pages), $1.20 to $1.50 for a 300-page
book; blank pages are not sent. Before sending anything, --llm prints
what this book should cost (within about 6% on the books we measured), and
--llm-estimate prints only that, after reading the pages locally. The
answers are kept in out/book.work/gemini-…, so later runs and --review
rebuilds cost nothing.
The page images and their OCR text are sent to Google; without --llm
nothing leaves your machine.
Cheaper models through OpenRouter. --llm openrouter:VENDOR/MODEL
asks the same questions to an open model served by
OpenRouter (key in OPENROUTER_API_KEY or
~/.config/openrouter/api_key). Only providers that keep no data, take none
for training and enforce the answer's format are used. On two of our
development books with many footnotes (326 pages, 64 footnotes checked):
--llm MODEL |
chapters found | extra chapters | footnotes right¹ | 300-page book |
|---|---|---|---|---|
gemini-3.8-flash (default) |
17 of 17 | 0 | 92% | about $1.25 |
openrouter:qwen/qwen3.8-27b |
17 of 17 | 0 | 79% (85%²) | about $0.50 |
openrouter:google/gemma-4-31b-it |
17 of 17 | 2 | 88% | about $0.15 |
¹ found with the right number and linked after the right word. ² with one
provider for every page, PARISAOCR_OPENROUTER_PROVIDER='{"order": ["deepinfra/bf16"]}'; by default OpenRouter spreads pages over several
providers, and their answers differ. Two books are too few to rank models
closely; the Gemini figures above for 10 test books are the measured ones.
PARISAOCR_OPENROUTER_PROVIDER is OpenRouter's provider object as JSON: it
can name providers or exclude some, and add to the privacy settings, not
weaken them. The cost estimate is calibrated on Gemini and only a rough guide
for these models. Gemma 4 31B is also free from Google with a Gemini key
(--llm gemma-4-31b-it --llm-jobs 2), but limited to
16,000 input tokens a minute: about 6 pages a minute, 50 minutes for a
300-page book; with more jobs Google refuses requests and the run stops after
a few dozen pages (running it again continues).
Anything the converter got wrong can be fixed by hand, with or without a
language model. --review opens a review panel (127.0.0.1, nothing leaves
your machine) that shows every page with each OCR line's role as the converter
decided it (body, heading, footnote, running head, quote, verse, caption …)
and how sure it is. You can change:
- a line's role, with one key, for one line or a dragged group, and a heading's level;
- where paragraphs start;
- the footnotes: one click sets where a page's footnotes start, notes are split or merged by their numbers, and a marker is placed by clicking the word it follows (the note then links exactly there, whatever digits the OCR read);
- a misread line's text; a line the OCR missed, of any role; a line the OCR read as one with another (split it at the cursor);
- a contents page's entries, the page's type and printed number, figure pages (crop, rotation), the pictures on a page (draw or remove), tables as images or HTML;
- the parts and chapters, the book's details and its cover.
The open issues come first, least sure first: notes without a marker,
contents entries no heading matches, chapters the contents do not list, gaps
in note numbers, lines the models doubt, lines the OCR read uncertainly. The
confidence shown is measured: the footnote and heading models' probabilities
find their own mistakes well (on books they never trained on, 0.995 and 0.98
on a scale where 0.5 is chance), the OCR's weakest word on a line only
roughly (0.74). Changes are saved as you make them in out/book.review.json
(chapters in out/book.marks.json). They can be undone, are found again if
the book is OCR'd anew, and are used by every later conversion. Rebuild
takes seconds. The panel is in Persian and English, and the app shows it in
its "Review and fix" tab.
This is experimental: it was developed on 23 books (novels, a poetry anthology, histories with many footnotes, collections, a play, a book exported from Word) and measured on 10 others, and will meet layouts it gets wrong. Typical errors: a chapter title with an OCR error or cut short, a footnote linked at the end of its page instead of at its marker, an unusual heading taken for a paragraph. Multi-column pages (magazines) and dictionaries are not supported. Example pages of books it gets wrong are very welcome in the issues.
From Python:
from parisaocr import OCR
ocr = OCR() # loads the models once
print(ocr.text("page.png"))
for line in ocr.lines("page.png"): # reading order
print(line.bbox, line.conf, line.text)
Accuracy
Measured on material the model never saw in training.
| Test set | ParisaOCR 0.1 | Gemini 3.8 Flash | Tesseract fas_print¹ |
|---|---|---|---|
| 32 book pages from 11 books, verified text: words found | 96.7% | 94.7% | 94.7% |
| 750 lines from the same books: character error, ignoring spaces | 0.39% | 0.36% | 1.19% |
| same lines: word error | 6.1% | 7.9% | 7.3% |
| 74 printed pages of PersianML/persian-ocr-benchmark: words found | 77.9% | 81.7% | 71.1% |
| cost per 1,000 pages | about $0.14 (consumer GPU at $0.60/h) | $2.86 (list price) | about $0.10 |
¹ Our earlier Persian Tesseract model (tessdata_contrib), reading the lines found by the Surya detector.
On the benchmark the gap to Gemini comes mostly from very small, low-resolution scans (lines under about 16 pixels high), where Gemini reads about 78% of the words and ParisaOCR 74%. Many benchmark pages are hard even for a human reader, and its page-level character error (about 40% for every reader) mostly measures reading order on multi-column pages.
The verified test set is small (750 lines from 11 books), so treat the numbers as a guide rather than a precise ranking. Details in MODEL_CARD.md.
Known limitations
- Printed Persian only. Handwriting, Nastaliq calligraphy, manuscripts and Urdu are not supported.
- Very small text (lines under about 14 pixels high, i.e. low-resolution scans) loses accuracy; ParisaOCR prints a warning for such pages. Scan at 300 dpi when you can.
- Layout is simple. Columns are read right to left and top to bottom; tables, captions and complex magazine layouts may come out in the wrong order. Text rotated sideways is not read.
- Small ornaments (a box, a dingbat) are sometimes read as a letter.
- Spacing conventions. The model follows standard Persian spelling for half-spaces (ZWNJ) and sometimes differs from the printed spacing.
- Diacritics (vowel marks) are read less reliably than letters.
How it works
- Lines. The PP-OCRv6 text detector (PaddleOCR, Apache-2.0) finds the text lines, run with ONNX Runtime.
- Crops. Each line is cut out with a margin that keeps the dots above and below the letters; neighbouring lines inside the margin are painted over.
- Reading. A Kraken recognition model (PP-OCR-style architecture, CTC) trained for Persian print reads each line. ParisaOCR turns the model's visual-order output into reading order itself, keeping the half-space (ZWNJ), and with each line's own direction, so English lines and footnotes come out right.
- Order. Lines are grouped into columns, read right to left.
The model was trained on about 83,000 lines from about 200 scanned Persian books, labelled automatically with Gemini, plus about 90,000 synthetic lines (Persian and English text, special characters, low-resolution copies). See MODEL_CARD.md, and the modules in docs/architecture.md.
Other models and detectors
--model accepts another Kraken model (kraken:model.safetensors) or a
Tesseract model (TESSDATA_DIR:LANG); --detector accepts other PP-OCR sizes
(ppocr:v6-medium), Kraken's segmenter (kraken), or Surya (surya, after
pip install parisaocr[surya]; note that Surya's weights are free only for
research, personal use and small companies). parisaocr cut cuts the lines of
scanned books for building training data.
Feedback
Please open an issue with the page (only if you are allowed to share it), the command you ran, and what came out wrong.
License
Apache License 2.0, for the code and the models. The bundled PP-OCRv6 detector is PaddleOCR's (Apache-2.0); see NOTICE.
فارسی
پریسا اوسیآر یک اوسیآر متنباز برای متن چاپی فارسی است. صفحههای اسکنشدهٔ کتاب را تقریباً هماندازهٔ مدلهای Gemini Flash گوگل میخواند، روی رایانهٔ خودتان اجرا میشود (بدون نیاز به اینترنت، با CPU یا GPU) و هزینهٔ اجرایش برای هر هزار صفحه چند سنت است.
وضعیت: پیشنمایش. روی کتابها و اسناد چاپی فارسی خوب کار میکند. محدودیتها را پایینتر ببینید. بازخورد همراه با صفحهٔ نمونه بسیار ارزشمند است.
نصب:
pip install parisaocr
استفاده:
parisaocr ocr page.png # چاپ متن
parisaocr ocr book.pdf --out out # out/txt و out/hocr
parisaocr ocr book.pdf --out out --format pdf # out/pdf/book.pdf: پیدیاف جستوجوپذیر
parisaocr app # همهٔ کارها در مرورگر
با parisaocr app صفحهای در مرورگر باز میشود که همهٔ این کارها را بدون خط فرمان انجام
میدهد: کتاب یا صفحههای اسکنشده را روی آن رها کنید (یا مسیرشان را بدهید)، گزینهها را
انتخاب کنید و پیشرفت هر مرحله را ببینید؛ سپس کتاب را پیشنمایش کنید، گزارش را بخوانید، فصلها
را علامت بزنید و نتیجه را دریافت کنید. برای اوسیآر، تصویر هر صفحه کنار متنش نشان داده
میشود. همهٔ گزینههای خط فرمان در صفحه هست و فرمان معادل هم نشان داده میشود. اگر ساختار
کتاب را مدل زبانی تعیین کند، پس از اوسیآر هزینهٔ همان کتاب نشان داده میشود و تا تأیید
نکنید چیزی فرستاده نمیشود. برنامه فقط روی رایانهٔ خودتان (127.0.0.1) در دسترس است و کارها
و فایلهایشان در پوشهٔ ~/ParisaOCR میمانند. صفحه فارسی و انگلیسی است.
با --format pdf خروجی یک پیدیاف جستوجوپذیر است: تصویر صفحهها همان است که بود و
یک لایهٔ متن نامرئی روی آن قرار میگیرد، تا بتوانید در آن جستوجو کنید و متن را انتخاب
و کپی کنید.
کتاب الکترونیکی (EPUB، آزمایشی):
parisaocr epub book.pdf --out out --title "…" --author "…"
parisaocr epub book.pdf --out out --llm # ساختار کتاب با Gemini
parisaocr epub book.pdf --out out --review # سپس بازبینی و اصلاح کتاب در مرورگر
از کتاب اسکنشده یک کتاب EPUB میسازد که روی گوشی و کتابخوان خوانده میشود: فصلها
و فهرست، پاراگرافها، پانویسها (با پیوند به شمارهٔ پانویس در متن)، شعر، تصویرها و
جدولها، و شمارهٔ صفحههای چاپی. گزارشی هم (out/book.report.md) مینویسد که نشان
میدهد کجا را باید بررسی کرد. این قابلیت آزمایشی است؛ اگر کتابی درست تبدیل نشد، لطفاً
در بخش Issues خبر بدهید.
اگر کلید API برای Gemini دارید، با --llm ساختار کتاب بسیار بهتر درمیآید: تصویر هر
صفحه همراه با سطرهای شمارهخوردهٔ اوسیآر به Gemini گوگل فرستاده میشود تا بگوید هر سطر
چیست (عنوان فصل یا زیربخش، پانویس، سرصفحه و …)؛ سپس ساختار کل کتاب و مشخصات آن
(عنوان، نویسنده، مترجم، ناشر) را از روی همهٔ عنوانها و صفحههای جلد و شناسنامه تعیین
میکند. روی ۱۰ کتاب آزمون که در ساختن ابزار به کار نرفته بودند، ۸۶٪ فصلها و ۸۹٪
پانویسها را پیدا کرد، در برابر ۴۸٪ و ۶۵٪ بدون آن. کلید را در GEMINI_API_KEY یا در
فایل ~/.config/gemini/api_key بگذارید. هزینه با Gemini 3.8 Flash برای هر صفحه حدود ۰٫۰۰۴
تا ۰٫۰۰۵ دلار است (۱٫۲ تا ۱٫۵ دلار برای کتابی ۳۰۰ صفحهای). پیش از ارسال، هزینهٔ تقریبی همان
کتاب چاپ میشود، و با --llm-estimate فقط همین برآورد را میبینید و چیزی فرستاده نمیشود.
پاسخها نگه داشته میشوند، پس اجرای دوباره هزینهای ندارد. با این گزینه تصویر صفحهها و متن اوسیآر به گوگل فرستاده میشود؛
بدون آن هیچ چیز از رایانهٔ شما بیرون نمیرود.
مدلهای ارزانتر از راه OpenRouter: با --llm openrouter:qwen/qwen3.8-27b (حدود
۰٫۵ دلار برای کتابی ۳۰۰ صفحهای) یا --llm openrouter:google/gemma-4-31b-it (حدود ۰٫۱۵
دلار) همین پرسشها از یک مدل باز پرسیده میشود. کلید را در OPENROUTER_API_KEY یا فایل
~/.config/openrouter/api_key بگذارید. فقط ارائهدهندههایی به کار میروند که دادهای نگه
نمیدارند و برای آموزش استفاده نمیکنند. روی دو کتاب پرپانویس، هر دو مدل همهٔ فصلها را پیدا
کردند و ۷۹ تا ۸۸ درصد پانویسها را درست پیوند دادند (Gemini: ۹۲ درصد).
با --review صفحهٔ بازبینی در مرورگر خودتان باز میشود (هیچ چیز از رایانه بیرون نمیرود).
هر صفحه با سطرهایش نشان داده میشود: مبدل هر سطر را چه دانسته (متن، عنوان، پانویس،
سرصفحه، نقلقول، شعر و …) و چقدر به آن مطمئن است. هر چیزی را میتوانید درست کنید:
نقش هر سطر (با یک کلید)، سطح عنوان، آغاز بند، پانویسها (با یک کلیک جای شروع پانویسها،
شمارهٔ هر پانویس، و جای نشانهٔ پانویس با زدن روی واژهای که بعد از آن میآید)، متنی که
اوسیآر بد خوانده، سطری که نخوانده، سطری که با سطر دیگری یکی خوانده (شکستن سطر در جای
مکاننما)، مدخلهای فهرست، نوع و شمارهٔ چاپی صفحه، صفحههای
تصویر (برش و چرخش)، تصویرهای یک صفحه، جدولها، فصلها و بخشها، مشخصات کتاب و جلد.
موردهای مشکوک اول میآیند و نامطمئنترینها جلوتر: پانویسهایی که نشانهشان پیدا نشده،
مدخلهای فهرستی که عنوانی برایشان نیست، سطرهایی که مدلها یا اوسیآر به آنها مطمئن نیستند.
اصلاحها همان لحظه در out/book.review.json ذخیره میشوند، واگردپذیرند و در همهٔ تبدیلهای
بعدی به کار میروند؛ بازسازی کتاب چند ثانیه طول میکشد. در برنامهٔ مرورگری (parisaocr app)
همین صفحه در زبانهٔ «بازبینی و اصلاح» است.
محدودیتها:
- فقط متن چاپی فارسی. دستنوشته، خط نستعلیق، نسخهٔ خطی و اردو پشتیبانی نمیشوند.
- متن خیلی ریز (اسکن با وضوح پایین) دقت کمتری دارد. در صورت امکان با ۳۰۰ dpi اسکن کنید.
- چیدمان صفحه ساده خوانده میشود. جدولها و صفحههای مجلهای پیچیده ممکن است ترتیب درستی نداشته باشند.
- نیمفاصلهها طبق املای استاندارد نوشته میشوند و گاهی با چاپ کتاب فرق دارند.
- اِعرابها کمتر از حروف دقیق خوانده میشوند.
بازخورد: لطفاً در بخش Issues صفحه (فقط اگر اجازهٔ انتشارش را دارید)، فرمانی که اجرا کردید و خطای خروجی را بفرستید.
مجوز: Apache 2.0، برای کد و مدلها.
Metadata
Release files for parisaocr 0.7.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| parisaocr-0.7.1.tar.gz | 23.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| parisaocr-0.7.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.4 MB
Release files / parisaocr-0.7.1.tar.gz
| Download URL | parisaocr-0.7.1.tar.gz |
|---|---|
| Size | 23.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
44891dfb37c02806cfa3746ad1c1c2d0a6a7f6a586b5653d7cc0e81daa549d2d
|
|
BLAKE2b-256 checksum How to use checksums |
d6420d7794ff027a10392f35286ace74821f113f69a1cb8b69110aaf9cabf7d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / parisaocr-0.7.1-py3-none-any.whl
| Download URL | parisaocr-0.7.1-py3-none-any.whl |
|---|---|
| Size | 23.7 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
96edddb50e3fb043cf229d3abb5a003813c10a36ae6c986bd019863fcd686b03
|
|
BLAKE2b-256 checksum How to use checksums |
ab41cf1b7e8f01e604e489ea7cb4a9a1c4ea01bdf6d8b624211553d5a4228cfa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|