ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C# WinForm集成PPOCRv6与OpenVINO的OCR识别方案

C# WinForm集成PPOCRv6与OpenVINO的OCR识别方案 简介本资源是一套基于C# WinForm实现的PPOCRv6 OCR模型部署演示工程面向具备基础C#与OCR应用开发经验的中高级开发者解决在Windows桌面端高效集成轻量化文字识别能力的实际需求适用于文档扫描、票据识别、工业质检等本地化OCR场景。压缩包共402个文件包含138个运行依赖DLL含OpenVINO推理引擎及ONNX Runtime组件、64个API说明XML文档、20个JPG/PNG格式示例图像、16个CS源码文件涵盖图像预处理、模型加载、推理调用与结果渲染全流程以及配套配置文件与NuGet包整体体积达348.23MB。目前已有88人学习下载提供完整可运行的Visual Studio解决方案含.sln与.csproj支持一键编译调试代码结构清晰分层关键模块如OCRProcessor、UI交互逻辑与OpenVINO推理适配均有详细注释附带README.md使用说明与模型版本兼容性提示便于快速迁移与二次开发。 做这个项目其实挺偶然的。之前一直在用Python跑PaddleOCR后来现场交付的时候客户环境压根没有Python运行时也不可能为了一个识别功能给人家机器装Anaconda。正好手头有几个C#的WinForm项目就想着能不能把OCR推理直接塞进桌面程序里。折腾了几天用PPOCRv6导出ONNX再挂到OpenVINO上跑总算是把整个链路跑通了。这篇文章就把我踩过的坑、调试过程、还有最终能直接跑的方案整理出来给同样需要在C#环境里做OCR落地的朋友一个参考。1. 项目背景与整体方案选型1.1 为什么选C# WinForm PPOCRv6 ONNX OpenVINO先说结论这套组合的核心价值在于完全脱离Python运行时纯C#环境下实现文字检测和识别。WinForm提供界面框架PPOCRv6提供模型能力ONNX作为模型分发格式OpenVINO负责推理加速。四者各司其职组合起来就是一套完整的桌面端OCR解决方案。选C# WinForm不是因为它多先进而是因为它落地足够稳。很多工业软件、上位机、管理系统都跑在Windows上C#是这些场景的主流语言。WinForm虽然看起来老但胜在生态成熟、部署简单一台没有装任何依赖的Windows机器双击exe就能跑。这一点在交付场景里比技术栈新不新重要一百倍。PPOCRv6是PaddleOCR系列里较新的版本相比旧版本检测和识别的精度都有明显提升尤其对模糊文字、倾斜文字、复杂排版的支持更好。但PaddleOCR原生的推理依赖Paddle Inference库这个库对C#的官方支持不够友好文档少、示例少、踩坑全靠自己试。所以自然想到把模型转成ONNX格式把推理后端换成OpenVINO。OpenVINO是英特尔开源的推理框架最大的优势是对Intel CPU做了深度优化。工业现场绝大多数部署机器的CPU都是Intel这意味着OpenVINO能直接在现有硬件上榨出不错的性能不需要额外买GPU。而且OpenVINO的C# API已经比较完善NuGet包直接装就能用集成成本远低于Paddle Inference。1.2 OCR模型落地的常用思路与各自优劣先说Python方案。按照PaddleOCR官方文档用paddleocr库三行代码就能跑通甚至不需要关心模型细节。但问题在于Python方案打包麻烦——PyInstaller打包Paddle全家桶动辄几百MB解压慢、杀软误报、环境兼容问题一抓一大把。如果部署机器没有网装依赖更是灾难。再说C方案。PaddleOCR官方提供了C推理示例性能确实好但开发效率低。用C写界面本来就是苦差事而且模型升级、参数调整都得重新编译迭代很痛苦。除非有专门的C团队维护否则不建议在桌面工具类项目里上C。第三种就是本文要说的ONNX OpenVINO方案。ONNX本身只是一个模型格式标准它不管推理。推理要么用ONNX Runtime要么用OpenVINO读取ONNX模型。这里选OpenVINO原因有两点一是它对ONNX模型的支持很成熟模型转换时对算子的兼容性做得不错二是在Intel CPU上OpenVINO的推理速度通常比ONNX Runtime还要快一截。表格对比方案开发效率部署体积推理性能C#集成难度Python PaddleOCR高大300MB中等需嵌入Python运行时C Paddle Inference低中等高需写C/CLI封装ONNX ONNX Runtime中小几十MB中等偏上低官方C# APIONNX OpenVINO本文中小高Intel CPU低官方C# API综合来看在Windows桌面端场景下ONNX OpenVINO是平衡开发效率、部署体积、推理性能的最佳选择。2. 前置准备模型导出与格式转换2.1 从PaddleOCR导出ONNX格式的完整流程这一步是很多人卡住的地方。PaddleOCR官方仓库并没有直接提供ONNX格式的模型文件需要自己从Paddle格式转过来。我在转换过程中试了两种方式一种是直接用paddle2onnx工具另一种是通过paddle.jit.save导出推理模型后再转换。最终稳定使用的是第一种。先说环境准备。需要一台装了Python的机器建议Python 3.9以上安装paddlepaddle、paddle2onnx、paddleocr这几个包。pip install paddlepaddle pip install paddle2onnx pip install paddleocr然后下载PPOCRv6的检测模型和识别模型。下载完成后目录结构大概是这样的inference/ ├── det_model/ │ ├── inference.pdmodel │ └── inference.pdiparams └── rec_model/ ├── inference.pdmodel └── inference.pdiparams接下来执行转换命令。我的检测模型用的是PP-OCRv6的mobile版本识别模型也是mobile版转换命令如下paddle2onnx --model_dir inference/det_model \ --model_filename inference.pdmodel \ --params_filename inference.pdiparams \ --save_file det_onnx/model.onnx \ --opset_version 11 paddle2onnx --model_dir inference/rec_model \ --model_filename inference.pdmodel \ --params_filename inference.pdiparams \ --save_file rec_onnx/model.onnx \ --opset_version 11这里有一个关键参数--opset_version。我试过opset 12、13OpenVINO读取时会报一些算子不兼容的警告换回11就安静了。如果你的环境报错说某个算子不支持优先把opset往低调一档试试。转换完成后用onnxruntime或者netron打开模型看一眼输入输出节点是否正常。正常情况下det模型输入是x输出是sigmoid_0.tmp_0概率图rec模型输入是x输出是softmax_0.tmp_0字符概率序列。2.2 检查ONNX模型的输入输出结构拿到一个ONNX模型第一件事就是看它的输入输出结构。这一步决定了C#代码里怎么组织数据、怎么解析结果。我用的是Python的onnx库来检查命令很简单import onnx model onnx.load(det_onnx/model.onnx) print(输入:, [(inp.name, inp.type.tensor_type.shape) for inp in model.graph.input]) print(输出:, [out.name for out in model.graph.output])检测模型打印出来大概是这样的输入: [(x, dims[-1, 3, -1, -1])] 输出: [sigmoid_0.tmp_0]识别模型打印出来大概是这样的输入: [(x, dims[-1, 3, 32, -1])] 输出: [softmax_0.tmp_0]-1表示动态维度也就是这批输入的长宽是可变的。这种动态shape的模型在OpenVINO里也能加载但如果想跑得更快建议把输入shape固定成一个具体值比如检测模型固定为1x3x960x960识别模型固定为1x3x32x320。OpenVINO提供了模型reshape接口可以在C#代码里调用。我实际测试的结论是动态shape在OpenVINO上推理速度略慢因为推理框架需要根据实际输入动态分配内存、做算子调度。如果业务场景是固定分辨率的图片强烈建议固定shape。如果图片分辨率差异很大只能保留动态shape否则会损失精度。注意PPOCRv6识别模型在训练时输入高度固定是32或者48宽度可以动态。导出的ONNX也继承了这一特性所以reshape时高度不要乱动只改宽度比较安全。3. OpenVINO推理引擎集成3.1 为什么用OpenVINO而不是ONNX Runtime跑ONNX模型很多人在这一步会纠结既然模型已经是ONNX格式了直接用ONNX Runtime跑不就行了为什么还要绕一圈用OpenVINO我一开始也是直接用ONNX Runtime在开发机上跑得挺好但到了客户现场就露馅了。客户的机器是几年前的i5处理器没有独立显卡同一张图ONNX Runtime跑了800多毫秒明显卡顿。后来换成OpenVINO同样的机器压到300多毫秒体感好了很多。这个差距主要来自两方面的优化。第一OpenVINO对Intel CPU的指令集做了针对性优化能充分利用AVX2、AVX512这些SIMD指令第二OpenVINO在模型加载时会做图优化把可以融合的算子合并在一起减少内存读写次数。ONNX Runtime默认的CPU执行提供者是通用的它没有针对特定CPU做深度调优自然跑不出极限性能。当然ONNX Runtime也提供了OpenMP、MLAS等优化选项效果也不错但和OpenVINO在Intel平台上的表现比还是有差距。如果你部署的机器恰好是AMD CPU那OpenVINO的优势就不明显了这种情况下建议直接用ONNX Runtime。选型一定要看目标硬件环境不能无脑跟风。3.2 C#环境中引入OpenVINO的两种方式接下来是集成环节。OpenVINO在C#里的使用方式有两条路线我分别踩了一遍说下区别。第一条路线是使用OpenVINO官方C# NuGet包。在Visual Studio的NuGet包管理器里搜索OpenVINO.CSharp或者OpenVINO.runtime能找到对应包。建议使用.NET 6以上版本因为官方对.NET Framework的支持不太积极直接用.NET 6可以省掉不少麻烦。安装命令dotnet add package OpenVINO.CSharp这个包会自动把对应平台的OpenVINO运行时DLL拉下来。注意Intel.OpenVINO这个包有不同的运行时版本建议选择带有runtime.win-x64字样的确保拉取的是Windows x64版本。第二条路线是通过OpenVINO的C API写一层P/Invoke封装自己调用C接口。这条路的工作量大很多需要自己处理指针生命周期、内存释放、异常传递等一堆问题好处是可以在老旧的.NET Framework 4.x项目里用。如果你被迫维护一个.NET Framework项目只能走这条路线。我的建议很直接新项目一律走NuGet包路线省心省力。.NET Framework项目如果不是非留不可尽量说服团队升级到.NET 6。C#这门语言的跨平台能力和性能表现在.NET Core之后有了质的飞跃没必要守着老框架不放。3.3 加载模型并完成一次前向推理加载模型的C#代码比想象中简单。OpenVINO C# API封装了大部分底层细节核心步骤就三步读取模型、编译模型、创建推理请求。检测模型加载的核心代码using OpenVinoSharp; using OpenVinoSharp.Extensions; // 1. 读取ONNX模型 Model model new Model(det_onnx/model.onnx); // 2. 编译模型到指定设备 Core core new Core(); CompiledModel compiled core.compile_model(model, CPU); // 3. 创建推理请求 InferRequest request compiled.create_infer_request(); // 4. 获取输入输出张量 Tensor input_tensor request.get_input_tensor(0); Tensor output_tensor request.get_output_tensor(0);代码里的CPU是设备名OpenVINO支持CPU、GPU、AUTO等设备。如果要指定GPU确保机器装了Intel核显并安装了对应的驱动。编译模型这一步其实是耗时大户。同一个模型第一次编译可能需要几百毫秒到几秒不等好在编译完成后可以复用CompiledModel对象不用每次推理都重新编译。所以实际项目中把Core和CompiledModel设计成单例或者静态字段只在程序启动时初始化一次。输入数据填充这一块有个坑。OpenVINO C#的Tensor对象提供set_dataT()方法但要求的是连续内存的一维数组也就是要把[B, C, H, W]的图片数据铺平成一个float[]。C#里拿到Bitmap后像素格式是BGR但模型训练时用的是RGB这个通道顺序不对会导致识别结果完全错误。我的做法是用OpenCvSharp做预处理核心逻辑如下// 用OpenCvSharp读取图片 Mat src Cv2.ImRead(imagePath, ImreadModes.Color); Mat rgb new Mat(); Cv2.CvtColor(src, rgb, ColorConversionCodes.BGR2RGB); // 缩放、归一化、排布成CHW Mat resized new Mat(); Cv2.Resize(rgb, resized, new OpenCvSharp.Size(960, 960)); resized.ConvertTo(resized, MatType.CV_32FC3, 1.0 / 255.0); // HWC转CHW并摊平 float[] inputData new float[3 * 960 * 960]; int index 0; for (int c 0; c 3; c) { for (int h 0; h 960; h) { for (int w 0; w 960; w) { Vec3f pixel resized.GetVec3f(h, w); inputData[index] pixel[c]; } } } // 填充到输入张量 input_tensor.set_data(inputData);识别模型的预处理逻辑和检测类似区别在于识别模型输入宽高是1x3x32x320而且需要先把检测到的文本区域裁剪出来做透视变换这里就不展开了后面第四章有完整串联逻辑。推理完成后的输出解析检测模型拿到的是一张概率图需要做阈值过滤和连通域分析最终得到文本框坐标。识别模型拿到的是一串概率序列需要做argmax解码再映射成中文和英文字符。字符映射表是关键PPOCRv6的自带字典有几千个字符。我在C#里把字典文件读成Listchar解码时用概率最高的index去取对应字符最后拼成字符串。4. WinForm界面与调用链设计4.1 界面布局与交互流程设计WinForm界面设计有两点考量一是操作简单二是状态可视化。整个工具最后交付给现场人员用他们不关心技术细节只关心“选图片、点识别、出结果”。界面布局我用了经典的上下分区顶部图片选择按钮、识别按钮、模型加载状态中间左侧显示原始图片右侧显示识别结果带文本覆盖底部日志输出区域显示每条推理耗时、识别内容![界面布局示意左侧图片预览区右侧结果区底部日志区]如果用C#原生的WinForm控件界面会比较朴素。可以引AntdUI这个开源控件库WinForm界面美化效果立竿见影。NuGet搜索AntdUI直接安装控件风格和Ant Design一致现代感很强客户看到的第一印象会好很多。这里提一句如果项目是内部工具界面朴素点无所谓如果是要交付给客户验收的花半小时套个好看的UI库回报率很高。交互流程相对简单识别按钮的点击事件里走完整链路检查模型是否已加载没加载则提示等待读取用户选中的图片进行缩放预处理检测模型推理获取文本区域对每个文本区域做透视纠正和裁剪裁剪后的图像送入识别模型汇总结果显示到界面4.2 图片预处理与文本检测/识别串联原理先讲检测环节。PPOCRv6的检测模型用的是DBNet结构输出的是每个像素属于文本的概率图。拿到概率图后先做一个阈值处理比如概率大于0.3的像素标记为1小于的标记为0。然后做一个膨胀操作把靠近的文本区域连成一体再做连通域分析每个连通域就是一个文本区域的候选框。这里有一个细节需要注意检测模型输出的候选框是四边形但文字在图片里不一定端端正正很可能有倾斜。所以不能直接按矩形裁剪要先做一个透视变换把倾斜的文本区域“扳正”成水平方向的矩形再送给识别模型。透视变换在OpenCvSharp里是现成的方法输入四个角点坐标输出变换矩阵再应用到原图上// box是检测得到的四边形四个角点按顺序排列 Point2f[] srcPoints new Point2f[] { new Point2f(box[0].X, box[0].Y), new Point2f(box[1].X, box[1].Y), new Point2f(box[2].X, box[2].Y), new Point2f(box[3].X, box[3].Y) }; // 计算目标矩形的宽高 float width Math.Max(Distance(box[0], box[1]), Distance(box[2], box[3])); float height Math.Max(Distance(box[1], box[2]), Distance(box[3], box[0])); Point2f[] dstPoints new Point2f[] { new Point2f(0, 0), new Point2f(width - 1, 0), new Point2f(width - 1, height - 1), new Point2f(0, height - 1) }; Mat transform Cv2.GetPerspectiveTransform(srcPoints, dstPoints); Mat cropped new Mat(); Cv2.WarpPerspective(src, cropped, transform, new OpenCvSharp.Size(width, height));拿到裁剪后的文本图像下一步送识别模型。识别模型内部是对一个定长序列做分类输出每个位置属于字典中每个字符的概率。PPOCRv6的识别模型会先做特征提取然后用CTC解码得到文字序列。CTC解码在C#里需要自己实现。原理不复杂沿时间步方向取每个位置概率最大的字符索引然后合并连续重复的字符最后删除空白符。有一点要注意CTC解码时“重复字符”的处理有讲究比如“hello”中两个连续的l会被CTC合并成一个l需要在解码逻辑里做特殊处理否则识别结果会少字符。PPOCR的模型里输出是带blank的CTC输出遇到blank需要特殊处理不能用简单的“连续相同合并”逻辑。我实现的解码逻辑核心如下// probs是概率矩阵 shape[seq_len, num_classes] Listint indices new Listint(); for (int t 0; t seqLen; t) { int maxIdx 0; float maxProb float.MinValue; for (int c 0; c numClasses; c) { if (probs[t * numClasses c] maxProb) { maxProb probs[t * numClasses c]; maxIdx c; } } indices.Add(maxIdx); } // CTC解码合并连续重复并删除blank Listchar result new Listchar(); char prev \0; foreach (int idx in indices) { if (idx blankIndex) // blank { prev \0; continue; } char ch dictionary[idx]; if (ch prev) continue; result.Add(ch); prev ch; }4.3 在多线程中跑推理避免界面卡死WinForm界面跑推理最大的坑就是卡界面。模型推理是CPU密集型的计算任务如果在UI线程里直接同步调用识别过程中的几秒内界面就会变成“未响应”状态用户体验很差。这个问题必须在架构上解决。解决思路是使用async/await配合Task.Run把推理任务放到线程池中执行。UI线程只负责展示结果。核心代码结构private async void btnRecognize_Click(object sender, EventArgs e) { btnRecognize.Enabled false; try { string imagePath txtImagePath.Text; var result await Task.Run(() RunOcrPipeline(imagePath)); DisplayResult(result); } catch (Exception ex) { LogError($识别失败: {ex.Message}); } finally { btnRecognize.Enabled true; } }这样写的好处是UI始终处于响应状态识别过程中用户可以取消、切换图片、查看日志。如果识别耗时特别长还可以加上进度条和取消机制但我的经验是单张图片识别的耗时一般在300毫秒到2秒之间任务取消的优先级不高。如果你的需求是连续识别视频流或者摄像头画面那就要用到更复杂的流水线架构。简单说就是两个线程加一个队列采集线程负责拉取图像帧放入队列推理线程从队列取帧做识别UI线程通过定时器或者BeginInvoke更新画面。这里不展开先用单张识别把链路跑通。5. 常见问题与排查实录5.1 加载程序集失败找不到DLL或依赖项这是我在开发过程中最常遇到的一类问题也是很多人一上来就劝退的原因。错误提示经常是“无法加载DLL‘openvino_c.dll’找不到指定的模块”或者直接抛BadImageFormatException。先定位原因OpenVINO的C#封装本质是对底层C/C DLL的P/Invoke调用。这些原生DLL除了主模块openvino_c.dll之外还依赖一堆其他DLL比如tbb.dll、openvino_intel_cpu_plugin.dll等。任何一个依赖缺失加载都会失败。排查步骤如下确认项目运行平台的位数。x86和x64的原生DLL不通用必须严格匹配。建议项目平台统一设为x64。检查NuGet包是否完整。正常安装OpenVINO.CSharp后runtimes/win-x64/native/目录下应该有一整套DLL。确认这些DLL被正确复制到了输出目录。在bin/Debug/net6.0-windows下检查是否能看到openvino_c.dll。如果DLL存在但仍然报错可以使用Dependencies工具Dependency Walker的替代品检查DLL的依赖链是否完整。这里说一个很多人的误区VS里“编译通过”不等于“运行能过”。编译只检查了托管代码的类型引用原生DLL的解析发生在运行时。所以编译通过后启动程序就崩大概率是原生依赖问题。另外一个容易踩的坑是Loading a native library报错。如果你的机器缺少VC运行库比如vcruntime140.dllOpenVINO的DLL也加载不起来。解决办法是安装微软提供的Visual C Redistributable或者直接把对应的运行时DLL放到exe的同级目录。5.2 推理结果精度异常全是乱码或空白如果程序能跑通但识别结果是乱码、空白、或者有一堆莫名其妙的字符优先排查以下几个问题。第一个问题是数据预处理错误。最常见的是通道顺序问题上面提了模型训练用的是RGB而OpenCvSharp默认读出来是BGR如果不转换通道识别结果会差到离谱。第二个是输入数据布局错误模型需要的是CHW排列我们填充数据时如果按照HWC排列结果也是完全错误的。第三个是归一化方式错误ONNX模型一般期望输入是0到1范围的float如果传了0到255的原始像素值模型输出大概率是不正常的。第二个大问题是字典文件不匹配。PPOCR的识别模型有很多个版本中英文混排、纯中文、纯英文、多语言不同版本的模型对应不同的字典文件。如果模型是中文版你给它配了英文版字典识别结果自然全是错的。解决方式是确认模型下载时配套的字典文件在C#代码里加载对应的字符表。第三个问题是模型输入尺寸设置不当。PPOCRv6识别模型虽然支持动态宽度但对宽高比有隐含限制。如果文本区域特别长或特别宽强行送入模型会导致精度严重下降。我的做法是在送入识别模型前先判断宽高比对长文本做拆分成多个短文本段分别识别再拼回去。这个方法在识别大段文字时效果显著。如果以上都没问题可以打开OpenVINO的日志输出看看推理是不是真的执行了。OpenVINO C# API支持设置日志级别Core core new Core(); core.set_property(CPU, LOG_LEVEL, LOG_INFO);开启日志后会输出每个算子的执行耗时和内存信息有助于判断推理是在正常执行还是走了错误分支。5.3 性能瓶颈分析与优化实录我的开发机上检测加识别一次大概400毫秒客户现场机器跑一次要1.5秒差距很直观。CPU性能差异是一方面代码层面的优化空间其实也很大。先说模型层面的优化。OpenVINO支持INT8量化如果精度损失在容忍范围内推理速度可以再翻一倍。但ONNX导出的模型默认是FP32权重要做INT8量化需要额外的校准流程。我在测试过程中发现如果对精度要求不高比如只需要识别数字和字母对汉字要求低INT8量化带来的精度损失基本无感但速度提升很明显。量化的做法需要先把模型转成IR格式再用OpenVINO的Post-Training Optimization工具做校准涉及的数据集准备比较繁琐这里先不展开。再说代码层面的优化。最容易忽略的是Tensor拷贝的开销。OpenCvSharp的Mat转float[]一次转换就在几毫秒到几十毫秒如果图片大还好说但识别这一环涉及270万个像素点的拷贝频繁做确实有开销。优化方案是提前分配好float[]数组避免每次推理都重新new一个大数组。我实测下来这个优化能省出10%到15%的时间。另一个优化点是OpenVINO的推理请求复用。InferRequest对象创建也是有开销的不要在每张图片识别时都重新创建。正确做法是在程序初始化时创建一个InferRequest后续推理重复使用。这样能省掉重复创建带来的UI线程卡顿。最后别忘了WinForm里还有一个隐藏性能杀手PictureBox的Image属性赋值。如果识别结果要叠加画框不要反复设置Image属性应该设置Paint事件或者用控件自带的绘制接口在OnPaint里统一绘制。否则图片刷新会吃掉大量UI线程时间。6. 踩坑总结与可用代码资源说明整个项目做完我最深的体会是C#和OpenVINO的组合完全可行但官方文档的零散程度超出想象。OpenVINO的C#示例在GitHub上就那么几个而且大部分是控制台程序没有正经的WinForm集成案例很多细节都是靠试错试出来的。如果不想从零开始踩一遍我踩过的坑有几个代码资源值得参考。PaddleOCR官方的C#方向一直在推进在GitHub上搜索PaddleOCRSharp这是一个社区维护的PaddleOCR C#封装项目虽然它使用的是Paddle Inference推理后端不是本文的OpenVINO体系但里面的图像预处理逻辑、结果解析逻辑、字典加载方式都写得比较规范可以借鉴。OpenVINO官方的C#绑定仓库在GitHub上可以搜到openvino_csharp相关项目里面有基础的C#示例代码虽然覆盖场景不多但至少能参考P/Invoke声明和内存管理方式。最后再分享一个小技巧一定要在WinForm项目里加一个全局异常处理器因为OpenVINO原生层抛出的异常有时候会带出很晦涩的错误信息直接抛到UI线程会导致程序崩溃。加了全局异常处理器后至少能把错误信息记录到日志文件里方便追踪问题。Application.ThreadException (sender, args) { File.AppendAllText(error.log, ${DateTime.Now}: {args.Exception}\n); MessageBox.Show($发生异常详情已记录到日志{args.Exception.Message}); };这个项目跑通之后我还用同样的套路接了几路摄像头识别后端还是这套ONNX OpenVINO的推理管线只是把输入从静态图片换成了视频帧。整个架构的可扩展性超出了我最初的预期这就是把推理从Python迁移到C#的好处离业务系统更近离目标用户更近落地的时候不用再折腾各种胶水代码。本文还有配套的精品资源点击获取
返回列表