All writing

RapidOCR vs Tesseract: Benchmarked on 14 Scanned Book Pages

Out of the box, Tesseract beat RapidOCR on scanned book spreads and ran 8× faster. With layout handling added, RapidOCR won on characters. Per-page numbers and the script.

The short answer

On 14 scanned pages from a 1959 book, Tesseract beat RapidOCR out of the box: a mean character error rate (CER) of 0.177 against 0.458, at 3.7 seconds a page against 29.5. RapidOCR's text recognition was fine. It lost because it read straight across both pages of each scanned spread, mixing the left and right pages line by line.

With image restoration, spread splitting and column ordering around RapidOCR (my B.L.A.S.T. pipeline), the measured CER was 0.108, lower than Tesseract's on average. Tesseract still won 9 of the 14 pages, got whole words right more often, and ran about 4.5× faster. B.L.A.S.T. only won on average because Tesseract fell apart on two nearly empty pages.

Everything below was run on one laptop CPU on 20 October 2026, with the same scoring code for every engine. The script is at the end. The OCR benchmark results include all four configurations, per-page scores and downloadable data.

Note: Start with the method. 14 pages from one 1959 book, CPU only, scored with the same harness for every engine. Configs A (Tesseract on the raw page), B (Tesseract on the restored image) and C (B.L.A.S.T.) were pre-registered. Config D, out-of-the-box RapidOCR on the raw page, was added after A-C were seen, because the post compares the OCR libraries rather than the full pipeline. Nothing was tuned after the results were in. The script, gold transcriptions and per-page JSON are in the B.L.A.S.T. repository. The pre-registration and the full config list are in this site's repo at data/benchmarks/2026-10-20-ocr/METHOD.md. For the step-by-step layout handling behind the pipeline result, see the scanned-book OCR guide and its pre-flight checklist.

What I compared

PipelineVersion and settingsInput
TesseractTesseract 4.1.1, LSTM, -l eng --psm 3 --oem 1the raw page image
RapidOCRrapidocr_onnxruntime 1.4.4, default models and settingsthe raw page image
B.L.A.S.T.RapidOCR plus image restoration, spread splitting and column orderingthe restored page

I also ran Tesseract on the restored images B.L.A.S.T. sees, to check the restoration step wasn't the difference. It wasn't: CER 0.187, slightly worse than on the raw images.

The corpus. 14 pages from a scanned copy of The Ideology of Pakistan and its Implementation (Javid Iqbal, 1959), each transcribed by hand. Most PDF pages in that scan are a two-page spread: left page and right page in one image. The set covers body text, a preface, chapter openings, a title page, a table of contents, endnotes, the start of the index, a mostly blank page with a hand-drawn circle on it, and a photo of the cloth back cover.

The scoring. CER is the edit distance between the engine's output and the hand transcription, divided by the transcription's length. WER is the same over words. Reading order is Kendall's tau between the word order in the output and in the transcription (1.0 means the right order). All three come from B.L.A.S.T.'s evaluation harness, used unchanged for every engine.

The machine. Intel Core i5-10210U (4 cores, 8 threads), 10 GB RAM, Ubuntu 22.04 under WSL2, CPU only. Times are for the OCR call alone, without the restoration step.


Results

PipelineMean CERMedian CERMean WERReading order (tau)Seconds / page
Tesseract 4.1.10.1770.0390.2790.9953.7
RapidOCR, out of the box0.4580.7290.6740.66829.5
B.L.A.S.T. (RapidOCR + layout)0.1080.0860.4490.99316.5

Lower is better for CER, WER and time. Higher is better for tau. Tau needs at least two lines of text in the transcription, so it is averaged over the 11 or 12 pages that have them.

Page by page, Tesseract vs B.L.A.S.T.

PageWhat's on itTesseract CERB.L.A.S.T. CER
p002title page, display type0.0390.092
p006spread, preface0.0220.042
p008table of contents0.2080.102
p009spread, chapter opening (left page blank)0.0260.056
p020spread, body text0.0300.044
p035spread, body text0.0390.067
p049spread, body text0.0270.041
p066spread, chapter opening (left page blank)0.0260.079
p069spread, body text0.0360.044
p093endnotes0.1790.117
p094spread, endnotes0.2040.121
p095spread, endnotes and two-column index0.0560.115
p096index tail, mostly blank, hand-drawn circle0.7130.132
p097photographed cloth cover0.8780.463

On ordinary prose, Tesseract wins every time. B.L.A.S.T. wins the table of contents, two of the three endnote pages, and the two pages that are hardly text at all. If you take out p096 and p097, the averages are a tie: 0.074 for Tesseract, 0.077 for B.L.A.S.T.

