YOlOv8 ONNXRunTime-gpu 推理

YOlOv8 ONNXRunTime-gpu 推理

_

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的约瑟夫-雷德蒙(Joseph Redmon)和阿里-法哈迪(Ali Farhadi)开发。YOLO 于 2015 年推出,因其高速度和高精确度而迅速受到欢迎。

YOLOv8是YOLO 的最新版本,由Ultralytics 提供。YOLOv8 YOLOv8 支持全方位的视觉 AI 任务,包括检测、分割、姿态估计、跟踪和分类。这种多功能性使用户能够在各种应用和领域中利用YOLOv8 的功能。

根据YOLOv8官方文档介绍,通过使用model.export()将.pt文件转为.onnx文件。

from ultralytics import YOLO

# Load a model

model = YOLO('yolov8n.pt') # load an official model

model = YOLO('path/to/best.pt') # load a custom trained model

# Export the model

model.export(format='onnx')

YOLOv8预测目标检测模型

from ultralytics import YOLO

# Load a pretrained YOLOv8n model

model = YOLO('yolov8n.pt')

# Run inference on 'bus.jpg' with arguments

model.predict('bus.jpg', save=True, imgsz=320, conf=0.5)

将yolov8目标检测过程转移到C#中,调用GPU处理目标图像需提前引用一下模块:

using System;

using Microsoft.ML.OnnxRuntime.Tensors;

using Microsoft.ML.OnnxRuntime;

using OpenCvSharp;

using System.Runtime.InteropServices;

using Cv = OpenCvSharp.Cv2;

using System.Diagnostics;

将检测模型转到GPU中推理

public OnnxRuntimeTest(string OnnxPATH, string DetectSavePath)

{

int gpuDeviceId = 0; // The GPU device ID to execute on

using var gpuSessionOptoins = SessionOptions.MakeSessionOptionWithCudaProvider(gpuDeviceId);

DetectSurface = new InferenceSession(OnnxPATH, gpuSessionOptoins);

SavePath = DetectSavePath;

}

定义模型参数:

private int DetectBatch = 1;

private int DetectChannl = 1;

private int DetectWith = 512;

private int DetectHeight = 512;

结合Opencv修改图片:

List<Predict> out_res = new List<Predict>();

//int class_max = 80;

Mat mat_ori = Cv2.ImRead(readimg);

int width = mat_ori.Width;

int height = mat_ori.Height;

//double r = Math.Min(640.0 / width, 640.0 / height);

//int scale_w = Convert.ToInt32(width r) / 2 2;

//int scale_h = Convert.ToInt32(height r) / 2 2;

//int dw = (int)((640 - scale_w) / 2f);

//int dh = (int)((640 - scale_h) / 2f);

Mat mat = new Mat();

Cv.Resize(mat_ori, mat, new Size(512, 512));

//Cv2.CopyMakeBorder(mat, mat, dh, dh, dw, dw, BorderTypes.Constant, new Scalar(114, 114, 114));

DetectHeight = mat.Width;

DetectWith = mat.Height;

List<Predict> predicts = new List<Predict>();

// 分类识别

int[] dimensions = {DetectBatch, DetectChannl, DetectHeight, DetectWith };

// int *dimensions :Numpy Array 数组每一维度数据的个数。

int length = DetectChannl DetectHeight DetectWith;

彩色图像通道转换,并行加速赋值

//并行运算加速赋值

Parallel.For(0, total_pixel_count, (p_idx, state) =>

{

int r = p_idx;

int g = r + total_pixel_count;

int b = g + total_pixel_count;

fbuff[r] = buffer[r * 3 + 2] / 255f;

fbuff[g] = buffer[r * 3 + 1] / 255f;

fbuff[b] = buffer[r * 3] / 255f;

});

引入ONNX模型输入,输出参数

Memory<float> memory = new Memory<float>(fbuff);

DenseTensor<float> input = new DenseTensor<float>(memory, dimensions);

string OutputMetadataName = DetectSurface!.OutputNames[0];

string InputMetadataName = DetectSurface!.InputNames[0];

// 将图片传至YOLO模型输入层

