☰
C# 部署 PP-HumanSeg 人像分割:从推理到背景替换实战
2026/10/1 13:54:31 网站建设 项目流程

简介:本资源面向C#桌面端开发者与图像处理方向的学习者,提供基于PaddleInference的PP-HumanSeg人像分割与背景替换完整源码方案,可解决抠像、换底色、证件照处理等实际需求。项目基于VS2022与.NET 4.8搭建,集成OpenCvSharp4与Sdcb.PaddleInference,并内置modnet-hrnet_w18、modnet-mobilenetv2、ppmatting-hrnet_w18-human_512、ppmattingv2-stdc1-human_512共4个分割模型,便于对比不同模型在精度与速度上的表现。压缩包共99个文件,约478.41MB,包含26个dll依赖库、10个cs源码文件、8组pdmodel与pdiparams模型参数、8个yaml配置及若干缓存与工程文件,结构完整可直接编译运行。已有554人学习下载。读者可从中获取底层推理调用代码、模型加载与切换逻辑、背景色替换实现思路以及可继续扩展的功能入口,适合作为二次开发与算法集成的参考基础。

1. 从一张证件照换底色说起:这套 C# 人像分割源码到底能干什么

上周帮朋友处理一批证件照,要求统一换成白底。手动抠图三十张,边缘头发丝全是锯齿,眼睛都快看瞎了。后来翻出这套基于 PaddleInference 部署 PP-HumanSeg 的 C# 源码,才算把这事从体力活变成了调接口。它干的事很明确:在 .NET 环境里加载 PP-HumanSeg 人像分割模型,对输入图片做推理,输出一张前景概率图,再根据这张图把背景替换成指定颜色。整套东西是 C# 写的,不依赖 Python 运行时,对做上位机、桌面工具、批量图像处理的开发者来说,省掉了跨语言调用的麻烦。适合谁?手里有 C# 项目、需要集成人像分割能力、又不想引入 Python 依赖的工程师。如果你正在找 c# 上位机图像处理或者 c# 开发桌面工具的素材,这套源码的参考价值在于它把推理流程和图像后处理都摊开了,不是封装成一个黑盒 DLL 丢给你。

2. 拆开推理管线:PP-HumanSeg 在 C# 里怎么跑起来

2.1 模型输入输出的形状与预处理参数

PP-HumanSeg 的输入是一个 NCHW 布局的 float32 张量,形状[1, 3, 192, 192]。注意这个 192 不是随便定的,是模型导出时固定的输入尺寸。你喂进去的图必须先 resize 到这个大小,否则推理直接报维度不匹配。预处理分三步:归一化到[0,1]、按 ImageNet 均值方差标准化、转成 CHW 排列。均值是[0.485, 0.456, 0.406],方差是[0.229, 0.224, 0.225],顺序是 RGB。很多翻车现场就出在这里——OpenCV 读进来默认是 BGR,你要是直接拿 BGR 通道去减均值,分割结果会莫名其妙地偏向某一边,边缘糊成一团。

输出是一个[1, 1, 192, 192]的概率图,值域在[0,1]之间,表示每个像素属于人像的概率。后处理就是拿这个概率图 resize 回原图尺寸,然后按阈值二值化,得到 mask。

// 预处理:BGR 转 RGB,resize,归一化,标准化,转 CHW Mat rgb = new Mat(); Cv2.CvtColor(src, rgb, ColorConversionCodes.BGR2RGB); Mat resized = new Mat(); Cv2.Resize(rgb, resized, new Size(192, 192)); float[] inputData = new float[1 * 3 * 192 * 192]; float[] mean = { 0.485f, 0.456f, 0.406f }; float[] std = { 0.229f, 0.224f, 0.225f }; for (int y = 0; y < 192; y++) { for (int x = 0; x < 192; x++) { Vec3b pixel = resized.At<Vec3b>(y, x); for (int c = 0; c < 3; c++) { // 先除 255 再减均值除方差,顺序不能反 float v = pixel[c] / 255.0f; v = (v - mean[c]) / std[c]; inputData[c * 192 * 192 + y * 192 + x] = v; } } }

