976 lines
39 KiB
C#
976 lines
39 KiB
C#
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using OrpaonVision.Core.LayerRecognition;
|
||
using OrpaonVision.Core.Results;
|
||
using OrpaonVision.SiteApp.Runtime.Contracts;
|
||
using OrpaonVision.SiteApp.Runtime.Options;
|
||
using System.Collections.Concurrent;
|
||
using System.Text.Json;
|
||
|
||
namespace OrpaonVision.SiteApp.Runtime.Services;
|
||
|
||
/// <summary>
|
||
/// 层识别服务实现。
|
||
/// </summary>
|
||
public sealed class LayerRecognitionService : ILayerRecognitionService
|
||
{
|
||
private readonly ILogger<LayerRecognitionService> _logger;
|
||
private readonly RuntimeOptions _options;
|
||
private readonly ConcurrentDictionary<Guid, LayerHistoryRecord> _layerHistory = new();
|
||
private readonly ConcurrentDictionary<Guid, LayerModelData> _layerModels = new();
|
||
private readonly object _lock = new();
|
||
private LayerModelData? _currentModel;
|
||
|
||
/// <summary>
|
||
/// 构造函数。
|
||
/// </summary>
|
||
public LayerRecognitionService(ILogger<LayerRecognitionService> logger, IOptions<RuntimeOptions> options)
|
||
{
|
||
_logger = logger;
|
||
_options = options.Value;
|
||
InitializeDefaultModel();
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerRecognitionResult>> RecognizeLayerAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("开始层级识别:当前层={CurrentLayer},推理结果={Label},置信度={Confidence:F3}",
|
||
currentLayer, inference.Label, inference.Confidence);
|
||
|
||
var startTime = DateTime.UtcNow;
|
||
|
||
// 提取层级特征
|
||
var featuresResult = await ExtractLayerFeaturesAsync(inference, cancellationToken);
|
||
if (!featuresResult.Succeeded)
|
||
{
|
||
return Result<LayerRecognitionResult>.Fail(featuresResult.Code, featuresResult.Message, featuresResult.Errors.ToArray());
|
||
}
|
||
|
||
if (featuresResult.Data == null)
|
||
{
|
||
return Result<LayerRecognitionResult>.Fail("FEATURES_EMPTY", "层级特征为空");
|
||
}
|
||
|
||
var features = featuresResult.Data;
|
||
|
||
// 多方法融合识别
|
||
var recognitionTasks = new List<Task<Result<LayerRecognitionResult>>>
|
||
{
|
||
RecognizeByRuleAsync(inference, currentLayer, features, cancellationToken),
|
||
RecognizeByFeatureMatchingAsync(inference, currentLayer, features, cancellationToken)
|
||
};
|
||
|
||
// 如果有机器学习模型,添加ML识别
|
||
if (_currentModel != null && _currentModel.IsEnabled)
|
||
{
|
||
recognitionTasks.Add(RecognizeByMachineLearningAsync(inference, currentLayer, features, cancellationToken));
|
||
}
|
||
|
||
var recognitionResults = await Task.WhenAll(recognitionTasks);
|
||
|
||
// 融合识别结果
|
||
var finalResult = FuseRecognitionResults(recognitionResults, inference, currentLayer, features);
|
||
|
||
// 记录历史
|
||
var historyRecord = new LayerHistoryRecord
|
||
{
|
||
RecordId = Guid.NewGuid(),
|
||
Layer = finalResult.RecognizedLayer,
|
||
Confidence = finalResult.Confidence,
|
||
Inference = inference,
|
||
Features = features,
|
||
RecordTimeUtc = startTime,
|
||
IsValid = finalResult.Confidence >= _options.LayerRecognitionConfidenceThreshold,
|
||
ExtendedProperties = new Dictionary<string, object>
|
||
{
|
||
["recognition_method"] = finalResult.Method.ToString(),
|
||
["recognition_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds
|
||
}
|
||
};
|
||
|
||
await AddHistoryRecordAsync(historyRecord, cancellationToken);
|
||
|
||
var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds;
|
||
_logger.LogInformation("层级识别完成:识别层级={RecognizedLayer},置信度={Confidence:F3},方法={Method},耗时={ElapsedMs:F1}ms",
|
||
finalResult.RecognizedLayer, finalResult.Confidence, finalResult.Method, elapsed);
|
||
|
||
return Result<LayerRecognitionResult>.Success(finalResult);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级识别失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_RECOGNITION_FAILED", "层级识别失败", traceId);
|
||
return Result<LayerRecognitionResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerStabilityResult>> ValidateLayerStabilityAsync(IReadOnlyList<LayerHistoryRecord> layerHistory, int stabilityWindow = 5, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("验证层级稳定性:窗口大小={Window},历史记录数={Count}", stabilityWindow, layerHistory.Count);
|
||
|
||
if (layerHistory.Count < stabilityWindow)
|
||
{
|
||
return Result<LayerStabilityResult>.Success(new LayerStabilityResult
|
||
{
|
||
IsStable = false,
|
||
StabilityScore = 0.0,
|
||
DominantLayer = layerHistory.LastOrDefault()?.Layer ?? 0,
|
||
StabilityWindow = stabilityWindow,
|
||
ValidationTimeUtc = DateTime.UtcNow,
|
||
Details = $"历史记录数量不足,需要至少 {stabilityWindow} 条记录"
|
||
});
|
||
}
|
||
|
||
// 获取最近的稳定性窗口内的记录
|
||
var recentRecords = layerHistory
|
||
.OrderByDescending(r => r.RecordTimeUtc)
|
||
.Take(stabilityWindow)
|
||
.ToList();
|
||
|
||
// 计算层级分布
|
||
var layerDistribution = new Dictionary<int, double>();
|
||
var totalRecords = recentRecords.Count;
|
||
|
||
foreach (var record in recentRecords)
|
||
{
|
||
if (layerDistribution.ContainsKey(record.Layer))
|
||
{
|
||
layerDistribution[record.Layer]++;
|
||
}
|
||
else
|
||
{
|
||
layerDistribution[record.Layer] = 1;
|
||
}
|
||
}
|
||
|
||
// 转换为百分比
|
||
foreach (var key in layerDistribution.Keys.ToList())
|
||
{
|
||
layerDistribution[key] = layerDistribution[key] / totalRecords * 100;
|
||
}
|
||
|
||
// 找出主导层级
|
||
var dominantLayer = layerDistribution.OrderByDescending(kvp => kvp.Value).First().Key;
|
||
var dominantPercentage = layerDistribution[dominantLayer];
|
||
|
||
// 计算稳定性得分
|
||
var stabilityScore = CalculateStabilityScore(layerDistribution, recentRecords);
|
||
|
||
// 判断是否稳定
|
||
var isStable = stabilityScore >= _options.LayerStabilityThreshold && dominantPercentage >= _options.DominantLayerThreshold;
|
||
|
||
var result = new LayerStabilityResult
|
||
{
|
||
IsStable = isStable,
|
||
StabilityScore = stabilityScore,
|
||
DominantLayer = dominantLayer,
|
||
LayerDistribution = layerDistribution,
|
||
StabilityWindow = stabilityWindow,
|
||
ValidationTimeUtc = DateTime.UtcNow,
|
||
Details = isStable
|
||
? $"层级稳定,主导层级 {dominantLayer} 占比 {dominantPercentage:F1}%"
|
||
: $"层级不稳定,主导层级 {dominantLayer} 占比 {dominantPercentage:F1}% 低于阈值"
|
||
};
|
||
|
||
_logger.LogInformation("层级稳定性验证完成:{IsStable},稳定性得分={Score:F3},主导层级={DominantLayer},占比={Percentage:F1}%",
|
||
result.IsStable, result.StabilityScore, result.DominantLayer, dominantPercentage);
|
||
|
||
return Result<LayerStabilityResult>.Success(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级稳定性验证失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_STABILITY_VALIDATION_FAILED", "层级稳定性验证失败", traceId);
|
||
return Result<LayerStabilityResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerTransitionResult>> DetectLayerTransitionAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, IReadOnlyList<LayerHistoryRecord> layerHistory, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("检测层级变化:当前层={CurrentLayer},历史记录数={Count}", currentLayer, layerHistory.Count);
|
||
|
||
// 先进行层级识别
|
||
var recognitionResult = await RecognizeLayerAsync(inference, currentLayer, cancellationToken);
|
||
if (!recognitionResult.Succeeded)
|
||
{
|
||
return Result<LayerTransitionResult>.Fail(recognitionResult.Code, recognitionResult.Message, recognitionResult.Errors.ToArray());
|
||
}
|
||
|
||
if (recognitionResult.Data == null)
|
||
{
|
||
return Result<LayerTransitionResult>.Fail("RECOGNITION_RESULT_EMPTY", "层级识别结果为空");
|
||
}
|
||
|
||
var recognizedLayer = recognitionResult.Data.RecognizedLayer;
|
||
|
||
// 检查是否有层级变化
|
||
var hasTransition = recognizedLayer != currentLayer;
|
||
|
||
if (!hasTransition)
|
||
{
|
||
return Result<LayerTransitionResult>.Success(new LayerTransitionResult
|
||
{
|
||
HasTransition = false,
|
||
FromLayer = currentLayer,
|
||
ToLayer = currentLayer,
|
||
TransitionConfidence = 0.0,
|
||
TransitionType = LayerTransitionType.Normal,
|
||
DetectionTimeUtc = DateTime.UtcNow,
|
||
Details = "无层级变化"
|
||
});
|
||
}
|
||
|
||
// 分析变化类型
|
||
var transitionType = AnalyzeTransitionType(currentLayer, recognizedLayer, layerHistory);
|
||
|
||
// 计算变化置信度
|
||
var transitionConfidence = CalculateTransitionConfidence(currentLayer, recognizedLayer, layerHistory, recognitionResult.Data);
|
||
|
||
var result = new LayerTransitionResult
|
||
{
|
||
HasTransition = true,
|
||
FromLayer = currentLayer,
|
||
ToLayer = recognizedLayer,
|
||
TransitionConfidence = transitionConfidence,
|
||
TransitionType = transitionType,
|
||
DetectionTimeUtc = DateTime.UtcNow,
|
||
Details = $"检测到层级变化:{currentLayer} -> {recognizedLayer},类型:{transitionType}",
|
||
ExtendedProperties = new Dictionary<string, object>
|
||
{
|
||
["recognition_confidence"] = recognitionResult.Data.Confidence,
|
||
["recognition_method"] = recognitionResult.Data.Method.ToString()
|
||
}
|
||
};
|
||
|
||
_logger.LogInformation("层级变化检测完成:{FromLayer} -> {ToLayer},类型={Type},置信度={Confidence:F3}",
|
||
result.FromLayer, result.ToLayer, result.TransitionType, result.TransitionConfidence);
|
||
|
||
return Result<LayerTransitionResult>.Success(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级变化检测失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_TRANSITION_DETECTION_FAILED", "层级变化检测失败", traceId);
|
||
return Result<LayerTransitionResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerFeatures>> ExtractLayerFeaturesAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("提取层级特征:目标数量={Count},标签={Label},置信度={Confidence:F3}",
|
||
inference.Detections?.Count ?? 0, inference.Label, inference.Confidence);
|
||
|
||
var detections = inference.Detections?.ToList() ?? new List<OrpaonVision.Core.LayerRecognition.DetectionDto>();
|
||
var objectCount = detections.Count;
|
||
var averageConfidence = objectCount > 0 ? detections.Average(d => d.Confidence) : inference.Confidence;
|
||
var maxConfidence = objectCount > 0 ? detections.Max(d => d.Confidence) : inference.Confidence;
|
||
var objectClassDistribution = detections
|
||
.GroupBy(d => d.Class)
|
||
.ToDictionary(g => g.Key, g => g.Count());
|
||
var boundingBoxes = detections.Select(d => new BoundingBox
|
||
{
|
||
X = d.X,
|
||
Y = d.Y,
|
||
Width = d.Width,
|
||
Height = d.Height,
|
||
Confidence = d.Confidence,
|
||
Class = d.Class
|
||
}).ToList();
|
||
|
||
var imageFeatures = await ExtractImageFeaturesAsync(inference, cancellationToken);
|
||
|
||
var vectorSource = new LayerFeatures
|
||
{
|
||
ObjectCount = objectCount,
|
||
AverageConfidence = averageConfidence,
|
||
MaxConfidence = maxConfidence,
|
||
ObjectClassDistribution = objectClassDistribution,
|
||
BoundingBoxes = boundingBoxes,
|
||
ImageFeatures = imageFeatures,
|
||
ExtractionTimeUtc = DateTime.UtcNow
|
||
};
|
||
|
||
var featureVector = await GenerateFeatureVectorAsync(vectorSource, cancellationToken);
|
||
|
||
var features = new LayerFeatures
|
||
{
|
||
ObjectCount = objectCount,
|
||
AverageConfidence = averageConfidence,
|
||
MaxConfidence = maxConfidence,
|
||
ObjectClassDistribution = objectClassDistribution,
|
||
BoundingBoxes = boundingBoxes,
|
||
ImageFeatures = imageFeatures,
|
||
FeatureVector = featureVector,
|
||
ExtractionTimeUtc = vectorSource.ExtractionTimeUtc
|
||
};
|
||
|
||
_logger.LogDebug("层级特征提取完成:目标数量={ObjectCount},特征向量维度={VectorDim}",
|
||
features.ObjectCount, features.FeatureVector.Length);
|
||
|
||
return Result<LayerFeatures>.Success(features);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级特征提取失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_FEATURE_EXTRACTION_FAILED", "层级特征提取失败", traceId);
|
||
return Result<LayerFeatures>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerModelTrainingResult>> TrainLayerModelAsync(IReadOnlyList<LayerTrainingData> trainingData, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation("开始训练层级识别模型:训练样本数={TrainingCount},验证样本数={ValidationCount}",
|
||
trainingData.Count(d => !d.IsValidationData), trainingData.Count(d => d.IsValidationData));
|
||
|
||
var startTime = DateTime.UtcNow;
|
||
|
||
// 数据预处理
|
||
var preprocessedData = await PreprocessTrainingDataAsync(trainingData, cancellationToken);
|
||
|
||
// 特征工程
|
||
var featureVectors = preprocessedData.Select(d => d.Features.FeatureVector).ToArray();
|
||
var labels = preprocessedData.Select(d => d.TargetLayer).ToArray();
|
||
|
||
// 训练模型(这里使用简化的随机森林实现)
|
||
var modelResult = await TrainRandomForestModelAsync(featureVectors, labels, cancellationToken);
|
||
|
||
var elapsed = DateTime.UtcNow - startTime;
|
||
|
||
var result = new LayerModelTrainingResult
|
||
{
|
||
TrainingId = Guid.NewGuid(),
|
||
Accuracy = modelResult.Accuracy,
|
||
Precision = modelResult.Precision,
|
||
Recall = modelResult.Recall,
|
||
F1Score = modelResult.F1Score,
|
||
TrainingSampleCount = trainingData.Count(d => !d.IsValidationData),
|
||
ValidationSampleCount = trainingData.Count(d => d.IsValidationData),
|
||
TrainingElapsedMs = (long)elapsed.TotalMilliseconds,
|
||
ModelVersion = $"v{DateTime.UtcNow:yyyyMMdd-HHmmss}",
|
||
TrainingTimeUtc = startTime,
|
||
Details = $"模型训练完成,准确率:{modelResult.Accuracy:F3}"
|
||
};
|
||
|
||
_logger.LogInformation("层级识别模型训练完成:准确率={Accuracy:F3},精确率={Precision:F3},召回率={Recall:F3},耗时={ElapsedMs}ms",
|
||
result.Accuracy, result.Precision, result.Recall, result.TrainingElapsedMs);
|
||
|
||
return Result<LayerModelTrainingResult>.Success(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级识别模型训练失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_MODEL_TRAINING_FAILED", "层级识别模型训练失败", traceId);
|
||
return Result<LayerModelTrainingResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result> UpdateLayerModelAsync(LayerModelData modelData, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation("更新层级识别模型:模型ID={ModelId},版本={Version},类型={Type}",
|
||
modelData.ModelId, modelData.Version, modelData.ModelType);
|
||
|
||
lock (_lock)
|
||
{
|
||
_layerModels[modelData.ModelId] = modelData;
|
||
_currentModel = modelData;
|
||
}
|
||
|
||
_logger.LogInformation("层级识别模型更新成功:模型ID={ModelId}", modelData.ModelId);
|
||
return Result.Success();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "层级识别模型更新失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "LAYER_MODEL_UPDATE_FAILED", "层级识别模型更新失败", traceId);
|
||
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<Result<LayerRecognitionStatistics>> GetRecognitionStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("获取层级识别统计信息:开始时间={StartTime},结束时间={EndTime}", startTime, endTime);
|
||
|
||
var historyRecords = _layerHistory.Values
|
||
.Where(r => r.RecordTimeUtc >= startTime && r.RecordTimeUtc <= endTime)
|
||
.ToList();
|
||
|
||
var totalRecognitions = historyRecords.Count;
|
||
var successfulRecognitions = historyRecords.Count(r => r.IsValid);
|
||
var failedRecognitions = historyRecords.Count(r => !r.IsValid);
|
||
var averageConfidence = historyRecords.Any() ? historyRecords.Average(r => r.Confidence) : 0.0;
|
||
var accuracy = totalRecognitions > 0
|
||
? (double)successfulRecognitions / totalRecognitions * 100
|
||
: 0.0;
|
||
|
||
var byLayer = historyRecords
|
||
.GroupBy(r => r.Layer)
|
||
.ToDictionary(g => g.Key, g => new LayerStatistics
|
||
{
|
||
Layer = g.Key,
|
||
RecognitionCount = g.Count(),
|
||
SuccessCount = g.Count(r => r.IsValid),
|
||
Accuracy = g.Any() ? (double)g.Count(r => r.IsValid) / g.Count() * 100 : 0.0,
|
||
AverageConfidence = g.Average(r => r.Confidence)
|
||
});
|
||
|
||
var byMethod = historyRecords
|
||
.Where(r => r.ExtendedProperties.ContainsKey("recognition_method"))
|
||
.GroupBy(r => Enum.Parse<LayerRecognitionMethod>(r.ExtendedProperties["recognition_method"].ToString()!))
|
||
.ToDictionary(g => g.Key, g => new MethodStatistics
|
||
{
|
||
Method = g.Key,
|
||
UsageCount = g.Count(),
|
||
SuccessCount = g.Count(r => r.IsValid),
|
||
Accuracy = g.Any() ? (double)g.Count(r => r.IsValid) / g.Count() * 100 : 0.0,
|
||
AverageElapsedMs = g.Where(r => r.ExtendedProperties.ContainsKey("recognition_time_ms"))
|
||
.Average(r => Convert.ToDouble(r.ExtendedProperties["recognition_time_ms"]))
|
||
});
|
||
|
||
var byTime = historyRecords
|
||
.GroupBy(r => new DateTime(r.RecordTimeUtc.Year, r.RecordTimeUtc.Month, r.RecordTimeUtc.Day, r.RecordTimeUtc.Hour, 0, 0))
|
||
.ToDictionary(g => g.Key, g => new TimeStatistics
|
||
{
|
||
Time = g.Key,
|
||
RecognitionCount = g.Count(),
|
||
SuccessCount = g.Count(r => r.IsValid),
|
||
LayerTransitions = CountLayerTransitions(g.ToList())
|
||
});
|
||
|
||
var layerTransitions = CountLayerTransitions(historyRecords);
|
||
|
||
var statistics = new LayerRecognitionStatistics
|
||
{
|
||
StartTimeUtc = startTime,
|
||
EndTimeUtc = endTime,
|
||
TotalRecognitions = totalRecognitions,
|
||
SuccessfulRecognitions = successfulRecognitions,
|
||
FailedRecognitions = failedRecognitions,
|
||
AverageConfidence = averageConfidence,
|
||
Accuracy = accuracy,
|
||
ByLayer = byLayer,
|
||
ByMethod = byMethod,
|
||
ByTime = byTime,
|
||
LayerTransitions = layerTransitions
|
||
};
|
||
|
||
_logger.LogInformation("层级识别统计信息获取完成:总识别次数={Total},准确率={Accuracy:F2}%,层级变化次数={Transitions}",
|
||
statistics.TotalRecognitions, statistics.Accuracy, statistics.LayerTransitions);
|
||
|
||
return Result<LayerRecognitionStatistics>.Success(statistics);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "获取层级识别统计信息失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "GET_RECOGNITION_STATISTICS_FAILED", "获取层级识别统计信息失败", traceId);
|
||
return Result<LayerRecognitionStatistics>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 初始化默认模型。
|
||
/// </summary>
|
||
private void InitializeDefaultModel()
|
||
{
|
||
_currentModel = new LayerModelData
|
||
{
|
||
ModelId = Guid.NewGuid(),
|
||
Version = "1.0.0",
|
||
ModelType = LayerModelType.RandomForest,
|
||
IsEnabled = true,
|
||
CreatedAtUtc = DateTime.UtcNow,
|
||
ModelParameters = new Dictionary<string, object>
|
||
{
|
||
["n_estimators"] = 100,
|
||
["max_depth"] = 10,
|
||
["random_state"] = 42
|
||
}
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基于规则识别层级。
|
||
/// </summary>
|
||
private async Task<Result<LayerRecognitionResult>> RecognizeByRuleAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, LayerFeatures features, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(1, cancellationToken); // 模拟异步操作
|
||
|
||
var recognizedLayer = currentLayer;
|
||
var confidence = 0.8;
|
||
|
||
// 基于目标数量的简单规则
|
||
if (features.ObjectCount == 0)
|
||
{
|
||
// 无目标检测到,可能是空层或错误
|
||
recognizedLayer = Math.Max(0, currentLayer - 1);
|
||
confidence = 0.6;
|
||
}
|
||
else if (features.ObjectCount > 10)
|
||
{
|
||
// 目标数量过多,可能是高层级
|
||
recognizedLayer = Math.Min(currentLayer + 1, 10);
|
||
confidence = 0.7;
|
||
}
|
||
|
||
return Result<LayerRecognitionResult>.Success(new LayerRecognitionResult
|
||
{
|
||
RecognizedLayer = recognizedLayer,
|
||
Confidence = confidence,
|
||
Method = LayerRecognitionMethod.RuleBased,
|
||
Features = features,
|
||
RecognitionTimeUtc = DateTime.UtcNow,
|
||
Details = $"基于规则识别:目标数量={features.ObjectCount}"
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基于特征匹配识别层级。
|
||
/// </summary>
|
||
private async Task<Result<LayerRecognitionResult>> RecognizeByFeatureMatchingAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, LayerFeatures features, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(5, cancellationToken); // 模拟异步操作
|
||
|
||
// 简化的特征匹配算法
|
||
var recognizedLayer = currentLayer;
|
||
var confidence = 0.75;
|
||
|
||
// 基于目标类别分布匹配
|
||
if (features.ObjectClassDistribution.ContainsKey("component_a") && features.ObjectClassDistribution.ContainsKey("component_b"))
|
||
{
|
||
recognizedLayer = 2;
|
||
confidence = 0.85;
|
||
}
|
||
else if (features.ObjectClassDistribution.ContainsKey("component_c"))
|
||
{
|
||
recognizedLayer = 3;
|
||
confidence = 0.8;
|
||
}
|
||
|
||
return Result<LayerRecognitionResult>.Success(new LayerRecognitionResult
|
||
{
|
||
RecognizedLayer = recognizedLayer,
|
||
Confidence = confidence,
|
||
Method = LayerRecognitionMethod.FeatureMatching,
|
||
Features = features,
|
||
RecognitionTimeUtc = DateTime.UtcNow,
|
||
Details = $"基于特征匹配识别:类别分布={string.Join(",", features.ObjectClassDistribution.Select(kvp => $"{kvp.Key}:{kvp.Value}"))}"
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基于机器学习识别层级。
|
||
/// </summary>
|
||
private async Task<Result<LayerRecognitionResult>> RecognizeByMachineLearningAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, LayerFeatures features, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(10, cancellationToken); // 模拟异步操作
|
||
|
||
if (_currentModel == null)
|
||
{
|
||
return Result<LayerRecognitionResult>.Fail("NO_MODEL_AVAILABLE", "没有可用的机器学习模型");
|
||
}
|
||
|
||
// 简化的ML预测(实际应该使用训练好的模型)
|
||
var recognizedLayer = currentLayer;
|
||
var confidence = 0.9;
|
||
|
||
// 基于特征向量的简单分类
|
||
if (features.FeatureVector.Length > 0)
|
||
{
|
||
var featureSum = features.FeatureVector.Sum();
|
||
recognizedLayer = (int)(featureSum % 10) + 1;
|
||
confidence = 0.85 + (featureSum % 100) / 1000.0;
|
||
}
|
||
|
||
return Result<LayerRecognitionResult>.Success(new LayerRecognitionResult
|
||
{
|
||
RecognizedLayer = recognizedLayer,
|
||
Confidence = confidence,
|
||
Method = LayerRecognitionMethod.MachineLearning,
|
||
Features = features,
|
||
RecognitionTimeUtc = DateTime.UtcNow,
|
||
Details = $"基于机器学习识别:模型版本={_currentModel.Version}"
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 融合识别结果。
|
||
/// </summary>
|
||
private LayerRecognitionResult FuseRecognitionResults(Result<LayerRecognitionResult>[] results, OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, int currentLayer, LayerFeatures features)
|
||
{
|
||
var validResults = results.Where(r => r.Succeeded && r.Data != null).Select(r => r.Data!).ToList();
|
||
|
||
if (!validResults.Any())
|
||
{
|
||
return new LayerRecognitionResult
|
||
{
|
||
RecognizedLayer = currentLayer,
|
||
Confidence = 0.0,
|
||
Method = LayerRecognitionMethod.RuleBased,
|
||
Features = features,
|
||
RecognitionTimeUtc = DateTime.UtcNow,
|
||
Details = "所有识别方法都失败"
|
||
};
|
||
}
|
||
|
||
// 加权平均融合
|
||
var weightedConfidence = 0.0;
|
||
var weightedLayer = 0.0;
|
||
var totalWeight = 0.0;
|
||
|
||
foreach (var result in validResults)
|
||
{
|
||
var weight = GetMethodWeight(result.Method);
|
||
weightedConfidence += result.Confidence * weight;
|
||
weightedLayer += result.RecognizedLayer * weight;
|
||
totalWeight += weight;
|
||
}
|
||
|
||
var finalConfidence = weightedConfidence / totalWeight;
|
||
var finalLayer = (int)Math.Round(weightedLayer / totalWeight);
|
||
|
||
// 检查是否为层级变化
|
||
var isTransition = finalLayer != currentLayer;
|
||
|
||
return new LayerRecognitionResult
|
||
{
|
||
RecognizedLayer = finalLayer,
|
||
Confidence = finalConfidence,
|
||
Method = LayerRecognitionMethod.Hybrid,
|
||
Features = features,
|
||
IsLayerTransition = isTransition,
|
||
RecognitionTimeUtc = DateTime.UtcNow,
|
||
Details = $"融合识别:{string.Join(", ", validResults.Select(r => $"{r.Method}:{r.Confidence:F3}"))}",
|
||
ExtendedProperties = new Dictionary<string, object>
|
||
{
|
||
["individual_results"] = validResults.Select(r => new { r.Method, r.Confidence, r.RecognizedLayer }).ToList()
|
||
}
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取方法权重。
|
||
/// </summary>
|
||
private double GetMethodWeight(LayerRecognitionMethod method)
|
||
{
|
||
return method switch
|
||
{
|
||
LayerRecognitionMethod.RuleBased => 0.3,
|
||
LayerRecognitionMethod.FeatureMatching => 0.4,
|
||
LayerRecognitionMethod.MachineLearning => 0.6,
|
||
LayerRecognitionMethod.DeepLearning => 0.8,
|
||
LayerRecognitionMethod.Hybrid => 0.7,
|
||
_ => 0.1
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加历史记录。
|
||
/// </summary>
|
||
private async Task AddHistoryRecordAsync(LayerHistoryRecord record, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
_layerHistory.TryAdd(record.RecordId, record);
|
||
|
||
// 保持历史记录数量在合理范围内
|
||
if (_layerHistory.Count > _options.MaxLayerHistoryCount)
|
||
{
|
||
var oldestRecords = _layerHistory.Values
|
||
.OrderBy(r => r.RecordTimeUtc)
|
||
.Take(_layerHistory.Count - _options.MaxLayerHistoryCount)
|
||
.Select(r => r.RecordId)
|
||
.ToList();
|
||
|
||
foreach (var id in oldestRecords)
|
||
{
|
||
_layerHistory.TryRemove(id, out _);
|
||
}
|
||
}
|
||
}, cancellationToken);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算稳定性得分。
|
||
/// </summary>
|
||
private double CalculateStabilityScore(Dictionary<int, double> layerDistribution, List<LayerHistoryRecord> records)
|
||
{
|
||
// 基于熵计算稳定性
|
||
var entropy = 0.0;
|
||
var totalRecords = records.Count;
|
||
|
||
foreach (var percentage in layerDistribution.Values)
|
||
{
|
||
if (percentage > 0)
|
||
{
|
||
entropy -= (percentage / 100) * Math.Log(percentage / 100, 2);
|
||
}
|
||
}
|
||
|
||
// 熵越小,稳定性越高
|
||
var maxEntropy = Math.Log(layerDistribution.Count, 2);
|
||
var stabilityScore = (1 - entropy / maxEntropy) * 100;
|
||
|
||
// 考虑时间一致性
|
||
var timeConsistency = CalculateTimeConsistency(records);
|
||
stabilityScore = stabilityScore * 0.7 + timeConsistency * 0.3;
|
||
|
||
return Math.Max(0, Math.Min(100, stabilityScore));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算时间一致性。
|
||
/// </summary>
|
||
private double CalculateTimeConsistency(List<LayerHistoryRecord> records)
|
||
{
|
||
if (records.Count < 2) return 100.0;
|
||
|
||
var consistentCount = 0;
|
||
for (int i = 1; i < records.Count; i++)
|
||
{
|
||
if (records[i].Layer == records[i - 1].Layer)
|
||
{
|
||
consistentCount++;
|
||
}
|
||
}
|
||
|
||
return (double)consistentCount / (records.Count - 1) * 100;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析变化类型。
|
||
/// </summary>
|
||
private LayerTransitionType AnalyzeTransitionType(int fromLayer, int toLayer, IReadOnlyList<LayerHistoryRecord> history)
|
||
{
|
||
if (toLayer == fromLayer + 1)
|
||
{
|
||
return LayerTransitionType.Normal;
|
||
}
|
||
else if (toLayer > fromLayer + 1)
|
||
{
|
||
return LayerTransitionType.Skip;
|
||
}
|
||
else if (toLayer < fromLayer)
|
||
{
|
||
return LayerTransitionType.Backward;
|
||
}
|
||
else if (history.Count > 0 && history.Last().Layer == toLayer)
|
||
{
|
||
return LayerTransitionType.Duplicate;
|
||
}
|
||
else
|
||
{
|
||
return LayerTransitionType.Abnormal;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算变化置信度。
|
||
/// </summary>
|
||
private double CalculateTransitionConfidence(int fromLayer, int toLayer, IReadOnlyList<LayerHistoryRecord> history, LayerRecognitionResult recognitionResult)
|
||
{
|
||
var baseConfidence = recognitionResult.Confidence;
|
||
|
||
// 基于历史记录调整置信度
|
||
if (history.Count >= 3)
|
||
{
|
||
var recentLayers = history.TakeLast(3).Select(r => r.Layer).ToList();
|
||
var trend = CalculateLayerTrend(recentLayers);
|
||
|
||
if (trend > 0 && toLayer > fromLayer)
|
||
{
|
||
baseConfidence += 0.1;
|
||
}
|
||
else if (trend < 0 && toLayer < fromLayer)
|
||
{
|
||
baseConfidence += 0.1;
|
||
}
|
||
}
|
||
|
||
return Math.Max(0, Math.Min(1, baseConfidence));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算层级趋势。
|
||
/// </summary>
|
||
private double CalculateLayerTrend(List<int> layers)
|
||
{
|
||
if (layers.Count < 2) return 0;
|
||
|
||
var trend = 0.0;
|
||
for (int i = 1; i < layers.Count; i++)
|
||
{
|
||
trend += layers[i] - layers[i - 1];
|
||
}
|
||
|
||
return trend / (layers.Count - 1);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提取图像特征。
|
||
/// </summary>
|
||
private async Task<ImageFeatures> ExtractImageFeaturesAsync(OrpaonVision.Core.LayerRecognition.InferenceResultDto inference, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(2, cancellationToken); // 模拟图像处理
|
||
|
||
// 模拟图像特征提取
|
||
return new ImageFeatures
|
||
{
|
||
BrightnessMean = 0.5 + (inference.Confidence - 0.5) * 0.3,
|
||
Contrast = 0.6,
|
||
TextureComplexity = 0.4,
|
||
EdgeDensity = 0.3,
|
||
ColorDistribution = new Dictionary<string, double>
|
||
{
|
||
["red"] = 0.3,
|
||
["green"] = 0.4,
|
||
["blue"] = 0.3
|
||
},
|
||
ImageSize = (640, 480),
|
||
AspectRatio = 4.0 / 3.0
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成特征向量。
|
||
/// </summary>
|
||
private async Task<double[]> GenerateFeatureVectorAsync(LayerFeatures features, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(1, cancellationToken);
|
||
|
||
var vector = new List<double>
|
||
{
|
||
features.ObjectCount,
|
||
features.AverageConfidence,
|
||
features.MaxConfidence,
|
||
features.ImageFeatures.BrightnessMean,
|
||
features.ImageFeatures.Contrast,
|
||
features.ImageFeatures.TextureComplexity,
|
||
features.ImageFeatures.EdgeDensity,
|
||
features.ImageFeatures.AspectRatio
|
||
};
|
||
|
||
// 添加类别分布特征
|
||
foreach (var kvp in features.ObjectClassDistribution.OrderByDescending(x => x.Value).Take(5))
|
||
{
|
||
vector.Add(kvp.Value);
|
||
}
|
||
|
||
// 填充到固定长度
|
||
while (vector.Count < 20)
|
||
{
|
||
vector.Add(0.0);
|
||
}
|
||
|
||
return vector.Take(20).ToArray();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预处理训练数据。
|
||
/// </summary>
|
||
private async Task<List<LayerTrainingData>> PreprocessTrainingDataAsync(IReadOnlyList<LayerTrainingData> trainingData, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(10, cancellationToken);
|
||
|
||
// 数据清洗和标准化
|
||
return trainingData
|
||
.Where(d => d.Features.FeatureVector.Length > 0 && d.TargetLayer >= 0)
|
||
.Select(d => new LayerTrainingData
|
||
{
|
||
DataId = d.DataId,
|
||
TargetLayer = d.TargetLayer,
|
||
Inference = d.Inference,
|
||
Features = NormalizeFeatures(d.Features),
|
||
Label = d.Label,
|
||
CreatedAtUtc = d.CreatedAtUtc,
|
||
IsValidationData = d.IsValidationData
|
||
})
|
||
.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标准化特征。
|
||
/// </summary>
|
||
private LayerFeatures NormalizeFeatures(LayerFeatures features)
|
||
{
|
||
// 简单的特征标准化
|
||
return new LayerFeatures
|
||
{
|
||
ObjectCount = Math.Min(features.ObjectCount, 20),
|
||
AverageConfidence = features.AverageConfidence,
|
||
MaxConfidence = features.MaxConfidence,
|
||
BoundingBoxes = features.BoundingBoxes,
|
||
ObjectClassDistribution = features.ObjectClassDistribution,
|
||
ImageFeatures = features.ImageFeatures,
|
||
FeatureVector = features.FeatureVector.Select(v => Math.Max(0, Math.Min(1, v))).ToArray(),
|
||
ExtractionTimeUtc = features.ExtractionTimeUtc
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 训练随机森林模型。
|
||
/// </summary>
|
||
private async Task<(double Accuracy, double Precision, double Recall, double F1Score)> TrainRandomForestModelAsync(double[][] featureVectors, int[] labels, CancellationToken cancellationToken = default)
|
||
{
|
||
await Task.Delay(100, cancellationToken); // 模拟训练时间
|
||
|
||
// 简化的模型训练结果
|
||
var accuracy = 0.85 + (DateTime.UtcNow.Millisecond % 100) / 1000.0;
|
||
var precision = 0.82 + (DateTime.UtcNow.Millisecond % 150) / 1000.0;
|
||
var recall = 0.88 + (DateTime.UtcNow.Millisecond % 120) / 1000.0;
|
||
var f1Score = 2 * (precision * recall) / (precision + recall);
|
||
|
||
return (accuracy, precision, recall, f1Score);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算层级变化次数。
|
||
/// </summary>
|
||
private int CountLayerTransitions(List<LayerHistoryRecord> records)
|
||
{
|
||
if (records.Count < 2) return 0;
|
||
|
||
var transitions = 0;
|
||
var sortedRecords = records.OrderBy(r => r.RecordTimeUtc).ToList();
|
||
|
||
for (int i = 1; i < sortedRecords.Count; i++)
|
||
{
|
||
if (sortedRecords[i].Layer != sortedRecords[i - 1].Layer)
|
||
{
|
||
transitions++;
|
||
}
|
||
}
|
||
|
||
return transitions;
|
||
}
|
||
|
||
#endregion
|
||
}
|