PaddleSharp:跨越一年的版本更新与亮点

我始终坚信,开源社区是技术进步的重要推动力,也是我抽出我业余时间,投入到PaddleSharp这个项目的原因,这个项目充分展现了.NET在复杂计算领域的潜力。今天很高兴地告诉大家,PaddleSharp有了新版本!

先来说说背景,有的朋友可能知道,PaddleSharp过去老版本存在一些东西过时或者无法使用的情况。但是,时光恰恰是优化和革新的好理由和契机,我在距离上一篇文章发布之后,做了许多优化,下面我挑重要的部分做介绍。

整体体验

文档和示例

我一直在更新Github首页的使用文档和示例:

里面包含了大致介绍、使用方式、使用示例、注意事项等。

我会持续维护这些文档,尤其是有客户有时向我反馈一些问题,我会将里面一些常见的问题和解决办法写在上面文档中,因此建议初接触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环境下行不通。因为LinuxLD_LIBRARY_PATH环境变量必须在进程启动前被确定。一旦进程启动,环境变量的值就被缓存起来,运行时的修改对程序无效。

新的加载方式

为了解决上述问题,新的PaddleSharp版本采用了逐步加载依赖的方式。在Linux环境中,依次加载以下动态库:

  1. libgomp.so.1
  2. libiomp5.so
  3. libdnnl.so.2
  4. libmklml_intel.so
  5. libonnxruntime.so.1.11.1
  6. 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不能代表它是本地模型还是线上模型(虽然它本质是线上模型、按需下载),如果使用LocalV3Online,则可以清晰地看出是本地模型或者线上模型。

识别阶段走batch

关于性能问题,新版本也做了一些重要的升级。OCR文字识别阶段能够自动支持batch处理,且走batch时会排序,将一样宽的文字行做一批识别,这样大大优化了程序的性能。

据一些客户的测试反馈,PaddleSharpPaddleOCR的性能表现很好,甚至在某些场景下和官方的C++Python版本相比有更好的表现。

总结

其实上面只是一些主要的,其实PaddleSharp项目还有许多非常有意思功能增强,比如RotationDetectionPaddle2Onnx,以后有机会我一一介绍。

我深信这些更新无疑会为.NET开源社区带来更多的可能性和便利。我将继续在这个领域上付出努力,为.NET社区做出更多的贡献。我期待着更多.NET爱好者能够加入我,一起提升PaddleSharp.NET深度学习实战应用中的影响力,它将始终保持好用且免费,让我们共同期待它的更多精彩!

想尝试PaddleSharp的朋友,欢迎访问我的Github,也请给个Star

喜欢的朋友 请关注我的微信公众号:【DotNet骚操作】

PaddleSharp:跨越一年的版本更新与亮点的更多相关文章

  1. 如果Android真的收费了,你怎么看?

    前言 今天突然看到一群里有人发了下面这样一张图片,然后群里又炸了!   于是又和同事讨论了android收费的问题,然后隔壁正在玩农药的UI妹子就笑了... 没错! 安卓可能要收费了!安卓可能要收费了 ...

  2. 133_Power BI 报表服务器2020年1月版本更新亮点

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一个很长的春节假期后,居家办公. 升级了Power BI 报表服务器(2020年1月版本). 具体的升级内容见官网博客: ...

  3. React版本更新及升级须知(持续更新)

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; font: 18.0px "PingFang SC Semibold& ...

  4. 灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟?

    灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟? 历史进入2019年,放眼望去,今天的整个技术大环境和生态都发生了很大的变化.在己亥猪年春节刚刚过去的早春时节,我们来梳理和展望一下整个 ...

  5. HMS Core 机器学习服务6.4.0版本更新啦,文本翻译功能增加10种小语种语言类型!

    近日,HMS Core机器学习服务(ML Kit)文本翻译功能在6.4.0版本更新中增加了10种小语种语言类型,分别是马其他语.马其顿.冰岛.乌尔都语.波斯尼亚语.乌克兰语.加泰罗尼亚语.斯洛文尼亚语 ...

  6. 凭吊一下ASP.NET 5,然后跨平台,越跨越开心

    ASP.NET 5 is dead ASP.NET 5在今年早些时候被宣判死刑了.但是这并不影响我们之前在ASP.NET 5乃至ASP.NET MVC平台上的经验累积--没错,微软改名部门又立功了!他 ...

  7. 跨越语言的障碍:C++/CLI 调用 C#

    首先我想投诉一下博客园首页右边栏的广告..最近总是出现很恐怖的整容脸的广告.真的是吓坏了.=.=大家有同感吗? 博客园前一阵子掀起了语言的广泛讨论,事实上语言的争执在整个程序员圈子也没有停止过.以我个 ...

  8. Python黑帽编程 3.4 跨越VLAN

    Python黑帽编程 3.4 跨域VLAN VLAN(Virtual Local Area Network),是基于以太网交互技术构建的虚拟网络,既可以将同一物理网络划分成多个VALN,也可以跨越物理 ...

  9. Jmeter3.0发布,版本更新都更新了什么

    Jmeter已发布了3.0,一个大版本的开源测试工具,加入了一些新的特性及软件的改进. Jmeter已隔10年的大版本更新 这是在过去12年里jmeter第一个大版本的更新,jmeter 2.0版本发 ...

  10. iTunes Connect 显示可供销售,但是AppStore 就是不显示新版本(异于往常版本更新)

    这次版本更新,从上传到审核通过不足8小时.由于是手动发布,第二天早上上班发布了新版本.但是不同于往常,这次等了很久也不见AppStore 更新新版本.检查一下iTunes Connect ,显示可供销 ...