这段代码里c * 192 * 192 + y * 192 + x就是 CHW 排列的索引计算。如果你用Mat的GetGenericIndexer会更简洁,但手动算索引能让你看清楚数据是怎么排的。参数方面,192这个尺寸写死在模型里,改不了;均值和方差必须和训练时一致,PP-HumanSeg 系列用的就是 ImageNet 那套。

2.2 PaddleInference C# API 的加载与推理调用

PaddleInference 的 C# 绑定核心就几个类:Config、Predictor、Tensor。加载模型时指定模型目录和是否用 GPU。常见做法是把model.pdmodel和model.pdiparams放在同一个目录,Config里设SetModel的路径。

// 创建配置并加载模型 var config = new Config(); config.SetModel("models/humanseg/model.pdmodel", "models/humanseg/model.pdiparams"); config.DisableGpu(); // 没有 GPU 就禁用,有的话换成 EnableUseGpu(100, 0) config.SwitchIrOptim(true); // 开启 IR 优化,推理速度会快一些 var predictor = CreatePredictor(config); // 准备输入 Tensor var inputTensor = predictor.GetInputHandle("x"); int[] inputShape = { 1, 3, 192, 192 }; inputTensor.Reshape(inputShape); inputTensor.CopyFromCpu(inputData); // 执行推理 predictor.Run(); // 取输出 var outputTensor = predictor.GetOutputHandle("save_infer_model/scale_0.tmp_1"); float[] outputData = new float[1 * 1 * 192 * 192]; outputTensor.CopyToCpu(outputData);

输入和输出的名字"x"和"save_infer_model/scale_0.tmp_1"是 PP-HumanSeg 导出时的默认名,不同版本可能不一样。我一般会先用 Netron 打开model.pdmodel看一眼输入输出节点的名字,确认后再写进代码。SwitchIrOptim(true)这个开关建议打开,它会把计算图做一些融合,实测推理耗时能降 15% 左右。DisableGpu和EnableUseGpu二选一,GPU 推理需要机器上有对应的 CUDA 和 cuDNN,版本对不上会直接抛异常。

2.3 后处理:概率图到 mask 再到背景替换

拿到outputData之后,先把它 reshape 成192x192的二维数组,再 resize 回原图尺寸。resize 用双线性插值就行,别用最近邻,否则边缘会有块状锯齿。然后按阈值二值化,阈值一般取0.5,但如果你发现头发丝区域被切掉了,可以降到0.3试试。

// 概率图转 mask 并替换背景 Mat probMap = new Mat(192, 192, MatType.CV_32FC1, outputData); Mat probResized = new Mat(); Cv2.Resize(probMap, probResized, src.Size(), 0, 0, InterpolationFlags.Linear); Mat mask = new Mat(); Cv2.Threshold(probResized, mask, 0.5, 255, ThresholdTypes.Binary); mask.ConvertTo(mask, MatType.CV_8UC1); // 生成纯色背景 Mat background = new Mat(src.Size(), MatType.CV_8UC3, new Scalar(255, 255, 255)); // 用 mask 做融合 Mat result = new Mat(); src.CopyTo(result, mask); // 人像区域 background.CopyTo(result, ~mask); // 背景区域

Cv2.Threshold的阈值参数是double类型,0.5对应概率 50%。~mask是按位取反,把背景区域选出来。CopyTo的第二个参数是 mask,只有 mask 非零的像素才会被拷贝。这套组合拳下来,一张图的分割加换底就完成了。如果你要换的不是纯色而是另一张背景图,把background换成读进来的图片就行,尺寸记得先 resize 到和原图一致。

3. 把源码跑通:环境配置与第一个可执行 Demo

3.1 依赖清单与版本对齐

