1、写在前面

之前有两篇文章是关于PP-OCRv5的ONNX部署的,使用的还都是python。而我真实的目的,是要将PP-OCRv5直接用在C#应用和Android应用上。毕竟很多场景就是这样纯粹。
后来对比了手搓ONNX方案和OnnxOCR,毅然选择了后者,确实有很多出彩的地方。
所以,今天我想先把OnnxOCR的python实现,转为C#实现。这样,离线桌面OCR用户就有福了。

2、从最小可行性(MVP)开始

参考OnnxOCR的核心流水线,使用WPF实现一个带界面的OCR应用,功能如下:

  1. 用户可选择一张图片(jpg / png / bmp / webp)
  2. 点击「开始识别」,在后台完成 OCR 推理
  3. 左侧显示原图及检测框叠加,右侧显示识别文本与置信度
  4. 推理结果与 OnnxOCR Python 版保持一致

3、核心技术栈

在核心技术选型中,我做出了一些突破,UI界面采用WPF+MVVM模式,之前咱可不管这那的直接上WinForm。而推理采用Microsoft.ML.OnnxRuntime,图像处理采用OpenCvSharp4 + runtime.win。

层次 技术 版本建议
UI WPF 内置
MVVM CommunityToolkit.Mvvm 8.x
推理 Microsoft.ML.OnnxRuntime 1.20.x
图像处理 OpenCvSharp4 + runtime.win 4.x

简要说明下:
CommunityToolkit.Mvvm是微软官方出品的轻量级 MVVM 工具包,比 Prism 轻量,比手写 MVVM 优雅。
Microsoft.ML.OnnxRuntime是微软官方出品的 ONNX 推理引擎,支持 CPU 和 GPU,C# 世界唯一正统选择。
OpenCvSharp4 + runtime.win是OpenCV的C#绑定库,C# 社区事实标准,OpenCV 官方 C++ 库的 C# 封装。我的项目名称准备借鉴OpenCvSharp4的命名规范,就叫做OnnxOCRSharp吧。

4、OnnxOCR实现参考

在我的上一篇文章《PP-OCRv5 ONNX部署但使用OnnxOCR》中,并没有提到OnnxOCR的相关技术架构,这次不同,我们需要将技术栈切换为C#,需要了解OnnxOCR的核心流水线。

4.1 核心流水线

对应 Python 源文件映射:

Python 模块 职责
predict_det.py 检测预处理、ONNX 推理、DB 后处理
db_postprocess.py Differentiable Binarization 后处理
predict_rec.py 识别预处理、批推理、归一化
rec_postprocess.py CTC 标签解码
orientation.py RapidOrientation 方向校正
predict_system.py 流水线编排
utils.py 图像裁剪、检测框绘制、默认参数
predict_base.py ONNX Session 管理
inference_engine/ 推理引擎抽象(CPU / GPU)

4.2 默认推理参数

参数 默认值 说明
det_limit_side_len 960 检测输入最长边
det_limit_type max 缩放策略
det_db_thresh 0.3 DB 二值化阈值
det_db_box_thresh 0.6 检测框置信度阈值
det_db_unclip_ratio 1.5 检测框扩展比例
det_box_type quad 四边形检测框
rec_image_shape 3, 48, 320 识别输入形状
rec_batch_num 6 识别批大小
drop_score 0.5 识别结果过滤阈值
use_angle_cls false 默认关闭方向校正

5、项目系统架构

5.1 项目分层

┌─────────────────────────────────────────┐
│         OnnxOcr.Desktop (WPF)           │  视图 + ViewModel
├─────────────────────────────────────────┤
│           OnnxOcr.App                   │  应用服务、DTO
├─────────────────────────────────────────┤
│           OnnxOcr.Core                  │  OCR 引擎(无 UI 依赖)
├─────────────────────────────────────────┤
│        OnnxOcr.Infrastructure           │  ONNX Session、配置加载
└─────────────────────────────────────────┘

5.2 项目架构

架构图如下:
架构

