Files
rapidocr/docs/API.md
Cursor AgentandDaniel 3622c4598e feat: add C ABI, Python package, and CPU ISA paths
Expose the PP-OCRv6 engine through a stable C ABI and installable
shared and static libraries, plus a ctypes Python package that follows
the RapidOCR call shape. Keep AVX-512 out of the AVX2 object file and
speed up GEMM accumulate and the AArch64 GEMM tile.

Co-authored-by: Daniel <znsoft@163.com>
2026-10-04 23:40:06 +00:00

4.8 KiB

C, C++, and Python API

The inference core is unchanged: a built-in PP-OCRv6 ONNX interpreter with CPU and optional Vulkan execution. This document is the public surface other programs should call.

Build and install

git submodule update --init --recursive
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build --prefix "$PWD/install"

That produces:

  • libppocr.a (ppocr::static)
  • libppocr.so (ppocr::ppocr), SONAME libppocr.so.5
  • headers under include/ppocr/
  • lib/cmake/ppocr/ppocrConfig.cmake
find_package(ppocr REQUIRED)
target_link_libraries(app PRIVATE ppocr::ppocr)   # shared
# or ppocr::static

PPOCR_BUILD_SHARED and PPOCR_BUILD_STATIC both default to ON. Vulkan shaders are embedded only when a host glslangValidator (or the vendored Windows glslang.exe on Windows) is available. The CPU engine does not need them.

Examples:

./build/ppocr_c_api_demo det.onnx rec.onnx dict.txt image.ppm
./build/ppocr_cpp_api_demo det.onnx rec.onnx dict.txt image.ppm
./build/ppocr_c_api_demo --cpu-info

Images passed to the library are packed RGB, row-major, 8-bit. ppocr_load_ppm reads binary P6 PPM. JPEG/PNG decoding stays in the application (or in the Python package, which uses Pillow).

C ABI

include/ppocr/ppocr.h is a stable C ABI:

  • extern "C"
  • opaque ppocr_ocr*
  • plain structs for options, images, and text boxes
  • integer status codes (PPOCR_OK, PPOCR_ERR_*)
  • ppocr_create / ppocr_destroy, ppocr_result_free, ppocr_image_free
  • thread-local ppocr_last_error()
  • ppocr_options.struct_size so new fields can be appended

ppocr_options_init fills the same defaults as ppocr::Options. One handle must not be used from two threads at once. ppocr_recognize_batch preserves input order.

Runtime CPU selection is reported by ppocr_query_cpu_info. active_isa is avx512, avx2, neon, or scalar. See docs/PERFORMANCE.md for the rule that keeps AVX-512 instructions out of an AVX2-only process.

C++ API

include/ppocr/ppocr.hpp is the C++ API already used by the in-tree tools:

ppocr::Options options;
options.backend = ppocr::Backend::cpu_only;
ppocr::OCR ocr(det_path, rec_path, dict_path, options);
for (const ppocr::Result& result : ocr.Recognize(ppocr::LoadPPM(path))) {
  // result.text, result.confidence, result.bbox, result.box
}

QueryCpuInfo() and SetDetectionThresholds() are part of that header. Exceptions use std::runtime_error. The C ABI catches them.

Python package

The package name and call shape follow main:

from rapidocr import RapidOCR, EngineType, ModelType, OCRVersion

engine = RapidOCR(params={
    "Det.model_path": "PP-OCRv6_det_tiny.onnx",
    "Rec.model_path": "PP-OCRv6_rec_tiny.onnx",
    "Rec.rec_keys_path": "ppocrv6_tiny_dict.txt",
    "Det.model_type": ModelType.TINY,
    "Rec.model_type": ModelType.TINY,
    "Det.ocr_version": OCRVersion.PPOCRV6,
    "Global.text_score": 0.5,
    "Global.use_cls": False,
})
result = engine("page.png", box_thresh=0.5, unclip_ratio=1.6)
result.txts
result.scores
result.boxes          # float32 array, shape (N, 4, 2), TL TR BR BL
result.elapse         # seconds
result.to_json()
result.to_markdown()
result.vis("vis.png")

params keys use the same Section.name form as main (Det.box_thresh, Global.text_score, Rec.rec_batch_num, ...). A YAML config_path is loaded with PyYAML and then overridden by params.

File paths, URLs, bytes, and PIL images are decoded as RGB. A numpy ndarray is treated as BGR, which is what main does for cv2 images, and converted to RGB before the native engine.

Binding choice

Python calls the C ABI with ctypes. One shared library is the contract for C, for other FFI languages, and for Python, so the result struct cannot drift from a second pybind11 wrapper. scikit-build-core still builds that library into a wheel:

pip install ./python

What does not match main

main this package
ONNX Runtime, OpenVINO, Paddle, PyTorch, TensorRT, MNN built-in PP-OCRv6 interpreter only (EngineType.ONNXRUNTIME or EngineType.PPOCR_CPP)
PP-OCRv4 and v5 model zoo PP-OCRv6 ONNX detector + recognizer
angle classifier (use_cls=True by default) flag is accepted and ignored, with one warning
use_det=False / use_rec=False raises RapidOCRError
word and character boxes return_word_box warns; word_results is empty
elapse_list is [det, cls, rec] [None, None, total] because the native call is one timed region
OpenCV drawing in vis Pillow polygons

result.txts, result.scores, and result.boxes stay the fields existing callers read. text_score, box_thresh, and unclip_ratio still filter or retune a call without rebuilding the models.