Files
OrpaonVision/OrpaonVision.SiteApp/Runtime/Services/YoloInferenceService.cs
2026-04-06 22:04:05 +08:00

313 lines
12 KiB
C#
Raw Permalink 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 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;
/// <summary>
/// YOLO推理服务实现。
/// 注意这是一个基于模拟的实现实际使用时需要替换为YoloDotNet或其他YOLO库的真实调用。
/// </summary>
public sealed class YoloInferenceService : IYoloInferenceService, IDisposable
{
private readonly ILogger<YoloInferenceService> _logger;
private readonly YoloInferenceOptions _options;
private readonly object _lockObject = new();
private bool _isInitialized;
private readonly List<string> _classNames = new();
private readonly Random _random = new();
private YoloModelInfo _modelInfo = new();
/// <summary>
/// 构造函数。
/// </summary>
public YoloInferenceService(ILogger<YoloInferenceService> logger, IOptions<YoloInferenceOptions> options)
{
_logger = logger;
_options = options.Value;
_logger.LogInformation("YOLO推理服务已初始化模拟模式");
}
/// <inheritdoc />
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());
}
}
}
/// <inheritdoc />
public Result<IReadOnlyList<YoloDetectionResult>> Predict(byte[] imageData, int width, int height, string pixelFormat = "BGR8Packed")
{
if (!_isInitialized)
{
var initResult = Initialize();
if (!initResult.Succeeded)
{
return Result<IReadOnlyList<YoloDetectionResult>>.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<YoloDetectionResult>();
// 生成模拟检测结果
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<IReadOnlyList<YoloDetectionResult>>.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<IReadOnlyList<YoloDetectionResult>>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public Result<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>> PredictBatch(
IReadOnlyList<byte[]> imageBatch,
IReadOnlyList<(int width, int height)> dimensions,
string pixelFormat = "BGR8Packed")
{
if (!_isInitialized)
{
var initResult = Initialize();
if (!initResult.Succeeded)
{
return Result<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>>.Fail(initResult.Code, initResult.Message, initResult.Errors.ToArray());
}
}
if (imageBatch.Count != dimensions.Count)
{
return Result<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>>.Fail(
"YOLO_BATCH_SIZE_MISMATCH", "图像批次与维度信息数量不匹配。");
}
try
{
_logger.LogDebug("开始YOLO批量推理批次大小: {BatchSize}", imageBatch.Count);
var results = new List<IReadOnlyList<YoloDetectionResult>>();
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<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>>.Fail(
predictResult.Code, $"第{i+1}张图像推理失败: {predictResult.Message}", predictResult.Errors.ToArray());
}
results.Add(predictResult.Data);
}
_logger.LogDebug("YOLO批量推理完成处理了 {Count} 张图像", results.Count);
return Result<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>>.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<IReadOnlyList<IReadOnlyList<YoloDetectionResult>>>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public IReadOnlyList<string> GetSupportedClasses()
{
return _classNames.ToList();
}
/// <inheritdoc />
public YoloModelInfo GetModelInfo()
{
return _modelInfo;
}
/// <inheritdoc />
public bool IsInitialized => _isInitialized;
/// <inheritdoc />
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());
}
}
/// <summary>
/// 释放资源。
/// </summary>
public void Dispose()
{
try
{
lock (_lockObject)
{
if (_isInitialized)
{
// 模拟资源释放 - 实际实现需要释放YOLO模型资源
_isInitialized = false;
_logger.LogInformation("YOLO推理服务资源已释放");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "释放YOLO推理服务资源时发生异常");
}
}
}