| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- 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
- {
- /// <summary>
- /// 图像清晰度分析引擎
- /// </summary>
- 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);
- }
- /// <summary>
- /// 计算图像清晰度
- /// </summary>
- 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<double>(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<byte>(y, x + 2) - gray.Get<byte>(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<double>(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<byte>(y, x + 1) - gray.Get<byte>(y, x));
- }
- }
- for (int y = 0; y < height - 1; y++)
- {
- for (int x = 0; x < width; x++)
- {
- sum += Math.Abs(gray.Get<byte>(y + 1, x) - gray.Get<byte>(y, x));
- }
- }
- return sum / (width * height);
- }
- /// <summary>
- /// 分析棋盘格对比度
- /// </summary>
- 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<Rect>();
- 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);
- }
- /// <summary>
- /// 计算图像质量评分
- /// </summary>
- 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;
- }
- /// <summary>
- /// 生成建议
- /// </summary>
- public static List<string> GenerateSuggestions(double sharpness, CheckerboardResult checkerboard,
- double sharpnessThreshold, double contrastThreshold)
- {
- var suggestions = new List<string>();
- // 清晰度建议
- 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;
- }
- /// <summary>
- /// 使用MathNet计算统计信息
- /// </summary>
- public static StatisticalSummary CalculateStatistics(List<double> 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
- };
- }
- /// <summary>
- /// 使用MathNet计算更详细的统计信息
- /// </summary>
- public static StatisticalSummary CalculateDetailedStatistics(List<double> 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
- };
- }
- /// <summary>
- /// 计算移动平均
- /// </summary>
- public static List<double> CalculateMovingAverage(List<double> data, int windowSize)
- {
- if (data == null || data.Count == 0 || windowSize <= 0)
- return new List<double>();
- var result = new List<double>();
- 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;
- }
- /// <summary>
- /// 计算指数移动平均
- /// </summary>
- public static List<double> CalculateExponentialMovingAverage(List<double> data, double alpha)
- {
- if (data == null || data.Count == 0 || alpha <= 0 || alpha > 1)
- return new List<double>();
- var result = new List<double> { 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;
- }
- /// <summary>
- /// 检测峰值
- /// </summary>
- public static List<int> DetectPeaks(List<double> data, double threshold = 0.5)
- {
- var peaks = new List<int>();
- 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;
- }
- /// <summary>
- /// 计算趋势线(线性回归)
- /// </summary>
- public static (double slope, double intercept) CalculateTrendLine(List<double> 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
- }
- /// <summary>
- /// 计算自相关性
- /// </summary>
- public static List<double> CalculateAutocorrelation(List<double> data, int maxLag = 20)
- {
- if (data == null || data.Count == 0)
- return new List<double>();
- var autocorr = new List<double>();
- 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;
- }
- }
- /// <summary>
- /// 扩展的统计摘要类
- /// </summary>
- 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; // 四分位距
- /// <summary>
- /// 计算分位数
- /// </summary>
- public void CalculateQuantiles(List<double> 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);
- }
- /// <summary>
- /// 生成统计摘要字符串
- /// </summary>
- public string ToSummaryString()
- {
- return $@"统计摘要:
- 样本数量: {Count}
- 均值: {Mean:F3}
- 标准差: {StdDev:F3}
- 最小值: {Min:F3}
- 最大值: {Max:F3}
- 范围: {Max - Min:F3}
- 方差: {Variance:F3}
- 偏度: {Skewness:F3}
- 峰度: {Kurtosis:F3}";
- }
- /// <summary>
- /// 获取详细统计信息(包含分位数)
- /// </summary>
- 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}";
- }
- }
- /// <summary>
- /// 分析结果增强类
- /// </summary>
- public class EnhancedAnalysisResult : AnalysisResult
- {
- public StatisticalSummary SharpnessStats { get; set; }
- public StatisticalSummary ContrastStats { get; set; }
- public List<int> 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<int>();
- }
- }
- /// <summary>
- /// 增强的分析引擎
- /// </summary>
- public static class EnhancedFocusAnalysisEngine
- {
- /// <summary>
- /// 执行增强分析
- /// </summary>
- public static EnhancedAnalysisResult PerformEnhancedAnalysis(
- List<DataPoint> 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;
- }
- /// <summary>
- /// 生成增强分析报告
- /// </summary>
- 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();
- }
- }
- }
|