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();
}
}
}