StagesRecognise

PaddleTextRecognizer

Full OCR in one pass — PaddleOCR detects and reads the page, across thirteen language presets.

Import
import { PaddleTextRecognizer } from '@stabrise/scaledp/ocr'
Group
Recognise
Reads
image
Writes
document
Needs
ppu-paddle-ocr

The default OCR engine, and the best all-rounder: about 6 MB of weights, WebGPU capable, and thirteen language presets.

import { Pipeline } from '@stabrise/scaledp'
import { PdfToImage } from '@stabrise/scaledp/pdf'
import { PaddleTextRecognizer } from '@stabrise/scaledp/ocr'

const rows = await new Pipeline([
    new PdfToImage({ resolution: 300 }),
    new PaddleTextRecognizer({ preset: 'v6-small', keepFormatting: true }),
]).transform(file)

rows[0].text.text      // the recognised text
rows[0].text.bboxes    // one box per detected region
Open in builder

It detects internally

This stage detects and recognises in a single pass over the page, so boxes produced by a separate detector never reach it — rotated boxes in particular. To read exactly the regions a detector found, use TesseractRecognizer.

Document.type is 'ocr'.

Strategy

  • 'per-box' (default) — the regions as detected.
  • 'per-line' — merge boxes that share a line.
  • 'cross-line' — merge across line breaks too.

This changes how the result is cut up, not what was read.

Keeping the layout

keepFormatting: true rebuilds the page's layout in the text: indents from the left edge of each line, blank lines where vertical gaps are large, spacing derived from the modal character width. lineTolerance controls line grouping in pixels, and 0 derives it from the measured character height — which is almost always better than a fixed number, because it scales with the render DPI.

The reconstruction is a port of ScaleDP's box_to_formatted_text and is verified against Python goldens.

Language presets

v6-small is the default and covers Latin and CJK. No single model covers every script, so this is a real choice.

FamilyPresets
PP-OCRv6v6-small, v6-medium, v6-tiny
PP-OCRv5 mobilev5-latin-mobile, v5-en-mobile, v5-eslav-mobile, v5-cyrillic-mobile, v5-greek-mobile, v5-arabic-mobile, v5-devanagari-mobile, v5-korean-mobile, v5-thai-mobile, v5-tamil-mobile, v5-telugu-mobile
import { PADDLE_OCR_PRESETS, detectScript, presetsForScript } from '@stabrise/scaledp/ocr'

const script = await detectScript(canvas)          // needs tesseract.js
const options = await presetsForScript(script.script)

detectScript() reports what a page contains and presetsForScript() lists what can read it — useful for offering the user a sensible default rather than guessing.

An unknown preset throws RangeError from the constructor.

Switching presets mid-session

Handled internally: the call into ppu-paddle-ocr passes noCache: true, without which changing the preset returns the previous model's result. Worth knowing if you are debugging why a language change did nothing.

Parameters

ParameterTypeDefaultMeaning
preset'v6-small' | 'v6-medium' | 'v6-tiny' | 'v5-latin-mobile' | … (14 total)'v6-small'Language/script pairing. Pick the one matching your documents.
scoreThresholdnumber 0–10.5Drop words below this confidence.
strategy'per-box' | 'per-line' | 'cross-line''per-box'How the detected regions are grouped. It cannot subdivide them: the boxes are whatever the preset’s detector found, which is line-level.
keepFormattingbooleanfalseRebuild the original layout with spaces and blank lines.
lineTolerancenumber0Line-grouping tolerance in pixels; 0 derives it from character height.

On this page