TesseractScriptDetector
Identifies a page's writing script and rotation with Tesseract's OSD model, and names the OCR presets that can read it.
- Import
import { TesseractScriptDetector } from '@stabrise/scaledp/ocr'- Group
- Detect
- Reads
- image
- Writes
- script
- Needs
tesseract.js
Picking the wrong recognition model does not produce slightly worse text — it produces noise. A Cyrillic page read by a Latin model comes back as plausible Latin nonsense, and nothing downstream can tell. This stage answers "what is this page written in?" before that decision, and names the PaddleOCR presets that can read the answer.
It runs Tesseract's OSD (orientation and script detection) model, which needs the
tesseract.js peer dependency. First use downloads the model (about 4 MB
compressed) plus Tesseract's legacy WASM core; the browser caches both, and the
model is kept in IndexedDB by tesseract.js itself.
import { Pipeline } from '@stabrise/scaledp'
import { PdfToImage } from '@stabrise/scaledp/pdf'
import { PaddleTextRecognizer, TesseractScriptDetector } from '@stabrise/scaledp/ocr'
const rows = await new Pipeline([
new PdfToImage(),
new TesseractScriptDetector(),
new PaddleTextRecognizer({ preset: 'v6-small' }),
]).transform(file)
rows[0].script
// { script: 'Cyrillic', script_confidence: 4.5, orientation_degrees: 0,
// presets: ['v5-eslav-mobile', 'v5-cyrillic-mobile'], ... }Letting the model follow the page
presets is not just a report. Point a PaddleOCR stage's presetCol at this
column and it takes its model from the page, per page:
new Pipeline([
new PdfToImage(),
new TesseractScriptDetector(), // writes `script`
new PaddleTextRecognizer({ presetCol: 'script' }), // reads it
])That is the only way a document whose language you do not know in advance — or
one that changes language partway through — gets the right recogniser on every
page. A fixed preset cannot: it is chosen before the first page is read.
preset stays as the fallback for pages OSD could not classify, so the stage
never leaves a page unread. Each preset the column lands on is downloaded and
kept, so a document that swings between scripts pays for both models.
Without presetCol the stage simply reports, which is useful on its own: run it
over a sample of a corpus to find out which preset that corpus wants.
suggestPresets() is the same answer as a plain function call, for when a
pipeline is more machinery than the question needs.
Why OSD rather than the recognised text
Script detection deliberately does not read the page. If it inferred the script from recognised text, it would be wrong in exactly the case it matters — the wrong model garbles the text, and a script guessed from garbled text is garbage too. OSD classifies the glyph shapes directly, so its answer is independent of whichever engine did, or failed to do, the recognition.
That independence has a cost: OSD uses Tesseract's legacy engine, which is the
only one carrying OSD data, so it needs a worker and a model of its own rather
than sharing the one TesseractOcr
already loaded.
The confidence is not a probability
script_confidence is Tesseract's own OSD score. It is unbounded — typically
somewhere between 1 and 20 — and not a 0–1 confidence like every other
scoreThreshold in this library. A value of 4.5 is a perfectly ordinary
result, not 450%.
scoreThreshold defaults to 0, which accepts whatever OSD reports. Raise it
to a few points if you would rather have "unknown" than a weak guess.
An unknown script is a result, not a failure
OSD needs a reasonable amount of text. On a blank page, a photograph, or a page
of pure tables it returns nothing, and the stage writes script: '' with an
empty presets and — importantly — an empty exception. Nothing failed.
Check script, not exception, to find out whether you got an answer.
orientation_degrees comes from the same pass at no extra cost: 0, 90, 180
or 270, telling you a scan went through the feeder sideways. The stage reports
it without rotating anything — to correct inverted lines rather than a whole
page, see
LineOrientationDetector.
Self-hosting the model
By default tesseract.js fetches its worker, its core and osd.traineddata.gz
from its own CDN. Point them at an origin you control through configure():
configure({
tesseract: {
osdWorkerPath: '/tesseract/worker.min.js',
osdCorePath: '/tesseract/core',
osdLangPath: '/tessdata',
osdGzip: false, // serving plain .traineddata, not .traineddata.gz
},
})Changing any of them rebuilds the worker on the next detection, so this can be set after the library has already run.
Parameters
| Parameter | Type | Default | Meaning |
|---|---|---|---|
scoreThreshold | number | 0 | Tesseract’s OSD score is unbounded — typically 1-20, not a 0-1 confidence. Below this the script is reported as unknown. |