RapidOCR 深度解析:三级 OCR 流水线与六种推理引擎是如何协作的
【免费下载链接】RapidOCR📄 Awesome OCR multiple programing languages toolkits based on ONNX Runtime, OpenVINO, MNN, PaddlePaddle, TensorRT and PyTorch.项目地址: https://gitcode.com/GitHub_Trending/ra/RapidOCR
RapidOCR 是一个把 PaddleOCR 系列模型搬到 ONNX Runtime、OpenVINO、TensorRT、MNN 等后端运行的开源 OCR 工具包。本文基于仓库源码与配置,拆解它"检测→方向分类→识别"三级流水线的实现机制、多引擎抽象层的设计、以及直接影响结果的关键配置参数,帮你判断它是否适合你的场景、该怎么配。
一张图片经过什么:三级流水线与输出结构
RapidOCR 的每次调用都是固定三步:文本检测(det)找出文字框、方向分类(cls)把颠倒的文字转正、识别(rec)把文字行转成文本,三级各自独立可选、各自可绑不同推理后端。最终输出一个包含框坐标、文本、置信度、以及三级各自耗时(elapse_list)的结果对象。
输入输出与坐标还原机制
从 main.py 的__call__可以看到调用链:load_img → preprocess_img → run_ocr_steps → build_final_output。输入支持路径、bytes、numpy 数组。预处理阶段会把图像缩放到min_side_len: 30与max_side_len: 2000的边界内,并记录缩放比例;识别完成后,检测框会通过map_boxes_to_original按比例映射回原图坐标系,所以你拿到的是原图尺寸下的框。
另一个值得注意的机制是竖向填充(use_vertical_padding: true):当图像宽高比超过width_height_ratio: 8时,流水线会在检测前补边,这正是它能处理竖排文字场景(仓库测试文件text_vertical_words.png)的前置处理。
识别结果的过滤逻辑
输出前有两层过滤:
- 空结果剔除:识别出空字符串的文字行,其对应的检测框、置信度、裁剪图会被同步移除,保证三级结果索引对齐。
- 置信度过滤:
Global.text_score默认 0.5,低于该值的识别结果整条丢弃。
基础用法就是下面这样,params字典可以直接覆盖配置文件中任意节点,不需要写配置文件:
from rapidocr import RapidOCR # params 可覆盖任意配置节点,det/cls/rec 三级各有独立 engine_type engine = RapidOCR(params={ "Rec.engine_type": "onnxruntime", "Global.use_cls": True, }) result = engine("path/to/img.jpg") # 返回框、文本、置信度与三级耗时多语言能力的来源
识别支持的语言由模型库决定:default_models.yaml中按"版本→任务→模型"三级索引,覆盖 ch、en、japan、korean、arabic、cyrillic、devanagari、latin、ta、te、th、el、eslav、chinese_cht 等语言。模型文件自带 SHA256 校验,首次使用时自动下载到models目录。
模型懒加载与线程安全:为什么首次调用更慢
模型不是在RapidOCR()构造时加载的,而是三级流水线中第一次真正用到该级模型时才加载,且每个模型都用双重检查锁(double-checked locking)保护,多线程并发调用不会重复建会话。
三级独立懒加载
_load_det_model、_load_cls_model、_load_rec_model三个方法结构一致:先无锁判断self.text_det is None,进入threading.Lock后再判断一次才实例化。这意味着如果你只调engine(img, use_det=False),检测模型永远不会被加载——省内存也省启动时间。
首次调用的真实成本
首次调用某一级模型时会发生三件事:按engine_type + ocr_version + task_type + lang_type + model_type五个键从default_models.yaml解析模型 URL、按 SHA256 校验下载、再构建推理会话。所以生产环境里正确姿势是长驻引擎实例,而不是每次请求新建RapidOCR对象——模型下载和会话构建是幂等的,但没必要重复。
三级可绑不同后端
配置里Det.engine_type、Cls.engine_type、Rec.engine_type是三个独立字段(默认都是 onnxruntime)。也就是说你可以让分类这种小模型留在 ONNX Runtime CPU 上跑,而把识别切到 TensorRT GPU 上,不需要为某个后端改写代码。
推理引擎层:一个抽象接口,六种后端
六后端的统一入口在 inference_engine:InferSession抽象类定义了__call__(np.ndarray) -> np.ndarray、have_key、get_character_list等契约,get_engine(engine_type)工厂按配置懒导入对应实现。上层的三级流水线只依赖这个接口,后端是可插拔的。
各后端的默认行为与关键配置
| 后端 | 默认执行设备 | 关键配置项(config.yaml) | 值得注意的机制 |
|---|---|---|---|
| onnxruntime | CPU;use_cuda: false需显式开启 | intra_op_num_threads/inter_op_num_threads、enable_cpu_mem_arena | provider 链自动回退:CUDA/DirectML/CANN/CoreML 不可用时落回 CPU 并打警告 |
| openvino | 仅 CPU(源码固定device_name="CPU") | inference_num_threads、performance_hint、num_streams | 配置经Core().set_property("CPU", ...)下发 |
| tensorrt | GPU | use_fp16: true、workspace_size: 1073741824(1GB)、det/rec/cls 形状 profile | 引擎文件按"模型名+GPU 架构+精度"命名缓存,二次启动直接反序列化 |
| pytorch | CPU;use_cuda/use_npu/use_mps开关 | device_id | 加载 .pth 模型,内置完整网络结构实现 |
| paddle | CPU;use_cuda时gpu_mem: 500 | cpu_math_library_num_threads | 加载 Paddle 原生推理格式 |
| mnn | CPU(面向移动端) | 无附加配置(mnn: {}) | 下载 .mnn 格式模型 |
数据来源:python/rapidocr/inference_engine/各后端 main.py 与python/rapidocr/config.yaml。
ONNX Runtime 的会话选项与设备回退
ONNX Runtime 后端有两个具体做法:一是graph_optimization_level固定设为ORT_ENABLE_ALL(启用全部图优化,含算子融合与常量折叠);二是线程数只有在你给出 1 到os.cpu_count()区间内的值时才生效,配置里的默认-1表示不干预、交给 Runtime 自决。设备选择上,ProviderConfig.get_ep_list()始终把 CPUExecutionProvider 放在列表末尾兜底,其他 EP 按"配置开关 + 实际可用"两个条件插入前面;verify_providers还会检查实际生效的首个 provider 是否与你的意图一致,不一致时明确告警——避免你误以为在 GPU 上跑、实际全在 CPU。
sess_opt.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL # 全量图优化 cpu_nums = os.cpu_count() if intra_op_num_threads != -1 and 1 <= intra_op_num_threads <= cpu_nums: sess_opt.intra_op_num_threads = intra_op_num_threads # 区间校验后才生效TensorRT 后端的引擎缓存机制
TensorRT 后端是整个仓库里工程化程度最高的部分:
- 缓存文件名即指纹:引擎文件命名为
{模型名}_{sm架构}_{fp16/fp32}_tf32{NVIDIA_TF32_OVERRIDE}.engine,GPU 架构或精度变了缓存自动失效,force_rebuild: false时命中缓存直接deserialize_cuda_engine。 - 动态形状 profile 与默认配置对齐:det 的
opt_shape是[1, 3, 736, 736],恰好等于检测配置的limit_side_len: 736;max 到2048×2048,对应全局max_side_len: 2000的上限。rec 的 profile 支持 batch 6、宽到 2048。 - 推理路径:输入按 max shape 预分配 host/device 双缓冲,
cudaMemcpyAsync + execute_async_v3全异步,最后一次读回时cudaStreamSynchronize,并用__enter__/__exit__支持上下文管理、显式释放 GPU 缓冲。
识别级批量推理:形状对齐、批排序与 CTC 解码
识别阶段是整个流水线里批处理逻辑最重的地方,TextRecognizer的核心动作是:把 N 条裁好的文字行图按宽高比排序、分批(rec_batch_num: 6)、对齐到同批次最大宽度后一次性过网络。
按宽高比排序的批量策略
源码里indices = np.argsort(width_list)之后才分批,注释写明排序是为了加速识别。原因很直接:宽高比相近的图 resize 后的实际有效宽度接近,同一批内的 padding 浪费最小,等效提升了 GPU/CPU 上有效计算占比。
对齐、归一化与填充
每条文字行被 resize 到固定高 48(rec_img_shape: [3, 48, 320]中的 H),宽度取max(模型默认宽 320, 本批最大宽高比对应宽),不足部分左侧填充零值;随后做/255 → -0.5 → /0.5的归一化(把像素从 [0,255] 映射到 [-1,1])。这意味着识别模型的输入宽度是动态的,rec_img_shape里的 320 只是最小基准——这也是 TensorRT rec profile 宽度允许到 2048 的原因。
字符表的两种获取路径
CTC 解码需要字符表。ONNX 模型把字符表写进了模型元数据(get_modelmeta().custom_metadata_map["character"]),推理时直接读取;MNN、PyTorch 等后端没有这个元数据,则按同一套五元组键去default_models.yaml解析dict_url下载词典文件。两条路径在get_character_dict里收敛,上层无感知。
仓库测试目录里的black_font_color_transparent.png/white_font_color_transparent.png这类样本说明透明底文字是被明确覆盖的回归场景,跑一遍python/tests即可验证你改动的后端是否在这些边界场景下表现一致。
直接影响结果的配置参数
config.yaml里真正决定"检出什么、留下什么"的参数不多,下面这份对照表全部来自默认配置,调参时优先看这里。
| 参数 | 默认值 | 直接影响 |
|---|---|---|
Global.use_cls | true | 关闭后跳过方向分类的整个推理;分类器是 0°/180° 二分类(label_list: ["0","180"]),cls_thresh: 0.9 |
Global.max_side_len/min_side_len | 2000 / 30 | 检测前图像缩放边界;过大图按 2000 上限缩放后再映射回原图,是小框召回的关键约束 |
Det.box_thresh | 0.5 | 检测概率图二值化阈值,调高 → 框更少更确定,调低 → 更多候选框进识别 |
Det.unclip_ratio | 1.6 | 检测多边形外扩比例,决定文字框松紧,影响识别阶段拿到的裁剪图内容 |
Global.text_score | 0.5 | 最终过滤线,低于它的识别结果整条不输出 |
Rec.rec_batch_num | 6 | 每批并行识别的文字行数,批量场景的主要吞吐参数 |
Global.return_word_box | false | true 时额外返回词级框,适合需要字段级定位的场景 |
线程数与性能提示该怎么设
- ONNX Runtime:
intra_op_num_threads控制单算子内部并行。对识别这种固定小 shape(高 48)的模型,算子本身的并行度有限,线程数收益取决于核数;不确定时保留-1让 Runtime 自决,这是仓库默认值。 - OpenVINO:
performance_hint是更有效的旋钮。实时交互设LATENCY,批量吞吐设THROUGHPUT(配合performance_num_requests),CPU 绑核可用enable_cpu_pinning。注意该后端固定跑 CPU,GPU 场景别选它。
按场景选后端:可执行的建议
结合上面拆解的机制,三种典型场景可以直接照抄:
实时交互(CPU 笔记本 / 桌面应用):保持默认 onnxruntime 后端即可;RapidOCR实例长驻复用(模型下载 + 会话构建只发生一次);大截图场景把Global.max_side_len从 2000 调低到 1280~1536,用检测精度换延迟——检测输入被限制在limit_side_len: 736附近,缩得越小检测越快。
GPU 服务器批量处理:切换Det/Rec的engine_type为 tensorrt,接受首次运行的引擎构建开销,之后靠引擎缓存(可指定cache_dir跨进程复用);rec 的rec_batch_num: 6与 TensorRT rec profile 的 opt batch 6 是对齐的,批量喂图时这是吞吐上限。
资源受限 / 移动端:选 mnn 后端(配置最简,mnn: {}),或把Det/Rec的model_type从默认的 small 换成default_models.yaml里列出的 tiny 档位(PP-OCRv6 提供 tiny/small/medium 三档 det 与 rec 模型),用模型档位换速度,语言支持范围以模型列表为准。
无论哪个场景,先用result.elapse_list里的三级耗时做基线:如果 rec 占大头,调rec_batch_num和模型档位;如果 det 占大头,优先调max_side_len与检测模型档位,而不是动线程数。
【免费下载链接】RapidOCR📄 Awesome OCR multiple programing languages toolkits based on ONNX Runtime, OpenVINO, MNN, PaddlePaddle, TensorRT and PyTorch.项目地址: https://gitcode.com/GitHub_Trending/ra/RapidOCR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考