news 2026/8/28 5:04:24

PaddleOCR-v3模型ONNXRuntime部署实战:C++/Python跨平台推理优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PaddleOCR-v3模型ONNXRuntime部署实战:C++/Python跨平台推理优化

简介:ONNX(Open Neural Network Exchange)作为一种开放的模型格式,实现了不同深度学习框架间模型的互操作性。其核心原理在于定义了一套通用的计算图表示标准,使得训练好的模型可以脱离原生框架,在统一的运行时环境中进行高效推理。这一特性为模型部署带来了巨大的技术价值,尤其在资源受限的边缘计算和需要高性能C++集成的生产环境中,能够显著降低依赖复杂性并提升执行效率。ONNXRuntime作为微软推出的高性能推理引擎,对ONNX模型提供了优异的支持,并具备多语言API和跨平台能力,成为工业级部署的理想选择。在实际应用场景中,例如光学字符识别(OCR),将业界知名的PaddleOCR-v3模型转换为ONNX格式,再利用ONNXRuntime进行部署,可以兼顾识别精度推理速度,有效解决Python方案在边缘设备或C++主程序中集成困难的问题。本文即围绕这一实践,详细阐述了从模型导出、环境配置到核心代码实现与性能优化的完整流程。

1. 项目背景与核心价值

最近在做一个需要离线OCR识别的项目,要求是部署在边缘设备上,对速度和稳定性都有比较高的要求。网上找了一圈,PaddleOCR的识别精度和速度口碑一直不错,但官方提供的Python推理方案在资源受限的环境下,尤其是需要多线程并发或者集成到C++主程序里时,就显得有点力不从心了。直接调用Paddle Inference的C++ API虽然可行,但依赖库的编译和部署又是一大堆麻烦事,而且模型转换、前后处理都得自己从头搞,非常折腾。

这时候,ONNXRuntime就进入了我的视野。它是一个高性能的推理引擎,对ONNX格式的模型支持得非常好,而且提供了C++、Python、C#、Java等多种语言的API,部署起来非常灵活。最关键的是,它的C++接口非常干净,依赖少,性能高,特别适合集成到产品里。所以,我的目标就很明确了:把训练好的PaddleOCR-v3模型转换成ONNX格式,然后用ONNXRuntime的C++和Python接口分别进行部署,实现一套代码、两种语言、随处可跑的效果。

这个“ONNXRuntime部署PaddleOCR-v3”的项目包,就是我这个折腾过程的完整总结。里面不仅包含了转换好的ONNX模型文件,还有写好的C++和Python推理源码,以及详细的部署说明。无论你是想快速在Python环境里验证效果,还是需要把OCR能力无缝嵌入到现有的C++桌面应用或服务器后端,这个项目都能给你提供一个扎实的起点,省去你从零开始摸索模型转换、前后处理对齐、性能优化这些坑的时间。

2. 环境准备与工具链搭建

工欲善其事,必先利其器。在开始代码之前,我们需要把整个工具链搭建好。这个过程虽然有点繁琐,但每一步都关系到后面能否顺利跑通。

2.1 Python环境与PaddleOCR模型导出

首先,我们需要一个Python环境来运行PaddlePaddle,以便导出原始模型。我强烈建议使用Anaconda或者Miniconda来管理环境,这样可以避免包冲突。

# 创建一个新的conda环境,Python版本建议3.8或3.9,兼容性最好 conda create -n paddle_onnx python=3.9 conda activate paddle_onnx # 安装PaddlePaddle GPU版本(如果你有NVIDIA显卡且配置了CUDA) # 请根据你的CUDA版本去Paddle官网选择对应的安装命令,例如CUDA 11.2: python -m pip install paddlepaddle-gpu==2.5.2.post112 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html # 或者安装CPU版本 python -m pip install paddlepaddle==2.5.2 -i https://mirror.baidu.com/pypi/simple # 安装PaddleOCR和paddle2onnx工具 pip install paddleocr==2.7.3 pip install paddle2onnx==1.0.8 pip install onnxruntime==1.16.3 # 这是Python版的ONNXRuntime

安装完成后,我们可以先验证一下PaddleOCR是否能正常工作,并用它提供的预训练模型进行导出。PaddleOCR-v3是一个统称,它通常包含三个子模型:文本检测(Detection)、方向分类(Classification)和文本识别(Recognition)。我们需要分别导出它们。

