跳到主要内容

模型加速器 Batch 批处理加速

下面以猫狗分类和气球实例分割为例,演示模型加速、C# 测速和批量调用。

注意事项

同一批输入图片的宽和高必须一致。

1. 准备模型和图片​

  1. 准备猫狗分类和气球实例分割的 DVP 模型。将每个模型复制成两份,放在不同文件夹,分别生成单张模型和批处理模型。
  2. 准备好输入图片:猫狗图片为 224 × 224,气球图片为 640 × 640。可直接使用下文展示的图片。

2. 猫狗分类:加速与测速​

猫图和狗图均为 224 × 224。两组测速都使用 cat.jpg。

猫:cat.jpg,224 × 224狗:dog.jpg,224 × 224
猫狗分类的猫图猫狗分类的狗图

2.1 生成单张和批处理模型​

  1. 打开模型加速器,选择猫狗分类 DVP、GPU 和已授权的加密狗,推理后端选择 TensorRT。
  2. 开启 FP16,关闭“通用架构 PLUS”和“使用多尺度输入”。
  3. 不勾选“使用批处理”,点击“加速”。将生成的 DVT 命名为 猫狗分类_batch1.dvt。
  4. 选择另一份猫狗分类 DVP,勾选“使用批处理”。这个选项让模型可以一次处理多张图片;保留默认推荐值 128,即一次最多处理 128 张图片。
  5. 点击“加速”,将生成的 DVT 命名为 猫狗分类_batch128.dvt。

下图左侧为单张模型,右侧为批处理模型,橙色框标出了设置位置。图片可以点击放大。

左:模型加速器,batch=1右:模型加速器,默认 batch=128
分类单张模型加速参数分类默认128张批处理模型加速参数

2.2 用 C# 测试程序测速​

  1. 打开 C# 测试程序,加载 猫狗分类_batch1.dvt,选择加速时使用的 GPU,设置 batch_size=1、线程数=1。
  2. 将 threshold 设为 0.50,关闭 RPC 和均值计算。点击“打开图片推理”,加载 cat.jpg,确认显示分类结果。
  3. 点击“多线程测试”,运行 10 秒后停止,记录结果。
  4. 换成 猫狗分类_batch128.dvt,将 batch_size 设为 32,也就是每次推理处理 32 张图片。线程数仍为 1,保持单线程进行对比。
  5. 对同一张 cat.jpg 再次运行“多线程测试”。
左:C# 测试程序,batch=1、线程=1右:C# 测试程序,batch=32、线程=1
分类单线程batch1测速结果分类单线程batch32测速结果

2.3 速度与模型大小​

指标batch=1batch=32
实时速率1444.11 张/秒9211.77 张/秒
平均延迟0.81 ms3.29 ms
模型大小23.98 MB24.93 MB

3. 气球实例分割:加速与测速​

3.1 生成单张和批处理模型​

  1. 在模型加速器中选择气球实例分割 DVP,使用同一块 GPU、已授权的加密狗和 TensorRT,开启 FP16。
  2. 保留“使用多尺度输入”,IoU 阈值设为 0.2、检测数量设为 100,关闭均值计算和“通用架构 PLUS”。
  3. 不勾选“使用批处理”,点击“加速”,将生成的 DVT 命名为 气球实例分割_batch1.dvt。
  4. 选择另一份气球实例分割 DVP,勾选“使用批处理”,保留默认推荐值 4,点击“加速”,将生成的 DVT 命名为 气球实例分割_batch4.dvt。
左:模型加速器,batch=1右:模型加速器,batch=4
气球单张模型加速参数气球4张批处理模型加速参数

3.2 用 C# 测试程序测速​

  1. 加载 气球实例分割_batch1.dvt,设置 batch_size=1、线程数=1,其余设置与分类测速相同。
  2. 点击“打开图片推理”,加载 640 × 640 的 balloon-1.jpg,再点击“多线程测试”,运行 10 秒后停止。
  3. 加载 气球实例分割_batch4.dvt,将 batch_size 设为 4、线程数保持 1。每次推理处理 4 张图片,对同一张 balloon-1.jpg 重复测速。
左:C# 测试程序,batch=1、线程=1右:C# 测试程序,batch=4、线程=1
气球单线程batch1测速结果气球单线程batch4测速结果

3.3 速度与模型大小​

指标batch=1batch=4
实时速率203.44 张/秒292.38 张/秒
平均延迟4.87 ms13.59 ms
模型大小31.32 MB32.12 MB

4. 用 C# 批量处理两张图片​

4.1 读取两张气球图片​

加载 气球实例分割_batch4.dvt,读取下面两张图片,并将颜色通道从 BGR 转为 RGB。