这套源码依赖三样东西:PaddleInference 的 C# 库、OpenCvSharp、以及 .NET 运行时。PaddleInference 的 C# 绑定在官方发布的预测库里有paddle_inference_csharp目录,里面是编译好的 DLL 和 C# 封装。OpenCvSharp 用 NuGet 装OpenCvSharp4和OpenCvSharp4.runtime.win就行。.NET 版本建议用 .NET 6 或以上,低版本可能缺一些 API。

依赖项推荐版本获取方式
PaddleInference C#2.5+官方预测库压缩包内paddle_inference_csharp
OpenCvSharp44.8+NuGet
.NET6.0 / 8.0官方安装包
PP-HumanSeg 模型通用版PaddleSeg 模型库导出

版本对齐的坑在于:PaddleInference 的 C# DLL 是用特定版本的 MSVC 编译的,如果你的项目用了不同版本的运行时,可能会报access violation。我一般会先把官方 Demo 跑通,确认 DLL 能加载,再往自己的项目里搬。

3.2 从零建一个控制台 Demo

新建一个 .NET 控制台项目,把 PaddleInference 的 C# 封装文件(PaddleInference.cs之类)和原生 DLL 拷到输出目录。原生 DLL 包括paddle_inference.dll、mkldnn.dll、mklml.dll等,缺一个都会在运行时崩。

dotnet new console -n HumanSegDemo cd HumanSegDemo dotnet add package OpenCvSharp4 dotnet add package OpenCvSharp4.runtime.win

然后把模型文件放到models/humanseg/下,图片放到images/下。主程序里先读图,再走一遍预处理、推理、后处理,最后把结果写出去。

