StagesRecognise

TesseractRecognizer

Reads exactly the regions a detector found, straightening each one first. This is the stage that connects a detector to recognition.

Import
import { TesseractRecognizer } from '@stabrise/scaledp/ocr'
Group
Recognise
Reads
image, boxes
Writes
document
Needs
tesseract-wasm

PaddleTextRecognizer detects and recognises in a single pass, so boxes from a separate detector never reach it. This stage does the opposite: it reads exactly the boxes it is handed, cropping and straightening each one.

import { Pipeline } from '@stabrise/scaledp'
import { PdfToImage } from '@stabrise/scaledp/pdf'
import { DbnetOnnxDetector, TesseractRecognizer } from '@stabrise/scaledp/ocr'

const rows = await new Pipeline([
    new PdfToImage(),
    new DbnetOnnxDetector({ outputCol: 'boxes' }),
    new TesseractRecognizer({ inputCols: ['image', 'boxes'] }),
]).transform(file)
Open in builder

Document.type is 'tesseract-recognizer'. A missing box column produces a helpful OcrError in exception rather than an empty result.

boxLevel decides how fine the output boxes are

'region' is ScaleDP's behaviour: one box per region the detector found, carrying everything read inside it. Since the detectors here are line-level, so are those boxes.

'word' returns Tesseract's own word boxes instead, mapped back through the crop — padding, scaleFactor and the rotation included — so a word inside a skewed line comes back skewed the same way. Python has no equivalent; the words were always in the response, only their geometry was being discarded.

scoreThreshold gates the region in both modes, so switching boxLevel changes how finely the result is cut up and nothing else.

Gating each word separately would also change what was read: a word scoring 0.3 between two at 0.9 rides out on the region's mean and would vanish on its own, so the same page would yield different text depending on the box size asked for. Each word still carries its own confidence in score, to filter on downstream.

onlyRotated defaults the other way from Python

ScaleDP defaults it to true, because there the stage refines an OCR pass that already ran and skipping upright boxes is the whole point. Standalone it is the primary recognizer, and that default would return an empty document for the ordinary detector-then-recognize pipeline.

Set it to true to use the stage as a fix-up pass over boxes that already carry text.

Straightening

The crop is a perspective warp onto the box's own axes, verified readable end to end at 0, +25 and −18 degrees. Recognition quality on skewed text is then bounded by the detector: a box whose reported angle does not match the text's produces a crop that is still skewed.

padding defaults to 5, which is what ScaleDP hardcodes. scaleFactor resizes the page before cropping — useful for small text — and coordinates are divided back out, so the boxes stay in the page's own space.

detectLineOrientation classifies each crop 0°/180° and turns the inverted ones before reading, using the same ~9 MB model as LineOrientationDetector.

A Python bug not reproduced

ScaleDP sets b.conf but filters on b.score, so its scoreThreshold is applied to the detector's score rather than the recognizer's. Here it gates the recognition confidence, as the name says.

Parameters

ParameterTypeDefaultMeaning
inputColsstring[] (2)['image', 'boxes']The page image, then the detector output whose regions to read.
langstring[]['eng']Tesseract language codes, e.g. eng or eng, deu. Each needs its traineddata file.
boxLevel'region' | 'word''region'The detectors here are line-level, so “region” gives line boxes. Pick “word” to get one box per word instead.
scaleFactornumber 0.1–81Resize the page by this factor before cropping.
paddingnumber5Grow each box before cropping. ScaleDP hardcodes 5.
scoreThresholdnumber 0–10.5Drop words below this confidence.
keepFormattingbooleanfalseRebuild the original layout with spaces and blank lines.
lineTolerancenumber0Line-grouping tolerance in pixels; 0 derives it from character height.
detectLineOrientationbooleantrueClassify each crop 0°/180° and turn the inverted ones.
onlyRotatedbooleanfalseRead only rotated or inverted boxes. On, an ordinary page returns nothing.

On this page