c# 使用yolov5模型

1. 训练模型

在python中用yolo v5 训练自己的数据集,得到best.pt

参考:

Yolov5训练自己的数据集(详细完整版)_yolov5缔宇-CSDN博客

在python中使用此模型:

python 复制代码
import torch
from models.experimental import attempt_load
from utils.general import non_max_suppression, scale_boxes
from utils.plots import Annotator, colors
import cv2

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = attempt_load('runs/train/exp/weights/best.pt').to(device)  # 加载模型
stride = int(model.stride.max())  # 模型步长(用于缩放坐标)
names = model.module.names if hasattr(model, 'module') else model.names  # 类别名称


img = cv2.imread("../ultralytics-main/dataset/images/val/camera0_20250611143415545667.jpg_0.0.png")
img_resize = cv2.resize(img,(640,640))
img_tensor = torch.from_numpy(img_resize).to(device).float() / 255.0  # 归一化
img_tensor = img_tensor.permute(2, 0, 1).unsqueeze(0)  # HWC → CHW,添加 batch 维度

#  推理
with torch.no_grad():
    pred = model(img_tensor)[0]  # 推理

# 后处理:NMS + 坐标缩放
pred = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45)  # NMS
annotator = Annotator(img_resize, line_width=2, example=str(names))  # 初始化标注器

# 绘制检测结果
for det in pred:  # 遍历每张图片的检测结果(batch=1 时只有 1 个 det)
    if len(det):
        det[:, :4] = scale_boxes(img_tensor.shape[2:], det[:, :4], img_resize.shape[:2]).round()  # 缩放坐标到原图
        for *xyxy, conf, cls in reversed(det):  # xyxy: 边界框坐标, conf: 置信度, cls: 类别 ID
            label = f'{names[int(cls)]} {conf:.2f}'
            annotator.box_label(xyxy, label, color=colors(int(cls)))  # 绘制边界框和标签

cv2.imshow("result",img_resize)
cv2.waitKey(0)

这里用一个比较简单的划痕污点检测模型,进行测试。

dataset:

2.模型转换

.pt是python格式,需要转换成.onnx。

pip install onnx

python 复制代码
import torch
import onnx
# 加载 YOLOv5 模型
model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt')

# 转换为 ONNX 格式
dummy_input = torch.randn(1, 3, 640, 640)
print("开始转换")


opset_version = 12

torch.onnx.export(
    model,
    dummy_input,
    "best.onnx",
    input_names=['images'],
    output_names=['output'],
    opset_version=opset_version,
    dynamic_axes=None  
)
print("转换完成")

得到best.onnx

3.在c#中使用此模型。

创建 winform 项目 test---yolo

在nuget中 添加Microsoft.ML.OnnxRuntime

项目属性,生成,取消 首选32

界面设计:

主要方法:

  1. test_yolov5 - 主流程控制

  2. LoadImage - 图像预处理

  3. ProcessOutput - 模型输出解析

  4. NonMaxSuppression - 非极大值抑制

  5. DrawPredictions - 结果可视化

代码:

cs 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Drawing.Imaging;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;

