|
| 1 | +--- |
| 2 | +title: RapidOCR集成PP-OCRv5_mobile_det模型记录 |
| 3 | +date: 2025-05-26 |
| 4 | +authors: [SWHL] |
| 5 | +categories: |
| 6 | + - 模型相关 |
| 7 | +comments: true |
| 8 | +hide: |
| 9 | + - toc |
| 10 | +--- |
| 11 | + |
| 12 | + |
| 13 | +> 该文章主要记录RapidOCR集成PP-OCRv5_mobile_det模型记录的,涉及模型转换,模型精度测试等步骤。 |
| 14 | +
|
| 15 | +<!-- more --> |
| 16 | + |
| 17 | +### 引言 |
| 18 | + |
| 19 | +### 以下代码运行环境 |
| 20 | + |
| 21 | +- OS: macOS Sequoia 15.5 |
| 22 | +- Python: 3.10.14 |
| 23 | +- PaddlePaddle: 3.0.0 |
| 24 | +- paddle2onnx: 2.0.2.rc1 |
| 25 | +- paddlex: 3.0.0 |
| 26 | +- rapidocr: 2.1.0 |
| 27 | + |
| 28 | +### 1. 模型跑通 |
| 29 | + |
| 30 | +该步骤主要先基于PaddleX可以正确使用PP-OCRv5_mobile_det模型得到正确结果。 |
| 31 | + |
| 32 | +该部分主要参考文档:[docs](https://paddlepaddle.github.io/PaddleX/latest/module_usage/tutorials/ocr_modules/text_recognition.html#_3) |
| 33 | + |
| 34 | +安装`paddlex`: |
| 35 | + |
| 36 | +```bash linenums="1" |
| 37 | +pip install "paddlex[ocr]==3.0.0rc1" |
| 38 | +``` |
| 39 | + |
| 40 | +测试PP-OCRv5_mobile_det模型能否正常识别: |
| 41 | + |
| 42 | +测试用图: |
| 43 | + |
| 44 | + |
| 45 | + |
| 46 | +!!! tip |
| 47 | + |
| 48 | + 运行以下代码时,模型会自动下载到 **/Users/用户名/.paddlex/official_models** 下。 |
| 49 | + |
| 50 | +```python linenums="1" |
| 51 | + |
| 52 | +from paddleocr import PaddleOCR |
| 53 | +# 初始化 PaddleOCR 实例 |
| 54 | + |
| 55 | +ocr = PaddleOCR( |
| 56 | + use_doc_orientation_classify=False, |
| 57 | + use_doc_unwarping=False, |
| 58 | + use_textline_orientation=False) |
| 59 | + |
| 60 | +# 对示例图像执行 OCR 推理 |
| 61 | +result = ocr.predict( |
| 62 | + input="https://paddle-model-ecology.bj.bcebos.com/paddlex/imgs/demo_image/general_ocr_002.png") |
| 63 | + |
| 64 | +# 可视化结果并保存 json 结果 |
| 65 | +for res in result: |
| 66 | + res.print() |
| 67 | + res.save_to_img("output") |
| 68 | + res.save_to_json("output") |
| 69 | +``` |
| 70 | + |
| 71 | +预期结果如下,表明成功运行: |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +### 2. 模型转换 |
| 76 | + |
| 77 | +该部分主要参考文档: [docs](https://paddlepaddle.github.io/PaddleX/latest/pipeline_deploy/paddle2onnx.html?h=paddle2onnx#22) |
| 78 | + |
| 79 | +PaddleX官方集成了paddle2onnx的转换代码: |
| 80 | + |
| 81 | +```bash linenums="1" |
| 82 | +paddlex --paddle2onnx --paddle_model_dir models/PP-OCRv5_mobile_det --onnx_model_dir models/PP-OCRv5_mobile_det |
| 83 | +``` |
| 84 | + |
| 85 | +输出日志如下,表明转换成功: |
| 86 | + |
| 87 | +```bash linenums="1" |
| 88 | +Input dir: models/PP-OCRv5_mobile_det |
| 89 | +Output dir: models/PP-OCRv5_mobile_det |
| 90 | +Paddle2ONNX conversion starting... |
| 91 | + warnings.warn(warning_message) |
| 92 | +[Paddle2ONNX] Start parsing the Paddle model file... |
| 93 | +[Paddle2ONNX] Use opset_version = 7 for ONNX export. |
| 94 | +[Paddle2ONNX] PaddlePaddle model is exported as ONNX format now. |
| 95 | +2025-05-14 08:21:23 [INFO] Try to perform optimization on the ONNX model with onnxoptimizer. |
| 96 | +2025-05-14 08:21:23 [INFO] ONNX model saved in models/PP-OCRv5_mobile_det/inference.onnx. |
| 97 | +Paddle2ONNX conversion succeeded |
| 98 | +Done |
| 99 | +``` |
| 100 | + |
| 101 | +### 3. 模型推理验证 |
| 102 | + |
| 103 | +该部分主要是在RapidOCR项目中测试能否直接使用onnx模型。要点主要是确定模型前后处理是否兼容。从PaddleX[官方文档](https://paddlepaddle.github.io/PaddleX/latest/module_usage/tutorials/ocr_modules/text_recognition.html#_2)中可以看到: |
| 104 | + |
| 105 | +> PP-OCRv5_mobile_det是在PP-OCRv4_server_rec的基础上,在更多中文文档数据和PP-OCR训练数据的混合数据训练而成,增加了部分繁体字、日文、特殊字符的识别能力,可支持识别的字符为1.5万+,除文档相关的文字识别能力提升外,也同时提升了通用文字的识别能力 |
| 106 | +
|
| 107 | +以上说明了该模型与PP-OCRv4_server_rec模型结构相同,前后处理也相同。唯一做的就是添加了更多数据,扩展了字典个数,从6623扩展到15630个。因此,可以直接使用RapidOCR来快速推理验证。代码如下: |
| 108 | + |
| 109 | +```python linenums="1" |
| 110 | +from rapidocr import RapidOCR |
| 111 | + |
| 112 | +model_path = "models/PP-OCRv5_mobile_det/inference.onnx" |
| 113 | +key_path = "models/ppocrv4_doc_dict.txt" |
| 114 | +engine = RapidOCR(params={"Rec.model_path": model_path, "Rec.rec_keys_path": key_path}) |
| 115 | + |
| 116 | +img_url = "https://img1.baidu.com/it/u=3619974146,1266987475&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=516" |
| 117 | +result = engine(img_path) |
| 118 | +print(result) |
| 119 | + |
| 120 | +result.vis("vis_result.jpg") |
| 121 | +``` |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | +### 4. 模型精度测试 |
| 126 | + |
| 127 | +该部分主要使用[TextRecMetric](https://github.com/SWHL/TextRecMetric)和测试集[text_rec_test_dataset](https://huggingface.co/datasets/SWHL/text_rec_test_dataset)来评测。 |
| 128 | + |
| 129 | +需要注意的是,**PP-OCRv5_mobile_det模型更加侧重生僻字和一些符号识别。** 当前测试集并未着重收集生僻字和一些符号的数据,因此以下指标会有些偏低。如需自己使用,请在自己场景下测试效果。 |
| 130 | + |
| 131 | +相关测试步骤请参见[TextRecMetric](https://github.com/SWHL/TextRecMetric)的README,一步一步来就行。我这里测试最终精度如下: |
| 132 | + |
| 133 | +```json |
| 134 | +{'ExactMatch': 0.8097, 'CharMatch': 0.9444, 'avg_elapse': 0.0818} |
| 135 | +``` |
| 136 | + |
| 137 | +该结果已经更新到[开源OCR模型对比](./model_summary.md)中。 |
| 138 | + |
| 139 | +### 5. 集成到rapidocr中 |
| 140 | + |
| 141 | +该部分主要包括将字典文件写入到ONNX模型中、托管模型到魔搭、更改rapidocr代码适配等。 |
| 142 | + |
| 143 | +#### 字典文件写入ONNX模型 |
| 144 | + |
| 145 | +该步骤仅存在文本识别模型中,文本检测模型没有这个步骤。 |
| 146 | + |
| 147 | +??? info "详细代码" |
| 148 | + |
| 149 | + ```python linenums="1" |
| 150 | + from pathlib import Path |
| 151 | + from typing import List, Union |
| 152 | + |
| 153 | + import onnx |
| 154 | + import onnxruntime as ort |
| 155 | + from onnx import ModelProto |
| 156 | + |
| 157 | + |
| 158 | + def read_txt(txt_path: Union[Path, str]) -> List[str]: |
| 159 | + with open(txt_path, "r", encoding="utf-8") as f: |
| 160 | + data = [v.rstrip("\n") for v in f] |
| 161 | + return data |
| 162 | + |
| 163 | + |
| 164 | + class ONNXMetaOp: |
| 165 | + @classmethod |
| 166 | + def add_meta( |
| 167 | + cls, |
| 168 | + model_path: Union[str, Path], |
| 169 | + key: str, |
| 170 | + value: List[str], |
| 171 | + delimiter: str = "\n", |
| 172 | + ) -> ModelProto: |
| 173 | + model = onnx.load_model(model_path) |
| 174 | + meta = model.metadata_props.add() |
| 175 | + meta.key = key |
| 176 | + meta.value = delimiter.join(value) |
| 177 | + return model |
| 178 | + |
| 179 | + @classmethod |
| 180 | + def get_meta( |
| 181 | + cls, model_path: Union[str, Path], key: str, split_sym: str = "\n" |
| 182 | + ) -> List[str]: |
| 183 | + sess = ort.InferenceSession(model_path) |
| 184 | + meta_map = sess.get_modelmeta().custom_metadata_map |
| 185 | + key_content = meta_map.get(key) |
| 186 | + key_list = key_content.split(split_sym) |
| 187 | + return key_list |
| 188 | + |
| 189 | + @classmethod |
| 190 | + def del_meta(cls, model_path: Union[str, Path]) -> ModelProto: |
| 191 | + model = onnx.load_model(model_path) |
| 192 | + del model.metadata_props[:] |
| 193 | + return model |
| 194 | + |
| 195 | + @classmethod |
| 196 | + def save_model(cls, save_path: Union[str, Path], model: ModelProto): |
| 197 | + onnx.save_model(model, save_path) |
| 198 | + |
| 199 | + |
| 200 | + dicts = read_txt( |
| 201 | + "models/ppocrv4_doc_dict.txt" |
| 202 | + ) |
| 203 | + model_path = "models/PP-OCRv5_mobile_det.onnx" |
| 204 | + model = ONNXMetaOp.add_meta(model_path, key="character", value=dicts) |
| 205 | + |
| 206 | + new_model_path = "models/PP-OCRv5_mobile_det_with_dict.onnx" |
| 207 | + ONNXMetaOp.save_model(new_model_path, model) |
| 208 | + |
| 209 | + t = ONNXMetaOp.get_meta(new_model_path, key="character") |
| 210 | + print(t) |
| 211 | + ``` |
| 212 | + |
| 213 | +#### 托管模型到魔搭 |
| 214 | + |
| 215 | +该部分主要是涉及模型上传到对应位置,并合理命名。注意上传完成后,需要打Tag,避免后续rapidocr whl包中找不到模型下载路径。 |
| 216 | + |
| 217 | +我这里已经上传到了魔搭上,详细链接参见:[link](https://www.modelscope.cn/models/RapidAI/RapidOCR/files?version=v2.1.0) |
| 218 | + |
| 219 | +#### 更改rapidocr代码适配 |
| 220 | + |
| 221 | +该部分主要涉及到更改[default_models.yaml](https://github.com/RapidAI/RapidOCR/blob/4d35ed272a1192afbcb95e823d99eb14c86b7893/python/rapidocr/default_models.yaml)和[paddle.py](https://github.com/RapidAI/RapidOCR/blob/4d35ed272a1192afbcb95e823d99eb14c86b7893/python/rapidocr/inference_engine/paddle.py)的代码来适配。 |
| 222 | + |
| 223 | +同时,需要添加对应的单元测试,在保证之前单测成功的同时,新的针对性该模型的单测也能通过。 |
| 224 | + |
| 225 | +我这里已经做完了,小伙伴们感兴趣可以去看看源码。 |
| 226 | + |
| 227 | +#### 发布新版本 |
| 228 | + |
| 229 | +因为这次算是功能新增,按照语义化版本号来看,我们版本号需要从v2.0.7 → v2.1.0。 |
| 230 | + |
| 231 | +我只需要在github仓库中,打一个v2.1.0的tag,Github Action会自动跑所有单元测试,自动发版到pypi。 |
| 232 | + |
| 233 | +### 写在最后 |
| 234 | + |
| 235 | +至此,集成工作就基本完成了。 |
0 commit comments