using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Contracts; using OrpaonVision.SiteApp.Runtime.Services; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// YOLO推理服务实现。 /// 注意:这是一个基于模拟的实现,实际使用时需要替换为YoloDotNet或其他YOLO库的真实调用。 /// public sealed class YoloInferenceService : IYoloInferenceService, IDisposable { private readonly ILogger _logger; private readonly YoloInferenceOptions _options; private readonly object _lockObject = new(); private bool _isInitialized; private readonly List _classNames = new(); private readonly Random _random = new(); private YoloModelInfo _modelInfo = new(); /// /// 构造函数。 /// public YoloInferenceService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; _logger.LogInformation("YOLO推理服务已初始化(模拟模式)"); } /// public Result Initialize() { lock (_lockObject) { try { if (_isInitialized) { return Result.Success(message: "YOLO模型已初始化。"); } _logger.LogInformation("正在初始化YOLO模型: {ModelPath}", _options.ModelPath); var stopwatch = Stopwatch.StartNew(); // 模拟模型加载 - 实际实现需要调用YoloDotNet或其他YOLO库 Thread.Sleep(1000); // 模拟加载时间 // 模拟加载类别标签 _classNames.Clear(); if (File.Exists(_options.LabelsPath)) { var lines = File.ReadAllLines(_options.LabelsPath); foreach (var line in lines) { var className = line.Trim(); if (!string.IsNullOrEmpty(className)) { _classNames.Add(className); } } } else { // 使用默认类别 _classNames.AddRange(new[] { "大电容", "螺丝", "铜排", "小电容", "电阻", "连接器", "散热片", "PCB板" }); } // 设置模型信息 _modelInfo = new YoloModelInfo { ModelName = Path.GetFileNameWithoutExtension(_options.ModelPath), ModelVersion = "1.0.0", InputSize = (_options.InputWidth, _options.InputHeight), ClassCount = _classNames.Count, ClassNames = _classNames.ToList(), UseGpu = _options.UseGpu, LoadTimeMs = stopwatch.Elapsed.TotalMilliseconds, ModelFileSize = File.Exists(_options.ModelPath) ? new FileInfo(_options.ModelPath).Length : 0 }; _isInitialized = true; stopwatch.Stop(); _logger.LogInformation("YOLO模型初始化成功,耗时: {ElapsedMs:F2}ms,类别数: {ClassCount}", stopwatch.Elapsed.TotalMilliseconds, _classNames.Count); // 执行预热 if (_options.EnableWarmup) { var warmupResult = Warmup(_options.WarmupCount); if (!warmupResult.Succeeded) { _logger.LogWarning("模型预热失败: {Message}", warmupResult.Message); } } return Result.Success(message: "YOLO模型初始化成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "YOLO模型初始化失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "YOLO_INIT_FAILED", "YOLO模型初始化失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result> Predict(byte[] imageData, int width, int height, string pixelFormat = "BGR8Packed") { if (!_isInitialized) { var initResult = Initialize(); if (!initResult.Succeeded) { return Result>.Fail(initResult.Code, initResult.Message, initResult.Errors.ToArray()); } } try { _logger.LogDebug("开始YOLO推理,图像尺寸: {Width}x{Height}", width, height); var stopwatch = Stopwatch.StartNew(); // 模拟推理过程 - 实际实现需要调用YoloDotNet的Predict方法 Thread.Sleep(50); // 模拟推理时间 var results = new List(); // 生成模拟检测结果 var detectionCount = _random.Next(0, 5); // 随机生成0-4个检测结果 for (int i = 0; i < detectionCount; i++) { var classId = _random.Next(0, _classNames.Count); var confidence = (float)(_random.NextDouble() * 0.4 + 0.6); // 0.6-1.0的置信度 var x = _random.Next(0, width / 2); var y = _random.Next(0, height / 2); var w = _random.Next(width / 8, width / 4); var h = _random.Next(height / 8, height / 4); results.Add(new YoloDetectionResult { ClassId = classId, ClassName = _classNames[classId], Confidence = confidence, X = x, Y = y, Width = w, Height = h }); } // 按置信度排序 results.Sort((a, b) => b.Confidence.CompareTo(a.Confidence)); stopwatch.Stop(); _modelInfo.InferenceTimeMs = stopwatch.Elapsed.TotalMilliseconds; _logger.LogDebug("YOLO推理完成,检测到 {Count} 个目标,耗时: {ElapsedMs:F2}ms", results.Count, stopwatch.Elapsed.TotalMilliseconds); return Result>.Success(results, message: "YOLO推理成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "YOLO推理失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "YOLO_PREDICT_FAILED", "YOLO推理失败。", traceId); return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result>> PredictBatch( IReadOnlyList imageBatch, IReadOnlyList<(int width, int height)> dimensions, string pixelFormat = "BGR8Packed") { if (!_isInitialized) { var initResult = Initialize(); if (!initResult.Succeeded) { return Result>>.Fail(initResult.Code, initResult.Message, initResult.Errors.ToArray()); } } if (imageBatch.Count != dimensions.Count) { return Result>>.Fail( "YOLO_BATCH_SIZE_MISMATCH", "图像批次与维度信息数量不匹配。"); } try { _logger.LogDebug("开始YOLO批量推理,批次大小: {BatchSize}", imageBatch.Count); var results = new List>(); for (int i = 0; i < imageBatch.Count; i++) { var predictResult = Predict(imageBatch[i], dimensions[i].width, dimensions[i].height, pixelFormat); if (!predictResult.Succeeded) { return Result>>.Fail( predictResult.Code, $"第{i+1}张图像推理失败: {predictResult.Message}", predictResult.Errors.ToArray()); } results.Add(predictResult.Data); } _logger.LogDebug("YOLO批量推理完成,处理了 {Count} 张图像", results.Count); return Result>>.Success(results, message: "YOLO批量推理成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "YOLO批量推理失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "YOLO_BATCH_PREDICT_FAILED", "YOLO批量推理失败。", traceId); return Result>>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public IReadOnlyList GetSupportedClasses() { return _classNames.ToList(); } /// public YoloModelInfo GetModelInfo() { return _modelInfo; } /// public bool IsInitialized => _isInitialized; /// public Result Warmup(int warmupCount = 3) { if (!_isInitialized) { return Result.Fail("YOLO_NOT_INITIALIZED", "YOLO模型未初始化。"); } try { _logger.LogInformation("开始YOLO模型预热,预热次数: {WarmupCount}", warmupCount); // 生成模拟图像数据 var dummyImage = new byte[_options.InputWidth * _options.InputHeight * 3]; // BGR格式 for (int i = 0; i < warmupCount; i++) { var warmupResult = Predict(dummyImage, _options.InputWidth, _options.InputHeight); if (!warmupResult.Succeeded) { return Result.Fail(warmupResult.Code, $"预热第{i+1}次失败: {warmupResult.Message}"); } } _logger.LogInformation("YOLO模型预热完成"); return Result.Success(message: "YOLO模型预热完成。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "YOLO模型预热失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "YOLO_WARMUP_FAILED", "YOLO模型预热失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// /// 释放资源。 /// public void Dispose() { try { lock (_lockObject) { if (_isInitialized) { // 模拟资源释放 - 实际实现需要释放YOLO模型资源 _isInitialized = false; _logger.LogInformation("YOLO推理服务资源已释放"); } } } catch (Exception ex) { _logger.LogError(ex, "释放YOLO推理服务资源时发生异常"); } } }