文字描述整个流程:

用户操作(拖拽图片)
    ↓
MainWindow(接收图片路径)
    ↓
MainViewModel(调用 OcrService)
    ↓
OcrService(传递给 TextSystem)
    ↓
TextSystem(编排):
    ├→ OrientationClassifier(判断方向)
    ├→ TextDetector(检测文字区域)→ DbPostProcess(转坐标)
    └→ TextRecognizer(识别文字)→ CtcLabelDecode(转字符串)
    ↓
ResultVisualizer(绘制标注框)
    ↓
OcrService(返回结果)
    ↓
MainViewModel(更新绑定属性)
    ↓
ImageCanvas(自动重绘,显示标注结果)

6、核心模块设计

6.1 数据模型

/// <summary>单个文本行识别结果</summary>
public sealed record TextLine(
    IReadOnlyList<Point2f> Box,   // 4 点四边形坐标
    string Text,
    float Score
);

/// <summary>整张图片的 OCR 结果</summary>
public sealed record OcrResult(
    IReadOnlyList<TextLine> Lines,
    TimeSpan Elapsed,
    int ImageWidth,
    int ImageHeight
);

6.2 TextSystem 流水线

public class TextSystem
{
    public OcrResult Run(Mat image, bool useCls = false)
    {
        // 1. 检测
        var boxes = _detector.Detect(image);
        if (boxes.Count == 0) return OcrResult.Empty;

        // 2. 排序
        boxes = BoxSorter.Sort(boxes);

        // 3. 裁剪
        var crops = boxes.Select(b => ImageCropper.Crop(image, b)).ToList();

        // 4. [可选] 方向校正
        if (useCls) crops = _classifier.Classify(crops);

        // 5. 识别
        var recResults = _recognizer.Recognize(crops);

        // 6. 过滤 + 组装
        return BuildResult(boxes, recResults);
    }
}

6.3 检测模块(TextDetector)

移植自 predict_det.py + db_postprocess.py

步骤 实现类 Python 参考
缩放 DetPreprocessor.ResizeForTest DetResizeForTest
归一化 DetPreprocessor.Normalize NormalizeImage
CHW 转换 DetPreprocessor.ToChw ToCHWImage
ONNX 推理 TextDetector.Infer det_onnx_session.run
DB 后处理 DbPostProcess DBPostProcess
边界裁剪 TextDetector.ClipBoxes clip_det_res

启动时执行 warm-up(OnnxOCR 在 predict_det.py:57 有同样逻辑),避免首次推理慢 2–10 倍。

6.4 识别模块(TextRecognizer)

移植自 predict_rec.py + rec_postprocess.py

步骤 说明
动态宽度 (imgH * ratio) 计算,上限 imgW=320
归一化 (pixel / 255.0 - 0.5) / 0.5
批推理 每批最多 rec_batch_num=6
CTC 解码 CtcLabelDecode 读取 ppocrv5_dict.txt

6.5 方向分类(OrientationClassifier)

移植自 orientation.py,MVP 阶段实现但默认关闭(use_angle_cls=false),在设置页提供开关。

6.6 ONNX Session 管理

public interface IOnnxSessionFactory
{
    InferenceSession Create(string modelPath);
}
  • CPU 推理是默认配置
  • OcrOptionsappsettings.json 加载模型路径与阈值
  • 应用启动时预加载 det + rec 模型并进行预热

7、来看看效果

模型采用ppocrv5的mobile版本,由于模型不是很大,直接放到项目源码中了。来看看效果吧。
效果

我的笔记本配置如下:

  • CPU:Intel Core i7-1355U
  • 内存:32GB
    还是那张老图,识别速度为390ms,识别结果大体OK,但是有一处文字没有识别出来,需要后续优化。

8、开源地址

代码已经开源,细节部分源码见!欢迎一起来完善这个项目。

https://github.com/lincyang/OnnxOCRSharp

欢迎关注公众号获取更多技术文章和项目更新!

参考:

1、https://github.com/jingsongliujOCR

更多推荐