ppocrv6中的字符识别模型转ncnn平台

发布时间:2026/7/2 19:51:19
ppocrv6中的字符识别模型转ncnn平台 一、下载模型去官网下载字符识别模型https://github.com/PaddlePaddle/PaddleOCR/blob/main/readme/README_cn.md我进了魔塔社区里面有基本使用样例二、使用pnxx转模型记录一下所有步骤1使用ncnn转在系统环境下 git clone --recursive https://github.com/Tencent/ncnn.git cd ncnn mkdir build cd build git submodule update --init cmake -DCMAKE_BUILD_TYPERelease -DNCNN_VULKANON -DNCNN_BUILD_EXAMPLESON .. cmake --build . -j$(nproc) ./ncnn/build/tools/onnx/onnx2ncnn inference_sim.onnx rec.param rec.bin2使用pnxx转模型1. 访问 PNNX 官方发布页面前往https://github.com/pnnx/pnnx/releases2. 下载对应系统的二进制包Linux (Ubuntu): 下载 pnnx-xxx-ubuntu.zip3. 解压后找到 pnnx./pnnx /home/fuxueping/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.onnx inputshape[1,3,48,128] inputshape2[1,3,48,512]可参考paddleocr转ncnn及验证_ncnn paddleocrv5-CSDN博客转的过程中的输出结果pnnxparam /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.pnnx.param pnnxbin /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.pnnx.bin pnnxpy /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference_pnnx.py pnnxonnx /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.pnnx.onnx ncnnparam /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.ncnn.param ncnnbin /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference.ncnn.bin ncnnpy /home/XXX/4tdisk/code/paddle_ocr/official_models/PP-OCRv6_medium_rec_onnx/inference_ncnn.py fp16 1 optlevel 2 device cpu inputshape [1,3,48,128]f32 inputshape2 [1,3,48,512]f32 input input2 customop moduleop get inputshape from traced inputs inputshape [1,3,48,128]f32 inputshape2 [1,3,48,512]f32 ############# pass_level0 onnx inline_containers ... 0.01ms eliminate_noop ... 13.52ms fold_constants ... 1.27ms canonicalize ... 2.48ms shape_inference ... 2026-06-30 17:52:25.899343269 [W:onnxruntime:pnnx, cpuid_info.cc:95 LogEarlyWarning] Unknown CPU vendor. cpuinfo_vendor value: 0 2026-06-30 17:52:25.899720281 [W:onnxruntime:pnnx, device_discovery.cc:283 GetGpuDevices] Failed to detect devices under /sys/class/drm/card0: device_discovery.cc:93 ReadFileContents Failed to open file: /sys/class/drm/card0/device/vendor 865.24ms fold_constants_dynamic_shape ... 334.99ms inline_if_graph ... 0.13ms fuse_constant_as_attribute ... 0.42ms eliminate_noop_with_shape ... 0.35ms ┌────────────────────┬──────────┬──────────┐ │ │ orig │ opt │ ├────────────────────┼──────────┼──────────┤ │ node │ 508 │ 358 │ │ initializer │ 254 │ 218 │ │ functions │ 0 │ 0 │ ├────────────────────┼──────────┼──────────┤ │ nn module op │ 0 │ 0 │ │ custom module op │ 0 │ 0 │ │ aten op │ 0 │ 0 │ │ prims op │ 0 │ 0 │ │ onnx native op │ 508 │ 358 │ ├────────────────────┼──────────┼──────────┤ │ Add │ 109 │ 109 │ │ AveragePool │ 1 │ 1 │ │ BatchNormalization │ 3 │ 3 │ │ Concat │ 3 │ 2 │ │ Conv │ 62 │ 62 │ │ Div │ 19 │ 19 │ │ Erf │ 14 │ 14 │ │ HardSigmoid │ 6 │ 6 │ │ Identity │ 143 │ 0 │ │ MatMul │ 13 │ 13 │ │ MaxPool │ 1 │ 1 │ │ Mul │ 46 │ 46 │ │ Pow │ 5 │ 5 │ │ ReduceMean │ 16 │ 16 │ │ Relu │ 11 │ 11 │ │ Reshape │ 8 │ 6 │ │ Shape │ 4 │ 1 │ │ Sigmoid │ 5 │ 5 │ │ Slice │ 8 │ 7 │ │ Softmax │ 3 │ 3 │ │ Sqrt │ 5 │ 5 │ │ Squeeze │ 8 │ 8 │ │ Sub │ 5 │ 5 │ │ Transpose │ 9 │ 9 │ │ Unsqueeze │ 1 │ 1 │ └────────────────────┴──────────┴──────────┘ ############# pass_level1 onnx ############# pass_level2 ############# pass_level3 ############# pass_level4 ############# pass_level5 ############# pass_ncnn model inputshape [1,3,48,?]f32 FLOPS 1.47M memory OPS 745.89K模型转成功三、测试验证测试代码测试pnnx转出来的模型#!/usr/bin/env python3 PP-OCRv6 NCNN 模型测试 (PNNX转换版本) 测试图片: 059279805_2.jpg 预期内容: ROU 问题排查 1. 测试脚本宽度设置错误 (128 vs 320) 2. 需要验证预处理是否正确 import os import cv2 import numpy as np import yaml import ncnn script_dir os.path.dirname(os.path.abspath(__file__)) model_dir os.path.join(script_dir, official_models/PP-OCRv6_medium_rec_onnx/pnnx) img_path os.path.join(script_dir, 2PZfbirjfxA88695lRmgk.jpeg) #可自行替换 print( * 60) print(PP-OCRv6 NCNN 模型测试 (PNNX版本)) print( * 60) # 加载字符字典 with open(os.path.join(script_dir, official_models/PP-OCRv6_medium_rec_onnx/inference.yml), r, encodingutf-8) as f: character yaml.safe_load(f)[PostProcess][character_dict] print(f字符字典大小: {len(character)}) # 加载图像 img cv2.imread(img_path) if img is None: print(f错误: 无法加载图像 {img_path}) exit(1) print(f原始图像尺寸: {img.shape}) # 正确预处理 (参考PP-OCR动态宽度方式): # 1. 等比例缩放到高度48 # 2. 宽度向上取整到8的倍数模型要求 # 3. 不使用固定宽度padding直接使用计算出的宽度 h, w img.shape[:2] target_h 48 # 动态计算宽度向上取整到8的倍数 new_w (int(target_h / h * w) 7) // 8 * 8 resized cv2.resize(img, (new_w, target_h)) print(f预处理后尺寸: {resized.shape}) # BGR转RGB, 归一化, CHW格式 rgb cv2.cvtColor(resized, cv2.COLOR_BGR2RGB) data rgb.astype(np.float32) / 255.0 data_chw data.transpose(2, 0, 1) # (C, H, W) print(f输入尺寸 (CHW): {data_chw.shape}) # 加载模型 param_file os.path.join(model_dir, inference.ncnn.param) bin_file os.path.join(model_dir, inference.ncnn.bin) net ncnn.Net() net.load_param(param_file) net.load_model(bin_file) print(f模型加载成功) # 创建NCNN Mat # NCNN Mat: widthnew_w, height48, channels3 # 数据格式: 每通道连续 (c0所有像素, c1所有像素, c2所有像素) mat ncnn.Mat(new_w, target_h, 3) for c in range(3): channel_data data_chw[c].flatten() # H*W for i, val in enumerate(channel_data): mat.channel(c)[i] val # 推理 with net.create_extractor() as ex: ex.input(in0, mat) ret, mat_out ex.extract(out0) output np.array(mat_out) print(f\n输出形状: {output.shape}) print(f 输出格式: (序列长度, 类别数) {output.shape}) # 数据有效性检查 print(f\n数据统计:) print(f 最大值: {np.max(output):.4f}) print(f 最小值: {np.min(output):.4f}) print(f 均值: {np.mean(output):.6f}) print(f 有NaN: {np.isnan(output).any()}) # 检查softmax输出 print(f\n检查输出是否像softmax:) for t in [0, 1, 2]: s output[t, :].sum() print(f t{t}: sum{s:.4f}) # 解码 # 模型输出18710类 (0-18709)字典有18708个字符 (0-18707) # argmax后-1修正原始索引-1后18709变成18708超出字典表示空格 indices np.argmax(output, axis1) indices np.where(indices 0, indices - 1, 0) # -1修正 print(f\n每时间步预测 (argmax, -1修正):) for t in range(min(20, output.shape[0])): idx indices[t] char character[idx] if idx len(character) else ? print(f t{t:2d}: idx{idx:5d}, char{char}) # CTC解码 # -1修正后idx0是blankidx18707表示空格模型输出18708/18709对应空格 def ctc_decode(preds): result [] prev -1 for idx in preds: if idx 0: # blank重置prev prev -1 continue if idx 18707: # 空格位置 if prev ! : result.append( ) prev continue if idx ! prev: result.append(character[idx]) prev idx return result decoded ctc_decode(indices) text .join(decoded) print(f\n识别结果: \{text}\) print(f预期结果: \ROU\) # 非空预测 non_blank [] for t in range(len(indices)): idx indices[t] if idx ! 0 and idx 18707: # 非blank且非空格 non_blank.append((t, character[idx])) if non_blank: print(f\n非空预测: {[(t, c) for t, c in non_blank[:10]]}{... if len(non_blank) 10 else }) print(\n * 60) # if text ROU: # print(✓ 测试通过: 识别结果与预期一致) # else: # print(✗ 测试失败: 识别结果与预期不符) print( * 60)记录至此基本后续在记录量化处理目前的模型输入尺寸w是动态的后面将记录它fp16处理结果