var ClassInputs = new List<NamedOnnxValue>()

{

NamedOnnxValue.CreateFromTensor(InputMetadataName, input)

};

float[] res = ClassResults.First(item => item.Name == OutputMetadataName).AsEnumerable<float>().ToArray();

创建模型推理

// 创建计时实例

Stopwatch stopwatch = new Stopwatch();

stopwatch.Start();

IDisposableReadOnlyCollection<DisposableNamedOnnxValue> ClassResults = DetectSurface.Run(ClassInputs);

stopwatch.Stop();

// 计算时差

TimeSpan elapsed = stopwatch.Elapsed;

Console.WriteLine("runTime:" + elapsed.TotalMilliseconds + "ms");

获取模型推理结果,遍历检测参数,筛选符合条件(符合置信度要求)的目标:

double maxVal = 0;

int Index0 = 0;

for (int j = 4; j < length0; j++)

{

if (classValue[0, j, i] > 0.25f && classValue[0, j, i] > maxVal)

{

maxVal = classValue[0, j, i];

Index0 = j;

}

}

将符合检测要求的目标框,添加至预测坐标中

if (Index0 != 0)

{

predicts.Add(new()

{

CenterX = (int)classValue[0, 0, i],

CenterY = (int)classValue[0, 1, i],

Width = (int)classValue[0, 2, i],

Height = (int)classValue[0, 3, i],

Class = Index0 - 4,

Confidence = classValue[0, Index0, i]

});

}

显示检测结果,获取检测目标框,输出检测框,打印检测标签,保存检测结果为.bmp(其它格式文件会降低算法执行保存速率,其它格式需要系统会进行压缩,影响执行速率)

if (have)

{

int a = 0;

foreach (Predict emu_pre in predicts)

{

//if (x_max < emu_pre.CenterX)

//{

// x_max = emu_pre.CenterX;

// i_max = a;

//}

int widthmax = (int)(width / 512f * predicts[a].Width);

int heightmax = (int)(height / 512f * predicts[a].Height);

int centerx = (int)(width / 512f * predicts[a].CenterX);

int centery = (int)(height / 512f * (predicts[a].CenterY));

// 检测目标坐标提取

Point point1 = new Point(centerx - (widthmax / 2), centery - (heightmax / 2));

Point point2 = new Point(centerx + (widthmax / 2), centery + (heightmax / 2));

Point point3 = new Point(centerx + (widthmax / 2), centery);

// 输出检测框

Cv.Rectangle(mat_ori, point1, point2, new Scalar(0, 0, 255), 2);

// 打印标签

string outtext = lableName[predicts[a].Class];

Cv.PutText(mat_ori, outtext, point3, HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));

//class_path = SavePath + "class\\" + predicts[a].Class.ToString() + "\\";

a++;

}

class_path = SavePath + "class\\" + predicts[i_max].Class.ToString() + "\\";

if (!Directory.Exists(class_path))

{

//创建文件夹

try

{

Directory.CreateDirectory(class_path);

}

catch (Exception e)

{

}

}

SaveFileName = class_path + data_time + ".bmp";

}

设置检测结果参数信息

public int CenterX { get; set; }

public int CenterY { get; set; }

public int Width { get; set; }

public int Height { get; set; }

public float Confidence { get; set; }

public int Class { get; set; }

public int CompareTo(Predict? other)

{

if (other?.Confidence - Confidence <= 0)

{

return -1;

}

else

{

return 1;

}

}

模型IOU计算

public float IOU(Predict predict)

{

float w_2 = Width / 2;

float h_2 = Height / 2;

float x1 = CenterX - w_2, x2 = CenterX + w_2;

float y1 = CenterY - h_2, y2 = CenterY + h_2;

w_2 = predict.Width / 2;

h_2 = predict.Height / 2;

float x3 = predict.CenterX - w_2, x4 = predict.CenterX + w_2;

float y3 = predict.CenterY - h_2, y4 = predict.CenterY + h_2;

float minX = Math.Max(x1, x3);

float maxX = Math.Min(x2, x4);

float minY = Math.Max(y1, y3);

float maxY = Math.Min(y2, y4);

float w = Math.Max(0, maxX - minX);

float h = Math.Max(0, maxY - minY);

float jiao = w * h;

float bing = Width Height + predict.Width predict.Height - jiao;

return jiao / bing;

}