这里有个关键点:PaddleOCR的推理过程是串联的,先检测出文本框,再对每个框判断方向(是否需要旋转),最后进行识别。在部署时,我们可以选择部署这三个独立的模型,然后在代码里组织调用逻辑;也可以尝试将它们组合成一个图。为了灵活性和可调试性,我选择分别导出三个独立的ONNX模型。

假设我们已经下载好了PaddleOCR的预训练模型文件(.pdmodel.pdiparams),或者使用PaddleOCR代码中自动下载的模型。模型通常保存在~/.paddleocr/whl/目录下。导出脚本大致如下:

import paddle2onnx from paddle2onnx.command import program2onnx # 以文本检测模型(ch_PP-OCRv3_det)为例 # 你需要指定模型的模型文件、参数文件路径,以及输入输出的名称和形状 det_model_dir = “/path/to/ch_PP-OCRv3_det_infer” # 使用paddle2onnx命令行工具转换 # 在终端执行: # paddle2onnx --model_dir {det_model_dir} --model_filename inference.pdmodel --params_filename inference.pdiparams --save_file det_model.onnx --opset_version 12 --enable_onnx_checker True

对于识别和分类模型,操作类似。opset_version我选择了12,这是一个在兼容性和性能上比较平衡的版本。导出的ONNX模型文件(det_model.onnx,cls_model.onnx,rec_model.onnx)就是后续推理的核心。

2.2 C++开发环境配置(Visual Studio / VSCode)

对于C++部署,我们需要准备ONNXRuntime的C++开发库。这里以Windows平台配合Visual Studio 2019/2022为例,Linux下的CMake流程也类似。

  1. 下载ONNXRuntime:去ONNXRuntime的GitHub Release页面,下载对应你平台的预编译包。对于Windows,我们通常下载onnxruntime-win-x64-1.16.3.zip(CPU版本)或带gpu标签的版本。解压到一个合适的目录,例如D:\Libs\onnxruntime

  2. 配置Visual Studio项目

    • 创建一个新的C++控制台项目。
    • 右键项目 -> 属性。
    • C/C++ -> 常规 -> 附加包含目录:添加ONNXRuntime解压目录下的include文件夹路径,如D:\Libs\onnxruntime\include
    • 链接器 -> 常规 -> 附加库目录:添加ONNXRuntime的lib文件夹路径,如D:\Libs\onnxruntime\lib
    • 链接器 -> 输入 -> 附加依赖项:添加onnxruntime.lib
    • 调试环境:为了运行时能找到DLL,你需要将ONNXRuntime的bin目录(包含onnxruntime.dll)添加到系统的PATH环境变量,或者更简单的方法是在Visual Studio的项目属性中,“调试 -> 环境”里添加一行,例如PATH=D:\Libs\onnxruntime\bin;%PATH%

如果你更喜欢使用VSCode配合CMake,那么你需要编写一个CMakeLists.txt文件来定位ONNXRuntime的库。网络上有很多关于“vscode 配置c++”和“vscode配置c/c++环境”的教程,核心就是配置好c_cpp_properties.json,tasks.json,launch.json这三个文件,并在CMake中通过find_package或直接指定路径来链接ONNXRuntime。

注意:ONNXRuntime有多个构建版本,如onnxruntime(默认)、onnxruntime_providers_cuda(GPU支持)等。如果你需要GPU推理,务必下载并链接对应的版本,并在代码中指定CUDA执行提供器。否则,默认使用CPU执行提供器。

2.3 Python推理环境

Python环境就简单多了,我们在第一步已经安装了onnxruntime包。如果你想用GPU加速,需要安装onnxruntime-gpu注意onnxruntimeonnxruntime-gpu不能同时安装,且onnxruntime-gpu的版本必须与你系统的CUDA版本严格匹配。

# 卸载CPU版本,安装对应CUDA 11.8的GPU版本 pip uninstall onnxruntime pip install onnxruntime-gpu==1.16.3

安装后,在Python代码中通过onnxruntime.InferenceSession加载模型时,可以通过providers参数指定使用‘CUDAExecutionProvider’来启用GPU。

3. 模型推理核心流程与代码实现