namespace test_yolo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button_start_Click(object sender, EventArgs e)
        {
            //使用 ONNX 格式的模型
            //string modelPath = "best.onnx";
            //string imagePath = "test.png";


            Image result = yolo_Program.test_yolov5(textBox_model.Text, textBox_img.Text);
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
            pictureBox1.Image = result;
        }

        private void button_img_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Title = "选择文件",
                Filter = "所有文件 (*.*)|*.*",
                //InitialDirectory = @"C:\"
            };

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                // 获取仅包含文件名的字符串
                string fileName = openFileDialog.SafeFileName;
                textBox_img.Text = fileName;
            }
        }

        private void button_model_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Title = "选择文件",
                Filter = "所有文件 (*.*)|*.*",
                //InitialDirectory = @"C:\"
            };

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                // 获取仅包含文件名的字符串
                string fileName = openFileDialog.SafeFileName;
                textBox_model.Text = fileName;
            }

        }
    }

    class yolo_Program
    {
        // 检测类别名称
        private static readonly string[] classNames = new[] {
            "A","B","C","D","E","F","G","H","I","J"
        };

        public static Image test_yolov5(string modelPath, string imagePath)
        {
            try
            {


                // 检查文件是否存在
                if (!System.IO.File.Exists(modelPath))
                {
                    MessageBox.Show($"模型文件不存在: {modelPath}");
                    return null;
                }

                if (!System.IO.File.Exists(imagePath))
                {
                    MessageBox.Show($"图像文件不存在: {imagePath}");
                    return null;
                }

                // 设置 ONNX Runtime 选项
                var options = new SessionOptions();

                // 根据您的需求选择合适的执行提供程序
                // 对于 CPU:
                options.AppendExecutionProvider_CPU();

                // 如果支持 GPU,可以添加 CUDA 提供程序(需要安装相应的包)
                // options.AppendExecutionProvider_CUDA();

                // 加载模型
                var session = new InferenceSession(modelPath, options);

                // 加载并预处理图像
                var (inputTensor, originalWidth, originalHeight) = LoadImage(imagePath);

                // 准备输入参数
                var inputs = new List<NamedOnnxValue>
                {
                    NamedOnnxValue.CreateFromTensor("images", inputTensor)
                };

                // 运行模型推理
                var results = session.Run(inputs);

                // 处理输出结果
                var outputTensor = results.First().AsTensor<float>();
                var predictions = ProcessOutput(outputTensor, originalWidth, originalHeight);

                // 绘制检测结果
               Image result =  DrawPredictions(imagePath, predictions, "output.jpg");

                // MessageBox.Show($"检测完成,结果已保存至 output.jpg\n检测到 {predictions.Count} 个目标");

                return result;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"错误: {ex.Message}\n{ex.StackTrace}");
                return null;
            }
        }

        // 加载并预处理图像
        private static (DenseTensor<float> tensor, int width, int height) LoadImage(string imagePath)
        {
            var image = Image.FromFile(imagePath);
            int originalWidth = image.Width;
            int originalHeight = image.Height;

            // 调整图像大小为模型输入尺寸
            var resizedImage = ResizeImage(image, 640, 640);

            // 图像转张量
            var tensor = new DenseTensor<float>(new[] { 1, 3, 640, 640 });

            using (var bitmap = new Bitmap(resizedImage))
            {
                for (int y = 0; y < bitmap.Height; y++)
                {
                    for (int x = 0; x < bitmap.Width; x++)
                    {
                        var pixel = bitmap.GetPixel(x, y);

                        // YOLOv5 使用 RGB 顺序,归一化到 0-1
                        tensor[0, 0, y, x] = pixel.R / 255.0f; // R
                        tensor[0, 1, y, x] = pixel.G / 255.0f; // G
                        tensor[0, 2, y, x] = pixel.B / 255.0f; // B
                    }
                }
            }

            return (tensor, originalWidth, originalHeight);
        }

        // 调整图像大小
        private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var destRect = new Rectangle(0, 0, width, height);
            var destImage = new Bitmap(width, height);

            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (var graphics = Graphics.FromImage(destImage))
            {
                graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

                using (var wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
                    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }

            return destImage;
        }

        // 处理模型输出
        private static List<Prediction> ProcessOutput(Tensor<float> output, int originalWidth, int originalHeight)
        {
            const float confidenceThreshold = 0.5f;
            const float nmsThreshold = 0.4f;

            var predictions = new List<Prediction>();

            // YOLOv5 ONNX 模型的输出格式通常是 [1, 25200, 85]
            // 其中 85 = [x, y, w, h, confidence, class_probabilities...]
            int numDetections = output.Dimensions[1];
            int numClasses = output.Dimensions[2] - 5;

            for (int i = 0; i < numDetections; i++)
            {
                float confidence = output[0, i, 4];

                if (confidence >= confidenceThreshold)
                {
                    // 找到概率最高的类别
                    int classId = 0;
                    float maxClassProb = 0;

                    for (int c = 0; c < numClasses; c++)
                    {
                        float classProb = output[0, i, 5 + c];
                        if (classProb > maxClassProb)
                        {
                            maxClassProb = classProb;
                            classId = c;
                        }
                    }

                    float score = confidence * maxClassProb;

                    if (score >= confidenceThreshold)
                    {
                        // 获取边界框坐标(相对于 640x640)
                        float cx = output[0, i, 0];
                        float cy = output[0, i, 1];
                        float w = output[0, i, 2];
                        float h = output[0, i, 3];

                        // 转换为绝对坐标
                        float x1 = (cx - w / 2) * originalWidth / 640;
                        float y1 = (cy - h / 2) * originalHeight / 640;
                        float x2 = (cx + w / 2) * originalWidth / 640;
                        float y2 = (cy + h / 2) * originalHeight / 640;

                        predictions.Add(new Prediction
                        {
                            ClassId = classId,
                            Score = score,
                            BBox = new RectangleF(x1, y1, x2 - x1, y2 - y1)
                        });
                    }
                }
            }

            // 非极大值抑制
            return NonMaxSuppression(predictions, nmsThreshold);
        }

        // 非极大值抑制
        private static List<Prediction> NonMaxSuppression(List<Prediction> predictions, float threshold)
        {
            var result = new List<Prediction>();
            predictions = predictions.OrderByDescending(p => p.Score).ToList();

            while (predictions.Count > 0)
            {
                var best = predictions[0];
                result.Add(best);
                predictions.RemoveAt(0);

                predictions = predictions.Where(p =>
                {
                    float iou = CalculateIoU(best.BBox, p.BBox);
                    return iou < threshold;
                }).ToList();
            }

            return result;
        }

        // 计算 IoU
        private static float CalculateIoU(RectangleF box1, RectangleF box2)
        {
            float x1 = Math.Max(box1.Left, box2.Left);
            float y1 = Math.Max(box1.Top, box2.Top);
            float x2 = Math.Min(box1.Right, box2.Right);
            float y2 = Math.Min(box1.Bottom, box2.Bottom);

            float intersection = Math.Max(0, x2 - x1) * Math.Max(0, y2 - y1);
            float area1 = box1.Width * box1.Height;
            float area2 = box2.Width * box2.Height;

            return intersection / (area1 + area2 - intersection);
        }

        // 绘制预测结果
        private static Image DrawPredictions(string inputImagePath, List<Prediction> predictions, string outputImagePath)
        {
            var image = Image.FromFile(inputImagePath);
            var graphics = Graphics.FromImage(image);

            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            var colors = new[]
            {
                Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange,
                Color.Purple, Color.Cyan, Color.Magenta, Color.Lime, Color.Pink
            };

            foreach (var prediction in predictions)
            {
                var bbox = prediction.BBox;
                var color = colors[prediction.ClassId % colors.Length];
                var label = $"{classNames[prediction.ClassId]}: {prediction.Score:F2}";

                var pen = new Pen(color, 3);
                graphics.DrawRectangle(pen, bbox.X, bbox.Y, bbox.Width, bbox.Height);

                var font = new Font("Arial", 12, FontStyle.Bold);
                var brush = new SolidBrush(color);
                var textBrush = new SolidBrush(Color.White);

                var textSize = graphics.MeasureString(label, font);
                var textBackground = new RectangleF(bbox.X, bbox.Y - textSize.Height, textSize.Width, textSize.Height);

                graphics.FillRectangle(brush, textBackground);
                graphics.DrawString(label, font, textBrush, bbox.X, bbox.Y - textSize.Height);
            }
            //保存
           // image.Save(outputImagePath, ImageFormat.Jpeg);

            return image;
        }



    }

    // 预测结果类
    public class Prediction
    {
        public int ClassId { get; set; }
        public float Score { get; set; }
        public RectangleF BBox { get; set; }
    }
}