static void Main(string[] args) { string imagePath = "images/test.jpg"; Mat src = Cv2.ImRead(imagePath); if (src.Empty()) { Console.WriteLine("图片读取失败,检查路径"); return; } // 预处理 + 推理 + 后处理(见第 2 章代码) Mat result = ProcessImage(src); Cv2.ImWrite("output/result.png", result); Console.WriteLine("处理完成,输出到 output/result.png"); }

跑之前确认output目录存在,Cv2.ImWrite不会自动建目录。如果报DllNotFoundException,检查原生 DLL 是不是在bin/Debug/net6.0/下。如果报EntryPointNotFoundException,多半是 DLL 版本和 C# 封装不匹配,换一套官方发布的预测库重新拷。

3.3 批量处理与性能观察

单张跑通之后,批量处理就是套个循环。但要注意,Predictor对象不要每次循环都创建,创建一次反复用就行,否则内存涨得很快。

var predictor = CreatePredictor(config); // 循环外创建 foreach (string file in Directory.GetFiles("images", "*.jpg")) { Mat img = Cv2.ImRead(file); Mat output = ProcessImage(img, predictor); Cv2.ImWrite($"output/{Path.GetFileName(file)}", output); img.Dispose(); output.Dispose(); } predictor.Dispose(); // 循环结束后释放

ProcessImage里把predictor当参数传进去,不要在里面 new。实测在 i7-12700 上,192x192 输入的单张推理耗时大约 40-60ms,加上前后处理,一张图总共 100ms 左右。如果开了 GPU,能降到 20ms 以内。批量处理时注意Mat对象要及时Dispose,OpenCvSharp 的Mat持有非托管内存,不释放的话跑几百张图内存就上去了。

4. 避坑与排查:那些让我加班到凌晨的报错

4.1 推理结果全黑或全白

现象:输出的 mask 要么全是 0,要么全是 255,人像区域完全不对。原因通常是预处理阶段通道顺序搞反了,或者归一化参数用错了。OpenCV 读图默认 BGR,而模型训练时用的是 RGB,不转换的话模型看到的颜色是错的,分割概率会整体偏移。解决:在CvtColor之后打印几个像素值确认通道顺序,再检查均值和方差是不是[0.485, 0.456, 0.406]和[0.229, 0.224, 0.225],顺序不能乱。

4.2 报 Access Violation c0000005

现象:程序运行到predictor.Run()或者CreatePredictor时直接崩溃,异常码c0000005。原因多半是原生 DLL 和 C# 封装版本不匹配,或者缺少某个依赖 DLL。PaddleInference 的原生库依赖mkldnn.dll、mklml.dll、libiomp5md.dll等,少一个都会在加载时崩。解决:用 Dependencies 工具或者dumpbin /dependents查看paddle_inference.dll的依赖,确保所有依赖都在输出目录。另外确认项目平台是 x64,PaddleInference 没有 32 位版本。

4.3 输出尺寸和原图对不上

现象:替换背景后,人像位置偏移或者被拉伸。原因是在后处理 resize 概率图时,目标尺寸传错了,或者原图在预处理时被裁剪过。解决:resize 的目标尺寸必须是原图src.Size(),不要用预处理时的 192x192。如果你在预处理时做了 padding 或裁剪,后处理要反向操作回去。我一般会在预处理前把原图尺寸存下来,后处理直接用这个值。

4.4 批量处理时内存持续增长

现象:跑几十张图之后内存占用越来越高,最后 OOM。原因是Mat和Tensor对象没有释放。OpenCvSharp 的Mat实现了IDisposable,但很多人忘了调Dispose。PaddleInference 的Tensor也是。解决:用using包裹,或者在循环末尾手动Dispose。另外Predictor不要每次循环创建,创建一次复用。

4.5 模型加载成功但推理结果随机

现象:每次运行输出的 mask 都不一样,像是随机数。原因是输入 Tensor 的数据没有正确拷贝进去,CopyFromCpu的数组长度和Reshape的形状不匹配。比如你Reshape成[1,3,192,192],但传进去的数组只有192*192个元素,剩下的就是未初始化内存。解决:确认inputData.Length == 1*3*192*192,并且CopyFromCpu在Reshape之后调用。

5. 进阶玩法:把分割结果用出更多花样

跑通基础换底之后,这套源码还能往下挖。第一个方向是换背景图而不是纯色。把background从new Mat(..., Scalar)换成Cv2.ImRead("bg.jpg"),resize 到原图尺寸,再走同样的CopyTo逻辑。注意背景图如果比原图小,resize 会拉伸变形,最好先按比例裁剪到相同宽高比。

第二个方向是羽化边缘。直接二值化的 mask 边缘很硬,头发丝区域会有锯齿。常见做法是对概率图做一次高斯模糊再二值化,或者用Cv2.GaussianBlur对 mask 做平滑。参数上,高斯核大小取5x5或7x7,标准差取0让 OpenCV 自己算。这样边缘会柔和很多,证件照看起来更自然。

第三个方向是导出 ONNX 再转其他推理引擎。PP-HumanSeg 可以用 Paddle2ONNX 导出成 ONNX 格式,然后用 ONNX Runtime 的 C# 包来跑。好处是 ONNX Runtime 的跨平台支持更好,Linux 和 macOS 上部署更省心。导出命令是paddle2onnx --model_dir humanseg --model_filename model.pdmodel --params_filename model.pdiparams --save_file humanseg.onnx --opset_version 11。导出后检查一下输入输出名字,ONNX Runtime 的 C# API 和 PaddleInference 不太一样,但预处理和后处理逻辑可以复用。

第四个方向是接摄像头做实时分割。用 OpenCvSharp 的VideoCapture读摄像头帧,每帧走一遍推理,把结果用Cv2.ImShow显示出来。注意实时场景下推理耗时必须小于帧间隔,否则会卡顿。192x192 的输入在 CPU 上大概 50ms,也就是 20fps 左右,勉强够用。要更流畅就上 GPU 或者换更小的模型。

我自己的习惯是,每次换新模型或者新环境,先拿一张纯色背景的图跑一遍,确认 mask 边缘干净、没有大面积误判,再上批量。从那以后我每次部署新模型都强制走一遍单图验证,省得批量跑完才发现问题。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询