Skip to content

How it works

Every OCR run goes through three stages behind a single Reader handle, mirroring EasyOCR’s latest pipeline:

  1. Load (imaging) — decode the input into an owned RGB8 buffer (Image), the shared DTO the rest of the pipeline works from.

  2. Detect (detect) — the CRAFT model produces region and link heat-maps at half the input resolution. These are aspect-ratio resized and ImageNet mean/variance normalized before inference, then thresholded, run through connected components, and turned into boxes. Boxes are grouped into lines, distinguishing horizontal text from rotated text.

  3. Recognize (recognize) — each detected line is cropped (a perspective transform handles rotated quads), resized to a fixed height, and normalized. Crops are batched and run through a gen2 CRNN recognizer; the resulting CTC logits ([batch, time, num_classes]) are greedy-decoded (blank = class index 0) into text, with a confidence score computed from the decode path.

  4. Inference backend (inference) — every model call — CRAFT and every recognizer — goes through one ModelBackend trait seam. The default is ort (native ONNX Runtime); tract (pure-Rust) is the WASM/Android alternative. Detection, recognition, and engine code never call a backend API directly. See Backends.

StageParameterDefault
Detectiontext_threshold0.7
Detectionlink_threshold0.4
Detectionlow_text0.4
Detectioncanvas_size2560
Detectionmag_ratio1.0
Recognitiondecodergreedy CTC
RecognitionimgH (recognizer input height)64

Full config surface and precedence rules in Configuration.

config, types, and the detection/recognition geometry code are entirely backend-agnostic — they never reference ort, tract, or candle types. Only the inference module and its ModelBackend implementations know about a specific runtime.