Files
CheckDevice/Check.Main/Infer/YoloDetector.cs

410 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Check.Main.Camera;
using Check.Main.Common;
using OpenCvSharp;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using YoloDotNet;
using YoloDotNet.Models;
namespace Check.Main.Infer
{
/// <summary>
/// YOLO 检测结果对象,包含标签、置信度与检测框
/// </summary>
public class YoloPrediction
{
public YoloLabel Label { get; set; }
public float Score { get; set; }
public BoundingBox BoundingBox { get; set; }
}
/// <summary>
/// YOLO 类别标签
/// </summary>
public class YoloLabel
{
public string Name { get; set; }
}
/// <summary>
/// 检测框坐标结构体
/// </summary>
public struct BoundingBox
{
public float Left { get; set; }
public float Top { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public BoundingBox(float left, float top, float width, float height)
{
Left = left;
Top = top;
Width = width;
Height = height;
}
}
/// <summary>
/// YOLO 目标检测器实现类
/// </summary>
public class YoloDetector : IDetector, IDisposable
{
private Yolo _yoloModel;
private int _modelID;
private float _confidenceThreshold = 0.25f;
private float _nmsThreshold = 0.45f;
/// <summary>
/// 初始化 YOLO 检测器并加载模型
/// </summary>
/// <param name="modelIdStr">模型 ID 字符串</param>
/// <param name="detectionSettings">可选检测参数</param>
/// <exception cref="ArgumentException">ID 无效</exception>
/// <exception cref="InvalidOperationException">模型未加载</exception>
public void Initialize(string modelIdStr, object detectionSettings = null)
{
if (string.IsNullOrWhiteSpace(modelIdStr))
throw new ArgumentException("模型ID字符串不能为空。", nameof(modelIdStr));
if (!int.TryParse(modelIdStr, out _modelID))
throw new ArgumentException("模型ID必须为有效整数。", nameof(modelIdStr));
_yoloModel = YoloModelManager.GetModel(_modelID)
?? throw new InvalidOperationException($"YOLO 模型 (ID: {_modelID}) 未加载或找不到。");
if (detectionSettings is YoloDetectionSettings yoloSettings)
{
_confidenceThreshold = Math.Clamp(yoloSettings.ConfidenceThreshold, 0f, 1f);
_nmsThreshold = Math.Clamp(yoloSettings.NmsThreshold, 0f, 1f);
}
}
/// <summary>
/// 执行检测,判断是否含有 logo 类对象
/// </summary>
public DetectionResult Detect(Bitmap image)
{
if (_yoloModel == null)
throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
if (image == null)
return new DetectionResult(false, "输入图像为空。");
try
{
using var skBitmap = CameraProcessor.ToSKBitmapFast(image);
if (skBitmap == null)
return new DetectionResult(false, "图像转换失败。");
using var skImage = SKImage.FromBitmap(skBitmap);
if (skImage == null)
return new DetectionResult(false, "无法生成 SKImage。");
var predictions = _yoloModel.RunObjectDetection(
skImage,
confidence: _confidenceThreshold,
iou: _nmsThreshold
);
// 检查是否检测到 logo
bool foundLogo = predictions.Any(p =>
p.Label.Name.Equals("logo", StringComparison.OrdinalIgnoreCase));
return foundLogo
? new DetectionResult(false, "NG")
: new DetectionResult(true, "OK");
}
catch (Exception ex)
{
return new DetectionResult(false, $"检测失败: {ex.Message}");
}
}
/// <summary>
/// 在图像上绘制检测框与标签
/// </summary>
public Bitmap DrawYoloPredictions(Bitmap source, IEnumerable<YoloPrediction> predictions)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (predictions == null || !predictions.Any())
return (Bitmap)source.Clone();
Bitmap output = (Bitmap)source.Clone();
using var graphics = Graphics.FromImage(output);
using var pen = new Pen(Color.Yellow, 2);
using var font = new Font("Arial", 10, FontStyle.Bold);
using var brush = new SolidBrush(Color.Yellow);
foreach (var pred in predictions)
{
var box = pred.BoundingBox;
var rect = new RectangleF(box.Left, box.Top, box.Width, box.Height);
// 绘制检测框
graphics.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
// 绘制标签
string label = $"{pred.Label?.Name ?? "unknown"} ({pred.Score:P1})";
graphics.DrawString(label, font, brush, rect.X, rect.Y - 15);
}
return output;
}
/// <summary>
/// 无需显式释放模型,资源由 YoloModelManager 管理
/// </summary>
public void Dispose()
{
//_yoloModel = null;
}
}
}
//总结区分好YoloModelManager.cs和YoloDetector.cs各自的职能谁负责模型管理谁负责yolo算法的执行现在这两个文件是交织在一起比较乱的有时间去处理一下
//看上面的定义的一些结构体胡总和类,就可以调用啦
//using Check.Main.Camera;
//using Check.Main.Common;
//using SkiaSharp;
//using System;
//using System.Collections.Generic;
//using System.Drawing;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//using YoloDotNet;
//using YoloDotNet.Models;
//namespace Check.Main.Infer
//{
// public class YoloDetector : IDetector
// {
// private Yolo _yoloModel;
// private int _modelID; // 需要知道模型ID来从 YoloModelManager 获取
// private float _confidenceThreshold = 0.25f;
// private float _nmsThreshold = 0.45f;
// public void Initialize(string modelIdStr, object detectionSettings = null)
// {
// if (!int.TryParse(modelIdStr, out _modelID))
// {
// throw new ArgumentException("YoloDetector 初始化需要有效的模型ID字符串。", nameof(modelIdStr));
// }
// _yoloModel = YoloModelManager.GetModel(_modelID);
// if (_yoloModel == null)
// {
// throw new InvalidOperationException($"YOLO 模型 (ID: {_modelID}) 未加载或找不到。");
// }
// if (detectionSettings is YoloDetectionSettings yoloSettings)
// {
// _confidenceThreshold = yoloSettings.ConfidenceThreshold;
// _nmsThreshold = yoloSettings.NmsThreshold;
// // 注意YOLO模型的置信度和NMS阈值最好在YoloModelManager加载时设置
// // 这里如果需要运行时调整可能需要Yolo.SetThresholds方法
// }
// }
// public DetectionResult Detect(Bitmap image)
// {
// if (_yoloModel == null)
// throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
// using (var skImage = CameraProcessor.ToSKBitmapFast(image)) // 使用 CameraProcessor 的静态方法
// {
// if (skImage == null)
// {
// return new DetectionResult(false, "图像转换失败");
// }
// // 在这里可以应用运行时阈值如果Yolo模型支持
// // _yoloModel.Confidence = _confidenceThreshold;
// // _yoloModel.Nms = _nmsThreshold;
// var predictions = _yoloModel.RunObjectDetection(skImage);
// bool isOk = !predictions.Any(); // 假设没有检测到任何目标为 OK
// string message = isOk ? "OK" : "NG";
// List<RectangleF> boundingBoxes = predictions.Select(p => new RectangleF(p.Rectangle.X, p.Rectangle.Y, p.Rectangle.Width, p.Rectangle.Height)).ToList();
// Bitmap resultImage = DrawYoloPredictions(image, predictions);
// return new DetectionResult(isOk, message, 0, boundingBoxes, resultImage);
// }
// }
// private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
// {
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
// using (Graphics g = Graphics.FromImage(resultBmp))
// {
// Pen ngPen = new Pen(Color.Red, 3);
// Font font = new Font("Arial", 12, FontStyle.Bold);
// Brush brush = new SolidBrush(Color.Red);
// foreach (var p in predictions)
// {
// Rectangle rect = new Rectangle((int)p.Rectangle.X, (int)p.Rectangle.Y, (int)p.Rectangle.Width, (int)p.Rectangle.Height);
// g.DrawRectangle(ngPen, rect);
// g.DrawString($"{p.Label} ({p.Confidence:P})", font, brush, rect.X, rect.Y - 20);
// }
// }
// return resultBmp;
// }
// public void Dispose()
// {
// // YOLO 模型生命周期由 YoloModelManager 管理,这里不需要额外释放
// }
// }
//}
//public DetectionResult Detect(Bitmap image)
//{
// if (_yoloModel == null)
// throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
// using (var skBitmap = CameraProcessor.ToSKBitmapFast(image))
// using (var skImage = SKImage.FromBitmap(skBitmap))
// {
// //注意深拷贝浅拷贝的概念,复制的图片别忘了释放
// var output = image.Clone();
// if (skImage == null)
// return new DetectionResult(false, "图像转换失败");
// //var results = _yoloModel.RunObjectDetection(skImage, confidence: 0.4f, iou: 0.5f);
// var predictions = _yoloModel.RunObjectDetection(
// skImage,
// confidence: _confidenceThreshold,
// iou: _nmsThreshold
// );
// var ExistBool = predictions.FirstOrDefault(res => res.Label.Name.Equals("logo", StringComparison.OrdinalIgnoreCase));
// if (ExistBool != null)
// {
// var box = ExistBool.BoundingBox;
// var rect = new Rect(box.Left, box.Top, box.Width, box.Height);
// //Cv2.Rectangle(output, rect, Scalar.Yellow, 2);
// //Cv2.PutText(output, $"{logoResult.Label.Name}: {logoResult.Confidence:P2}", new CvPoint(rect.X, rect.Y - 10), HersheyFonts.HersheySimplex, 0.6, Scalar.Yellow, 2);
// }
// bool isOk = !predictions.Any(); // 没有检测结果为OK
// string message = isOk ? "OK" : "NG";
// //var boundingBoxes = predictions
// // .Select(p => new RectangleF(p.Rectangle.X, p.Rectangle.Y, p.Rectangle.Width, p.Rectangle.Height))
// // .ToList();
// //Bitmap resultImage = DrawYoloPredictions(image, predictions);
// //return new DetectionResult(isOk, message, 0, boundingBoxes, resultImage);
// }
//}
//// 注意:如果你使用的是 YoloResult请改为 IEnumerable<YoloResult> IEnumerable<YoloPrediction>
//private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
//{
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
// using (Graphics g = Graphics.FromImage(resultBmp))
// {
// Pen boxPen = new Pen(Color.Red, 3);
// Font font = new Font("Arial", 12, FontStyle.Bold);
// Brush brush = new SolidBrush(Color.Red);
// foreach (var p in predictions)
// {
// Rectangle rect = new Rectangle(
// (int)p.Rectangle.X,
// (int)p.Rectangle.Y,
// (int)p.Rectangle.Width,
// (int)p.Rectangle.Height
// );
// g.DrawRectangle(boxPen, rect);
// g.DrawString($"{p.Label} ({p.Confidence:P0})", font, brush, rect.X, rect.Y - 20);
// }
// boxPen.Dispose();
// font.Dispose();
// brush.Dispose();
// }
// return resultBmp;
//}
// 错误②CS0246 解决方法:确保 YoloPrediction 的完整命名空间正确引用。
// 由于你的文件开头已经有了 `using YoloDotNet.Models;`
// 所以这里直接使用 `YoloPrediction` 应该是正确的,除非 `YoloPrediction` 不在该命名空间下。
// 如果问题依然存在,检查 YoloDotNet.Models 命名空间中 YoloPrediction 的具体定义。
//private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
//{
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
// using (Graphics g = Graphics.FromImage(resultBmp))
// {
// Pen boxPen = new Pen(Color.Red, 3);
// Font font = new Font("Arial", 12, FontStyle.Bold);
// Brush brush = new SolidBrush(Color.Red);
// foreach (var p in predictions)
// {
// Rectangle rect = new Rectangle(
// (int)p.Rectangle.X,
// (int)p.Rectangle.Y,
// (int)p.Rectangle.Width,
// (int)p.Rectangle.Height
// );
// g.DrawRectangle(boxPen, rect);
// g.DrawString($"{p.Label} ({p.Confidence:P0})", font, brush, rect.X, rect.Y - 20);
// }
// boxPen.Dispose();
// font.Dispose();
// brush.Dispose();
// }
// return resultBmp;
//}