410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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;
 | ||
| //}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 |