If you have a set of scanned spreads and want the layout step built and scored against your own pages, that is the document OCR service. The pipeline behind these numbers is B.L.A.S.T..

CER vs WER: which number should you optimize?

CER counts character edits. WER counts whole-word errors. They disagree here, and the disagreement tells you which engine fits your use.

PipelineMean CERMean WER
Tesseract 4.1.10.1770.279
B.L.A.S.T. (RapidOCR + layout)0.1080.449

The spacing example under “Where B.L.A.S.T. still loses” shows why WER is worse despite the lower CER.

That changes the choice:

  • If a human reads the output, CER is the closer measure. A missing space is ugly, but the characters are there.
  • If search, embeddings or an LLM read the output, WER matters more. A merged token like Theology,therefore is a different token, and a hyphen left inside a word breaks exact matching.
  • If you need both, plan a post-processing pass for spacing and hyphenation. I have not built that pass yet, so the WER numbers above still include those errors.

Why RapidOCR lost out of the box

RapidOCR finds text boxes and recognises each one well. It then returns them roughly top to bottom across the whole image. On a two-page spread, that means line 1 of the left page, then line 1 of the right page, then line 2 of the left page:

What the book says:            What came back:
[Left page, line 1]            Left page, line 1  Right page, line 1
[Left page, line 2]            Left page, line 2  Right page, line 2
...                            ...
[Right page, line 1]

Every word is there, but in the wrong order, so the character and word scores collapse. On the seven spreads with text on both pages, RapidOCR's CER was between 0.73 and 0.77. On the title page, the contents and the two chapter openings, where only one side has text, it was between 0.05 and 0.09. Tesseract's page segmentation (--psm 3) finds the two pages as separate blocks by itself, which is why its reading order scored 0.995 without any help.

So for scanned books, "RapidOCR vs Tesseract" is mostly a question about layout, not recognition.

What fixed it

B.L.A.S.T. puts a layout step between detection and output:

  1. Split the spread. If the image is wider than 1.1× its height, look for the vertical white gap in the middle 40 to 60% of the width and treat each side as its own page.
  2. Find columns. Within each page, cut wherever a vertical gap is wider than max(35px, 1.8 × glyph height).
  3. Rebuild lines. Group boxes into lines with a tolerance scaled to the glyph height, then sort left to right.
  4. Group blocks. Join consecutive lines into paragraphs where the gap between them is small.

The complete B.L.A.S.T. configuration, including image restoration and layout handling, scored 0.993 for reading order and 0.108 CER, compared with 0.668 and 0.458 for raw RapidOCR. This comparison does not isolate layout handling from the other pipeline changes.

RapidOCR on the whole spread

  1. 60Before entering on the subject of this
  2. 61thestructure ofthebeetlewhich dives
  3. 60chapter, I must make a few preliminary
  4. 61through the water; in the plumed seed which is
  5. 60remarks, to show how the struggle for
  6. 61wafted by the gentlest breeze; in short, we
  7. 60existencebearsonNatural Selection.It has
  8. 61see beautiful adaptations everywhere and in
  9. 60been seen in the last chapter that amongst

B.L.A.S.T., split at the gutter

  1. 60chapter, I must make a few preliminary
  2. 60remarks, to show how the struggle for
  3. 60existencebearsonNaturalSelection.Ithas
  4. 60beenseeninthelastchapterthatamongst
  5. 60organic beings in a state of nature there is
  6. 60some individual variability; indeed I am not
  7. 60aware that this has ever been disputed. It is
  8. 60immaterial for uswhether a multitude of
  9. 60doubtful forms be called species or sub-
Lines three to eleven of each read of the Darwin demo spread, with the page each line came from. Read whole, the spread alternates between pages 60 and 61; split first, page 60 reads in order. Tesseract read this spread better still (CER 0.0024 against 0.011).

Where B.L.A.S.T. still loses

Words. B.L.A.S.T.'s WER is 0.449 against Tesseract's 0.279, even though its CER is lower. RapidOCR often drops the space after punctuation and sometimes between words:

Transcription:  degree in Theology, therefore, should be high enough to include all the above
B.L.A.S.T.:     degree in Theology,therefore,
                should be high enough to include all the above
                ...subjectmay be enabled to carry onadvanced

One missing space turns two right words into one wrong one. It also keeps the line-end hyphen in "philoso-\nphy" and picks up running headers that the transcription leaves out. Each of these is fixable in post-processing, and fixing them is next on the list. Until then, if you need clean words more than clean characters, Tesseract is the better choice on pages like these.