运行后:

相关推荐
PHOSKEY3 小时前
应用案例丨3D工业相机如何实现「焊接全工序守护」
人工智能
喜欢吃豆3 小时前
从指令到智能:大型语言模型提示词工程与上下文工程的综合分析
人工智能·语言模型·自然语言处理·大模型·提示词工程·上下文工程
七元权3 小时前
论文阅读-FoundationStereo
论文阅读·深度学习·计算机视觉·零样本·基础模型·双目深度估计
Fuly10243 小时前
prompt构建技巧
人工智能·prompt
智驱力人工智能3 小时前
使用手机检测的智能视觉分析技术与应用 加油站使用手机 玩手机检测
深度学习·算法·目标检测·智能手机·视觉检测·边缘计算
XXX-X-XXJ4 小时前
二:RAG 的 “语义密码”:向量、嵌入模型与 Milvus 向量数据库实操
人工智能·git·后端·python·django·milvus
艾醒(AiXing-w)4 小时前
探索大语言模型(LLM):大模型微调方式全解析
人工智能·语言模型·自然语言处理
科兴第一吴彦祖4 小时前
基于Spring Boot + Vue 3的乡村振兴综合服务平台
java·vue.js·人工智能·spring boot·推荐算法
姚瑞南4 小时前
【AI 风向标】四种深度学习算法(CNN、RNN、GAN、RL)的通俗解释
人工智能·深度学习·算法