C#是由C和C++衍生出來(lái)的一種安全的、穩(wěn)定的、簡(jiǎn)單的、優(yōu)雅的面向?qū)ο?a target="_blank">編程語(yǔ)言,它綜合了 VB簡(jiǎn)單的可視化操作和 C++的高運(yùn)行效率,成為支持成為 .NET 開發(fā)的首選語(yǔ)言。作為人工智能開發(fā)人員,如果你希望在 C# 中使用OpenVINO,OpenVINO C# API將是你的首選。OpenVINO C# API提供了NuGet程序包,實(shí)現(xiàn)在 C# 中一站式安裝與使用OpenVINO。

OpenVINO C# API 項(xiàng)目地址(復(fù)制到瀏覽器打開)
https://github.com/guojin-yan/OpenVINO-CSharp-API
OpenVINO C# API 基于 OpenVINOC++ API 研發(fā),下表展示了C#與C++ API的對(duì)應(yīng)關(guān)系:

本文根據(jù) AI 模型部署的典型步驟,演示 OpenVINO C# API 使用方式,并跟 C++ API 做對(duì)比。
01
安裝 OpenVINO C# API
OpenVINO C# API支持NuGet程序包安裝方式,這與 OpenVINO C++ 庫(kù)的安裝過程相比,更加簡(jiǎn)單。如果使用 Visual Studio 開發(fā) AI 項(xiàng)目,則可以通過NuGet程序包管理功能直接安裝即可,如下圖所示:

如果通過 dotnet 命令方式安裝,通過下面語(yǔ)句進(jìn)行安裝即可:
dotnet add package OpenVINO.CSharp.win
02
導(dǎo)入程序集
OpenVINO C# API程序集全部在 CSharp 命名空間下,因此若要使用OpenVINO C# API,需要先引入命名空間:
using OpenVinoSharp;
03
初始化 OpenVINO運(yùn)行時(shí)
Core 類代表一個(gè) OpenVINO 運(yùn)行時(shí)核心實(shí)體,后續(xù)的讀取模型、加載模型等方法都需要通過Core類進(jìn)行創(chuàng)建,在封裝 C# API時(shí),為了與C++ API對(duì)應(yīng),也對(duì)Core類進(jìn)行了封裝,并封裝了與C++ API中對(duì)應(yīng)的方法。
在 C# 中的初始化方式:
Core core = new Core();
在 C++ 中的初始化方式:
ov::Core core;
04
加載并獲取模型信息
4.1 加載模型
OpenVINO2022.1 版本更新之后,加載模型是使用下面的API方法:

在 C# 中加載模型的方式:
Model model = core.read_model(model_path);
在 C++ 中的初始化方式:
std::shared_ptr model = core.read_model(model_path);
4.2 獲取模型信息
通過Core.read_model () 方法獲得的Model對(duì)象和通過Core.compile_model () 方法獲得的CompiledModel對(duì)象,都支持直接訪問屬性獲取輸入與輸出層信息。以 Model 對(duì)象獲取模型信息為例,下面是所使用的API方法:

Input/Output主要是封裝了模型網(wǎng)絡(luò)層,可以通過下面API實(shí)現(xiàn)獲取模型的詳細(xì)信息:

在C#中通過下方代碼,可以直接獲取模型的輸入、輸入層以及模型的 friendly name:
string model_name = model.get_friendly_name();
Input input = model.input();
Output output = model.output();
然后將模型具體信息打印到控制臺(tái)頁(yè)面:
Console.WriteLine("Model name: {0}", model_name);
Console.WriteLine("/------- [In] -------/");
Console.WriteLine("Input name: {0}", input.get_any_name());
Console.WriteLine("Input type: {0}", input.get_element_type().to_string());
Console.WriteLine("Input shape: {0}", input.get_shape().to_string());
Console.WriteLine("/------- [Out] -------/");
Console.WriteLine("Output name: {0}", output.get_any_name());
Console.WriteLine("Output type: {0}", output.get_element_type().to_string());
Console.WriteLine("Output shape: {0}", output.get_shape().to_string());
獲取模型網(wǎng)絡(luò)層信息如下:
Model name: torch_jit
/------- [In] -------/
Input name: data
Input type: float
Input shape: [1,3,224,224]
/------- [Out] -------/
Output name: prob
Output type: float
Output shape: [1,1000]
同樣的輸出信息,我們使用C++ API實(shí)現(xiàn)如下:
std::cout << "Model name: " << model->get_friendly_name() << std::endl;
ov::Output input = model->input();
std::cout << "/------- [In] -------/" << std::endl;
std::cout << "Input name: " << input.get_any_name() << std::endl;
std::cout << "Input type: " << input.get_element_type().c_type_string() << std::endl;
std::cout << "Input shape: " << input.get_shape().to_string() << std::endl;
ov::Output output = model->output();
std::cout << "/------- [Out] -------/" << std::endl;
std::cout << "Output name: " << output.get_any_name() << std::endl;
std::cout << "Output type: " << output.get_element_type().c_type_string() << std::endl;
std::cout<"Outputshape:"<std::endl;
05
編譯模型并創(chuàng)建推理請(qǐng)求
在讀取本地模型后,調(diào)用模型編譯方法將模型編譯為可以在目標(biāo)設(shè)備上執(zhí)行的compile_model對(duì)象,并通過該對(duì)象創(chuàng)建用于推斷已編譯模型的推斷請(qǐng)求對(duì)象。下面是所使用的API方法:

在C#中編譯模型并創(chuàng)建推理請(qǐng)求的方式:
CompiledModel compiled_model = core.compile_model(model, "AUTO");
InferRequest infer_request = compiled_model.create_infer_request();
使用 C++ API 中編譯模型并創(chuàng)建推理請(qǐng)求的方式:
CompiledModel compiled_model = core.compile_model(model, "AUTO");
InferRequest infer_request = compiled_model.create_infer_request();
06
張量 Tensor
6.1 張量的獲取與設(shè)置
在創(chuàng)建推理請(qǐng)求后,系統(tǒng)會(huì)自動(dòng)創(chuàng)建和分配輸入和輸出的張量,張量可以通過 InferRequest對(duì)象獲得,并且可以自定義張量并加載到模型指定節(jié)點(diǎn);可以根據(jù)張量的輸入輸出序號(hào)、名稱以及模型節(jié)點(diǎn) Node 對(duì)象獲取和設(shè)置,主要 C# API如下:

6.2 張量的信息獲取與設(shè)置
張量中主要包含的信息有張量的形狀 (Shape) 、張量的數(shù)據(jù)格式 (OvType-> element.Type) 以及張量中的內(nèi)存數(shù)據(jù)。可以通過以下 API 方法操作張量的參數(shù):

以上方法是對(duì)張量的一些基礎(chǔ)操作,除了 set_data、get_data 是 OpenVINO C# API 獨(dú)有的,其他接口都與 C++API 一致。
07
加載推理數(shù)據(jù)
7.1 獲取輸入張量
對(duì)于單輸入的模型可以直接通過 get_input_tensor() 方法獲得,并調(diào)用 Tensor 的相關(guān)方法獲取 Tensor 的相關(guān)信息,C# 代碼如下所示:
Tensor input_tensor = infer_request.get_input_tensor();
Console.WriteLine("/------- [Input tensor] -------/");
Console.WriteLine("Input tensor type: {0}", input_tensor.get_element_type().to_string());
Console.WriteLine("Input tensor shape: {0}", input_tensor.get_shape().to_string());
Console.WriteLine("Input tensor size: {0}", input_tensor.get_size());
獲取輸出結(jié)果為:
/------- [Input tensor] -------/
Input tensor type: f32
Input tensor shape: Shape : {1, 3, 224, 224}
Input tensor size: 150528
對(duì)于上述的同樣輸出內(nèi)容,我們也可以通過 C++ API實(shí)現(xiàn),C++代碼如下:
ov::Tensor input_tensor = infer_request.get_input_tensor();
std::cout << "/------- [Input tensor] -------/" << std::endl;
std::cout << "Input tensor type: " << input_tensor.get_element_type().c_type_string() << std::endl;
std::cout << "Input tensor shape: " << input_tensor.get_shape().to_string() << std::endl;
std::cout << "Input tensor size: " << input_tensor.get_size() << std::endl;
7.2 添加推理數(shù)據(jù)
這一步主要是將處理好的圖片數(shù)據(jù)加載到 Tensor 數(shù)據(jù)內(nèi)存中, OpenVINO 的 API 中提供了訪問內(nèi)存地址的接口,可以獲取數(shù)據(jù)內(nèi)存首地址,不過為了更好的加載推理數(shù)據(jù),我們此處封裝了 set_data
Mat input_mat = new Mat();
Shape input_shape = input_tensor.get_shape();
long channels = input_shape[1];
long height = input_shape[2];
long width = input_shape[3];
float[] input_data = new float[channels * height * width];
Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
input_tensor.set_data(input_data);
下面是在 C++ 中實(shí)現(xiàn)上述功能的代碼:
cv::Mat input_mat;
float* input_data = input_tensor.data<float>();
ov::Shape input_shape = input_tensor.get_shape();
size_t channels = input_shape[1];
size_t height = input_shape[2];
size_t width = input_shape[3];
for (size_t c = 0; c < channels; ++c) {
for (size_t h = 0; h < height; ++h) {
for (size_t w = 0; w < width; ++w) {
input_data[c * height * width + h * width + w] = input_mat.atfloat , 3>>(h, w)[c];
}
}
}
08
模型推理
在加載完推理數(shù)據(jù)后,就可以調(diào)用模型推理的 API 方法推理當(dāng)前數(shù)據(jù),主要使用到的 API 方法為:

調(diào)用該方法也較為簡(jiǎn)單,只需要調(diào)用該 API 接口即可,在 C# 中的代碼為:
infer_request.infer();
C++ 中的代碼與 C++ 中一致。
09
獲取推理結(jié)果
對(duì)于單輸出的模型可以直接通過 get_output_tensor() 方法獲得,并調(diào)用 Tensor 的相關(guān)方法獲取 Tensor 的相關(guān)信息,C#代碼如下所示:
Tensor output_tensor = infer_request.get_output_tensor();
Console.WriteLine("/------- [Output tensor] -------/");
Console.WriteLine("Output tensor type: {0}", output_tensor.get_element_type().to_string());
Console.WriteLine("Output tensor shape: {0}", output_tensor.get_shape().to_string());
Console.WriteLine("Output tensor size: {0}", output_tensor.get_size());
獲取輸出 output_tensor 信息為:
/------- [Output tensor] -------/
Output tensor type: f32
Output tensor shape: Shape : {1, 1000}
Output tensor size: 1000
對(duì)于輸出 Tensor,我們只需要讀取輸出內(nèi)存上的數(shù)據(jù)即可,此處我們封裝了 get_data
float[] result = output_tensor.get_data<float>(1000);
同樣獲取推理結(jié)果,在 C++ 中的代碼為:
const float* output_data = output_tensor.data<const float>();
float result[1000];
for (int i = 0; i < 1000; ++i) {
result[i] = *output_data;
output_data++;
}
在獲取結(jié)果后,后續(xù)的處理需要根據(jù)模型的輸出類型做相應(yīng)的處理。
10
釋放分配的內(nèi)存
由于 C# 在封裝時(shí)采用的 C API接口實(shí)現(xiàn)的,因此在 C# 中會(huì)產(chǎn)生較多的非托管內(nèi)存,若該對(duì)象出現(xiàn)循環(huán)重復(fù)創(chuàng)建,會(huì)導(dǎo)致過多的內(nèi)存未釋放導(dǎo)致內(nèi)存泄漏,因此對(duì)于臨時(shí)創(chuàng)建的對(duì)象在使用后要即使銷毀,銷毀方式也較為簡(jiǎn)單,只需要調(diào)用對(duì)象的 dispose() 方法即可。
output_tensor.dispose();
input_shape.dispose();
infer_request.dispose();
compiled_model.dispose();
input.dispose();
output.dispose();
model.dispose();
core.dispose();
11
Yolov8 分類模型示例
下面代碼展示了 Yolov8 分類模型使用 OpenVINO C# API API 方法部署模型的完整代碼:
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenVinoSharp;
using System.Data;
using System.Runtime.InteropServices;
namespace test_openvino_csharp_api
{
internal class Program
{
static void Main(string[] args)
{
string model_path = "E:\GitSpace\ OpenVINO-CSharp-API \model\yolov8\yolov8s-cls.xml";
Core core = new Core(); // 初始化推理核心
Model model = core.read_model(model_path); // 讀取本地模型
CompiledModel compiled_model = core.compile_model(model, "AUTO"); // 便喲模型到指定設(shè)備
// 獲取模型的輸入輸出信息
Console.WriteLine("Model name: {0}", model.get_friendly_name());
Input input = compiled_model.input();
Console.WriteLine("/------- [In] -------/");
Console.WriteLine("Input name: {0}", input.get_any_name());
Console.WriteLine("Input type: {0}", input.get_element_type().to_string());
Console.WriteLine("Input shape: {0}", input.get_shape().to_string());
Output output = compiled_model.output();
Console.WriteLine("/------- [Out] -------/");
Console.WriteLine("Output name: {0}", output.get_any_name());
Console.WriteLine("Output type: {0}", output.get_element_type().to_string());
Console.WriteLine("Output shape: {0}", output.get_shape().to_string());
// 創(chuàng)建推理請(qǐng)求
InferRequest infer_request = compiled_model.create_infer_request();
// 獲取輸入張量
Tensor input_tensor = infer_request.get_input_tensor();
Console.WriteLine("/------- [Input tensor] -------/");
Console.WriteLine("Input tensor type: {0}", input_tensor.get_element_type().to_string());
Console.WriteLine("Input tensor shape: {0}", input_tensor.get_shape().to_string());
Console.WriteLine("Input tensor size: {0}", input_tensor.get_size());
// 讀取并處理輸入數(shù)據(jù)
Mat image = Cv2.ImRead(@"E:GitSpace OpenVINO-CSharp-API datasetimagedemo_7.jpg");
Mat input_mat = new Mat();
input_mat = CvDnn.BlobFromImage(image, 1.0 / 255.0, new Size(224, 224), 0, true, false);
// 加載推理數(shù)據(jù)
Shape input_shape = input_tensor.get_shape();
long channels = input_shape[1];
long height = input_shape[2];
long width = input_shape[3];
float[] input_data = new float[channels * height * width];
Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
input_tensor.set_data(input_data);
// 模型推理
infer_request.infer();
// 獲取輸出張量
Tensor output_tensor = infer_request.get_output_tensor();
Console.WriteLine("/------- [Output tensor] -------/");
Console.WriteLine("Output tensor type: {0}", output_tensor.get_element_type().to_string());
Console.WriteLine("Output tensor shape: {0}", output_tensor.get_shape().to_string());
Console.WriteLine("Output tensor size: {0}", output_tensor.get_size());
// 獲取輸出數(shù)據(jù)
float[] result = output_tensor.get_data<float>(1000);
List<float[]> new_list = new List<float[]> { };
for (int i = 0; i < result.Length; i++)
{
new_list.Add(new float[] { (float)result[i], i });
}
new_list.Sort((a, b) => b[0].CompareTo(a[0]));
KeyValuePair<int, float>[] cls = new KeyValuePair<int, float>[10];
for (int i = 0; i < 10; ++i)
{
cls[i] = new KeyValuePair<int, float>((int)new_list[i][1], new_list[i][0]);
}
Console.WriteLine("
Classification Top 10 result :
");
Console.WriteLine("classid probability");
Console.WriteLine("------- -----------");
for (int i = 0; i < 10; ++i)
{
Console.WriteLine("{0} {1}", cls[i].Key.ToString("0"), cls[i].Value.ToString("0.000000"));
}
// 銷毀非托管內(nèi)存
output_tensor.dispose();
input_shape.dispose();
infer_request.dispose();
compiled_model.dispose();
input.dispose();
output.dispose();
model.dispose();
core.dispose();
}
}
}
12
總結(jié)
在本文中我們基于模型推理流程,演示了 OpenVINO C# API 使用方法,并和 OpenVINO C++API 進(jìn)行了對(duì)比,展示了 OpenVINO C# API 與 C++API 在使用的區(qū)別,這也對(duì)使用過 C++ API 的開發(fā)者十分友好,上手會(huì)十分容易。
在本文中我們只展示了基礎(chǔ)的模型推理流程代碼,也對(duì)各個(gè) API 進(jìn)行了測(cè)試,針對(duì)其他比較高級(jí)的 API 方法,我們后續(xù)會(huì)繼續(xù)進(jìn)行測(cè)試其他 API 方法,向各位開發(fā)者展示其用法。
總的來(lái)說(shuō),目前 OpenVINO C# API 已經(jīng)完全支持在 Windows 環(huán)境下的安裝使用,歡迎各位開發(fā)者安裝使用,如有相關(guān)問題或優(yōu)化方法,也歡迎大家提出意見與指導(dǎo)。
-
API
+關(guān)注
關(guān)注
2文章
1559瀏覽量
63505 -
編程語(yǔ)言
+關(guān)注
關(guān)注
10文章
1955瀏覽量
36042 -
C++
+關(guān)注
關(guān)注
22文章
2117瀏覽量
74765
原文標(biāo)題:OpenVINO? C# API 詳解與演示 | 開發(fā)者實(shí)戰(zhàn)
文章出處:【微信號(hào):英特爾物聯(lián)網(wǎng),微信公眾號(hào):英特爾物聯(lián)網(wǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
C#集成OpenVINO?:簡(jiǎn)化AI模型部署

OpenVINO C#如何運(yùn)行YOLO11實(shí)例分割模型

OpenVINO Java API詳解與演示

如何使用OpenVINO C++ API部署FastSAM模型

在Visual Studio中使用OpenVINO? C API時(shí)無(wú)法讀取網(wǎng)絡(luò)怎么解決?
c#源碼_C# 2008源代碼案例下載

C#平臺(tái)調(diào)用OpenVINO的可行性
OpenVINO工具套件預(yù)處理API的概念及使用方法
用OpenVINO? C++ API編寫YOLOv8-Seg實(shí)例分割模型推理程序

基于OpenVINO Python API部署RT-DETR模型

基于OpenVINO C++ API部署RT-DETR模型

基于OpenVINO C# API部署RT-DETR模型

用OpenVINO C# API在intel平臺(tái)部署YOLOv10目標(biāo)檢測(cè)模型

使用OpenVINO C# API部署YOLO-World實(shí)現(xiàn)實(shí)時(shí)開放詞匯對(duì)象檢測(cè)

C#中使用OpenVINO?:輕松集成AI模型!

評(píng)論