两张输入图片均为 640 × 640,按以下顺序放入同一个 List:

images[0]:balloon-1.jpg,640 × 640images[1]:balloon-2.jpg,640 × 640
第一张输入:一个黑色气球第二张输入:三个彩色气球

4.2 调用 InferBatch​

将两张图片放入 List<Mat>,调用一次 InferBatch。与逐张调用相比:

调用方式单张模型逐张处理批处理模型一次传多张
加载模型new Model("气球实例分割_batch1.dvt", 0)new Model("气球实例分割_batch4.dvt", 0)
输入每次一个 Matnew List<Mat> { rgb0, rgb1 }
调用两次 Infer一次 InferBatch
返回值每次一个 CSharpResult,各含一份图片结果一个 CSharpResult,按输入顺序含两份图片结果
第一张图片第一次结果的 SampleResults[0]批量结果的 SampleResults[0]
第二张图片第二次结果的 SampleResults[0]批量结果的 SampleResults[1]

核心调用如下;rgb0、rgb1 是两张已转换为 RGB 的图片,parameters 是下方完整示例中的参数对象:

// 单张调用:分别读取每次返回的 SampleResults[0]。
Utils.CSharpResult first = singleModel.Infer(rgb0, parameters);
Utils.CSharpResult second = singleModel.Infer(rgb1, parameters);

// 批量调用:列表顺序就是 SampleResults 的图片顺序。
var images = new List<Mat> { rgb0, rgb1 };
Utils.CSharpResult batch = batchModel.InferBatch(images, parameters);
List<Utils.CSharpObjectResult> objects0 = batch.SampleResults[0].Results;
List<Utils.CSharpObjectResult> objects1 = batch.SampleResults[1].Results;

本次加载的气球模型支持最多 4 张图片,GetMaxBatchSize() 返回 4。这里传入 2 张图片,就返回 2 份图片结果。

分类使用同一套接口:一次传入 32 张 RGB 图片,从 SampleResults[0] 到 SampleResults[31] 读取对应图片的分类结果。Results.Count > 0 时读取该图片的 Results[0]。

4.3 运行完整 C# 示例​

创建 .NET Framework 4.7.2、x64 控制台项目,按 C# 接口说明引用 DlcvCsharpApi.dll、OpenCvSharp.dll、Newtonsoft.Json.dll,并保持高性能 SDK 的原生运行库可用。输入是普通彩色 JPG,OpenCV 读取为 BGR,调用前转换为 RGB。

将下方代码放入控制台项目,编译后传入模型路径和两张图片路径:

.\BatchBalloonExample.exe ".\气球实例分割_batch4.dvt" ".\balloon-1.jpg" ".\balloon-2.jpg"
展开完整 C# 代码
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using dlcv_infer_csharp;
using Newtonsoft.Json.Linq;
using OpenCvSharp;

class BatchBalloonExample
{
static void Main(string[] args)
{
Console.OutputEncoding = new UTF8Encoding(false);
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
if (args.Length != 3)
throw new ArgumentException("需要模型路径和两张图片路径。");

Model.EnableConsoleLog = false;
using (var model = new Model(args[0], 0))
using (var bgr0 = Cv2.ImRead(args[1], ImreadModes.Color))
using (var bgr1 = Cv2.ImRead(args[2], ImreadModes.Color))
using (var rgb0 = new Mat())
using (var rgb1 = new Mat())
{
if (bgr0.Empty() || bgr1.Empty())
throw new InvalidOperationException("图片读取失败。");
if (bgr0.Width != bgr1.Width || bgr0.Height != bgr1.Height)
throw new ArgumentException("同一批图片的宽和高必须一致。");
if (model.GetMaxBatchSize() < 2)
throw new InvalidOperationException("需要支持至少两张图片的批处理模型。");

Cv2.CvtColor(bgr0, rgb0, ColorConversionCodes.BGR2RGB);
Cv2.CvtColor(bgr1, rgb1, ColorConversionCodes.BGR2RGB);
var images = new List<Mat> { rgb0, rgb1 };
var parameters = new JObject
{
["threshold"] = 0.5,
["with_mask"] = true,
["calc_mean"] = false
};
Utils.CSharpResult result = model.InferBatch(images, parameters);
try
{
if (result.SampleResults.Count != images.Count)
throw new InvalidOperationException("返回图片数量与输入数量不一致。");

var text = new StringBuilder();
text.AppendLine("C# InferBatch: two different balloon images");
text.AppendLine($"Input size = {bgr0.Width}x{bgr0.Height}");
text.AppendLine("GetMaxBatchSize() = " + model.GetMaxBatchSize());
text.AppendLine("images.Count = " + images.Count);
text.AppendLine("result.SampleResults.Count = " + result.SampleResults.Count);
text.AppendLine();
for (int i = 0; i < result.SampleResults.Count; i++)
{
List<Utils.CSharpObjectResult> objects = result.SampleResults[i].Results;
text.AppendLine($"SampleResults[{i}] <- {Path.GetFileName(args[i + 1])}");
text.AppendLine($" Results.Count = {objects.Count}");
for (int j = 0; j < objects.Count; j++)
{
var obj = objects[j];
text.AppendLine($" Results[{j}]: CategoryId={obj.CategoryId}, CategoryName={obj.CategoryName}");
text.AppendLine($" Score={obj.Score:F4}, Bbox=[{string.Join(", ", obj.Bbox.ConvertAll(v => v.ToString("F1")))}]");
text.AppendLine($" WithMask={obj.WithMask}, Mask={obj.Mask.Width}x{obj.Mask.Height}");
}
text.AppendLine();
}
Console.Write(text.ToString());
File.WriteAllText("batch-result.txt", text.ToString(), new UTF8Encoding(false));

List<Mat> painted = Utils.VisualizeResults(new List<Mat> { bgr0, bgr1 }, result);
try
{
for (int i = 0; i < painted.Count; i++)
Cv2.ImWrite($"balloon-{i + 1}-result.png", painted[i]);
}
finally
{
foreach (Mat image in painted) image.Dispose();
}
}
finally
{
foreach (var sample in result.SampleResults)
foreach (var obj in sample.Results)
obj.Mask?.Dispose();
}
}
}
}

