Put your validation images in python_reference/validation/images/ and generate expected outputs into
python_reference/validation/expected/.
Example (CPU-only, stable-ish reference):
cd python_reference
uv run python generate_reference.py ./validation/images \
--relative-to ./validation/images \
--out-dir ./validation/expected \
--forceThis produces one JSON per image (plus manifest.json) that can later be consumed by JS integration tests.
When the JS OCR output differs from Python EasyOCR, use the tracing scripts to find the first step that drifts (image decode → resize/pad → normalize → detector outputs → box decode → crops → recognizer input).
Both JS and Python emit a trace directory with a shared structure:
trace.json: ordered step index (names + kinds + per-step folder)steps/<NNN>_<step_name>/meta.json: summary +sha256_rawsteps/<NNN>_<step_name>/image.png: visual artifact (when the step is image-like)steps/<NNN>_<step_name>/raw.bin/tensor.bin/boxes.bin: canonical raw bytes for stable hashing/diffingsteps/<NNN>_<step_name>/*.meta.json: dtype/layout/shape metadata for the raw artifact
From repo root:
bun run buildFrom repo root:
node examples/node-ocr.mjs ./python_reference/validation/images/Screenshot_20260201_193653.png \
--trace-dir /tmp/trace_jsNotes:
examples/node-ocr.mjsalso loads a grayscale image for recognition (to match Python EasyOCR).- Tracing only runs if the built Node bundle exports
createFsTraceWriter(so runbun run buildfirst).
From repo root:
python3 python_reference/trace_easyocr.py ./python_reference/validation/images/Screenshot_20260201_193653.png \
--trace-dir /tmp/trace_pyOptional: also store the final Python readtext() results inside the trace:
python3 python_reference/trace_easyocr.py ./python_reference/validation/images/Screenshot_20260201_193653.png \
--trace-dir /tmp/trace_py --run-readtextPrereqs:
- Your Python environment must have
easyocrinstalled (and its deps likeopencv-python,torch, etc.). - This script uses
easyocr==1.7.2APIs (but should remain mostly stable across patch versions).
From repo root:
python3 python_reference/validation/diff_traces.py \
--js /tmp/trace_js \
--py /tmp/trace_py \
--out /tmp/trace_reportTo see all drifts instead of stopping at the first:
python3 python_reference/validation/diff_traces.py \
--js /tmp/trace_js \
--py /tmp/trace_py \
--out /tmp/trace_report \
--continueThe report directory can include *_diff.png images for quick visual inspection (when Pillow is installed).
Common “first drift” patterns:
- Drift at
load_image: image decoder mismatch (color space / alpha removal / orientation). - Drift at
resize_aspect_ratioorpad_to_stride: resize interpolation or 32-stride padding mismatch. - Drift at
normalize_mean_variance: mean/std constants or formula mismatch. - Drift at
detector_raw_output_*: runtime/model/provider mismatch (or preprocessing still differs). - Drift at
threshold_and_box_decode: postprocess mismatch (connected components / dilation / min-area-rect / scaling). - Drift at crop/recognizer steps: perspective warp direction/interpolation, resize/pad policy, or recognizer input height.
To add a new checkpoint:
- Add a
traceStep(...)call inpackages/core/src/pipeline.tswith a stable step name. - Add the matching step emission in
python_reference/trace_easyocr.pywith the same step name. - Rebuild JS (
bun run build), re-run both traces, and re-run the diff.
Tip: keep step names stable and append new steps to the end to avoid breaking existing comparisons.