# OnnxObjectDetection **Repository Path**: hllxml/onnx-object-detection ## Basic Information - **Project Name**: OnnxObjectDetection - **Description**: 官网例程中的一个,升级了最新的包,并机翻了一下readme,凑合看吧 https://github.com/dotnet/machinelearning-samples.git - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-08-23 - **Last Updated**: 2022-08-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 这是从官网 https://github.com/dotnet/machinelearning-samples.git 下载的示例,升级了包。 # Object Detection - WPF Desktop Sample | ML.NET version | API type | Status | App Type | Data type | Scenario | ML Task | Algorithms | |----------------|-------------|------------|-------------|-------------|------------------|---------------|-----------------------------------| | v1.7.0 | Dynamic API | Up-to-date | End-End app | image files | Object Detection | Deep Learning | ONNX: Tiny YOLOv2 & Custom Vision | 对象检测是计算机视觉中的经典问题之一:识别给定图像中的哪些对象以及它们在图像中的位置。对于这些情况,可以使用预先训练的模型或训练自己的模型来对特定于自定义域的图像进行分类。默认情况下,此示例使用预先训练的模型,但您也可以添加从自定义视觉导出的自己的模型。 ## 示例内容 WPF Core 桌面应用,可呈现设备网络摄像头的实时流,使用 ML.NET 通过对象检测模型运行视频帧,并使用标签绘制边界框,以指示实时检测到的对象。 ## ONNX The Open Neural Network eXchange i.e [ONNX](http://onnx.ai/) 是一种表示深度学习模型的开放格式。借助 ONNX,开发人员可以在最先进的工具之间移动模型,并选择最适合他们的组合。ONNX 由包括 Microsoft 在内的合作伙伴社区开发和支持。 ## 预先训练的模型 有多个预先训练的模型用于识别图像中的多个对象。WPF 应用程序和Web 应用程序都默认使用从ONNX 模型 Zoo下载的预训练模型 Tiny YOLOv2;一系列经过预先训练的、最先进的 ONNX 格式模型。Tiny YOLOv2是一个用于对象检测的实时神经网络,可检测20个不同的类,并在Pascal VOC数据集上进行训练。它由9个卷积层和6个最大池化层组成,是更复杂的完整YOLOv2网络的较小版本。 ## 自定义视觉模型 此示例默认使用上述预先训练的 Tiny YOLOv2 模型。但是,它也是为了支持从 Microsoft [Custom Vision](https://www.customvision.ai)导出的ONNX模型而编写的. ## 模型输入和输出 为了解析ONNL模型的预测输出,我们需要了解输入和输出张量的格式(或形状)。为此,我们将首先使用[Netron](https://lutzroeder.github.io/netron/)(一个用于神经网络和机器学习模型的 GUI 可视化工具)来检查模型。 下面是我们在使用 Netron 打开此示例的 Tiny YOLOv2 模型时看到的示例: ![Output from inspecting the Tiny YOLOv2 model with Netron](./docs/Netron/TinyYolo2_model_onnx.png) 从上面的输出中,我们可以看到Tiny YOLOv2模型具有以下输入/输出格式: ### 输入:"图像"3x416x416 首先要注意的是,输入张量的名称是"图像"。稍后在定义估计管道的输入参数时,我们将需要此名称。 我们还可以看到输入张量的或形状为3x416x416。这说明传递到模型中的位图图像应为 416 高 x 416 宽。"3"表示图像应采用BGR格式;前 3 个"通道"分别为蓝色、绿色和红色。 ### 输出:'数据'125x13x13 与输入张量一样,我们可以看到输出的名称是"data"。同样,我们将在定义估计管道的输出参数时注意这一点。 我们还可以看到,输出张量的形状是125x13x13。 125x13x13 的"13x13"部分表示图像被划分为 13x13 的"单元格"网格(13 列 x 13 行)。因为我们知道输入图像是416x416,我们可以推断出这些"单元格"中的每一个都是32高x 32宽(416/13 = 32)。 ``` ├──────────────── 416 ─────────────────┤ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ ┬ 416/13=32 ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ┌──┐ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ └──┘ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ 32x32 ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ 13 ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ 416 ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ ┴ 13 ``` 那么125呢?"125"告诉我们,对于每个网格像元,模型返回 125 个"通道"(或数据片段)作为该单个像元的预测输出。 为了理解为什么有125个通道,我们首先需要了解模型不会预测对象的任意边界框。相反,每个像元负责预测 5 个预定的边界框。这 5 个箱子是根据以下每个箱子的偏移量计算得出的:anchor ``` ┌───────────────────┐ │ ┌───┐ │ │ ┌─────┼───┼─────┐ │ │ │ ┌──┼───┼──┐ │ │ │ │ │ │┌─┐│ │ │ │ │ │ │ │└─┘│ │ │ │ │ │ └──┼───┼──┘ │ │ │ └─────┼───┼─────┘ │ │ └───┘ │ └───────────────────┘ ``` 因此,对于每个单独的单元格,模型返回 5 个预测(每个锚点一个,由上面的框形状表示),每个预测包括以下 25 个参数: - 指示边界框位置的 4 个参数(x、y、宽度、高度) - 1 个参数,表示框的置信度分数(或对象性) - 20 个类概率(每个类一个概率分数,表示对象是该类的可能性) 5 盒 x 25 参数 = 125 个"通道" 请注意,如果模型被训练为检测不同数量的类,则此值将不同。例如,仅能够检测到 3 个不同类的模型的输出格式为 40x13x13: -(x、y、宽度、高度、对象性)+ 3 个类概率 = 8 个参数 - 5 盒 x 8 参数 = 40 个"通道" ## Code Walkthrough 此示例与[入门对象检测示例](https://github.com/dotnet/machinelearning-samples/tree/main/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx)的不同之处在于,此处我们在内存中加载/处理图像,而入门示例从文件加载图像。 创建一个类,定义在将数据加载到' IDataView '时使用的数据模式。 ML.NET支持图像的“Bitmap”类型,因此我们将指定“Bitmap”属性,用“ImageTypeAttribute”装饰,并传递我们通过检查模型得到的高度和宽度尺寸,如下所示。 ```csharp public struct ImageSettings { public const int imageHeight = 416; public const int imageWidth = 416; } public class ImageInputData { [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)] public Bitmap Image { get; set; } } ``` ### ML.NET: 配置模型 第一步是创建一个空的“DataView”,以获得配置模型时使用的数据模式。 ```csharp var dataView = _mlContext.Data.LoadFromEnumerable(new List()); ``` 第二步是定义估计器管道。 通常在处理深度神经网络时,必须使图像适应网络所期望的格式。 出于这个原因,下面的代码调整并转换图像(像素值在所有R、G、B通道上都是标准化的)。 ```csharp var pipeline = mlContext.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: onnxModel.ModelInput, imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(ImageInputData.Image)) .Append(mlContext.Transforms.ExtractPixels(outputColumnName: onnxModel.ModelInput)) .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: onnxModel.ModelPath, outputColumnName: onnxModel.ModelOutput, inputColumnName: onnxModel.ModelInput)); ``` Next, we'll use the input and output tensor names we got by [inspecting the model](#model-input-and-output) to define the **input** and **output** parameters of the Tiny YOLOv2 Onnx Model. 接下来,我们将使用通过[检查模型](#model-input-and-output)得到的输入和输出张量名称来定义Tiny YOLOv2 Onnx模型的**输入**和**输出**参数。 ```csharp public struct TinyYoloModelSettings { public const string ModelInput = "image"; public const string ModelOutput = "grid"; } ``` Last, create the model by fitting the `DataView`. 最后,通过匹配“DataView”来创建模型。 ```csharp var model = pipeline.Fit(dataView); ``` ## 加载模型并创建预测引擎 在模型配置好之后,我们需要保存模型,加载保存的模型,创建一个“PredictionEngine”,然后将图像传递给引擎来检测使用模型的对象。 ```csharp public ObjectDetectionService(PredictionEnginePool predictionEngine) { this.predictionEngine = predictionEngine; } ``` 然而,WPF桌面应用程序创建一个单一的' PredictionEngine '和缓存本地用于每个帧预测。 需要澄清的关键点是,实例化' PredictionEngine '的调用代码负责处理缓存(与' predictionengineepool '相比)。 ```csharp public PredictionEngine GetMlNetPredictionEngine() { return mlModel.Model.CreatePredictionEngine(mlModel); } ``` ## 检测图像中的对象 When obtaining the prediction, we get a `float` array of size **21125** in the `PredictedLabels` property. This is the 125x13x13 output of the model [discussed earlier](#output-data-125x13x13). We then use the [`OnnxOutputParser`](./OnnxObjectDetection/OnnxOutputParser.cs) class to interpret and return a number of bounding boxes for each image. Again, these boxes are filtered so that we retrieve only 5 with high confidence. 当获得预测时,我们在' PredictedLabels '属性中获得一个大小为**21125**的' float '数组。 这是[前面讨论的模型的125x13x13输出](#output-data-125x13x13)。 然后使用[' OnnxOutputParser '](./OnnxObjectDetection/OnnxOutputParser.cs)类解释并返回每个图像的一些边框。 同样,对这些盒子进行了过滤,因此我们只检索到5个具有高可信度的盒子。 ```csharp var labels = tinyYoloPredictionEngine?.Predict(imageInputData).PredictedLabels; var boundingBoxes = outputParser.ParseOutputs(labels); var filteredBoxes = outputParser.FilterBoundingBoxes(boundingBoxes, 5, 0.5f); ``` ## 在图像中检测到的对象周围绘制边界框 WPF应用在与流式处理视频播放重叠的Canvas元素上绘制边界框。 ```csharp DrawOverlays(filteredBoxes, WebCamImage.ActualHeight, WebCamImage.ActualWidth); WebCamCanvas.Children.Clear(); foreach (var box in filteredBoxes) { var objBox = new Rectangle {/* ... */ }; var objDescription = new TextBlock {/* ... */ }; var objDescriptionBackground = new Rectangle {/* ... */ }; WebCamCanvas.Children.Add(objDescriptionBackground); WebCamCanvas.Children.Add(objDescription); WebCamCanvas.Children.Add(objBox); } ``` ## 关于精度的说明 精简的 YOLOv2 模型明显不如完整的 YOLOv2 模型准确,但微小的版本足以满足此示例应用的需求。