Speed. Tesseract took 3.7 seconds a page; B.L.A.S.T. took 16.5. RapidOCR's detection and recognition models are bigger than Tesseract's LSTM, and on a 4-core laptop CPU that shows.

Tesseract wasn't tuned. I ran one standard configuration and didn't try other page-segmentation modes, a newer Tesseract 5.x build or other language models. A tuned Tesseract might close the gap on the index pages.


Which one should you use?

Your documentsWhat I would useWhy
Clean printed prose, single pages, speed mattersTesseract 4.1.1It won 9 of 14 pages and ran about 4.5x faster than B.L.A.S.T.
Scanned books with facing pages, or multi-column pagesEither engine, but only with a layout stepWithout a layout step, RapidOCR's spread output is unreadable. B.L.A.S.T. is that step around RapidOCR.
Tables of contents, endnotes, sparse or noisy pagesRapidOCR with a layout stepB.L.A.S.T. won the contents page, two endnote pages, and the two nearly blank or photographed pages.
Clean words for search or an LLMTesseract, or RapidOCR plus a spacing fixB.L.A.S.T. wins CER but loses WER to missing spaces and hyphenation.

The corpus is small: 14 pages from one book, one typeface family, English only. Treat these numbers as a reason to test on your own documents, not as a verdict.

Important: What I did not test. Tesseract 5.x, PaddleOCR, Surya, DocTR, cloud OCR services (Textract, Vision, Document AI), GPU inference, table extraction accuracy, and handwriting. I also did not tune Tesseract beyond one standard --psm 3 run. These results cover 14 English pages from one book, not OCR in general.

Run it yourself

This is the script I used. It runs inside the B.L.A.S.T. repository at commit 43590fd (see the B.L.A.S.T. case study) and reuses its gold transcriptions and scoring code.

tesseract_vs_rapidocr.py
import statistics, sys, time
from pathlib import Path

import pytesseract
from rapidocr_onnxruntime import RapidOCR

REPO = Path(".")  # the B.L.A.S.T. repository root
sys.path.insert(0, str(REPO))
from eval.gold_loader import load_all_gold
from eval.metrics import compute_cer, compute_wer

rapid = RapidOCR()

def tesseract(path):
    return pytesseract.image_to_string(path, lang="eng", config="--psm 3 --oem 1")

def rapidocr(path):
    result, _ = rapid(path)
    return "\n".join(line[1] for line in (result or []))

for name, run in [("tesseract", tesseract), ("rapidocr", rapidocr)]:
    cers, wers, secs = [], [], []
    for gold in load_all_gold(REPO / "eval" / "gold"):
        image = str(REPO / "eval" / "pages" / f"{gold.page_id}.png")
        start = time.monotonic()
        text = run(image)
        secs.append(time.monotonic() - start)
        cers.append(compute_cer(gold.flat_text, text))
        wers.append(compute_wer(gold.flat_text, text))
    print(name, f"CER {statistics.mean(cers):.3f}", f"WER {statistics.mean(wers):.3f}",
          f"{statistics.mean(secs):.1f}s/page")

The B.L.A.S.T. row comes from the repository's own harness: python eval/run.py --engine rapidocr --no-save.


Frequently Asked Questions

Is RapidOCR more accurate than Tesseract?

Not out of the box on scanned book pages. On 14 pages, Tesseract 4.1.1 scored a mean CER of 0.177 and RapidOCR 0.458, mainly because RapidOCR read across both pages of each spread. With restoration and layout handling, the B.L.A.S.T. pipeline reached 0.108, but Tesseract still won 9 of the 14 pages.

Is RapidOCR faster than Tesseract on CPU?

Not in this test. On a 4-core laptop CPU, Tesseract took 3.7 seconds a page, RapidOCR 29.5 seconds on the full spreads, and the B.L.A.S.T. pipeline 16.5 seconds on split pages.

How do you stop OCR from mixing up two columns or two pages?

Detect the gap between them before you join the text. Split the image at the widest vertical white gap, sort lines within each side top to bottom, then output the left side before the right. Tesseract's automatic page segmentation does this for simple layouts; RapidOCR needs it added.

How is Character Error Rate (CER) calculated?

CER is the Levenshtein edit distance (insertions, deletions and substitutions) between the OCR output and a hand-made transcription, divided by the number of characters in the transcription. 0.10 means about one character in ten needs fixing.

Need this in your product?

I build OCR pipelines for scans, multi-column PDFs and tables, with a benchmark and a script you can rerun on your own pages.

Document OCR

Building something like this, or stuck on one that keeps failing?

Send a brief →