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推理服务资源时发生异常");
}
}
}