# SimdPaddleOCR **Repository Path**: imiyu/SimdPaddleOCR ## Basic Information - **Project Name**: SimdPaddleOCR - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-07 - **Last Updated**: 2026-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Sdcb.SimdPaddleOCR [![test](https://github.com/sdcb/SimdPaddleOCR/actions/workflows/test.yml/badge.svg)](https://github.com/sdcb/SimdPaddleOCR/actions/workflows/test.yml) [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) [![QQ](https://img.shields.io/badge/QQ_Group-579060605-52B6EF?style=social&logo=tencent-qq&logoColor=000&logoWidth=20)](https://qm.qq.com/q/bPw5jAK4qk) **中文** | [English](README_EN.md) 纯 C# PP-OCRv6 推理库:多平台手写 Kernel / GEMM 等算子、超高性能、低内存需求、超高准确率。 自带托管 ONNX 解释器,不依赖 Paddle Inference、ONNX Runtime 或 OpenCV 原生库。 核心 API 接收交错像素内存(默认 BGR24,也可直接传 RGB24 / BGRA32 / RGBA32),不负责图片解码,因此不会强制引入 ImageSharp、SkiaSharp 或 OpenCvSharp。 ## 快速开始 安装核心包和 tiny 模型(tiny 会传递引用 CLS 与 `ModelProvider`): ```powershell dotnet add package Sdcb.SimdPaddleOCR dotnet add package Sdcb.SimdPaddleOCR.Models.ChineseV6Tiny dotnet add package SixLabors.ImageSharp --version 3.1.11 ``` 模型从程序集嵌入资源直接加载,不会解压或写入临时文件。`stride = 0` 表示紧密排列(`width *` 每像素字节数)。默认 `ImagePixelFormat.Bgr24`;其它布局在 resize / crop 里就地 swizzle,不会先转成一张中间 BGR 图。 ### ImageSharp 3(推荐) ```csharp using System.Runtime.InteropServices; using Sdcb.SimdPaddleOCR; using Sdcb.SimdPaddleOCR.Models.ChineseV6Tiny; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using PaddleOcrAll ocr = await PaddleOcrAll.LoadAsync(ChineseV6TinyModels.Default); using Image image = await Image.LoadAsync(CreateDecoderOptions(), "sample.jpg"); Rgba32[]? packedCopy = null; if (!image.DangerousTryGetSinglePixelMemory(out Memory memory)) { packedCopy = new Rgba32[checked(image.Width * image.Height)]; image.CopyPixelDataTo(packedCopy); memory = packedCopy; } PaddleOcrResult result = ocr.Run(MemoryMarshal.AsBytes(memory.Span), image.Width, image.Height, format: ImagePixelFormat.Rgba32); Console.WriteLine(result.Text); // 必须 Clone Default:直接改 Configuration.Default 会丢掉 PNG/JPEG 解码器。 // 默认分配器按 4MB 分块,大图会让 DangerousTryGetSinglePixelMemory 失败。 static DecoderOptions CreateDecoderOptions() { Configuration configuration = Configuration.Default.Clone(); configuration.PreferContiguousImageBuffers = true; return new DecoderOptions { Configuration = configuration }; } ``` ImageSharp 默认分配器会把像素拆成 4MB 块,大图上 `DangerousTryGetSinglePixelMemory` 会失败。用独立 `Configuration` 打开 `PreferContiguousImageBuffers` 尽量零拷贝;仍不连续再 `CopyPixelDataTo`。不要改 `Configuration.Default`。 后续三个示例由调用方把 lock / 原生指针包成 `ReadOnlySpan` 再交给 `Run`,加载方式与上面相同。整段 `Run` 期间不要 Unlock / Dispose 源图。 ### SkiaSharp ```csharp using SkiaSharp; SKBitmap bitmap = SKBitmap.Decode("sample.jpg") ?? throw new InvalidDataException("无法读取图片"); if (bitmap.ColorType != SKColorType.Bgra8888) bitmap = bitmap.Copy(SKColorType.Bgra8888) ?? throw new InvalidDataException("无法转换到 BGRA"); int stride = bitmap.RowBytes; unsafe { PaddleOcrResult result = ocr.Run(new ReadOnlySpan((byte*)bitmap.GetPixels(), stride * bitmap.Height), bitmap.Width, bitmap.Height, stride, ImagePixelFormat.Bgra32); } ``` ### OpenCvSharp5 ```csharp using OpenCvSharp; using Mat image = Cv2.ImRead("sample.jpg", ImreadModes.Color); if (image.Empty()) throw new InvalidDataException("无法读取图片"); int stride = (int)image.Step(); unsafe { PaddleOcrResult result = ocr.Run(new ReadOnlySpan((byte*)image.Data, stride * image.Height), image.Width, image.Height, stride, ImagePixelFormat.Bgr24); } ``` ### Bitmap ```csharp using System.Drawing; using System.Drawing.Imaging; using Bitmap bitmap = new("sample.jpg"); Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); BitmapData data = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); try { unsafe { PaddleOcrResult result = ocr.Run(new ReadOnlySpan((byte*)data.Scan0, data.Stride * bitmap.Height), bitmap.Width, bitmap.Height, data.Stride, ImagePixelFormat.Bgra32); } } finally { bitmap.UnlockBits(data); } ``` ## NuGet 包 | NuGet 包 | 版本 | 说明 | | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | `Sdcb.SimdPaddleOCR` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR) | 纯托管推理核心(`net10.0;netstandard2.0`) | | `Sdcb.SimdPaddleOCR.ModelProvider` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.ModelProvider.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR.ModelProvider) | 模型契约(`IPaddleOcrModelProvider` / `PaddleOcrModelBundle`),通常被传递引用 | | `Sdcb.SimdPaddleOCR.Models.ChineseV6Tiny` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.Models.ChineseV6Tiny.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR.Models.ChineseV6Tiny) | PP-OCRv6 tiny DET+REC+字典;`ChineseV6TinyModels.Default` 含 CLS | | `Sdcb.SimdPaddleOCR.Models.ChineseV6Small` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.Models.ChineseV6Small.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR.Models.ChineseV6Small) | PP-OCRv6 small;`ChineseV6SmallModels.Default` | | `Sdcb.SimdPaddleOCR.Models.ChineseV6Medium` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.Models.ChineseV6Medium.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR.Models.ChineseV6Medium) | PP-OCRv6 medium;`ChineseV6MediumModels.Default` | | `Sdcb.SimdPaddleOCR.Models.TextLineOrientation` | [![NuGet](https://img.shields.io/nuget/v/Sdcb.SimdPaddleOCR.Models.TextLineOrientation.svg)](https://www.nuget.org/packages/Sdcb.SimdPaddleOCR.Models.TextLineOrientation) | PP-LCNet 文本行方向 CLS,被三个中文模型包传递引用 | 每个 `IPaddleOcrModelProvider` 提供 `Name`、`Kind`、`Format`、语言和版本元数据以及 `OpenRead()` / `OpenReadAsync()`。完整 OCR 组合由 `PaddleOcrModelBundle` 表达(DET、REC、字典和可选 CLS)。当前语言代码为 `zh`。单个模型也可被其他推理实现消费,例如 `ChineseV6TinyModel.Detection.OpenReadAsync()`。`Model`、`PaddleOcrDetector`、`PaddleOcrClassifier`、`PaddleOcrRecognizer` 和 `PaddleOcrAll` 均提供 Stream 加载入口;解析完成后不会继续保留完整的 ONNX 原始字节。 ## 使用本地模型 核心不下载模型。使用本地 DET、CLS、REC 和字典文件时: ```csharp using Sdcb.SimdPaddleOCR; using PaddleOcrAll ocr = await PaddleOcrAll.LoadAsync( detectionPath: "models/det.onnx", classificationPath: "models/cls.onnx", recognitionPath: "models/rec.onnx", dictionaryPath: "models/ppocr_keys.txt"); ``` `PaddleOcrOptions` 里两套并行不要混用:`DetIntraOpThreads` 是检测图内的卷积线程 (一份 session,默认最多 8);`LineWorkerCount` 是一行一组的 CLS/REC worker 路数 (每路一个 session,上限,实际 `min(请求, ProcessorCount)`;`0` 为 `min(ProcessorCount, 4)`)。检测阈值、边界长度、方向分类、 动态识别宽度和 Session 缓存上限等也在同一组 options 里。 ## 示例 四个示例共用 `examples/sample.jpg`,图片由示例负责解码并转换为 BGR: - `examples/ImageSharp.AspNetCore`:ASP.NET Core + ImageSharp 3,可上传体验与 `POST /api/ocr` JSON API。 - `examples/SkiaSharp.Avalonia`:Avalonia 桌面示例,SkiaSharp 解码。 - `examples/OpenCvSharp5.Wpf`:WPF 示例,OpenCvSharp5 解码。 - `examples/SystemDrawing.WinForms`:.NET 10 Windows / .NET Framework 4.8 双目标 WinForms 示例,使用 `Bitmap`/`LockBits`;运行 `net48` 前请安装 .NET Framework 4.8 Developer Pack,项目平台选择 x64。 ```powershell dotnet run --project examples/ImageSharp.AspNetCore dotnet run --project examples/OpenCvSharp5.Wpf -- path/to/image.jpg dotnet run --project examples/SkiaSharp.Avalonia -- path/to/image.jpg dotnet run --project examples/SystemDrawing.WinForms --framework net10.0-windows ``` Web 示例打开站点即可上传;API 为 `POST /api/ocr`(`multipart/form-data` 字段 `file`、`model`),文档在 `/scalar`。 `Sdcb.SimdPaddleOCR` 还带 4 个 LINQPad 脚本(`imagesharp` / `skiasharp` / `opencvsharp5` / `bitmap`):下载示例图,tiny 模型识别,控制台输出文字。`bitmap` 同时能在 LINQPad 5 / .NET Framework 4.8 和 .NET 10 上跑。包打了 `linqpad-samples` 标签,免费版也能用。 ## 常见问题 ### 为什么调试时 OCR 识别特别慢? 调试器可能会在模块加载时取消 JIT 优化,使 OCR 的计算密集型代码无法获得应有的运行时优化,从而导致识别明显变慢。 请关闭该选项,然后重新启动调试会话: - Visual Studio:`工具 > 选项 > 调试 > 常规`,取消勾选`在模块加载时取消 JIT 优化`。 - Rider:`构建、执行、部署 > 调试器 > JIT`,取消勾选`在加载模块时禁用 JIT 优化`。 - VS Code:打开`设置 (JSON)`,添加 `"csharp.debug.suppressJITOptimizations": false`。 ### Native AOT 发布后为什么比 JIT 慢很多? x64 发布 Native AOT 时,可执行项目里**必须**设置: ```xml avx2 ``` 不设的话,ILC 按 SSE2 / 128-bit `Vector` 基线编译,`Avx2.IsSupported` 会被折成 `false`,AVX2 内核整段裁掉,推理会慢一截。没有 AVX2 的 CPU 不要设这项。ARM64 的 AOT 基线已带 NEON / `AdvSimd`,一般不用写 `IlcInstructionSet`。 ### ImageSharp 报「图片像素不是连续内存」或 `DangerousTryGetSinglePixelMemory` 失败? ImageSharp 默认分配器会把像素拆成 4MB 块,大图上拿不到一整块连续缓冲。按上面「ImageSharp 3」示例:`Clone` 一份 `Configuration` 后打开 `PreferContiguousImageBuffers`,仍不连续再 `CopyPixelDataTo`。不要改 `Configuration.Default`,否则 PNG/JPEG 解码器会丢。完整写法见 `examples/ImageSharp.AspNetCore/Program.cs`。 ## 支持范围 | | 说明 | | ---------- | --------------------------------------------------------------------------------------------------------------------------- | | 目标框架 | 核心 `net10.0;netstandard2.0`;`ModelProvider` 与全部模型包为 `netstandard2.0` | | 推荐运行时 | .NET 10:完整 x86 SIMD 与 NativeAOT(`IsAotCompatible`) | | 兼容运行时 | `netstandard2.0` 可在 .NET Framework 4.8 等环境使用;编译时去掉 AVX / AVX-512 / VNNI 源,走 `System.Numerics.Vector` / 标量 | | CI 架构 | Windows x64 / x86 / ARM64,Linux x64 / ARM64,macOS x64 / ARM64 | | SIMD | .NET 10 运行时探测 AVX → AVX2 → AVX-512 / VNNI;无对应指令集或 ARM 时用 Vector/标量 | | 输入 | 交错像素内存(默认 BGR24,也可 RGB24 / BGRA32 / RGBA32);无图片路径、文件或图片库 API | | 设备 | CPU only,无 GPU | | NativeAOT | 裁剪发布时请保留核心程序集和所用模型程序集 | ## 许可证与第三方组件 本仓库中由本项目编写的源代码和文档采用 [Apache License 2.0](LICENSE) 发布。 Apache-2.0 提供明确的专利授权条款,更适合公开发布的库和 NuGet 包。 模型资源和第三方代码不因本项目许可证而被重新授权: - PP-OCRv6 DET/REC、TextLineOrientation CLS 及字典来自 PaddleOCR 生态,来源资料标记为 Apache-2.0;发布模型包时请保留来源和许可证说明。 - 示例依赖遵循各自上游许可证;特别是 ImageSharp 3.x 使用 Six Labors Split License, 不是普通 MIT 许可证。 完整的第三方归属和分发说明见 [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md)。PaddleOCR、PP-OCR 及相关名称归其 各自权利人所有,本项目不代表官方,也不构成官方背书。 ## 性能 **1.4.2** 相对 **1.3.0**:图级 NHWC 从仅 AVX2 扩到 ns2 / x64 scalar / net10 AdvSIMD,预处理直接写 NHWC。 **内存占用大幅下降**:tiny-4w 工作集峰值大约少 **300 MB**(win-x64 817→**515 MB**,linux-arm64 840→**572 MB**),Δ WS 从约 400 MB 降到约 100–160 MB。 CI tiny 满勤 **767/1032**、CER **2.36%**(cls 1020/1020;1.3 跳过首张是 757/1022、3.53%,尺子不同,不能当涨幅)。本机行精确仍是 742 / 950 / 1004;CER **2.78% → 2.37%**、**0.60% → 0.41%**、**0.67% → 0.14%**(左 1:4 CLS,倒长行先转正再进 REC)。 GitHub-hosted runner、PP-OCRv6 tiny、去掉首张 warmup 后的中位墙钟: | 路径 | 1.3 | 1.4.2 | 相对 | 工作集峰值 | Δ WS | | ----------------------------------------- | ------: | -------: | -----------: | ---------------: | --------------: | | linux-arm64 N2 `tiny-4w`(net10 AdvSIMD) | 241 | **180** | **0.75×** | 840 → **572 MB** | 418 → **162 MB** | | linux-arm64 `tiny-4w-ns2` | 374 | **295** | **0.79×** | | | | linux-arm64 `tiny-4w-scalar` | 984 | **856** | **0.87×** | | | | win-x64 7763 `tiny-4w`(AVX2) | 167 | ~184 | 持平(噪声) | 817 → **515 MB** | 398 → **107 MB** | | win-x64 7763 `tiny-4w-ns2` | **343** | **228** | **0.66×** | | | | win-x64 7763 `tiny-4w-noavx` | 481 | **380** | **0.79×** | | | | win-x64 7763 `tiny-4w-scalar` | 1368 | **1220** | **0.89×** | | | 本机 Ryzen 7 5800X、4 worker、仓库 `dataset/` 100 张(n=99)本库 mean(1.3 NuGet → 1.4.2):tiny **86.0 → 63.1 ms**(0.73×),small **222 → 200 ms**(0.90×),medium **628 → 585 ms**(0.93×)。同机 ns2:tiny **203 → 96.5 ms**(0.48×),small **432 → 303 ms**(0.70×),medium **1606 → 874 ms**(0.54×)。 同机 C 引擎与各 ISA 比值见 [`docs/perf.md`](docs/perf.md)。 ## 性能复现 [GitHub Actions `test` 工作流](https://github.com/sdcb/SimdPaddleOCR/actions/workflows/test.yml) 会跑单元测试,并在 Windows / Linux / macOS 多架构上对 tiny / small / medium 做 bench (含关闭 AVX-512 / AVX2 / AVX / 全部硬件加速,以及 `netstandard2.0` 库)。汇总报告写入 job summary 与 `perf-report` artifact。 ## 微信群 ![](https://io.starworks.cc:88/cv-public/2026/ocr-wxg-qr.png?0922) 如果微信群二维码过期了,请加入 QQ 群 [C#/.NET计算机视觉技术交流 579060605](https://qm.qq.com/q/bPw5jAK4qk)。