有了模型和环境,接下来就是最核心的部分:编写推理代码。无论是C++还是Python,其核心逻辑都是一致的:加载模型 -> 预处理输入数据 -> 运行会话(Session) -> 后处理输出结果。PaddleOCR的三个模型需要按顺序调用。

3.1 图像预处理:与PaddleOCR对齐的关键

这是最容易出错的一步。PaddleOCR在训练和原始推理时,对输入图像有一系列特定的预处理操作,包括归一化(Normalize)、通道顺序转换(BGR->RGB)、尺寸缩放(Resize)等。如果我们直接用原始图片喂给ONNX模型,结果肯定不对。我们必须完全复现PaddleOCR推理脚本中的预处理逻辑。

以检测模型为例,PaddleOCR的输入通常是一个经过Normalize(减均值、除方差)、ToCHW(HWC转CHW)和Scale(可能缩放)处理的float32数组,形状为[1, 3, H, W],其中H和W是固定的,或者是动态的。

我们需要仔细查看PaddleOCR源码中(通常是tools/infer/utility.pyppocr/data/imaug/operators.py)关于DetResizeForTest,NormalizeImage,ToCHWImage等操作的具体参数。例如,均值可能是[123.675, 116.28, 103.53],方差是[58.395, 57.12, 57.375]

在我的项目代码中,我分别用C++(使用OpenCV)和Python(使用NumPy和cv2)实现了完全一致的预处理函数。例如,C++的预处理片段:

