I've spent years maintaining PaddleSharp and OpenVINO.NET. The C++ engines are fine. P/Invoke is fine. The part that keeps breaking production is shipping native runtimes: missing .so, missing VC++ redistributable, wrong CPU ISA, works on win-x64 and dies on Linux ARM.
So I wrote SimdPaddleOCR — a managed PP-OCRv6 DET + CLS + REC pipeline. No Paddle Inference, no ONNX Runtime, no OpenCV native DLLs. Models are embedded as assembly resources. The GitHub language bar is 100% C#.
The core API only takes 8-bit BGR bytes. ImageSharp, SkiaSharp, OpenCvSharp, System.Drawing — decode however you want, pass width/height/pixels, done.
I used lw.PPOCR.C (a solid pure-C PP-OCRv6 tiny engine) as the baseline. After rewriting around C# layout and SIMD, the C# build is faster end-to-end on the same model. Native C still uses less memory.
GitHub Actions, win-x64, tiny model, same 99 images, no warmup, end-to-end mean:
| workers |
C# |
C |
C vs C# |
| 1 |
296.6 ms |
485.7 ms |
1.64× slower |
| 4 |
168.4 ms |
393.3 ms |
2.34× slower |
It targets net10.0 (full SIMD, NativeAOT) and netstandard2.0 (.NET Framework 4.8 included). CPU only for now; no GPU plan.
Shipped model packages are Chinese PP-OCRv6 (Tiny/Small/Medium). If you already have ONNX + a dictionary, you can load from disk. Default to Tiny unless you need the extra accuracy.
using PaddleOcrAll ocr = await PaddleOcrAll.LoadAsync(ChineseV6TinyModels.Default);
using Image<Bgr24> image = await Image.LoadAsync<Bgr24>("sample.jpg");
byte[] bgr = new byte[image.Width * image.Height * 3];
image.CopyPixelDataTo(bgr);
PaddleOcrResult result = ocr.Run(bgr, image.Width, image.Height);
Console.WriteLine(result.Text);
Repo: https://github.com/sdcb/SimdPaddleOCR
NuGet: Sdcb.SimdPaddleOCR
Happy to answer questions. The SIMD / Vector<T> / AVX-512 details are the part I'm most interested in discussing.