using MathNet.Numerics.Statistics; using OpenCvSharp; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TeamAAS_VP.Enums; using TeamAAS_VP.Models; namespace TeamAAS_VP.Core { /// /// 图像清晰度分析引擎 /// public class FocusAnalysisEngine { // 自定义Clamp方法 private static double Clamp(double value, double min, double max) { return value < min ? min : (value > max ? max : value); } private static int Clamp(int value, int min, int max) { return value < min ? min : (value > max ? max : value); } /// /// 计算图像清晰度 /// public static double CalculateImageSharpness(Mat image, FocusMethod method, Rect? roi = null) { if (image == null || image.Empty()) throw new ArgumentException("输入图像无效"); // 转换为灰度图 Mat gray = new Mat(); if (image.Channels() == 3) Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY); else gray = image.Clone(); // 应用ROI if (roi.HasValue) { gray = new Mat(gray, roi.Value); } double sharpness = 0; switch (method) { case FocusMethod.Tenengrad: sharpness = CalculateTenengrad(gray); break; case FocusMethod.Laplacian: sharpness = CalculateLaplacianVariance(gray); break; case FocusMethod.Brenner: sharpness = CalculateBrenner(gray); break; case FocusMethod.GrayVariance: sharpness = CalculateGrayVariance(gray); break; case FocusMethod.SMD: sharpness = CalculateSMD(gray); break; } gray.Dispose(); return sharpness; } private static double CalculateTenengrad(Mat gray) { Mat gradX = new Mat(); Mat gradY = new Mat(); Mat magnitude = new Mat(); Cv2.Sobel(gray, gradX, MatType.CV_32F, 1, 0, 3); Cv2.Sobel(gray, gradY, MatType.CV_32F, 0, 1, 3); Cv2.Magnitude(gradX, gradY, magnitude); Scalar mean = Cv2.Mean(magnitude); gradX.Dispose(); gradY.Dispose(); magnitude.Dispose(); return mean.Val0; } private static double CalculateLaplacianVariance(Mat gray) { Mat laplacian = new Mat(); Cv2.Laplacian(gray, laplacian, MatType.CV_32F); Mat mean = new Mat(); Mat stdDev = new Mat(); Cv2.MeanStdDev(laplacian, mean, stdDev); double variance = stdDev.Get(0); variance = variance * variance; laplacian.Dispose(); mean.Dispose(); stdDev.Dispose(); return variance; } private static double CalculateBrenner(Mat gray) { double sum = 0; int width = gray.Cols; int height = gray.Rows; for (int y = 0; y < height; y++) { for (int x = 0; x < width - 2; x++) { int diff = gray.Get(y, x + 2) - gray.Get(y, x); sum += diff * diff; } } return sum / (width * height); } private static double CalculateGrayVariance(Mat gray) { Mat mean = new Mat(); Mat stdDev = new Mat(); Cv2.MeanStdDev(gray, mean, stdDev); double variance = stdDev.Get(0); variance = variance * variance; mean.Dispose(); stdDev.Dispose(); return variance; } private static double CalculateSMD(Mat gray) { double sum = 0; int width = gray.Cols; int height = gray.Rows; for (int y = 0; y < height; y++) { for (int x = 0; x < width - 1; x++) { sum += Math.Abs(gray.Get(y, x + 1) - gray.Get(y, x)); } } for (int y = 0; y < height - 1; y++) { for (int x = 0; x < width; x++) { sum += Math.Abs(gray.Get(y + 1, x) - gray.Get(y, x)); } } return sum / (width * height); } /// /// 分析棋盘格对比度 /// public static CheckerboardResult AnalyzeCheckerboardContrast(Mat image, Rect? roi = null) { var result = new CheckerboardResult(); try { // 转换为灰度图 Mat gray = new Mat(); if (image.Channels() == 3) Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY); else gray = image.Clone(); // 应用ROI if (roi.HasValue) { gray = new Mat(gray, roi.Value); } // 简化版的棋盘格检测 bool detected = TryDetectCheckerboard(gray, out double blackMean, out double whiteMean); if (detected) { result.Detected = true; result.BlackMean = blackMean; result.WhiteMean = whiteMean; result.Contrast = Math.Abs(whiteMean - blackMean); result.OptimalBrightness = CalculateOptimalBrightness(blackMean, whiteMean); } gray.Dispose(); } catch (Exception ex) { Console.WriteLine($"棋盘格分析出错: {ex.Message}"); } return result; } private static bool TryDetectCheckerboard(Mat gray, out double blackMean, out double whiteMean) { blackMean = 0; whiteMean = 0; try { // 使用自适应阈值进行二值化 Mat binary = new Mat(); Cv2.AdaptiveThreshold(gray, binary, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2); // 查找轮廓 var contours = Cv2.FindContoursAsArray(binary, RetrievalModes.External, ContourApproximationModes.ApproxSimple); // 寻找近似矩形的轮廓(可能是棋盘格) var rectangles = new List(); foreach (var contour in contours) { var poly = Cv2.ApproxPolyDP(contour, 0.02 * Cv2.ArcLength(contour, true), true); if (poly.Length == 4) // 四边形 { var rect = Cv2.BoundingRect(contour); if (rect.Width > 50 && rect.Height > 50) // 忽略太小的区域 { rectangles.Add(rect); } } } if (rectangles.Count >= 2) { // 采样黑色和白色区域 blackMean = SampleRegionMean(gray, rectangles[0]); whiteMean = SampleRegionMean(gray, rectangles.Count > 1 ? rectangles[1] : rectangles[0]); // 确保黑色比白色暗 if (blackMean > whiteMean) { double temp = blackMean; blackMean = whiteMean; whiteMean = temp; } return true; } binary.Dispose(); } catch { // 如果检测失败,返回false } return false; } private static double SampleRegionMean(Mat gray, Rect region) { var sample = new Mat(gray, region); Scalar mean = Cv2.Mean(sample); sample.Dispose(); return mean.Val0; } private static int CalculateOptimalBrightness(double blackMean, double whiteMean) { double currentMid = (blackMean + whiteMean) / 2; return (int)Clamp(128 + (128 - currentMid), 30, 225); } /// /// 计算图像质量评分 /// public static double CalculateQualityScore(double sharpness, CheckerboardResult checkerboard) { // 清晰度评分(0-50分) double sharpnessScore = Clamp(sharpness / 0.5, 0, 50); // 对比度评分(0-50分) double contrastScore = 0; if (checkerboard.Detected) { contrastScore = Clamp(checkerboard.Contrast / 2, 0, 50); } return sharpnessScore + contrastScore; } /// /// 生成建议 /// public static List GenerateSuggestions(double sharpness, CheckerboardResult checkerboard, double sharpnessThreshold, double contrastThreshold) { var suggestions = new List(); // 清晰度建议 if (sharpness < sharpnessThreshold) suggestions.Add("图像模糊,请调整焦距使图像变清晰"); else if (sharpness < sharpnessThreshold * 2) suggestions.Add("清晰度一般,可以继续微调焦距"); else suggestions.Add("图像清晰度良好"); // 棋盘格建议 if (checkerboard.Detected) { if (checkerboard.Contrast < contrastThreshold) suggestions.Add("棋盘格对比度过低,建议调整光源"); else if (checkerboard.Contrast < contrastThreshold * 2) suggestions.Add("棋盘格对比度适中"); else suggestions.Add("棋盘格对比度很好"); suggestions.Add($"建议亮度值: {checkerboard.OptimalBrightness}"); } else { suggestions.Add("未检测到棋盘格,请确保棋盘格在视野中"); } return suggestions; } /// /// 使用MathNet计算统计信息 /// public static StatisticalSummary CalculateStatistics(List data) { if (data == null || data.Count == 0) return new StatisticalSummary(); // 使用MathNet.Numerics.Statistics计算 var stats = MathNet.Numerics.Statistics.Statistics.MeanVariance(data); return new StatisticalSummary { Mean = stats.Item1, // 均值 Variance = stats.Item2, // 方差 StdDev = Math.Sqrt(stats.Item2), // 标准差 Min = data.Min(), Max = data.Max(), Count = data.Count }; } /// /// 使用MathNet计算更详细的统计信息 /// public static StatisticalSummary CalculateDetailedStatistics(List data) { if (data == null || data.Count == 0) return new StatisticalSummary(); // 使用DescriptiveStatistics获取更多统计信息 var descriptiveStats = new MathNet.Numerics.Statistics.DescriptiveStatistics(data); return new StatisticalSummary { Mean = descriptiveStats.Mean, Variance = descriptiveStats.Variance, StdDev = descriptiveStats.StandardDeviation, Min = descriptiveStats.Minimum, Max = descriptiveStats.Maximum, Count = data.Count, // 还可以添加更多统计信息 Skewness = descriptiveStats.Skewness, Kurtosis = descriptiveStats.Kurtosis }; } /// /// 计算移动平均 /// public static List CalculateMovingAverage(List data, int windowSize) { if (data == null || data.Count == 0 || windowSize <= 0) return new List(); var result = new List(); for (int i = 0; i < data.Count; i++) { int start = Math.Max(0, i - windowSize + 1); int count = Math.Min(windowSize, i + 1); double sum = 0; for (int j = start; j <= i; j++) { sum += data[j]; } result.Add(sum / count); } return result; } /// /// 计算指数移动平均 /// public static List CalculateExponentialMovingAverage(List data, double alpha) { if (data == null || data.Count == 0 || alpha <= 0 || alpha > 1) return new List(); var result = new List { data[0] }; for (int i = 1; i < data.Count; i++) { double ema = alpha * data[i] + (1 - alpha) * result[i - 1]; result.Add(ema); } return result; } /// /// 检测峰值 /// public static List DetectPeaks(List data, double threshold = 0.5) { var peaks = new List(); if (data == null || data.Count < 3) return peaks; var stats = CalculateStatistics(data); double mean = stats.Mean; double stdDev = stats.StdDev; for (int i = 1; i < data.Count - 1; i++) { if (data[i] > data[i - 1] && data[i] > data[i + 1]) { // 峰值需要超过阈值 if (data[i] > mean + threshold * stdDev) { peaks.Add(i); } } } return peaks; } /// /// 计算趋势线(线性回归) /// public static (double slope, double intercept) CalculateTrendLine(List data) { if (data == null || data.Count == 0) return (0, 0); var xData = Enumerable.Range(0, data.Count).Select(x => (double)x).ToArray(); var yData = data.ToArray(); // 使用MathNet进行线性回归 var fit = MathNet.Numerics.Fit.Line(xData, yData); return (fit.Item2, fit.Item1); // slope, intercept } /// /// 计算自相关性 /// public static List CalculateAutocorrelation(List data, int maxLag = 20) { if (data == null || data.Count == 0) return new List(); var autocorr = new List(); var stats = CalculateStatistics(data); double mean = stats.Mean; double variance = stats.Variance; if (variance == 0) return autocorr; int n = data.Count; maxLag = Math.Min(maxLag, n - 1); for (int lag = 0; lag <= maxLag; lag++) { double sum = 0; for (int i = 0; i < n - lag; i++) { sum += (data[i] - mean) * (data[i + lag] - mean); } autocorr.Add(sum / ((n - lag) * variance)); } return autocorr; } } /// /// 扩展的统计摘要类 /// public class StatisticalSummary { public double Mean { get; set; } public double Variance { get; set; } public double StdDev { get; set; } public double Min { get; set; } public double Max { get; set; } public int Count { get; set; } // 扩展的统计信息 public double Skewness { get; set; } // 偏度 public double Kurtosis { get; set; } // 峰度 // 分位数(可选) public double Median { get; set; } public double Q1 { get; set; } // 第一四分位数 public double Q3 { get; set; } // 第三四分位数 public double IQR => Q3 - Q1; // 四分位距 /// /// 计算分位数 /// public void CalculateQuantiles(List data) { if (data == null || data.Count == 0) return; var sortedData = data.OrderBy(x => x).ToList(); // 中位数 Median = MathNet.Numerics.Statistics.Statistics.Median(sortedData); // 四分位数 Q1 = MathNet.Numerics.Statistics.Statistics.Quantile(sortedData, 0.25); Q3 = MathNet.Numerics.Statistics.Statistics.Quantile(sortedData, 0.75); } /// /// 生成统计摘要字符串 /// public string ToSummaryString() { return $@"统计摘要: 样本数量: {Count} 均值: {Mean:F3} 标准差: {StdDev:F3} 最小值: {Min:F3} 最大值: {Max:F3} 范围: {Max - Min:F3} 方差: {Variance:F3} 偏度: {Skewness:F3} 峰度: {Kurtosis:F3}"; } /// /// 获取详细统计信息(包含分位数) /// public string ToDetailedString() { return $@"详细统计信息: 样本数量: {Count} 均值: {Mean:F3} ± {StdDev:F3} 中位数: {Median:F3} 第一四分位数(Q1): {Q1:F3} 第三四分位数(Q3): {Q3:F3} 四分位距(IQR): {IQR:F3} 最小值: {Min:F3} 最大值: {Max:F3} 范围: {Max - Min:F3} 方差: {Variance:F3} 标准差: {StdDev:F3} 变异系数: {(StdDev / Mean * 100):F1}% 偏度: {Skewness:F3} 峰度: {Kurtosis:F3}"; } } /// /// 分析结果增强类 /// public class EnhancedAnalysisResult : AnalysisResult { public StatisticalSummary SharpnessStats { get; set; } public StatisticalSummary ContrastStats { get; set; } public List SharpnessPeaks { get; set; } public double TrendSlope { get; set; } public double TrendIntercept { get; set; } public double AutocorrelationAtLag1 { get; set; } public EnhancedAnalysisResult() { SharpnessStats = new StatisticalSummary(); ContrastStats = new StatisticalSummary(); SharpnessPeaks = new List(); } } /// /// 增强的分析引擎 /// public static class EnhancedFocusAnalysisEngine { /// /// 执行增强分析 /// public static EnhancedAnalysisResult PerformEnhancedAnalysis( List historyData, AnalysisResult baseResult) { var enhancedResult = new EnhancedAnalysisResult { // 复制基础结果 Sharpness = baseResult.Sharpness, Checkerboard = baseResult.Checkerboard, QualityScore = baseResult.QualityScore, FrameCount = baseResult.FrameCount, BestSharpness = baseResult.BestSharpness, AnalysisTime = baseResult.AnalysisTime, Suggestions = baseResult.Suggestions }; if (historyData == null || historyData.Count == 0) return enhancedResult; // 提取清晰度和对比度数据 var sharpnessData = historyData.Select(d => d.Sharpness).ToList(); var contrastData = historyData .Where(d => d.Contrast > 0) .Select(d => d.Contrast) .ToList(); // 计算统计信息 enhancedResult.SharpnessStats = FocusAnalysisEngine.CalculateDetailedStatistics(sharpnessData); if (contrastData.Count > 0) { enhancedResult.ContrastStats = FocusAnalysisEngine.CalculateDetailedStatistics(contrastData); } // 计算分位数 enhancedResult.SharpnessStats.CalculateQuantiles(sharpnessData); // 检测峰值 enhancedResult.SharpnessPeaks = FocusAnalysisEngine.DetectPeaks(sharpnessData, 1.0); // 计算趋势线 var trend = FocusAnalysisEngine.CalculateTrendLine(sharpnessData); enhancedResult.TrendSlope = trend.slope; enhancedResult.TrendIntercept = trend.intercept; // 计算自相关性 var autocorr = FocusAnalysisEngine.CalculateAutocorrelation(sharpnessData, 1); if (autocorr.Count > 1) { enhancedResult.AutocorrelationAtLag1 = autocorr[1]; } return enhancedResult; } /// /// 生成增强分析报告 /// public static string GenerateEnhancedReport(EnhancedAnalysisResult result) { var report = new System.Text.StringBuilder(); report.AppendLine("=== 增强分析报告 ==="); report.AppendLine($"分析时间: {result.AnalysisTime:yyyy-MM-dd HH:mm:ss}"); report.AppendLine($"总帧数: {result.FrameCount}"); report.AppendLine(); report.AppendLine("一、清晰度统计分析"); report.AppendLine(result.SharpnessStats.ToDetailedString()); report.AppendLine(); report.AppendLine("二、趋势分析"); report.AppendLine($"趋势线斜率: {result.TrendSlope:F6}"); report.AppendLine($"趋势线截距: {result.TrendIntercept:F3}"); report.AppendLine($"自相关性(lag=1): {result.AutocorrelationAtLag1:F3}"); report.AppendLine($"检测到峰值数量: {result.SharpnessPeaks.Count}"); if (result.SharpnessPeaks.Any()) { report.Append("峰值位置(帧): "); report.AppendLine(string.Join(", ", result.SharpnessPeaks)); } report.AppendLine(); if (result.ContrastStats.Count > 0) { report.AppendLine("三、对比度统计分析"); report.AppendLine(result.ContrastStats.ToSummaryString()); report.AppendLine(); } report.AppendLine("四、分析建议"); if (result.SharpnessPeaks.Count > 0) { int lastPeak = result.SharpnessPeaks.Last(); report.AppendLine($"发现清晰度峰值,最后峰值在第 {lastPeak + 1} 帧"); if (result.TrendSlope < -0.01) report.AppendLine("清晰度呈下降趋势,可能已过最佳对焦点"); else if (result.TrendSlope > 0.01) report.AppendLine("清晰度呈上升趋势,可继续当前方向调整"); else report.AppendLine("清晰度趋势平稳"); } if (Math.Abs(result.AutocorrelationAtLag1) > 0.5) { report.AppendLine("数据具有较强自相关性,表明调整过程平稳"); } return report.ToString(); } } }