cv::Mat preprocess_det(const cv::Mat& src_img, int target_height, int target_width) { cv::Mat img_resized; cv::resize(src_img, img_resized, cv::Size(target_width, target_height)); // 转换颜色空间 BGR -> RGB cv::cvtColor(img_resized, img_resized, cv::COLOR_BGR2RGB); // 转换为float并归一化 img_resized.convertTo(img_resized, CV_32FC3); // 手动进行减均值除方差 std::vector<float> mean = {123.675f, 116.28f, 103.53f}; std::vector<float> std = {58.395f, 57.12f, 57.375f}; // ... 对每个通道每个像素进行计算 ... // 转换为 CHW 格式: [H, W, C] -> [C, H, W] std::vector<cv::Mat> split_channels; cv::split(img_resized, split_channels); // 将三个通道的数据合并到一个一维vector中,准备输入到模型 std::vector<float> input_tensor_values; for (int c = 0; c < 3; ++c) { input_tensor_values.insert(input_tensor_values.end(), (float*)split_channels[c].data, (float*)split_channels[c].data + target_height * target_width); } return input_tensor_values; // 实际上返回的是vector<float> }

3.2 C++接口封装与推理

在C++中,我们使用ONNXRuntime的C++ API。主要步骤是创建Ort::Env(环境)、Ort::Session(会话),然后准备输入输出。

#include <onnxruntime_cxx_api.h> class PaddleOCROnnx { public: PaddleOCROnnx(const std::string& det_model_path, const std::string& rec_model_path, const std::string& cls_model_path = “”) { // 1. 初始化环境 env_ = Ort::Env(ORT_LOGGING_LEVEL_WARNING, “PaddleOCR”); Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(1); // 设置线程数 // 如果需要GPU,在这里添加CUDA提供器 // Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0)); // 2. 创建会话 det_session_ = Ort::Session(env_, det_model_path.c_str(), session_options); rec_session_ = Ort::Session(env_, rec_model_path.c_str(), session_options); if (!cls_model_path.empty()) { cls_session_ = Ort::Session(env_, cls_model_path.c_str(), session_options); } // 3. 获取输入输出信息(名称、形状等),这部分代码略长,主要是调用session_.GetInputName/GetOutputName等 } std::vector<OCRRect> detect(const cv::Mat& image) { // 预处理图像,得到符合检测模型输入的float数组 auto input_tensor_values = preprocess_det(image); // 创建Ort::Value输入张量 std::vector<int64_t> input_shape = {1, 3, det_input_height_, det_input_width_}; auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>( memory_info, input_tensor_values.data(), input_tensor_values.size(), input_shape.data(), input_shape.size()); // 运行推理 auto output_tensors = det_session_.Run( Ort::RunOptions{nullptr}, det_input_names_.data(), // 输入节点名数组 &input_tensor, // 输入张量指针数组 1, det_output_names_.data(), // 输出节点名数组 det_output_names_.size()); // 后处理:从output_tensors[0](可能是score map)和output_tensors[1](可能是geometry)中解析出文本框坐标 // 这里涉及到OpenCV的findContours、阈值化、多边形逼近等操作,是检测模型后处理的核心,代码较长。 return postprocess_det(output_tensors, image.size()); } // 识别和分类函数类似... private: Ort::Env env_; Ort::Session det_session_; Ort::Session rec_session_; Ort::Session cls_session_; // ... 其他成员变量,如输入输出名称、尺寸等 };

C++代码的关键在于内存管理和数据转换。ONNXRuntime的Ort::Value封装了张量数据,我们需要确保输入数据的形状、类型和内存布局完全正确。后处理部分,特别是检测模型的后处理(从热力图和几何图中提取文本框),是算法核心,也是性能瓶颈之一,需要仔细优化。

3.3 Python接口封装与推理

Python接口就友好得多,代码也更简洁。我们可以利用onnxruntimenumpy的便利性。

import onnxruntime as ort import cv2 import numpy as np class PaddleOCROnnxPy: def __init__(self, det_model_path, rec_model_path, cls_model_path=None): # 创建会话,可以指定使用CPU或GPU providers = [‘CPUExecutionProvider’] # 如果想用GPU,改为: # providers = [‘CUDAExecutionProvider’, ‘CPUExecutionProvider’] sess_options = ort.SessionOptions() sess_options.intra_op_num_threads = 4 # 设置线程数 self.det_session = ort.InferenceSession(det_model_path, sess_options=sess_options, providers=providers) self.rec_session = ort.InferenceSession(rec_model_path, sess_options=sess_options, providers=providers) if cls_model_path: self.cls_session = ort.InferenceSession(cls_model_path, sess_options=sess_options, providers=providers) # 获取输入输出名 self.det_input_name = self.det_session.get_inputs()[0].name # ... def detect(self, image): # 预处理 img_preprocessed, (ratio_h, ratio_w) = self.preprocess_det(image) # 将numpy数组转换为模型输入需要的格式 det_inputs = {self.det_input_name: img_preprocessed} # 运行推理 det_outputs = self.det_session.run(None, det_inputs) # 后处理 boxes = self.postprocess_det(det_outputs, (ratio_h, ratio_w), image.shape) return boxes def recognize(self, image_crop_list): """对裁剪出的每个文本框图像进行识别""" rec_results = [] for img_crop in image_crop_list: # 对每个小图进行预处理(归一化、resize到rec模型输入尺寸等) rec_input = self.preprocess_rec(img_crop) rec_outputs = self.rec_session.run(None, {self.rec_input_name: rec_input}) # 后处理:将输出向量解码为文本字符串,这里通常需要一个CTC解码器和一个字典文件 text = self.decode_rec_output(rec_outputs[0]) rec_results.append(text) return rec_results

Python版本的后处理逻辑与C++一致,但实现起来更快捷。识别模型的后处理通常需要一个CTC解码算法(如贪婪解码或束搜索)和一个包含所有字符的字典文件(ppocr_keys_v1.txt),将模型输出的概率序列转换成最终的文本字符串。

3.4 串联调用与结果整合

最后,我们需要将三个模型串联起来,形成一个完整的OCR流水线。

// C++ 示例 std::vector<OCRRect> run_ocr(const cv::Mat& img) { // 1. 文本检测 auto text_boxes = ocr_engine.detect(img); std::vector<OCRRect> results; for (const auto& box : text_boxes) { // 2. 裁剪文本框图像 cv::Mat crop = crop_rotate_image(img, box); // 3. 方向分类(可选) float cls_score; bool need_rotate = false; if (use_cls) { need_rotate = ocr_engine.classify(crop, cls_score); if (need_rotate) { cv::rotate(crop, crop, cv::ROTATE_180); } } // 4. 文本识别 std::string text = ocr_engine.recognize(crop); // 5. 保存结果 results.push_back({box, text, cls_score}); } // 6. 按位置排序(可选,例如从上到下,从左到右) sort_text_boxes(results); return results; }

Python版本的流程完全一样。这里的一个优化点是,识别模型通常一次只能处理一张小图。为了提高吞吐量,可以对多个裁剪出的文本框进行批处理(Batch Inference)。这需要我们将多个小图预处理后,在批次维度(batch dimension)上堆叠成一个张量,然后一次性输入给识别模型。这要求识别模型的ONNX导出时支持动态的批次维度(-1),并且在代码中做好批次的组装和结果的拆分。

4. 性能优化与实战踩坑记录

把流程跑通只是第一步,要让它在实际项目中可用,性能优化和避坑是必不可少的环节。下面分享几个我在实战中遇到的关键问题和解决方案。

4.1 模型优化与动态输入

问题:最初导出的检测模型输入尺寸是固定的(例如[1, 3, 960, 960]),这意味着无论原图多大,都会被强制缩放到这个尺寸。对于长宽比悬殊的图片,缩放会导致变形,影响检测精度。

解决方案:使用Paddle2ONNX导出时,可以指定输入为动态形状。例如,将高度和宽度维度设置为-1

paddle2onnx ... --input_shape_dict="{‘x’:[-1,3,-1,-1]}"

这样导出的ONNX模型输入形状为[batch_size, 3, height, width]。在推理时,我们可以根据原图尺寸动态计算预处理后的尺寸(保持原比例缩放,短边缩放到某个值,长边按比例缩放,并填充到32的倍数)。这要求我们的预处理和后处理代码能适应可变的输入尺寸,计算复杂度会稍微增加,但精度提升是值得的。

另一个优化点:ONNXRuntime支持模型优化。我们可以使用onnxruntime_tools中的optimizer或者onnxruntime自带的GraphOptimizationLevel进行图优化。在创建Session时设置:

// C++ session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
# Python sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_EXTENDED

这可以融合一些操作符,提高推理速度。

4.2 内存管理与多线程安全

问题:在C++服务端部署时,需要处理高并发请求。如果每个请求都创建和销毁ONNXRuntime Session,开销巨大。同时,多个线程同时调用同一个Session的Run方法是否安全?

解决方案

  1. Session复用:在程序初始化时创建好OCR引擎(包含所有Session),并将其作为全局或单例对象。所有推理请求共享这个引擎。
  2. 多线程安全:根据ONNXRuntime官方文档,一个Ort::Session对象的Run方法不是线程安全的。如果多个线程同时调用同一个Session的Run,会导致未定义行为。
    • 方案A(线程独占):为每个处理线程创建独立的Session实例。这适用于线程数量固定且不多的场景(如线程池)。内存消耗会随线程数增加。
    • 方案B(Session池):维护一个Session对象池。当线程需要推理时,从池中借用一个Session,用完后归还。这需要自己实现池化和锁机制。
    • 方案C(请求队列):采用生产者-消费者模式。所有OCR请求放入一个队列,由单个或少数几个专用的“推理线程”从队列中取出任务并执行,然后将结果返回。这是最稳妥的方案,能保证最高的吞吐量和稳定性,我在生产环境中采用了这种模式。

4.3 前后处理性能瓶颈

问题: profiling(性能分析)后发现,整个OCR流程中,模型推理时间只占一小部分,大量的时间花在了图像的预处理(resize, normalize, color convert)和后处理(检测框的解析、NMS、多边形点排序、图像裁剪、识别结果解码)上。

解决方案

  • 使用高性能库:确保使用OpenCV的优化版本(如带IPP、OpenCL支持的)。在C++中,避免在循环中频繁创建临时cv::Mat,尽量复用内存。
  • 向量化操作:将归一化等操作从逐像素循环改为对整个cv::Mat进行矩阵运算,或者使用cv::convertTo配合cv::Scalar进行减均值除方差。
  • 优化后处理算法
    • 检测后处理中的findContoursapproxPolyDP是瓶颈。可以尝试调整轮廓发现的模式(如RETR_LIST)和方法(如CHAIN_APPROX_SIMPLE)。
    • 非极大值抑制(NMS)可以尝试使用更快的实现,或者调整阈值,在精度和速度间权衡。
    • 识别解码部分,如果使用束搜索(Beam Search),束宽(beam width)是影响速度的关键参数,在保证准确率的前提下尽量调小。
  • 批处理:如前所述,对多个文本框的识别进行批处理,能极大减少模型启动开销和GPU利用率低的问题。

4.4 版本兼容性与依赖地狱

问题:项目在Windows上编译运行良好,移植到Linux(如CentOS 7)或嵌入式设备(如Jetson Nano)上,出现各种链接错误或运行时崩溃。

解决方案

  1. 静态链接:对于C++部署,可以考虑将ONNXRuntime库静态链接到你的程序中。ONNXRuntime提供了静态库的构建方式。这样可以避免目标机器上缺少特定版本的动态库(如libonnxruntime.so)的问题。缺点是最终可执行文件体积会变大。
  2. 交叉编译与依赖打包:对于嵌入式设备,最好在设备本身或相同架构的虚拟机上编译。将所有依赖的共享库(如OpenCV, ONNXRuntime, protobuf等)打包,并通过LD_LIBRARY_PATH指定路径。
  3. Docker容器化:这是解决环境问题最彻底的方法。创建一个包含所有依赖(指定版本的系统库、OpenCV、ONNXRuntime)的Docker镜像。无论在什么宿主机上,只要运行这个容器,环境就是一致的。这对于服务端部署尤其友好。
  4. 谨慎选择第三方库版本:OpenCV、Protobuf等库的版本与ONNXRuntime可能有兼容性要求。最好参考ONNXRuntime官方构建文档中推荐的依赖版本。例如,某个版本的ONNXRuntime可能要求Protobuf的版本不低于3.xx。

4.5 精度对齐与调试技巧

问题:用ONNXRuntime推理的结果,与直接使用PaddlePaddle原版推理的结果有细微差异,导致最终识别文本不同。

解决方案

  1. 确保预处理一致:这是99%的问题来源。务必使用相同的归一化均值、方差,相同的插值算法(如cv::INTER_LINEAR),相同的填充(padding)策略。将预处理后的第一个像素值打印出来,和Paddle版本对比。
  2. 检查模型导出:使用netron工具打开导出的ONNX模型,检查输入输出节点名称、数据类型(float32)、形状是否正确。确保导出时没有启用某些可能改变计算图的优化选项。
  3. 中间结果对比:分别用Paddle和ONNXRuntime运行同一张图片,不仅对比最终文本,更要比对三个模型各自的原始输出张量(det的score map, rec的output)。如果原始输出就不同,那问题出在模型转换或推理引擎上。如果原始输出相同但后处理结果不同,问题出在后处理代码。
  4. 使用ONNXRuntime的Python API进行桥接调试:先用Python版的ONNXRuntime跑通整个流程,并和Paddle原版结果对齐。因为Python环境调试更方便。对齐后,再将完全相同的预处理、后处理逻辑移植到C++版本。这样可以隔离语言环境带来的问题。

5. 项目源码结构与应用示例

我提供的项目包ONNXRuntime部署PaddleOCR-v3包含C++和Python源码+模型+说明.zip,就是按照上述思路组织的。解压后,你可能会看到类似如下的结构:

PaddleOCR-ONNX-Deploy/ ├── models/ # 存放ONNX模型文件 │ ├── ch_PP-OCRv3_det_infer.onnx │ ├── ch_PP-OCRv3_rec_infer.onnx │ └── ch_PP-OCRv3_cls_infer.onnx ├── cpp/ # C++部署源码 │ ├── CMakeLists.txt │ ├── src/ │ │ ├── main.cpp # 示例主程序 │ │ ├── ocr_engine.cpp/.h # OCR引擎核心封装类 │ │ ├── preprocess.cpp/.h # 预处理函数 │ │ ├── postprocess.cpp/.h # 后处理函数 │ │ └── utils.cpp/.h │ └── third_party/ # 可能包含必要的第三方头文件 ├── python/ # Python部署源码 │ ├── paddleocr_onnx.py # 封装的OCR类 │ ├── demo.py # 使用示例 │ ├── tools/ │ │ └── model_export.py # 模型导出脚本 │ └── requirements.txt ├── docs/ # 详细说明文档 │ ├── build_windows.md │ ├── build_linux.md │ └── faq.md └── test_images/ # 测试图片

C++示例:编译后,运行程序,指定图片路径即可。

./ocr_system_demo --image ./test.jpg --det ./models/det.onnx --rec ./models/rec.onnx --cls ./models/cls.onnx

Python示例:更加简单直接。

from paddleocr_onnx import PaddleOCROnnxPy ocr = PaddleOCROnnxPy(det_model_path=‘./models/det.onnx’, rec_model_path=‘./models/rec.onnx’, cls_model_path=‘./models/cls.onnx’) image = cv2.imread(‘test.jpg’) results = ocr.ocr(image) for box, text, confidence in results: print(f“Text: {text}, Box: {box}”) # 也可以在图片上画框 cv2.polylines(image, [box.astype(np.int32)], True, (0, 255, 0), 2)

这个项目包的价值在于,它提供了一个端到端、可编译、可运行的参考实现。你不需要再从零开始研究PaddleOCR的模型结构、前后处理细节,也不需要手动去摸索ONNXRuntime的C++ API调用。你可以直接基于我的代码进行修改,适配你的业务逻辑,比如修改后处理的阈值、调整识别字典、增加批量处理逻辑,或者将其集成到你的Qt、MFC桌面应用,或者HTTP服务后端中。

部署这条路,坑多且杂,但一旦走通,收益也是巨大的。它意味着你的应用摆脱了对庞大Python环境和PaddlePaddle框架的依赖,获得了更快的启动速度、更低的内存占用,以及更灵活的集成能力。希望这个项目和你正在读的这篇总结,能帮你少走些弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/28 5:03:22

宽范围输入DC-DC电源模块设计实战:6W与10W方案选型、验证与整改

宽输入范围的小功率电源模块&#xff0c;一直是工业设计里最不性感但又最绕不开的环节。最近把两款占位相近、规格互补的DC-DC方案从头到尾做了完整的选型、验证和整改&#xff0c;一款是6W、一款是10W&#xff0c;输入范围都是4:1&#xff0c;今天把过程里踩过的坑和沉淀下来的…

作者头像 李华
网站建设 2026/8/28 5:01:51

LSTM时间序列预测工程化实践:从数据清洗到API部署

简介&#xff1a;时间序列预测是工业智能、能源调度与电商运营中的核心基础能力&#xff0c;其本质是在有限历史中建模动态演化规律。LSTM凭借门控机制实现选择性记忆&#xff0c;在中短期&#xff08;24–72小时&#xff09;预测任务中兼顾记忆深度、计算效率与物理可解释性。…

作者头像 李华
网站建设 2026/8/28 5:00:51

RepairFormer:基于Transformer的JSON/YAML等结构化输入自动修复实战

RepairFormer 是面向结构化输入的自动化修复方向的一种命名&#xff1a;当 JSON、YAML、XML 或配置文本因为少逗号、引号未闭合、字段拼错、内容被截断而无法解析时&#xff0c;不再靠手写正则逐一补救&#xff0c;而是用 Transformer 学习“坏输入 -> 好输入”的映射。这正…

作者头像 李华
网站建设 2026/8/28 5:00:46

CUDA深度学习环境搭建与排错实战:从驱动到框架的完整指南

1. 从“能用”到“用好”&#xff1a;我的CUDA深度学习环境搭建与排错全记录 最近在折腾一个基于PyTorch的扩散模型项目&#xff0c;训练时频繁遇到那个让人血压飙升的“CUDA out of memory”。这让我意识到&#xff0c;之前对CUDA的认知&#xff0c;可能还停留在“装上了&…

作者头像 李华
网站建设 2026/8/28 5:00:35

YOLOv8火灾检测毕业设计全流程实战指南

简介&#xff1a;YOLOv8作为当前主流轻量目标检测模型&#xff0c;凭借Anchor-Free结构和高效C2f模块&#xff0c;在火灾、火焰与烟雾等多尺度小目标检测中展现出优异鲁棒性与部署适应性&#xff1b;其原理在于动态特征融合与无锚框预测机制&#xff0c;技术价值体现在精度-速度…

作者头像 李华
网站建设 2026/8/28 5:00:09

用agent.md项目级提示文件,让AI编程助手真正提升代码质量

先说明一个判断&#xff1a;大语言模型辅助编码&#xff0c;真正提升代码质量的关键不一定在于模型本身有多强&#xff0c;而在于你通过什么方式把项目的“隐性规则”告诉它。普通提问是一次性的&#xff0c;模型只能靠临时对话里的少量上下文做猜测&#xff1b;项目级提示文件…

作者头像 李华