NMS计算过程

public static bool NMS(IList<Predict> predicts, float ioc_threshold = 0.25f)

{

bool flag = false;

int index = -1;

for (int i = 0; i < predicts.Count; i++)

{

for (int j = i + 1; j < predicts.Count; j++)

{

float iou = predicts[i].IOU(predicts[j]);

if (iou >= ioc_threshold)

{

index = j;

flag = true;

break;

}

}

if (flag)

{

break;

}

}

if (flag)

{

predicts.RemoveAt(index);

}

return flag;

}

完整代码如下

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using Microsoft.ML.OnnxRuntime.Tensors;

using Microsoft.ML.OnnxRuntime;

using OpenCvSharp;

using System.Runtime.InteropServices;

using System.Numerics.Tensors;

using System.Runtime.CompilerServices;

using Cv = OpenCvSharp.Cv2;

using System.Security.Cryptography.X509Certificates;

using Microsoft.VisualBasic;

using static System.Collections.Specialized.BitVector32;

using System.Data.Common;

using System.Data;

using System.Timers;

using System.Diagnostics;

namespace YoloOnnxProject

{

// 创建检测类

public class OnnxRuntimeTest

{

private InferenceSession DetectSurface;

public OnnxRuntimeTest(string OnnxPATH, string DetectSavePath)

{

int gpuDeviceId = 0; // The GPU device ID to execute on

using var gpuSessionOptoins = SessionOptions.MakeSessionOptionWithCudaProvider(gpuDeviceId);

DetectSurface = new InferenceSession(OnnxPATH, gpuSessionOptoins);

SavePath = DetectSavePath;

}

public void Dispose()

{

DetectSurface?.Dispose();

}

// 分类标签

private Dictionary<int, string> lableName = new()

{

{0, "person"} ,

{1, "bicycle"} ,

{2, "car"} ,

{3, "motorcycle"} ,

{4, "airplane"} ,

{5, "bus"},

{6, "train"} ,

//{0, "person"} ,

//{1, "bicycle"} ,

//{2, "car"} ,

//{3, "motorcycle"} ,

//{4, "airplane"} ,

//{5, "bus"},

//{6, "train"} ,

//{7, "truck"},

//{8, "boat"} ,

//{9, "traffic light"} ,

//{10, "fire hydrant"} ,

//{11, "stop sign"} ,

//{12, "parking meter"} ,

//{13, "bench"} ,

//{14, "bird"} ,

//{15, "cat"},

//{16, "dog"},

//{17, "horse"} ,

//{18, "sheep"} ,

//{19, "cow"} ,

//{20, "elephant"} ,

//{21, "bear"},

//{22, "zebra"} ,

//{23, "giraffe"} ,

//{24, "backpack"} ,

//{25, "umbrella"} ,

//{26, "handbag"} ,

//{27, "tie"},

//{28, "suitcase"} ,

//{29, "frisbee"} ,

//{30, "skis"} ,

//{31, "snowboard"} ,

//{32, "sports ball"},

//{33, "kite"} ,

//{34, "baseball bat"},

//{35, "baseball glove"} ,

//{36, "skateboard"},

//{37, "surfboard"} ,

//{38, "tennis racket"} ,

//{39, "bottle"} ,

//{40, "wine glass"} ,

//{41, "cup"} ,

//{42, "fork"} ,

//{43, "knife"} ,

//{44, "spoon"},

//{45, "bowl"} ,

//{46, "banana"} ,

//{47, "apple"} ,

//{48, "sandwich"} ,

//{49, "orange"} ,

//{50, "broccoli"} ,

//{51, "carrot"} ,

//{52, "hot dog"} ,

//{53, "pizza"} ,

//{54, "donut"} ,

//{55, "cake"} ,

//{56, "chair"} ,

//{57, "couch"} ,

//{58, "potted plant"} ,

//{59, "bed"} ,

//{60, "dining table"} ,

//{61, "toilet"} ,

//{62, "tv"} ,

//{63, "laptop"},

//{64, "mouse"} ,

//{65, "remote"} ,

//{66, "keyboard"},

//{67, "cell phone"} ,

//{68, "microwave"} ,

//{69, "oven"} ,

//{70, "toaster"} ,

//{71, "sink"} ,

//{72, "refrigerator"} ,

//{73, "book"} ,

//{74, "clock"} ,

//{75, "vase"},

//{76, "scissors"} ,

//{77, "teddy bear"} ,

//{78, "hair drier"} ,

//{79, "toothbrush"}

};

private int DetectBatch = 1;

private int DetectChannl = 1;

private int DetectWith = 512;

private int DetectHeight = 512;

//private string OnnxPATH;

private string SavePath;

private string SaveFileName;

private string class_path;

public List<Predict> UnionInference(string readimg)

{

List<Predict> out_res = new List<Predict>();

//int class_max = 80;

Mat mat_ori = Cv2.ImRead(readimg);

int width = mat_ori.Width;

int height = mat_ori.Height;

//double r = Math.Min(640.0 / width, 640.0 / height);

//int scale_w = Convert.ToInt32(width r) / 2 2;

//int scale_h = Convert.ToInt32(height r) / 2 2;

//int dw = (int)((640 - scale_w) / 2f);

//int dh = (int)((640 - scale_h) / 2f);

Mat mat = new Mat();

Cv.Resize(mat_ori, mat, new Size(512, 512));

//Cv2.CopyMakeBorder(mat, mat, dh, dh, dw, dw, BorderTypes.Constant, new Scalar(114, 114, 114));

DetectHeight = mat.Width;

DetectWith = mat.Height;

List<Predict> predicts = new List<Predict>();

// 分类识别

int[] dimensions = {DetectBatch, DetectChannl, DetectHeight, DetectWith };

// int *dimensions :Numpy Array 数组每一维度数据的个数。

int length = DetectChannl DetectHeight DetectWith;

byte[] buffer = new byte[length];

Marshal.Copy(mat.Data, buffer, 0, length);

float[] fbuff = new float[length];

int total_pixel_count = mat.Rows * mat.Cols;

//并行运算加速赋值

Parallel.For(0, total_pixel_count, (p_idx, state) =>

{

int r = p_idx;

int g = r + total_pixel_count;

int b = g + total_pixel_count;

fbuff[r] = buffer[r * 3 + 2] / 255f;

fbuff[g] = buffer[r * 3 + 1] / 255f;

fbuff[b] = buffer[r * 3] / 255f;

});

Memory<float> memory = new Memory<float>(fbuff);

DenseTensor<float> input = new DenseTensor<float>(memory, dimensions);

string OutputMetadataName = DetectSurface!.OutputNames[0];

string InputMetadataName = DetectSurface!.InputNames[0];

// 将图片传至YOLO模型输入层

var ClassInputs = new List<NamedOnnxValue>()

{

NamedOnnxValue.CreateFromTensor(InputMetadataName, input)

};

// 创建计时实例

Stopwatch stopwatch = new Stopwatch();

stopwatch.Start();

IDisposableReadOnlyCollection<DisposableNamedOnnxValue> ClassResults = DetectSurface.Run(ClassInputs);

stopwatch.Stop();

// 计算时差

TimeSpan elapsed = stopwatch.Elapsed;

Console.WriteLine("runTime:" + elapsed.TotalMilliseconds + "ms");

float[] res = ClassResults.First(item => item.Name == OutputMetadataName).AsEnumerable<float>().ToArray();

var classValue = (DenseTensor<float>)ClassResults.ToArray().First().Value;

//ClassResults.Dispose();

if (ClassResults?.Any() == true)

{

//int[] dimen = [classValue.Dimensions[0], classValue.Dimensions[2], classValue.Dimensions[1]];

int length1 = classValue.Dimensions[2]; // 8400

int length0 = classValue.Dimensions[1]; // 84

for (int i = 0; i < length1; i++)

{

double maxVal = 0;

int Index0 = 0;

for (int j = 4; j < length0; j++)

{

if (classValue[0, j, i] > 0.25f && classValue[0, j, i] > maxVal)

{

maxVal = classValue[0, j, i];

Index0 = j;

}

}

if (Index0 != 0)

{

predicts.Add(new()

{

CenterX = (int)classValue[0, 0, i],

CenterY = (int)classValue[0, 1, i],

Width = (int)classValue[0, 2, i],

Height = (int)classValue[0, 3, i],

Class = Index0 - 4,

Confidence = classValue[0, Index0, i]

});

}

//float[] arr = new float[] { classValue[0, 4, i], classValue[0, 5, i], classValue[0, 6, i], classValue[0, 7, i] };

//var b = arr.Select((item, indx) => new { Item = item, Index = indx }).OrderByDescending(x => x.Item).Select(x => x.Index).Take(1).ToArray();

阈值

//if (classValue[0, 4, i] >= 0.5f || classValue[0, 5, i] >= 0.5f || classValue[0, 6, i] >= 0.5f || classValue[0, 7, i] >= 0.5f)

//{

// predicts.Add(new()

// {

// CenterX = (int)classValue[0, 0, i],

// CenterY = (int)classValue[0, 1, i],

// Width = (int)classValue[0, 2, i],

// Height = (int)classValue[0, 3, i],

// // ---------------

// Class = b[0],

// Confidence = classValue[0, (4 + b[0]), i]

// });

//}

}

predicts.Sort();

while (Predict.NMS(predicts, 0.25f)) ;

//Stack<int> stack = new Stack<int>();

//for (int i = 0; i < predicts.Count; i++)

//{

// if (predicts[i].Confidence < 0.25f)

// {

// stack.Push(i);

// }

//}

//while (stack.Any())

//{

// int peek = stack.Pop();

// predicts.Remove(predicts[peek]);

//}

}

// class处理部分

int x_max = 0;

int i_max = 0;

bool have = (predicts.Count != 0);

string data_time = System.DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_ffff");

if (have)

{

int a = 0;

foreach (Predict emu_pre in predicts)

{

//if (x_max < emu_pre.CenterX)

//{

// x_max = emu_pre.CenterX;

// i_max = a;

//}

int widthmax = (int)(width / 512f * predicts[a].Width);

int heightmax = (int)(height / 512f * predicts[a].Height);

int centerx = (int)(width / 512f * predicts[a].CenterX);

int centery = (int)(height / 512f * (predicts[a].CenterY));

// 检测目标坐标提取

Point point1 = new Point(centerx - (widthmax / 2), centery - (heightmax / 2));

Point point2 = new Point(centerx + (widthmax / 2), centery + (heightmax / 2));

Point point3 = new Point(centerx + (widthmax / 2), centery);

// 输出检测框

Cv.Rectangle(mat_ori, point1, point2, new Scalar(0, 0, 255), 2);

// 打印标签

string outtext = lableName[predicts[a].Class];

Cv.PutText(mat_ori, outtext, point3, HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));

//class_path = SavePath + "class\\" + predicts[a].Class.ToString() + "\\";

a++;

}

//int widthmax = (int)(width / 640f * predicts[i_max].Width);

//int heightmax = (int)(width / 640f * predicts[i_max].Height);

//int centerx = (int)(width / 640f * predicts[i_max].CenterX);

//int centery = (int)(width / 640f * (predicts[i_max].CenterY - dh));

检测目标坐标提取

//Point point1 = new Point(centerx - (widthmax / 2), centery - (heightmax / 2));

//Point point2 = new Point(centerx + (widthmax / 2), centery + (widthmax / 2));

输出检测框

//Cv.Rectangle(mat_ori, point1, point2, new Scalar(0, 0, 255), 2);

打印标签

//string outtext = lableName[predicts[i_max].Class];

//Cv.PutText(mat_ori, outtext, point1, HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255));

// 裁剪检测目标

//Mat cropped = mat_ori[centery - heightmax / 2, centery + heightmax / 2, centerx - widthmax / 2, centerx + widthmax / 2];

//UTC时间

//转北京时间,毫秒精确后两位

// 保存检测目标

class_path = SavePath + "class\\" + predicts[i_max].Class.ToString() + "\\";

if (!Directory.Exists(class_path))

{

//创建文件夹

try

{

Directory.CreateDirectory(class_path);

}

catch (Exception e)

{

}

}

SaveFileName = class_path + data_time + ".jpg";

//Cv2.ImWrite(SaveFileName, mat_ori);

//class_max = predicts[i_max].Class;

}

else

{

class_path = SavePath + "NoneObject\\" + "\\";

if (!Directory.Exists(class_path))

{

//创建文件夹

try

{

Directory.CreateDirectory(class_path);

}

catch (Exception e)

{

}

}

SaveFileName = class_path + data_time + ".jpg";

//Cv2.ImWrite(SaveFileName, mat_ori);

//Predict re_lable = outputLable(SaveFileName);

}

return out_res;

}

public class Predict : IComparable<Predict>

{

public int CenterX { get; set; }

public int CenterY { get; set; }

public int Width { get; set; }

public int Height { get; set; }

public float Confidence { get; set; }

public int Class { get; set; }

public int CompareTo(Predict? other)

{

if (other?.Confidence - Confidence <= 0)

{

return -1;

}

else

{

return 1;

}

}

public float IOU(Predict predict)

{

float w_2 = Width / 2;

float h_2 = Height / 2;

float x1 = CenterX - w_2, x2 = CenterX + w_2;

float y1 = CenterY - h_2, y2 = CenterY + h_2;

w_2 = predict.Width / 2;

h_2 = predict.Height / 2;

float x3 = predict.CenterX - w_2, x4 = predict.CenterX + w_2;

float y3 = predict.CenterY - h_2, y4 = predict.CenterY + h_2;

float minX = Math.Max(x1, x3);

float maxX = Math.Min(x2, x4);

float minY = Math.Max(y1, y3);

float maxY = Math.Min(y2, y4);

float w = Math.Max(0, maxX - minX);

float h = Math.Max(0, maxY - minY);

float jiao = w * h;

float bing = Width Height + predict.Width predict.Height - jiao;

return jiao / bing;

}

public static bool NMS(IList<Predict> predicts, float ioc_threshold = 0.25f)

{

bool flag = false;

int index = -1;

for (int i = 0; i < predicts.Count; i++)

{

for (int j = i + 1; j < predicts.Count; j++)

{

float iou = predicts[i].IOU(predicts[j]);

if (iou >= ioc_threshold)

{

index = j;

flag = true;

break;

}

}

if (flag)

{

break;

}

}

if (flag)

{

predicts.RemoveAt(index);

}

return flag;

}

}

}

}

