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 { /// /// YOLO 检测结果对象,包含标签、置信度与检测框 /// public class YoloPrediction { public YoloLabel Label { get; set; } public float Score { get; set; } public BoundingBox BoundingBox { get; set; } } /// /// YOLO 类别标签 /// public class YoloLabel { public string Name { get; set; } } /// /// 检测框坐标结构体 /// 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; } } /// /// YOLO 目标检测器实现类 /// public class YoloDetector : IDetector, IDisposable { private Yolo _yoloModel; private int _modelID; private float _confidenceThreshold = 0.25f; private float _nmsThreshold = 0.45f; /// /// 初始化 YOLO 检测器并加载模型 /// /// 模型 ID 字符串 /// 可选检测参数 /// ID 无效 /// 模型未加载 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); } } /// /// 执行检测,判断是否含有 logo 类对象 /// 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}"); } } /// /// 在图像上绘制检测框与标签 /// public Bitmap DrawYoloPredictions(Bitmap source, IEnumerable 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; } /// /// 无需显式释放模型,资源由 YoloModelManager 管理 /// 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 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 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 IEnumerable //private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable 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 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; //}