PaddleSharp:跨越一年的版本更新与亮点
PaddleSharp:跨越一年的版本更新与亮点
我始终坚信,开源社区是技术进步的重要推动力,也是我抽出我业余时间,投入到PaddleSharp这个项目的原因,这个项目充分展现了.NET在复杂计算领域的潜力。今天很高兴地告诉大家,PaddleSharp有了新版本!
先来说说背景,有的朋友可能知道,PaddleSharp过去老版本存在一些东西过时或者无法使用的情况。但是,时光恰恰是优化和革新的好理由和契机,我在距离上一篇文章发布之后,做了许多优化,下面我挑重要的部分做介绍。
整体体验
文档和示例
我一直在更新Github首页的使用文档和示例:
- https://github.com/sdcb/PaddleSharp (项目首页+整体介绍+推理库部分)
- https://github.com/sdcb/PaddleSharp/blob/master/docs/ocr.md (PaddleOCR)
- https://github.com/sdcb/PaddleSharp/blob/master/docs/detection.md (PaddleDetection)
- https://github.com/sdcb/PaddleSharp/blob/master/docs/rotation-detection.md (RotationDetection)
- https://github.com/sdcb/PaddleSharp/blob/master/docs/paddle2onnx.md (Paddle模型转Onnx)
里面包含了大致介绍、使用方式、使用示例、注意事项等。
我会持续维护这些文档,尤其是有客户有时向我反馈一些问题,我会将里面一些常见的问题和解决办法写在上面文档中,因此建议初接触PaddleSharp的朋友看看。
xml注释和snuget调试
作为一名程序员,编程体验很重要,方法怎么用,一个是看示例,另一个就是看注释。
为此我将PaddleSharp中所有的公有方法、受保护方法都加上了详尽的xml注释,这一点在Github上显示了超过9000行代码变动,以后在Visual Studio中鼠标放在PaddleSharp里面的类、参数、方法上时,就会显示详尽的注释,比如下面这个注释:
/// <summary>
/// Returns an Action delegate that configures PaddleConfig for use with Onnx.
/// </summary>
/// <param name="cpuMathThreadCount">The number of CPU threads to use for math operations. A value of 0 sets it to minimum of 4 and the available number of processors.</param>
/// <param name="enableOnnxOptimization">Flag to enable or disable Onnx runtime optimization.</param>
/// <param name="memoryOptimized">Flag to enable or disable memory optimization.</param>
/// <param name="glogEnabled">Flag to enable or disable logging with glog.</param>
/// <returns>The ONNX Runtime paddle device definition.</returns>
public static Action<PaddleConfig> Onnx(int cpuMathThreadCount = 0, bool enableOnnxOptimization = true, bool memoryOptimized = true, bool glogEnabled = false)
{
return cfg =>
{
cfg.OnnxEnabled = true;
if (enableOnnxOptimization) cfg.EnableOnnxOptimization();
cfg.CpuMathThreadCount = cpuMathThreadCount switch
{
0 => Math.Min(4, Environment.ProcessorCount),
_ => cpuMathThreadCount
};
CommonAction(cfg, memoryOptimized, glogEnabled);
};
}
可见它会每个成员函数、参数、返回值都作出了详尽的xml注释。
以此为基础,我还将所有的.NET包发布了.snuget包,这些包自带pdb调试符号文件,以后编程中按F11即可单步调试进入PaddleSharp的源代码中,。
Paddle推理库
设备管理
其中,一项重要的改变在于设备使用接口的设计。老版本中只有PaddleConfig.Defaults.UseGpu这一设备启用选项,为了增强扩展性和用户体验,便对其进行了扩展:新版本中我引入了下列设备:
PaddleDevice.Gpu()PaddleDevice.Openblas()PaddleDevice.Onnx()PaddleDevice.Mkldnn()PaddleDevice.TensorRt()(需要和PaddleDevice.Gpu()配合使用)
不同的方法代表着不同的设备类型,这无疑为用户提供了更大的选择空间,这是PaddleOCR的新版本使用示例(它需要作为PaddleOcrAll的参数传进去):
// 注:需要先安装如下NuGet包:
// * Sdcb.PaddleInference
// * Sdcb.PaddleOCR
// * Sdcb.PaddleOCR.Models.LocalV3
// * Sdcb.PaddleInference.runtime.win64.mkl
// * OpenCvSharp4.runtime.win
FullOcrModel model = LocalFullModels.ChineseV3;
byte[] sampleImageData;
string sampleImageUrl = @"https://www.tp-link.com.cn/content/images2017/gallery/4288_1920.jpg";
using (HttpClient http = new HttpClient())
{
Console.WriteLine("Download sample image from: " + sampleImageUrl);
sampleImageData = await http.GetByteArrayAsync(sampleImageUrl);
}
// 下面的PaddleDevice.Mkldnn()是新加的
// 之前是用的PaddleConfig.Defaults.UseMkldnn = true
// 如果想要GPU,则改为PaddleDevice.Gpu()即可
using (PaddleOcrAll all = new PaddleOcrAll(model, PaddleDevice.Mkldnn())
{
AllowRotateDetection = true, /* 允许识别有角度的文字 */
Enable180Classification = false, /* 不允许识别旋转角度大于90度的文字 */
})
{
// 如果需要读取本地文件,使用如下被注释的代码
// using (Mat src2 = Cv2.ImRead(@"C:\test.jpg"))
using (Mat src = Cv2.ImDecode(sampleImageData, ImreadModes.Color))
{
PaddleOcrResult result = all.Run(src);
Console.WriteLine("Detected all texts: \n" + result.Text);
foreach (PaddleOcrResultRegion region in result.Regions)
{
Console.WriteLine($"Text: {region.Text}, Score: {region.Score}, RectCenter: {region.Rect.Center}, RectSize: {region.Rect.Size}, Angle: {region.Rect.Angle}");
}
}
}
其中用于设备管理的代码在:
using (PaddleOcrAll all = new PaddleOcrAll(model, PaddleDevice.Mkldnn())
它可以换为PaddleDevice.Openblas()(表示不使用Mkldnn):
using (PaddleOcrAll all = new PaddleOcrAll(model, PaddleDevice.Openblas())
或者换成PaddleDevice.Gpu()(表示使用GPU——但必须先安装Gpu的相关包并配好环境):
using (PaddleOcrAll all = new PaddleOcrAll(model, PaddleDevice.Gpu())
当然,我会尽量简化和清晰地解释这个部分。以下是我的修改提案:
库加载方式优化
在旧版PaddleSharp中,库加载方式主要有两种:在.NET Framework中采用Autoload方式,在.NET Core中采用SearchPathLoad方式。然而,这两种方式在某些情况下并不理想,特别是在Linux环境下。
Autoload方式
Autoload方式的主要问题在于,PaddleSharp依赖于paddle_inference_c.dll,而paddle_inference_c.dll又依赖于其他dll如openblas.dll。即使paddle_inference_c.dll成功加载,也可能因为其他依赖dll的问题导致推理失败。
解决办法是在调用依赖dll加载的函数前,先调用一个不会触发加载的函数,例如PaddleConfig.Version。然后在当前进程模型中找到paddle_inference_c模块,定位到它所在的文件夹,并把文件夹路径导入到环境变量中。
SearchPathLoad方式
SearchPathLoad方式利用了.NET Core 3.1引入的AppContext变量:NATIVE_DLL_SEARCH_DIRECTORIES。这种方式不需要读取进程模块就能知道dll的位置。
但是,这种方法在Linux环境下行不通。因为Linux的LD_LIBRARY_PATH环境变量必须在进程启动前被确定。一旦进程启动,环境变量的值就被缓存起来,运行时的修改对程序无效。
新的加载方式
为了解决上述问题,新的PaddleSharp版本采用了逐步加载依赖的方式。在Linux环境中,依次加载以下动态库:
- libgomp.so.1
- libiomp5.so
- libdnnl.so.2
- libmklml_intel.so
- libonnxruntime.so.1.11.1
- libpaddle2onnx.so.1.0.0rc2
这种新的加载方式有效解决了在Linux环境下的问题。
PaddleOCR
已经支持表格识别
这个许多客户反馈了许久,我在大概2023年五一的时候实现了表格识别功能,同时表格识别的模型我都加入了Sdcb.PaddleOCR.Models.LocalV3/Sdcb.PaddleOCR.Models.Online包,可以全离线表格识别或者按需下载模型表格识别。
它的使用示例如下(最新版本请参考这个链接:https://github.com/sdcb/PaddleSharp/blob/master/docs/ocr.md#table-recognition ):
// Install following packages:
// Sdcb.PaddleInference
// Sdcb.PaddleOCR
// Sdcb.PaddleOCR.Models.LocalV3
// Sdcb.PaddleInference.runtime.win64.mkl (required in Windows, linux using docker)
// OpenCvSharp4.runtime.win (required in Windows, linux using docker)
using PaddleOcrTableRecognizer tableRec = new(LocalTableRecognitionModel.ChineseMobileV2_SLANET);
using Mat src = Cv2.ImRead(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "table.jpg"));
// Table detection
TableDetectionResult tableResult = tableRec.Run(src);
// Normal OCR
using PaddleOcrAll all = new(LocalFullModels.ChineseV3);
all.Detector.UnclipRatio = 1.2f;
PaddleOcrResult ocrResult = all.Run(src);
// Rebuild table
string html = tableResult.RebuildTable(ocrResult);
效果如图:
| Raw table | Table model output | Rebuilt table |
|---|---|---|
![]() |
![]() |
![]() |
值得注意的是,PaddleSharp的表格识别是基于飞桨的深度学习模型,对于一些规整的表格,它的效果可能不如使用传统的OpenCV算法,如果想了解传统算法,可以参考我2021年.NET Conf China做的技术分享的pdf:.NET玩转计算机视觉OpenCV - 周杰
两个新的模型包LocalV3/Online
新版本中,还引入了两个新的本地模型包:Sdcb.PaddleOCR.Models.LocalV3/Sdcb.PaddleOCR.Models.Online。一个表示完全本地——不用联网即可使用OCR,另一个表示需要联网,模型按需下载。
下面是使用Sdcb.PaddleOCR.Models.LocalV3的示例:
FullOcrModel model = LocalFullModels.EnglishV3; // 将EnglishV3换为其它模型,如ChineseV3
using (PaddleOcrAll all = new PaddleOcrAll(model))
{
// ...
}
下面是使用Sdcb.PaddleOCR.Models.Online的示例:
FullOcrModel model = await OnlineFullModels.EnglishV3.DownloadAsync();
using (PaddleOcrAll all = new PaddleOcrAll(model))
{
// ...
}
其中值得一提的是LocalV3,它将所有已知PaddleOCR的v3模型都包含了,安装这个包可以实现完全不联网部署。
为什么我需要淘汰原来的Sdcb.PaddleOCR.KnownModels?
说来话长,首先KnownModels有下面几个缺点:
主要原因是OCR需要使用的文字检测、180度分类、文字识别3个模型会下载到以语言命名的同一个文件夹中:
C:\Users\ZhouJie\AppData\Roaming\paddleocr-models\ppocr-v3>tree /f
C:.
│ key.txt
│
├─cls
│ inference.pdiparams
│ inference.pdiparams.info
│ inference.pdmodel
│
├─det
│ inference.pdiparams
│ inference.pdiparams.info
│ inference.pdmodel
│
└─rec
inference.pdiparams
inference.pdiparams.info
inference.pdmodel
如上图,每个模型的cls文件夹都可能重复占用磁盘空间、且需要重复下载——这不合理。
因此我引入了
Sdcb.PaddleOCR.Models.Online,已经下载过的模型不会重复下载,这个行为和PaddleOCR上游Python代码一致。次要问题是它的命名,
KnownModels不能代表它是本地模型还是线上模型(虽然它本质是线上模型、按需下载),如果使用LocalV3和Online,则可以清晰地看出是本地模型或者线上模型。
识别阶段走batch
关于性能问题,新版本也做了一些重要的升级。OCR文字识别阶段能够自动支持batch处理,且走batch时会排序,将一样宽的文字行做一批识别,这样大大优化了程序的性能。
据一些客户的测试反馈,PaddleSharp的PaddleOCR的性能表现很好,甚至在某些场景下和官方的C++、Python版本相比有更好的表现。
总结
其实上面只是一些主要的,其实PaddleSharp项目还有许多非常有意思功能增强,比如RotationDetection和Paddle2Onnx,以后有机会我一一介绍。
我深信这些更新无疑会为.NET开源社区带来更多的可能性和便利。我将继续在这个领域上付出努力,为.NET社区做出更多的贡献。我期待着更多.NET爱好者能够加入我,一起提升PaddleSharp在.NET深度学习实战应用中的影响力,它将始终保持好用且免费,让我们共同期待它的更多精彩!
想尝试PaddleSharp的朋友,欢迎访问我的Github,也请给个Star
喜欢的朋友 请关注我的微信公众号:【DotNet骚操作】

PaddleSharp:跨越一年的版本更新与亮点的更多相关文章
- 如果Android真的收费了,你怎么看?
前言 今天突然看到一群里有人发了下面这样一张图片,然后群里又炸了! 于是又和同事讨论了android收费的问题,然后隔壁正在玩农药的UI妹子就笑了... 没错! 安卓可能要收费了!安卓可能要收费了 ...
- 133_Power BI 报表服务器2020年1月版本更新亮点
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一个很长的春节假期后,居家办公. 升级了Power BI 报表服务器(2020年1月版本). 具体的升级内容见官网博客: ...
- React版本更新及升级须知(持续更新)
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; font: 18.0px "PingFang SC Semibold& ...
- 灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟?
灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟? 历史进入2019年,放眼望去,今天的整个技术大环境和生态都发生了很大的变化.在己亥猪年春节刚刚过去的早春时节,我们来梳理和展望一下整个 ...
- HMS Core 机器学习服务6.4.0版本更新啦,文本翻译功能增加10种小语种语言类型!
近日,HMS Core机器学习服务(ML Kit)文本翻译功能在6.4.0版本更新中增加了10种小语种语言类型,分别是马其他语.马其顿.冰岛.乌尔都语.波斯尼亚语.乌克兰语.加泰罗尼亚语.斯洛文尼亚语 ...
- 凭吊一下ASP.NET 5,然后跨平台,越跨越开心
ASP.NET 5 is dead ASP.NET 5在今年早些时候被宣判死刑了.但是这并不影响我们之前在ASP.NET 5乃至ASP.NET MVC平台上的经验累积--没错,微软改名部门又立功了!他 ...
- 跨越语言的障碍:C++/CLI 调用 C#
首先我想投诉一下博客园首页右边栏的广告..最近总是出现很恐怖的整容脸的广告.真的是吓坏了.=.=大家有同感吗? 博客园前一阵子掀起了语言的广泛讨论,事实上语言的争执在整个程序员圈子也没有停止过.以我个 ...
- Python黑帽编程 3.4 跨越VLAN
Python黑帽编程 3.4 跨域VLAN VLAN(Virtual Local Area Network),是基于以太网交互技术构建的虚拟网络,既可以将同一物理网络划分成多个VALN,也可以跨越物理 ...
- Jmeter3.0发布,版本更新都更新了什么
Jmeter已发布了3.0,一个大版本的开源测试工具,加入了一些新的特性及软件的改进. Jmeter已隔10年的大版本更新 这是在过去12年里jmeter第一个大版本的更新,jmeter 2.0版本发 ...
- iTunes Connect 显示可供销售,但是AppStore 就是不显示新版本(异于往常版本更新)
这次版本更新,从上传到审核通过不足8小时.由于是手动发布,第二天早上上班发布了新版本.但是不同于往常,这次等了很久也不见AppStore 更新新版本.检查一下iTunes Connect ,显示可供销 ...
随机推荐
- ts中报错信息收集
1. 错误代码 参考:https://www.mmbyte.com/article/92849.html 1 state.localuserInfo = JSON.parse(localStorage ...
- class(类)和构造函数(原型对象)
构造函数和class的关系,还有面向对象和原型对象,其实很多人都会很困惑这些概念,这是第二次总结这些概念了,之前一次,没有class类,其实了解了构造函数,class也就很容易理解了 一. 构造函数和 ...
- 「学习笔记」tarjan求最近公共祖先
Tarjan 算法是一种 离线算法,需要使用并查集记录某个结点的祖先结点. 并没有传说中的那么快. 过程 将询问都记录下来,将它们建成正向边和反向边. 在 dfs 的过程中,给走过的节点打上标记,同时 ...
- 云原生时代崛起的编程语言Go并发编程实战
@ 目录 概述 基础理论 并发原语 协程-Goroutine 通道-Channel 多路复用-Select 通道使用 超时-Timeout 非阻塞通道操作 关闭通道 通道迭代 定时器-TimerAnd ...
- 讯飞星火大模型 与New Bing实测对比
昨天科大讯飞发布了讯飞星火认知大模型,在发布会现场实测大模型的7种核心能力,并发布了它在教育.办公.汽车.数字员工领域的应用成果.科大讯飞董事长刘庆峰表示:认知大模型展示了通用人工智能的曙光,讯飞星火 ...
- Django, urls的参数name的demo
Django的路由变化 遇到需要修改路由的需求,特别记录一下 项目开始 django-admin startproject sandboxOA. # 外部文件夹可以改变名字, '.'的意思是上一级不需 ...
- flask接口参数校验 jsonschema 的使用
开头 flask接口开发中参数校验可以用到的方法有很多,但是我比较喜欢跟前端的js检验类似,故选用到了 jsonschema 这个参数校验的库 Demo 下面是一个比较全的参数校验的接口,日后方便参考 ...
- 2021-05-03:给定一个非负整数num, 如何不用循环语句, 返回>=num,并且离num最近的,2的某次方 。
2021-05-03:给定一个非负整数num, 如何不用循环语句, 返回>=num,并且离num最近的,2的某次方 . 福大大 答案2021-05-03: 32位整数,N=32. 1.非负整数用 ...
- 2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的头节点
2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的 ...
- 2021-08-15:给定一个字符串Str,返回Str的所有子序列中有多少不同的字面值。
2021-08-15:给定一个字符串Str,返回Str的所有子序列中有多少不同的字面值. 福大大 答案2021-08-15: 返回值=上+新-修正. 时间复杂度:O(N) 空间复杂度:O(N). 代码 ...