程序输出 batch-result.txt、balloon-1-result.png、balloon-2-result.png。代码中的两张图片、批量结果和可视化结果使用相同的顺序。

5. 读取每张图片的结果​

5.1 按图片读取目标​

InferBatch 返回 Utils.CSharpResult。通过 SampleResults 逐图读取结果,再通过每张图片的 Results 读取目标:

CSharpResult
└─ SampleResults:List<CSharpSampleResult>,Count = 2
├─ [0] 对应 images[0],即 balloon-1.jpg
│ └─ Results:List<CSharpObjectResult>,Count = 1
│ └─ [0] 黑色气球
└─ [1] 对应 images[1],即 balloon-2.jpg
└─ Results:List<CSharpObjectResult>,Count = 3
├─ [0] 左侧黄色气球
├─ [1] 右侧绿色气球
└─ [2] 中间浅色气球

外层按图片组织,内层按目标组织。本次输入 2 张图片,SampleResults.Count 为 2;两张图片分别检测到 1 个和 3 个气球,各自的 Results.Count 为 1 和 3。

5.2 C# 输出与结果图​

以下为 C# 程序保存的结果列表,在控制台中展示:

C#双图InferBatch实际返回的SampleResults和Results列表

与列表对应的可视化图由同次调用结果经 Utils.VisualizeResults 保存:

SampleResults[0]:1 个气球SampleResults[1]:3 个气球
第一张气球图片的真实分割结果第二张气球图片的真实分割结果

本次 SampleResults[0].Results[0] 的主要值为:

C# 字段实际值读取说明
CategoryId / CategoryName0 / 气球模型类别编号与名称
Score0.9893置信度,对应 98.93%
Bbox[25.6, 22.8, 324.4, 521.3][x, y, width, height],对应原图中的左上角坐标、宽度和高度
WithMasktrue本目标包含分割 mask
Mask324 × 521 的 Mat目标区域 mask,使用 SDK 可视化放回原图

通过 WithMask 判断目标是否包含 mask,通过 Mask 读取分割图。使用完毕后释放各个 Mask、输入图片和模型。

6. 测试记录与相关文档​

下载 测试记录。

本次测试环境与设置
项目本文设置
测试日期与系统2026-09-29,气球 batch=4 重测于 2026-09-30;Windows x64
显卡NVIDIA GeForce RTX 5080
模型加速器模型加速器 dlcvpro_deploy-2026.9.28.1
C# 测试程序C# 测试程序 dlcvpro_infer_csharp-2026.9.29.0
推理运行库分类与气球 batch=1:dlcvpro_infer-2026.9.28.0a0;气球 batch=4:dlcvpro_infer-2026.9.29.0
加速后端TensorRT,开启 FP16,关闭通用架构 PLUS
C# 参数GPU 0;线程数 1;threshold=0.50;关闭 RPC;关闭均值计算
测速图片同组使用同一张已读入内存的图片,按 batch_size 重复组批后连续推理
测速方法加载模型、完成首次图片推理后,运行“多线程测试”,各组串行运行
实例分割输出保留 mask;IoU 阈值 0.2;检测数量 100

相关说明:模型加速器基本操作、C# 接口、C# 测试程序例程。