main.cs

using OpenCvSharp;

using System;

using System.Diagnostics;

using System.Drawing;

using static YoloOnnxProject.OnnxRuntimeTest;

using Cv = OpenCvSharp.Cv2;

namespace YoloOnnxProject

{

internal class Program

{

static void Main(string[] args)

{

// Onnx文件地址

string OnnxPATH = @"E:\DeskTop\Surface_detect\best.onnx";

// 检测输出文件地址

string imgSavePath = @"E:\DeskTop\Surface_detect\runs\detect1\";

// 调用算法

OnnxRuntimeTest OnnxYoloV8 = new OnnxRuntimeTest(OnnxPATH, imgSavePath);

// 检测目标地址

string imgPath = @"E:\DeskTop\Surface_detect\Annotation\val\images\";

int imgnum = 0;

// 遍历图片

foreach (string img in Directory.GetFiles(imgPath))

{

// 创建记时实例

Stopwatch stopwatch = new Stopwatch();

string readimg = img;

// 开始记时

stopwatch.Start();

List<Predict> predicts = OnnxYoloV8.UnionInference(readimg);

// 结束记时

stopwatch.Stop();

// 输出时间

TimeSpan timeSpan = stopwatch.Elapsed;

//Console.WriteLine("**********************");

//Console.WriteLine("第 " + imgnum + " 张图像" + "程序执行时间:" + timeSpan.TotalMilliseconds + "ms");

imgnum++;

}

Cv.DestroyAllWindows();

}

}

ONNX 优化技巧:加速模型推理 2025-01-15