PP-OCRv6 Small for LiteRT β€” Android GPU FP32

Run text detection and recognition locally with the official PP-OCRv6 Small weights converted through Google LiteRT Torch. The validated Android configuration is LiteRT 2.2.0 with explicit GPU FP32 computation on a Samsung Galaxy S26 (SM-S942Q, SM8850, Android 16). It passes all 37 identical-input tensor cases and all six image-to-text pipeline cases against the pinned official CPU implementation. Other Android GPU families have not been validated here.

Synthetic OCR comparison with known missing spaces retained

Synthetic text fixture and actual host official CPU / host LiteRT CPU results. This is not a phone screenshot or a real-world accuracy benchmark. The two omitted spaces are retained. The original fixture artwork and comparison are included under Apache-2.0; no personal document or font binary is distributed.

Files and supported configuration

File FP32 NCHW input FP32 output Bytes
ppocrv6_small_det_640_fp32.tflite [1,3,640,640] [1,1,640,640] 12,380,664
ppocrv6_small_rec_320_fp32.tflite [1,3,48,320] [1,40,18710] 21,477,068
ppocrv6_small_rec_640_fp32.tflite [1,3,48,640] [1,80,18710] 21,477,068
ppocrv6_small_rec_960_fp32.tflite [1,3,48,960] [1,120,18710] 21,476,976
characters.json β€” Complete 18,710-entry CTC vocabulary β€”

The outputs are probabilities; do not apply another softmax. The dictionary already contains blank at index 0 and space at the final index. Greedy CTC collapses adjacent repeated tokens, then drops blank. Do not append tokens or trim the decoded whitespace.

FP32 model storage and GPU computation precision are separate settings. The same four files passed only 27/37 tensor cases with the runtime's default GPU precision setting, then 37/37 with explicit FP32. No weights, fixtures or gate thresholds were changed. Use the explicit option below. FP16 and INT8 artifact variants were not evaluated. NPU execution completed, but its parity failed (19/37 tensor cases and 0/6 complete image cases); NPU is not a supported configuration for this release.

Minimal usage

Download the repository files, including references/, examples/, and the dictionary, using hf download litert-community/PP-OCRv6-Small-LiteRT --local-dir ppocrv6. The integration recipe explains Android dependencies, packaging, model lifetimes, exact preprocessing and host postprocessing.

Python β€” complete pipeline, desktop CPU

Use Python 3.12 and the pinned requirements. This path uses LiteRT CompiledModel CPU for both neural networks and the exact pinned official processors for image preparation and DB polygons.

from pathlib import Path
from examples.run_ocr import Ocr

# Run from the downloaded repository after installing requirements-lock.txt.
# Input is a square RGB image; this fixture is exactly 640x640.
root = Path(".")
with Ocr(root) as ocr:
    for region in ocr(root / "fixtures/det/japanese.png"):
        print(region["polygon"], region["text"])

The underlying calls load CompiledModel with explicit CPU selection, prepare NCHW tensors, write inputs, run inference, read outputs, extract DB polygons, perspective-crop each line and decode CTC. See the complete example and exact processing utilities. Python GPU execution is not claimed by this example.

Kotlin β€” Android GPU with explicit FP32

Use implementation("com.google.ai.edge.litert:litert:2.2.0") and copy the three Kotlin helper files from examples/android/. Stage the four model files and characters.json in your application's private model directory. Run the entire GPU lifetime on the same worker thread.

import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
import com.ppocrv6.litert.OcrPreprocessing
import com.ppocrv6.litert.PpocrV6Gpu
import java.io.File

// cropRgb is one upright RGB uint8 text-line crop, at most natural width320.
// Pick rec_640 or rec_960 for wider crops; see PpocrV6Gpu.recognize().
fun recognizeLine(modelDir: File, cropRgb: ByteArray, cropWidth: Int, cropHeight: Int): String {
    val options = CompiledModel.Options(setOf(Accelerator.GPU)).apply {
        gpuOptions = CompiledModel.GpuOptions(
            precision = CompiledModel.GpuOptions.Precision.FP32)
    }
    val vocabulary = PpocrV6Gpu.loadVocabulary(File(modelDir, "characters.json"))
    CompiledModel.create(File(modelDir, "ppocrv6_small_rec_320_fp32.tflite").path,
        options, null).use { rec ->
        val inputs = rec.createInputBuffers()
        val outputs = rec.createOutputBuffers()
        try {
            inputs[0].writeFloat(OcrPreprocessing.rec(cropRgb, cropWidth, cropHeight, 320))
            rec.run(inputs, outputs)
            val probabilities = outputs[0].readFloat() // Includes completion/readback.
            return PpocrV6Gpu.decodeCtc(probabilities, vocabulary).text
        } finally {
            inputs.forEach { it.close() }
            outputs.forEach { it.close() }
        }
    }
}

