YOLOv5 是革命性的 "單階段"對象檢測模型的第五次迭代,旨在實時提供高速、高精度的結果,是世界上最受歡迎的視覺人工智慧模型,代表了Ultralytics對未來視覺人工智慧方法的開源研究,融合了數千小時研發中積累的經驗教訓和最佳實踐。同時官方發佈的模型已經支持 OpenVINO™ 部署工具加速模... ...
項目介紹目錄YOLOv5 是革命性的 "單階段"對象檢測模型的第五次迭代,旨在實時提供高速、高精度的結果,是世界上最受歡迎的視覺人工智慧模型,代表了Ultralytics對未來視覺人工智慧方法的開源研究,融合了數千小時研發中積累的經驗教訓和最佳實踐。同時官方發佈的模型已經支持 OpenVINO™ 部署工具加速模型推理,因此在該項目中,我們將結合之前開發的 OpenVINO™ C# API 部署 YOLOv5 DET 模型實現物體對象檢測。
項目鏈接為:
https://github.com/guojin-yan/OpenVINO-CSharp-API
項目源碼鏈接為:
https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/tree/master/model_samples/yolov5/yolov5_det_opencvsharp https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/tree/master/model_samples/yolov5/yolov5_det_emgucv
1. 前言
1.1 OpenVINO™ C# API
英特爾發行版 OpenVINO™ 工具套件基於 oneAPI 而開發,可以加快高性能電腦視覺和深度學習視覺應用開發速度工具套件,適用於從邊緣到雲的各種英特爾平臺上,幫助用戶更快地將更準確的真實世界結果部署到生產系統中。通過簡化的開發工作流程,OpenVINO™ 可賦能開發者在現實世界中部署高性能應用程式和演算法。
![](https://s2.loli.net/2024/01/18/vc5VeJ2BQknhitp.png)
OpenVINO™ 2023.2 於 2023 年 11 月 16 日發佈,該工具包帶來了挖掘生成人工智慧全部潛力的新功能。更多的生成式 AI 覆蓋和框架集成,以最大限度地減少代碼更改,並且擴展了對直接 PyTorch 模型轉換的模型支持。支持更多新的模型,包括 LLaVA、chatGLM、Bark 和 LCM 等著名模型。支持更廣泛的大型語言模型(LLM)和更多模型壓縮技術,支持運行時推理支持以下 Int4 模型壓縮格式,通過神經網路壓縮框架(NNCF) 進行本機 Int4 壓縮等一系列新的功能。
OpenVINO™ C# API 是一個 OpenVINO™ 的 .Net wrapper,應用最新的 OpenVINO™ 庫開發,通過 OpenVINO™ C API 實現 .Net 對 OpenVINO™ Runtime 調用,使用習慣與 OpenVINO™ C++ API 一致。OpenVINO™ C# API 由於是基於 OpenVINO™ 開發,所支持的平臺與 OpenVINO™ 完全一致,具體信息可以參考 OpenVINO™。通過使用 OpenVINO™ C# API,可以在 .NET、.NET Framework等框架下使用 C# 語言實現深度學習模型在指定平臺推理加速。
1.2 YOLOv5
YOLOv5 是革命性的 "單階段"對象檢測模型的第五次迭代,旨在實時提供高速、高精度的結果,是世界上最受歡迎的視覺人工智慧模型,代表了Ultralytics對未來視覺人工智慧方法的開源研究,融合了數千小時研發中積累的經驗教訓和最佳實踐。
![](https://s2.loli.net/2024/01/21/AlOgk92WGQBXMJ6.png)
2. 模型下載與轉換
2.1 環境安裝
首先創建Yolov5模型下載與轉換環境,此處為了更好的管理環境,使用Anaconda
創建一個虛擬環境用於安裝Yolov5模型下載與轉換環境,首先使用conda
創建一個虛擬環境,在命令行中依次輸入以下指令:
conda create -n yolo python=3.10
conda activate yolo
接下來安裝Yolov5模型下載與轉換環境,基礎的Yolov5模型下載需要通過克隆官方源碼實現,在命令行中依次輸入以下指令實現環境的安裝與配置即可:
git clone https://github.com/ultralytics/yolov5
cd yolov5
pip install -r requirements.txt
pip install --upgrade openvino-nightly
2.2 Yolov5 模型下載
Yolov5 官方提供了模型導出與轉換的方式,用戶只需要調用該介面便可以,在命令行中輸入以下指令便可以直接導出Yolov5模型:
cd yolov5
python export.py --weights yolov5s.pt --include onnx
結果輸出如下圖所示:
![](https://s2.loli.net/2024/02/01/HJmswpdlhBKiE8P.png)
使用Netron工具打開模型文件,查看模型結構,如下圖所示:
![](https://s2.loli.net/2024/02/01/ERklz2bQymIAr6Y.png)
官方預訓練模型是在COCO數據集上訓練的,因此導出的模型可以識別80種物體。模型輸入節點為images
,輸入為歸一化後的圖像數據,其輸入大小為640×640;模型的輸出節點為output0
,輸出大小為25200×85,其中25200(640÷8=80,640÷16=40,640÷32=20,3×80×80+3×40×40+3×20×20=25200)表示識別結果個數,85表示[cx, cy, w, h, confidence, score0, ···,score79],分別為識別框信息、識別結果中最大置信度以及80中類別結果的分數。
2.3 轉換IR模型
接下來直接使用 OpenVINO™ 工具直接進行模型轉換,在CMD中輸入以下指令即可:
ovc yolov5s.onnx
![](https://s2.loli.net/2024/02/01/Cr5UKHaQjkqwcz8.png)
3. Yolov5 DET 項目配置(OpenCvSharp版)
3.1 項目創建
如果開發者第一次在MacOS系統上使用C#編程語言,可以參考《在MacOS系統上配置OpenVINO™ C# API》文章進行配置。
首先使用dotnet創建一個測試項目,在終端中輸入一下指令:
dotnet new console --framework net6.0 --use-program-main -o yolov5-det
![](https://s2.loli.net/2024/02/01/NvJaTo5XwG6YI7g.png)
3.2 添加項目依賴
MacOS系統目前主要分為兩類,一類是使用intel處理器的X64位的系統,一類是使用M系列晶元的arm64位系統,目前OpenVINO官方針對這兩種系統都提供了編譯後的系統,所以目前OpenVINO.CSharp.API針對這兩種系統都提供了支持。
此處以M系列處理器的MacOS平臺為例安裝項目依賴,首先是安裝OpenVINO™ C# API項目依賴,在命令行中輸入以下指令即可:
dotnet add package OpenVINO.CSharp.API
dotnet add package OpenVINO.runtime.macos-arm64
dotnet add package OpenVINO.CSharp.API.Extensions
dotnet add package OpenVINO.CSharp.API.Extensions.OpenCvSharp
關於在MacOS上搭建 OpenVINO™ C# API 開發環境請參考以下文章: 在MacOS上搭建OpenVINO™C#開發環境
接下來安裝使用到的圖像處理庫 OpenCvSharp,在命令行中輸入以下指令即可:
dotnet add package OpenCvSharp4
dotnet add package OpenCvSharp4.Extensions
dotnet add package OpenCvSharp4.runtime.osx_arm64 --prerelease
關於在MacOS上搭建 OpenCvSharp 開發環境請參考以下文章: 【OpenCV】在MacOS上使用OpenCvSharp
添加完成項目依賴後,項目的配置文件如下所示:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>yolov5_det</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenCvSharp4" Version="4.9.0.20240103" />
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.9.0.20240103" />
<PackageReference Include="OpenCvSharp4.runtime.osx_arm64" Version="4.8.1-rc" />
<PackageReference Include="OpenVINO.CSharp.API" Version="2023.2.0.4" />
<PackageReference Include="OpenVINO.CSharp.API.Extensions" Version="1.0.1" />
<PackageReference Include="OpenVINO.CSharp.API.Extensions.OpenCvSharp" Version="1.0.4" />
<PackageReference Include="OpenVINO.runtime.macos-arm64" Version="2023.3.0.1" />
</ItemGroup>
</Project>
3.3 定義預測方法
(1) 使用常規方式部署模型
Yolov5 屬於比較經典單階段目標檢測模型,其模型輸入為640*640的歸一化處理後的圖像數據,輸出為未進行NMS的推理結果,因此在獲取推理結果後,需要進行NMS,其實現代碼如下所示:
static void yolov5_det(string model_path, string image_path, string device)
{
// -------- Step 1. Initialize OpenVINO Runtime Core --------
Core core = new Core();
// -------- Step 2. Read inference model --------
Model model = core.read_model(model_path);
OvExtensions.printf_model_info(model);
// -------- Step 3. Loading a model to the device --------
start = DateTime.Now;
CompiledModel compiled_model = core.compile_model(model, device);
// -------- Step 4. Create an infer request --------
InferRequest infer_request = compiled_model.create_infer_request();
// -------- Step 5. Process input images --------
Mat image = new Mat(image_path); // Read image by opencvsharp
int max_image_length = image.Cols > image.Rows ? image.Cols : image.Rows;
Mat max_image = Mat.Zeros(new OpenCvSharp.Size(max_image_length, max_image_length), MatType.CV_8UC3);
Rect roi = new Rect(0, 0, image.Cols, image.Rows);
image.CopyTo(new Mat(max_image, roi));
float factor = (float)(max_image_length / 640.0);
// -------- Step 6. Set up input data --------
Tensor input_tensor = infer_request.get_input_tensor();
Shape input_shape = input_tensor.get_shape();
Mat input_mat = CvDnn.BlobFromImage(max_image, 1.0 / 255.0, new OpenCvSharp.Size(input_shape[2], input_shape[3]), 0, true, false);
float[] input_data = new float[input_shape[1] * input_shape[2] * input_shape[3]];
Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
input_tensor.set_data<float>(input_data);
// -------- Step 7. Do inference synchronously --------
infer_request.infer();
// -------- Step 8. Get infer result data --------
Tensor output_tensor = infer_request.get_output_tensor();
int output_length = (int)output_tensor.get_size();
float[] output_data = output_tensor.get_data<float>(output_length);
// -------- Step 9. Process reault --------
Mat result_data = new Mat(25200, 85, MatType.CV_32F, output_data);
// Storage results list
List<Rect> position_boxes = new List<Rect>();
List<int> class_ids = new List<int>();
List<float> confidences = new List<float>();
// Preprocessing output results
for (int i = 0; i < result_data.Rows; i++)
{
float confidence = result_data.At<float>(i, 4);
if (confidence < 0.5)
{
continue;
}
Mat classes_scores = new Mat(result_data, new Rect(5, i, 80, 1));
OpenCvSharp.Point max_classId_point, min_classId_point;
double max_score, min_score;
// Obtain the maximum value and its position in a set of data
Cv2.MinMaxLoc(classes_scores, out min_score, out max_score,
out min_classId_point, out max_classId_point);
// Confidence level between 0 ~ 1
// Obtain identification box information
if (max_score > 0.25)
{
float cx = result_data.At<float>(i, 0);
float cy = result_data.At<float>(i, 1);
float ow = result_data.At<float>(i, 2);
float oh = result_data.At<float>(i, 3);
int x = (int)((cx - 0.5 * ow) * factor);
int y = (int)((cy - 0.5 * oh) * factor);
int width = (int)(ow * factor);
int height = (int)(oh * factor);
Rect box = new Rect();
box.X = x;
box.Y = y;
box.Width = width;
box.Height = height;
position_boxes.Add(box);
class_ids.Add(max_classId_point.X);
confidences.Add((float)confidence);
}
}
// NMS non maximum suppression
int[] indexes = new int[position_boxes.Count];
CvDnn.NMSBoxes(position_boxes, confidences, 0.5f, 0.5f, out indexes);
for (int i = 0; i < indexes.Length; i++)
{
int index = indexes[i];
Cv2.Rectangle(image, position_boxes[index], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Rectangle(image, new OpenCvSharp.Point(position_boxes[index].TopLeft.X, position_boxes[index].TopLeft.Y + 30),
new OpenCvSharp.Point(position_boxes[index].BottomRight.X, position_boxes[index].TopLeft.Y), new Scalar(0, 255, 255), -1);
Cv2.PutText(image, class_ids[index] + "-" + confidences[index].ToString("0.00"),
new OpenCvSharp.Point(position_boxes[index].X, position_boxes[index].Y + 25),
HersheyFonts.HersheySimplex, 0.8, new Scalar(0, 0, 0), 2);
}
string output_path = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(image_path)),
Path.GetFileNameWithoutExtension(image_path) + "_result.jpg");
Cv2.ImWrite(output_path, image);
Slog.INFO("The result save to " + output_path);
Cv2.ImShow("Result", image);
Cv2.WaitKey(0);
}
(2) 使用模型結構處理處理數據
目前 OpenVINO™ 已經支持在模型結構中增加數據的前後處理流程,並且在 OpenVINO™ C# API 中也已經實現了該功能介面,所以在此處演示瞭如何將模型輸入數據處理流程封裝到模型中,通過 OpenVINO™ 進行數據處理的加速處理,如下麵代碼所示:
static void yolov5_det_with_process(string model_path, string image_path, string device)
{
······
// -------- Step 2. Read inference model --------
start = DateTime.Now;
Model model = core.read_model(model_path);
OvExtensions.printf_model_info(model);
PrePostProcessor processor = new PrePostProcessor(model);
Tensor input_tensor_pro = new Tensor(new OvType(ElementType.U8), new Shape(1, 640, 640, 3));
InputInfo input_info = processor.input(0);
InputTensorInfo input_tensor_info = input_info.tensor();
input_tensor_info.set_from(input_tensor_pro).set_layout(new Layout("NHWC")).set_color_format(ColorFormat.BGR);
PreProcessSteps process_steps = input_info.preprocess();
process_steps.convert_color(ColorFormat.RGB).resize(ResizeAlgorithm.RESIZE_LINEAR)
.convert_element_type(new OvType(ElementType.F32)).scale(255.0f).convert_layout(new Layout("NCHW"));
Model new_model = processor.build();
// -------- Step 3. Loading a model to the device --------
CompiledModel compiled_model = core.compile_model(new_model, device);
// -------- Step 4. Create an infer request --------
InferRequest infer_request = compiled_model.create_infer_request();
// -------- Step 5. Process input images --------
Mat image = new Mat(image_path); // Read image by opencvsharp
int max_image_length = image.Cols > image.Rows ? image.Cols : image.Rows;
Mat max_image = Mat.Zeros(new OpenCvSharp.Size(max_image_length, max_image_length), MatType.CV_8UC3);
Rect roi = new Rect(0, 0, image.Cols, image.Rows);
image.CopyTo(new Mat(max_image, roi));
Cv2.Resize(max_image, max_image, new OpenCvSharp.Size(640, 640));
float factor = (float)(max_image_length / 640.0);
// -------- Step 6. Set up input data --------
Tensor input_tensor = infer_request.get_input_tensor();
Shape input_shape = input_tensor.get_shape();
byte[] input_data = new byte[input_shape[1] * input_shape[2] * input_shape[3]];
Marshal.Copy(max_image.Ptr(0), input_data, 0, input_data.Length);
IntPtr destination = input_tensor.data();
Marshal.Copy(input_data, 0, destination, input_data.Length);
// -------- Step 7. Do inference synchronously --------
······
}
由於目前還沒有完全實現所有的 OpenVINO™ 的預處理介面,因此只能實現部分預處理過程封裝到模型中,此處主要是做了以下處理:
- 數據類型轉換:byte->float
- 數據維度轉換:NHWC->NCHW
- 圖像色彩空間轉換:BGR->RGB
- 數據歸一化處理:[0,1]->[0,255]
因此將一些數據處理流程封裝到模型中後,在進行模型推理時,只需要將讀取到的圖片數據Resize為640*640後,就可以直接將數據載入到模型即可。
(3) 使用 OpenVINO™ C# API 封裝的介面
YOLOv5 是當前工業領域十分流行的目標檢測模型,因此在封裝 OpenVINO™ C# API 時,提供了快速部署 Yolov5 模型的介面,實現代碼如下所示:
static void yolov5_det_using_extensions(string model_path, string image_path, string device)
{
Yolov5DetConfig config = new Yolov5DetConfig();
config.set_model(model_path);
Yolov5Det yolov8 = new Yolov5Det(config);
Mat image = Cv2.ImRead(image_path);
DetResult result = yolov8.predict(image);
Mat result_im = Visualize.draw_det_result(result, image);
Cv2.ImShow("Result", result_im);
Cv2.WaitKey(0);
}
3.4 預測方法調用
定義好上述方法後,便可以直接在主函數中調用該方法,只需要在主函數中增加以下代碼即可:
yolov5_det("yolov5s.xml", "test_image.png", "AUTO");
yolov5_det_with_process("yolov5s.xml", "test_image.png", "AUTO");
yolov5_det_using_extensions("yolov5s.xml", "test_image.png", "AUTO");
如果開發者自己沒有進行模型下載與轉換,又同時想快速體驗該項目,我此處提供了線上的轉換後的模型以及帶預測圖片,開發者可以直接在主函數中增加以下代碼,便可以直接自動下載模型以及推理數據,並調用推理方法,實現程式直接運行。
static void Main(string[] args)
{
string model_path = "";
string image_path = "";
string device = "AUTO";
if (args.Length == 0)
{
if (!Directory.Exists("./model"))
{
Directory.CreateDirectory("./model");
}
if (!File.Exists("./model/yolov5s.bin") && !File.Exists("./model/yolov5s.bin"))
{
if (!File.Exists("./model/yolov5s.tar"))
{
_ = Download.download_file_async("https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/releases/download/Model/yolov5s.tar",
"./model/yolov5s.tar").Result;
}
Download.unzip("./model/yolov585s.tar", "./model/");
}
if (!File.Exists("./model/test_image.jpg"))
{
_ = Download.download_file_async("https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/releases/download/Image/test_det_02.jpg",
"./model/test_image.jpg").Result;
}
model_path = "./model/yolov5s.xml";
image_path = "./model/test_image.jpg";
}
else if (args.Length >= 2)
{
model_path = args[0];
image_path = args[1];
device = args[2];
}
else
{
Console.WriteLine("Please enter the correct command parameters, for example:");
Console.WriteLine("> 1. dotnet run");
Console.WriteLine("> 2. dotnet run <model path> <image path> <device name>");
}
// -------- Get OpenVINO runtime version --------
OpenVinoSharp.Version version = Ov.get_openvino_version();
Slog.INFO("---- OpenVINO INFO----");
Slog.INFO("Description : " + version.description);
Slog.INFO("Build number: " + version.buildNumber);
Slog.INFO("Predict model files: " + model_path);
Slog.INFO("Predict image files: " + image_path);
Slog.INFO("Inference device: " + device);
Slog.INFO("Start yolov8 model inference.");
yolov5_det(model_path, image_path, device);
//yolov5_det_with_process(model_path, image_path, device);
//yolov5_det_using_extensions(model_path, image_path, device);
}
為了減少文章篇幅,所以此處只提供了有差異的代碼,如果想獲取完整代碼,請訪問GitHub代碼倉庫,獲取項目源碼,鏈接為:
https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/tree/master/model_samples/yolov5/yolov5_det_opencvsharp
4. Yolov5 DET 項目配置(Emgu.CV 版)
同樣地,為了滿足Emgu.CV開發者的需求,此處同樣地提供了Emgu.CV版本的Yolov5的模型部署代碼以及使用流程,此處為了簡化文章內容,對於和上文重覆的步驟不在進行展開講述。
4.1 添加項目依賴
首先是安裝OpenVINO™ C# API項目依賴,在命令行中輸入以下指令即可:
dotnet add package OpenVINO.CSharp.API
dotnet add package OpenVINO.runtime.macos-arm64
dotnet add package OpenVINO.CSharp.API.Extensions
dotnet add package OpenVINO.CSharp.API.Extensions.EmguCV
接下來安裝使用到的圖像處理庫 Emgu.CV,在命令行中輸入以下指令即可:
dotnet add package Emgu.CV
dotnet add package Emgu.CV.runtime.mini.macos
關於在MacOS上搭建 OpenCvSharp 開發環境請參考以下文章: 【OpenCV】在MacOS上使用Emgu.CV
添加完成項目依賴後,項目的配置文件如下所示:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>yolov5_det</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Emgu.CV" Version="4.8.1.5350" />
<PackageReference Include="Emgu.CV.runtime.mini.macos" Version="4.8.1.5350" />
<PackageReference Include="OpenVINO.CSharp.API" Version="2023.2.0.4" />
<PackageReference Include="OpenVINO.CSharp.API.Extensions" Version="1.0.1" />
<PackageReference Include="OpenVINO.CSharp.API.Extensions.EmguCV" Version="1.0.4.1" />
<PackageReference Include="OpenVINO.runtime.macos-arm64" Version="2023.3.0.1" />
</ItemGroup>
</Project>
4.2 定義預測方法
模型部署流程與上一節中使用OpenCvSharp的基本一致,主要是替換了圖像處理的工具,同時提供瞭如上一節中所展示的三種部署方式。此處為了減少文章篇幅,此處不在展示詳細的部署代碼,如果想獲取相關代碼,請訪問項目GitHub,下載所有的測試代碼,項目鏈接為:
https://github.com/guojin-yan/OpenVINO-CSharp-API-Samples/tree/master/model_samples/yolov5/yolov5_det_emgucv
5. 項目運行與演示
5.1 項目編譯
接下來輸入項目編譯指令進行項目編譯,輸入以下指令即可:
dotnet build
程式編譯後輸出為:
![](https://s2.loli.net/2024/02/01/gejy9DoWSRZ7YdO.png)
5.2 項目文件運行
接下來運行編譯後的程式文件,在CMD中輸入以下指令,運行編譯後的項目文件:
dotnet run --no-build
運行後項目輸出為:
![](https://s2.loli.net/2024/02/01/lGRr1diWtHn6pYk.png)
![](https://s2.loli.net/2024/02/01/wPvx5W1HQdNynrR.jpg)
6. 總結
在該項目中,我們結合之前開發的 OpenVINO C# API 項目部署YOLOv5模型,成功實現了對象目標檢測,並且根據不同開發者的使用習慣,同時提供了OpenCvSharp以及Emgu.CV兩種版本,供各位開發者使用。最後如果各位開發者在使用中有任何問題,歡迎大家與我聯繫。
![](https://s2.loli.net/2024/01/29/VIPU1MSwjEh2QAY.png)