随机推荐

  1. Array.prototype.at。Arrat和 String 中的 at 方法

    一篇有关新 js 特性 at 方法的思考 入参只能是number 类型,允许入参有小数(按照 chrome DevTools Console 测试确实可以带小数) 有返回值,如果对应下标在实例中存在, ...

  2. 学习笔记——树形dp

    树形 dp 介绍 概念 树形 dp,顾名思义,就是在树上做 dp,将 dp 的思想建立在树状结构之上. 常见的树形 dp 有两种转移方向: 从叶节点向根节点转移,这种也是树形 dp 中较为常见的一种. ...

  3. 快速傅里叶变换FFT学习笔记

    点值表示法 我们正常表示一个多项式的方式,形如 \(A(x)=a_0+a_1x+a_2x^2+...+a_nx^n\),这是正常人容易看懂的,但是,我们还有一种表示法. 我们知道,\(n+1\)个点可 ...

  4. 手记系列之四 ----- 关于使用MySql的经验

    前言 本篇文章主要介绍的关于本人在使用MySql记录笔记的一些使用方法和经验,温馨提示,本文有点长,约1.5w字,几十张图片,建议收藏查看. 一.MySql安装 下载地址:https://dev.my ...

  5. .NET 6学习笔记(8)生成自签证书

    上一篇我们通过导出IIS Express的自签证书,供ASP.NET Core程序启用HTTPS.本篇我们讨论如何生成自签证书.自签证书的生成,有多种方式.比如OpenSSL或PowerShell都可 ...

  6. 2023-03-17:使用Go语言和FFmpeg库实现音频重采样解码,并将其保存为PCM格式的文件。

    2023-03-17:使用Go语言和FFmpeg库实现音频重采样解码,并将其保存为PCM格式的文件. 答案2023-03-17: 在音视频处理领域,常常需要对音频进行重采样和解码,以便于后续的处理和分 ...

  7. 2022-03-04:爱吃香蕉的珂珂。 珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。 珂珂可以决定她吃香蕉的速度 K (单位:根

    2022-03-04:爱吃香蕉的珂珂. 珂珂喜欢吃香蕉.这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉.警卫已经离开了,将在 H 小时后回来. 珂珂可以决定她吃香蕉的速度 K (单位:根 ...

  8. 2021-10-24:快乐数。编写一个算法来判断一个数 n 是不是快乐数。「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是

    2021-10-24:快乐数.编写一个算法来判断一个数 n 是不是快乐数.「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和.然后重复这个过程直到这个数变为 1,也可能是 ...

  9. 与世界分享我刚编的mysql http隧道工具-hersql原理与使用

    原文地址:https://blog.fanscore.cn/a/53/ 1. 前言 本文是与世界分享我刚编的转发ntunnel_mysql.php的工具的后续,之前的实现有些拉胯,这次重构了下.需求背 ...

  10. git上传对象文件错误解决方案

    git上传对象文件错误解决方案 ​ 时隔一个星期, 当我再次完成开发之后, 准备将代码上传, 却出现了一个上传代码的错误, 记录一下错误和解决方案 解决方案: 运行git fsck --full (b ...