SimdGcdrNet
纯 C# 的 GCDRNet(IEEE TAI 2023):手机拍摄文档的去阴影与外观增强。不依赖 onnxruntime 或任何第三方推理引擎。
dotnet add package Sdcb.SimdGcdrNet
dotnet add package Sdcb.SimdGcdrNet.Models
拖动分割线对比:手机拍摄的手抄报,硬手影
为什么
手机拍文档,先去掉影子再谈识别
手机拍的文档照片几乎必有手影、折痕和背面透字。GCDRNet 用两级网络处理:GC-Net 先估计逐像素的光照/阴影图,做一次除法校正,再把原图和校正图一起喂给 DR-Net 还原细节。
SimdGcdrNet 把这个模型连同推理引擎一起搬进了 C#:自带 ONNX proto 读取和图编译器(shape 特化、常量折叠、算子融合、arena 规划),卷积、LayerNorm、激活全部手写 SIMD。没有 onnxruntime,没有任何第三方推理引擎——一个 NuGet 包就是全部。
- 任意 8 位交错格式 Bgr24 / Rgb24 / Bgra32 / Rgba32
- 任意 stride,可以原地增强
- GcdrEnhancer 线程安全
- Native AOT 兼容
- 模型以嵌入资源零拷贝加载
- 补齐 32 倍数 replicate padding,自动裁回
青色为 GPU 后端。浏览器里固定走 CPU 的 PackedSimd 档。
上手
选你喜欢的图像库,像素内存直接传
using Sdcb.SimdGcdrNet;
using Sdcb.SimdGcdrNet.Models;
// Auto:macOS Apple Silicon 优先 Metal,否则 Vulkan,最后退回 CPU(默认值是 Cpu)
using GcdrEnhancer enh = GcdrEnhancer.FromBytes(GcdrModels.GcNet, GcdrModels.DrNet, GcdrBackend.Auto);
// 不用模型包时:GcdrEnhancer.FromFiles("gcdrnet_gcnet_sim.onnx", "gcdrnet_drnet_sim.onnx", ...)
byte[] dst = enh.Enhance(bgr, width: 1600, height: 1152); // 紧密排列的 BGR24,HWC
// 带 stride、任意 8 位交错格式,输出与输入同格式(alpha 原样拷回),可原地写:
enh.Enhance(srcSpan, w, h, srcStride, dstSpan, dstStride, GcdrPixelFormat.Rgba32);using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using System.Runtime.InteropServices;
// 克隆 Default:改 Configuration.Default 会丢掉解码器注册
Configuration cfg = Configuration.Default.Clone();
cfg.PreferContiguousImageBuffers = true;
using Image<Rgba32> image = Image.Load<Rgba32>(new DecoderOptions { Configuration = cfg }, "sample.jpg");
if (!image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> memory))
throw new InvalidOperationException("pixel buffer is not contiguous");
Span<byte> pixels = MemoryMarshal.AsBytes(memory.Span);
// 原地增强:src 与 dst 是同一块内存
enh.Enhance(pixels, image.Width, image.Height, image.Width * 4, pixels, image.Width * 4, GcdrPixelFormat.Rgba32);using SkiaSharp;
using SKBitmap bitmap = SKBitmap.Decode("sample.jpg").Copy(SKColorType.Bgra8888);
unsafe
{
enh.Enhance(
new ReadOnlySpan<byte>((byte*)bitmap.GetPixels(), bitmap.RowBytes * bitmap.Height),
bitmap.Width, bitmap.Height, bitmap.RowBytes,
new Span<byte>((byte*)bitmap.GetPixels(), bitmap.RowBytes * bitmap.Height),
bitmap.RowBytes, GcdrPixelFormat.Bgra32);
}using OpenCvSharp;
using Mat image = Cv2.ImRead("sample.jpg", ImreadModes.Color);
int stride = (int)image.Step(); // Mat 的 Step() 直接当 stride 传
unsafe
{
enh.Enhance(
new ReadOnlySpan<byte>((byte*)image.Data, stride * image.Height),
image.Width, image.Height, stride,
new Span<byte>((byte*)image.Data, stride * image.Height),
stride, GcdrPixelFormat.Bgr24);
}using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new("sample.jpg");
BitmapData data = bitmap.LockBits(new(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
try
{
unsafe
{
Span<byte> pixels = new((byte*)data.Scan0, data.Stride * bitmap.Height);
enh.Enhance(pixels, data.Width, data.Height, data.Stride, pixels, data.Stride, GcdrPixelFormat.Bgr24);
}
}
finally { bitmap.UnlockBits(data); }using GcdrEnhancer enh = GcdrEnhancer.FromBytes(GcdrModels.GcNet, GcdrModels.DrNet, GcdrBackend.Auto);
// Auto:macOS Apple Silicon 上 Metal,有 Vulkan 计算设备则 Vulkan,否则 CPU;
// 也可显式写 GcdrBackend.Vulkan / Metal / Cpu。
// Vulkan:单命令缓冲图执行,融合流水线 bgr2nchw → GC → div6 → DR → nchw2bgr。性能
从 RTX 到骁龙,再到浏览器
数据来自仓库 docs/ 下各机型报告(49 张 RealDAE 语料,去掉预热后的均值)。perf.md ·wasm.md ·corpus.md
RTX 3080 Ti · 1600×1152 单张
PyTorch vs SimdGcdrNet Vulkan
SimdGcdrNet 的 9.6 ms 本身已包含上传/下载与前后处理。
语料均值 · CPU vs GPU
49 RealDAE · ms/img
浏览器 browser-wasm
Edge · Ryzen 9 5950X · ms/img
同一台 5950X 的桌面 AVX2 约 420 ms/张——浏览器里达到了它的六成性能。
组合拳
增强完,再接 SimdPaddleOCR
两个库都吃同一块解码后的像素内存:SimdGcdrNet 原地增强,SimdPaddleOCR 直接识别,中间不需要任何编码/拷贝。右图这页 RealDAE 样张背面透字严重——原图 OCR 出 56 行(大多是背面透过来的乱码),增强后只剩正文的 14 行。

背面透字(show-through)在增强后基本消失
平台
一个包,到处都能跑
- 目标框架:net10.0
- NativeAOT 兼容(IsAotCompatible,无裁剪/AOT 警告)
- 模型(约 21 MB)从程序集嵌入资源直接加载
- 骁龙 8 Gen 3 / RK3399 / 浏览器实测
| OS | CPU | GPU |
|---|---|---|
| Windows | x64ARM64 | Vulkan |
| Linux | x64ARM64 | Vulkan |
| macOS | x64ARM64 | Metal |
| Android | ARM64 | Vulkan |
| Browser | wasm | – |
NuGet
包与模型
Models 包约 21 MB(GC-Net + DR-Net 两个 ONNX),以嵌入资源提供,版本独立于引擎。
致谢
模型来自论文《Appearance Enhancement for Camera-captured Document Images in the Wild》(IEEE TAI 2023)与上游实现 ZZZHANG-jx/GCDRNet(权重 MIT,Copyright (c) 2024 Jiaxin Zhang)。
示例图与测试语料取自同一论文的 RealDAE 数据集(sdcb/DocGainFixture)。推理引擎 Sdcb.SimdGcdrNet 为 Apache-2.0。