For DET, PpocrV6Gpu.detect(rgb640) applies exact preprocessing and returns the 640x640 probability map. DB polygon extraction and perspective crops run on the host before recognize(). Their algorithm and reference implementation are included; the minimal Kotlin helper is not a complete camera application. It propagates GPU failures and has no implicit CPU retry.

Preprocessing contract

  • DET uses square640, float32 NCHW, the official RGB-to-BGR ordering and mean/std normalization. The exact fused arithmetic is preserved in the supplied helpers.
  • DB thresholds are probability 0.2, box score 0.45, unclip ratio 1.4, maximum 3,000 candidates, and minimum size 3.
  • Crops follow PaddleX minimum-area rectangle and cubic perspective warp with replicated borders, including rotation of tall crops and the documented reading order.
  • REC keeps aspect ratio at height48, selects width320/640/960, uses the official uint8 bilinear resize, BGR, (pixel-127.5)/127.5, and normalized-zero right padding. Natural widths over960 are rejected rather than compressed.

These static overrides differ from upstream's default dynamic resize policy. This release does not claim default PaddleOCR pipeline equivalence. Its official oracle is the pinned Hugging Face implementation; an independent Paddle-native oracle was not measured.

Measured quality and performance

CPU and explicitly FP32 GPU passed 37/37 tensor cases and 6/6 complete Android image pipelines. All CPU/GPU detection polygons and CTC strings matched the official oracle; GPU maximum absolute error was 0.00009996. Own-process runtime logs established complete OpenCL delegation for all four GPU graphs.

The six synthetic pages contain 14 labeled regions. Detection produced 14 true positives, 3 false positives and 0 false negatives: precision82.35%, recall100%. The circle, triangle and rectangle on the nontext page produced O, V, _. Two spaces disappeared on the mixed-numbers page. Full-pipeline region CER was 5/181 = 2.7624%; standalone positive recognition was 12/12 exact, CER0/161. These errors occur in the official model too. The predeclared perfect-quality smoke gate remains FAIL. Conversion parity does not establish general OCR accuracy; see evaluation details, machine-readable evidence, and the nontext failure example.

Graph S26 CPU median ms S26 GPU FP32 median ms
DET640 61.593 14.221
REC320 4.659 3.435
REC640 8.774 5.393
REC960 15.745 7.461

LiteRT2.2.0, Android16, separate fresh backend processes, five timed samples per fixture after warm-up. Graph timings include run() and output readback. For the Japanese fixture, complete image-to-text medians were CPU181.432ms and GPU117.418ms, including image decoding, preprocessing, DET, DB/crops, REC/CTC and all readbacks. Model initialization and saved-evidence I/O are excluded from those warm timings. This is a small fixture measurement, not a device benchmark. Cache-free cold initialization was not established. Runtime2.1.6, Pixel/Mali GPUs, non-Android GPU platforms and real-camera accuracy were not validated by these trials.

Provenance, conversion and license

Official Apache-2.0 models:

They contain 2,475,373 and 5,292,614 stored FP32 values including running statistics; learned parameter counts are 2,460,709 and 5,275,238. No retraining was performed. Training-data details are inherited from the upstream PP-OCRv6 report; this conversion did not access or independently audit the original training corpus. The released evaluation fixtures are synthetic and contain no private user documents or personal records.

The conversion recipe includes strict weight loading, exact forward rewrites, Google LiteRT Torch export and executable numerical/task gates with negative controls. Dependency versions, source revisions and hashes are in upstream.json; all distributed files are listed in manifest.json and SHA256SUMS.

See LICENSE and third-party notices. The PyTorch-derived resize helper retains its accompanying BSD terms. This is a community conversion, not an official PaddlePaddle or Google release.

Downloads last month
45
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for litert-community/PP-OCRv6-Small-LiteRT

Finetuned
(2)
this model

Paper for litert-community/PP-OCRv6-Small-LiteRT