ML.NET 示例:对象检测-ASP.NET Core Web和WPF桌面示例
| v1.5.0 | 動態API | 最新 | 端到端應用 | 圖像文件 | 對象檢測 | 深度學習 | ONNX: Tiny YOLOv2 & Custom Vision |
問題
對象檢測是計算機視覺中的經典問題之一:識別給定圖像中包含哪些對象以及它們在圖像中的位置。對于這些情況,您可以使用預先訓練的模型,也可以訓練自己的模型對自定義域特定的圖像進行分類。默認情況下,此示例使用預先訓練的模型,但是您也可以添加從Custom Vision導出的模型。
示例的工作原理
此示例由兩個獨立的應用程序組成:
一個WPF Core 桌面應用程序呈現攝像頭的實時流,使用ML.NET通過對象檢測模型運行視頻幀,并用標簽繪制邊界框,指示實時檢測到的對象。
一個允許用戶上載或選擇圖像的ASP.NET Core Web應用。Web應用程序使用ML.NET通過一個對象檢測模型運行圖像,并用指示檢測到的對象的標簽繪制邊界框。
Web應用程序顯示右側列出的圖像,可以選擇每個圖像進行處理。一旦圖像被處理,它將被繪制在屏幕的中間,每個檢測到的對象周圍都有標記的邊界框,如下所示。
或者,您可以嘗試上傳自己的圖片,如下所示。
ONNX
開放式神經網絡交換即ONNX是一種表示深度學習模型的開放格式。使用ONNX,開發人員可以在最先進的工具之間移動模型,并選擇最適合他們的組合。ONNX是由包括微軟在內的眾多合作伙伴共同開發和支持的。
預訓練模型
有多個預先訓練的模型用于識別圖像中的多個對象。WPF app和Web app都默認使用從ONNX Model Zoo下載的預先訓練好的模型Tiny YOLOv2; 一組經過預先訓練的、最先進的ONNX格式模型。Tiny YOLOv2是一種用于目標檢測的實時神經網絡,用于檢測20個不同的類,并在Pascal VOC數據集上進行訓練。它由9個卷積層和6個最大池層組成,是更復雜的完整的YOLOv2網絡的較小版本。
Custom Vision 模型
此示例默認使用上述預先訓練的Tiny YOLOv2模型。不過,它也支持從微軟Custom Vision導出的ONNX模型。
要使用自己的模型,請使用以下步驟
使用 Custom Vision 創建和訓練物體探測器。要導出模型,請確保選擇一個緊湊域,例如常規(緊湊)。要導出現有的對象檢測器,請通過選擇右上角的齒輪圖標將域轉換為緊湊型。在_ 設置 _中,選擇一個緊湊的模型,保存并訓練您的項目。
轉到_性能選項卡導出模型。選擇一個用緊湊域訓練的迭代,將出現一個“導出”按鈕。選擇_導出、ONNX、ONNX1.2,然后選擇導出。文件準備好后,選擇“下載”按鈕。
導出的是一個包含多個文件的zip文件,包括一些示例代碼、標簽列表和ONNX模型。將.zip文件放到OnnxObjectDetection項目中的OnnxModels文件夾中。
在解決方案資源管理器中,右鍵單擊OnnxModels文件夾,然后選擇_添加現有項_。選擇剛添加的.zip文件。
在解決方案資源管理器中,從OnnxModels文件夾中選擇.zip文件。更改文件的以下屬性:
生成操作 -> 內容
復制到輸出目錄 -> 如果較新則復制
現在,當你生成和運行應用程序時,它將使用你的模型而不是Tiny YOLOv2模型。
模型輸入和輸出
為了解析ONNL模型的預測輸出,我們需要了解輸入和輸出張量的格式(或形狀)。為此,我們將首先使用Netron,一個用于神經網絡和機器學習模型的GUI可視化工具,用于檢查模型。
下面是一個例子,我們將看到使用Netron打開這個示例的Tiny YOLOv2模型:
從上面的輸出中,我們可以看到Tiny YOLOv2模型具有以下輸入/輸出格式:
輸入: 'image' 3x416x416
首先要注意的是,輸入張量的名稱是**'image'。稍后在定義評估管道的input**參數時,我們將需要這個名稱。
我們還可以看到,輸入張量的形狀是3x416x416。這說明傳入模型的位圖圖像應該是416高x 416寬。“3”表示圖像應為BGR格式;前3個“通道”分別是藍色、綠色和紅色。
輸出: 'data' 125x13x13
與輸入張量一樣,我們可以看到輸出名稱是**'data'。同樣,在定義評估管道的output**參數時,我們會注意到這一點。
我們還可以看到, 輸出張量的形狀是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個'通道'
解決方案
此解決方案中的項目使用.NET Core 3.0。為了運行此示例,您必須安裝.NET Core SDK 3.0。為此,請執行以下任一操作:
通過轉到.NET Core 3.0下載頁面手動安裝SDK,并在“SDK”列中下載最新的“.NET Core安裝程序”。
或者,如果您使用的是Visual Studio 2019,請轉至:?工具>選項>環境>預覽功能 ,然后選中以下復選框: 使用.NET Core SDK的預覽
解決方案包含三個項目
OnnxObjectDetection是WPF應用程序和Web應用程序都使用的.NET Standard庫。它包含用于通過模型運行圖像并解析結果預測的大多數邏輯。該項目還包含ONNX模型文件。除了在圖像/屏幕上繪制標簽邊界框之外,此項目包含下面的所有代碼段。
OnnxObjectDetectionWeb 包含一個ASP.NET Core Web App,其中包含Razor UI頁面和API controller以處理和呈現圖像。
OnnxObjectDetectionApp 包含一個.NET CORE WPF桌面應用程序,該應用程序使用OpenCvSharp從設備的網絡攝像頭捕獲視頻。
代碼演練
該示例與getting-started object detection sample不同,在這里我們加載/處理在內存中的圖像**,而入門示例從文件中加載圖像。
創建一個類,該類定義將數據加載到IDataView中時要使用的數據模式。ML.NET支持圖像的Bitmap類型,因此我們將指定以ImageTypeAttribute修飾的Bitmap屬性,并傳入通過[檢查模型]](#model-input-and-output)得到的高度和寬度尺寸,如下所示。
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,以獲取配置模型時要使用的數據架構。
var?dataView?=?_mlContext.Data.LoadFromEnumerable(new?List<ImageInputData>());第二步是定義評估器管道。通常在處理深度神經網絡時,必須使圖像適應網絡所期望的格式。因此,下面的代碼將調整圖像的大小并對其進行變換(所有R、G、B通道的像素值都已標準化)。
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));接下來,我們將使用通過檢查模型 得到的輸入和輸出張量名稱來定義Tiny YOLOv2 Onnx模型的input和output參數。
public?struct?TinyYoloModelSettings {public?const?string?ModelInput?=?"image";public?const?string?ModelOutput?=?"grid"; }最后,通過擬合“DataView”來創建模型。
var?model?=?pipeline.Fit(dataView);加載模型并創建PredictionEngine
配置模型后,我們需要保存模型,加載保存的模型,創建一個PredictionEngine,然后將圖像傳遞給引擎以使用模型檢測對象。這是一個Web應用程序和WPF應用程序略有不同的地方。
Web應用程序使用一個PredictionEnginePool來高效地管理服務,并為服務提供一個PredictionEngine來進行預測。在內部,它進行了優化,以便在創建這些對象時,以最小的開銷在Http請求之間緩存和共享對象依賴關系。
public?ObjectDetectionService(PredictionEnginePool<ImageInputData,?TinyYoloPrediction>?predictionEngine) {this.predictionEngine?=?predictionEngine; }而WPF桌面應用程序創建一個PredictionEngine,并在本地緩存以用于每個幀預測。需要澄清的關鍵點是,實例化PredictionEngine的調用代碼負責處理緩存(與PredictionEnginePool相比)。
public?PredictionEngine<ImageInputData,?TinyYoloPrediction>?GetMlNetPredictionEngine() {return?mlModel.Model.CreatePredictionEngine<ImageInputData,?TinyYoloPrediction>(mlModel); }檢測圖像中的對象
獲取預測時,我們在PredictedLabels屬性中得到一個大小為21125的 float數組。這是前面討論過的模型的125x13x13輸出。然后,我們使用OnnxOutputParser類解釋并返回每個圖像的多個邊界框。同樣,這些框被過濾,因此我們只檢索到5個具有高置信度的框。
var?labels?=?tinyYoloPredictionEngine?.Predict(imageInputData).PredictedLabels; var?boundingBoxes?=?outputParser.ParseOutputs(labels); var?filteredBoxes?=?outputParser.FilterBoundingBoxes(boundingBoxes,?5,?0.5f);在圖像中檢測到的對象周圍繪制邊界框
最后一步是在對象周圍繪制邊界框。
Web應用程序使用Paint API將框直接繪制到圖像上,并返回圖像以在瀏覽器中顯示。
var?img?=?_objectDetectionService.DrawBoundingBox(imageFilePath);using?(MemoryStream?m?=?new?MemoryStream()) {img.Save(m,?img.RawFormat);byte[]?imageBytes?=?m.ToArray();//?Convert?byte[]?to?Base64?Stringbase64String?=?Convert.ToBase64String(imageBytes);var?result?=?new?Result?{?imageString?=?base64String?};return?result; }另外,WPF應用程序在與流式視頻播放重疊的Canvas 元素上繪制邊界框。
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); }關于準確性的說明
Tiny YOLOv2的精確度明顯低于完整的YOLOv2模型,但是對于這個示例應用程序來說,Tiny版本已經足夠了。
疑難解答(Web應用程序)
通過應用程序服務在Azure上部署此應用程序時,您可能會遇到一些常見問題。
應用程序返回5xx代碼
要在Azure門戶中添加.NET Core 3.0支持,請選擇相應應用程序服務的開發工具>擴展部分中的添加按鈕。
然后,從擴展列表中選擇選擇擴展并選擇ASP.NET Core 3.0(x64)Runtime并接受法律條款,繼續將擴展添加到應用程序服務。
部署應用程序后,您可能會得到5xx代碼的一個原因是平臺。web應用程序僅在64位體系結構上運行。在Azure中,更改設置>配置>常規設置菜單中相應應用程序服務中的平臺設置。
部署應用程序后使用5xx代碼的另一個原因是web應用程序的目標框架是.NET Core 3.0,它目前正在預覽中。您可以將應用程序和引用的項目還原為.NET Core 2.x或向應用程序服務添加擴展。
相對路徑
在本地和Azure上工作時,路徑的工作方式略有不同。如果成功地部署了應用程序,但單擊其中一個預加載的映像或上載自己的映像不起作用,請嘗試更改相對路徑。為此,在Controllers/ObjectDetectionController.cs文件中,更改構造函數中的_imagesTmpFolder的值。
_imagesTmpFolder?=?CommonHelpers.GetAbsolutePath(@"ImagesTemp");對Get操作中的imageFileRelativePath執行相同的操作。
string?imageFileRelativePath?=?@"assets"?+?url;或者,您可以根據環境(dev / prod)設置條件,是使用路徑的本地版本還是Azure首選的版本。
總結
以上是生活随笔為你收集整理的ML.NET 示例:对象检测-ASP.NET Core Web和WPF桌面示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VS2005~VS2022,那些年用过的
- 下一篇: .NET Core with 微服务 -