| --- |
| license: apache-2.0 |
| --- |
| |
| # Introduction |
|
|
| This repository hosts [PaddleOCR PP-OCRv6](https://github.com/PaddlePaddle/PaddleOCR) (the |
| [detector](https://huggingface.co/PaddlePaddle/PP-OCRv6_small_det_safetensors) + |
| [recognizer](https://huggingface.co/PaddlePaddle/PP-OCRv6_small_rec_safetensors)) for the |
| [React Native ExecuTorch](https://www.npmjs.com/package/react-native-executorch) library, |
| exported to `.pte` for the **ExecuTorch** runtime (XNNPACK, CoreML and Vulkan backends). |
|
|
| If you'd like to run these models in your own ExecuTorch runtime, refer to the |
| [official documentation](https://pytorch.org/executorch/stable/index.html) for setup instructions. |
|
|
| PP-OCRv6 is the **primary** OCR pipeline — smallest and fastest. It ships as **one fused |
| `.pte`** per backend with a single **dynamic** `detect` and `recognize` method each (no |
| per-size method buckets). The `.pte` is a pure tensor→tensor function; all pre/post-processing |
| (resize, normalize, DBNet box decode, perspective crop, CTC decode) is the client's job and is |
| driven by `config.json`. One model covers **all languages** (18 709-entry multilingual charset). |
|
|
| ## Methods & I/O contract |
|
|
| | method | input | output | |
| |---|---|---| |
| | `detect` (DBNet) | `[1,3,H,W]` f32 RGB, **ImageNet-normalized by the client**: `(x/255 − mean)/std`, `mean=[0.485,0.456,0.406]`, `std=[0.229,0.224,0.225]` | `[1,1,H,W]` probability map (sigmoid baked) | |
| | `recognize` (SVTR) | `[1,3,48,W]` f32 RGB, client-normalized `(x/255 − 0.5)/0.5` | `[1,W/8,18709+1]` probs (softmax baked); `charset[i]` → logit `i+1`, blank = 0 | |
|
|
| **Nothing is baked for input normalization** — the client normalizes before calling. |
| Note the two methods use *different* norms (ImageNet for detect, `0.5/0.5` for recognize). |
|
|
| ## Shape discovery (companion methods) |
|
|
| Every method carries exactly **one** no-arg discovery companion: |
|
|
| - `get_dynamic_dims_<method>` — dynamic method. Returns one `int32 [rank, 3]` tensor per |
| tensor input; each row is `[min, max, step]` (static dims are `[n, n, 1]`). Any conforming |
| shape is valid. |
| - `get_enum_shapes_<method>` — enumerated method. Returns one `int32 [N, rank]` tensor per |
| tensor input; each row is a complete legal shape (cross-dimension coupling is exact — |
| a listed `1280×640` does **not** imply `640×1280`). Snap inputs to the nearest row. |
|
|
| | backend | `detect` | `recognize` | |
| |---|---|---| |
| | `xnnpack`, `vulkan` | `get_dynamic_dims_detect` → H, W ∈ `[640, 1280]` step 32 | `get_dynamic_dims_recognize` → W ∈ `[160, 1280]` step 8 (H fixed 48) | |
| | `coreml` | `get_enum_shapes_detect` → `640²`, `960²`, `1280²`, `1280×640` | `get_enum_shapes_recognize` → widths `160, 320, 480, 640, 1280` | |
|
|
| ## Backends |
|
|
| | backend | target | detect | recognize | warm latency (detect @960² / recognize) | |
| |---|---|---|---|---| |
| | `xnnpack` | CPU | fp32, true-dynamic | fp32, true-dynamic | ~574 ms / ~28 ms (Galaxy S24) | |
| | `coreml` | Apple ANE | weight-only int8, enumerated | weight-only int8, enumerated | ~12–15 ms / ~2 ms (Apple M-series ANE) | |
| | `vulkan` | Android GPU | fp16, true-dynamic (resize) | fp32 on **XNNPACK** (mixed-delegate) | ~73 ms / ~27 ms (Galaxy S24, Xclipse 940) | |
|
|
| > **Vulkan is mixed-delegate**: DBNet detects on the GPU, the SVTR recognizer runs on the CPU |
| > (XNNPACK) — the 18 709-token vocab head is not Vulkan-safe, and int8 SVTR is lossy, so the |
| > recognizer stays fp32 on CPU for correctness. |
|
|
| > **Why fp32 on CPU?** Static-activation int8 quantization is not stable across dynamic input |
| > sizes for the detector (measured broken at ≥960px — including in the old per-bucket builds); |
| > fp32 is bit-exact at every shape. |
|
|
| ## CoreML notes (iOS) |
|
|
| - The CoreML `.pte` is a **multifunction** Core ML model (`detect` + `recognize` share one |
| precompiled `.mlmodelc`). Requires **iOS 18+** and an ExecuTorch runtime ≥ 1.3 (multifunction |
| loading via `functionName`). |
| - First-ever load on a device triggers a one-time per-shape ANE specialization (OS-cached |
| afterwards) — warm each model once after install. |
|
|
| ## Compatibility |
|
|
| If you intend to use these models outside of React Native ExecuTorch, make sure your runtime is |
| compatible with the **ExecuTorch** version used to export the `.pte` files. For more details, see |
| the compatibility note in the |
| [ExecuTorch GitHub repository](https://github.com/pytorch/executorch/blob/main/runtime/COMPATIBILITY.md). |
| If you work with React Native ExecuTorch, the library constants guarantee compatibility with the |
| runtime used behind the scenes. |
|
|