using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using OpenCvSharp; namespace TeamAAS_VP.Core { public class CameraFocusAnalyzer { // 清晰度评估方法枚举 public enum FocusMethod { Tenengrad, // Tenengrad梯度法(推荐) Laplacian, // 拉普拉斯方差法 Brenner, // Brenner梯度法 GrayVariance, // 灰度方差法 SMD // 灰度差分法 } /// /// 计算图像清晰度(多种方法) /// public static double CalculateImageSharpness(Mat image, FocusMethod method = FocusMethod.Tenengrad, 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; } /// /// Tenengrad梯度法(对噪声敏感度较低) /// 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; } /// /// Brenner梯度法(简单快速) /// 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; } /// /// 灰度差分法(SMD) /// 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 CheckerboardContrastResult AnalyzeCheckerboardContrast(Mat image, Rect? roi = null) { var result = new CheckerboardContrastResult { Success = false, BlackMean = 0, WhiteMean = 0, Contrast = 0, OptimalBrightness = 128 }; if (image == null || image.Empty()) return result; 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); } // 1. 自适应检测棋盘格角点 var corners = DetectCheckerboardCorners(gray); if (corners == null || corners.Length < 20) // 至少需要一定数量的角点 { gray.Dispose(); return result; } // 2. 自动分析网格结构 var gridInfo = AnalyzeGridStructure(corners); if (!gridInfo.IsValid) { gray.Dispose(); return result; } // 3. 采样黑白格子像素 SampleCheckerboardColors(gray, corners, gridInfo, out double blackMean, out double whiteMean, out int sampleCount); if (sampleCount > 0) { result.BlackMean = blackMean; result.WhiteMean = whiteMean; result.Contrast = Math.Abs(whiteMean - blackMean); result.OptimalBrightness = CalculateOptimalBrightness(blackMean, whiteMean); result.Success = true; // 计算图像质量评分 result.QualityScore = CalculateQualityScore(result.Contrast, blackMean, whiteMean); } gray.Dispose(); return result; } catch (Exception ex) { Console.WriteLine($"棋盘格分析出错: {ex.Message}"); return result; } } /// /// 自适应检测棋盘格角点 /// private static Point2f[] DetectCheckerboardCorners(Mat gray) { // 首先尝试标准的棋盘格检测 for (int boardWidth = 5; boardWidth <= 15; boardWidth++) { for (int boardHeight = 5; boardHeight <= 15; boardHeight++) { Size patternSize = new Size(boardWidth, boardHeight); Point2f[] corners; bool found = Cv2.FindChessboardCorners(gray, patternSize, out corners, ChessboardFlags.AdaptiveThresh | ChessboardFlags.NormalizeImage | ChessboardFlags.FastCheck); if (found && corners.Length > 0) { // 提高角点精度 Mat grayFloat = new Mat(); gray.ConvertTo(grayFloat, MatType.CV_32F); Cv2.CornerSubPix(grayFloat, corners, new Size(5, 5), new Size(-1, -1), new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.MaxIter, 30, 0.1)); grayFloat.Dispose(); return corners; } } } // 如果标准方法失败,尝试使用角点检测 return DetectCornersByHarris(gray); } /// /// 使用Harris角点检测作为备选方案 /// private static Point2f[] DetectCornersByHarris(Mat gray) { Mat cornersMat = new Mat(); Mat grayFloat = new Mat(); gray.ConvertTo(grayFloat, MatType.CV_32F); // Harris角点检测 Cv2.CornerHarris(grayFloat, cornersMat, 2, 3, 0.04); // 找到局部最大值 var corners = new List(); double maxVal = 0; Cv2.MinMaxLoc(cornersMat, out _, out maxVal); float threshold = (float)(maxVal * 0.01); for (int y = 0; y < cornersMat.Rows; y++) { for (int x = 0; x < cornersMat.Cols; x++) { float value = cornersMat.Get(y, x); if (value > threshold) { // 检查是否是局部最大值 bool isLocalMax = true; int neighborhood = 5; for (int dy = -neighborhood; dy <= neighborhood && isLocalMax; dy++) { for (int dx = -neighborhood; dx <= neighborhood && isLocalMax; dx++) { int ny = y + dy; int nx = x + dx; if (ny >= 0 && ny < cornersMat.Rows && nx >= 0 && nx < cornersMat.Cols && !(dy == 0 && dx == 0)) { if (cornersMat.Get(ny, nx) > value) { isLocalMax = false; } } } } if (isLocalMax) { corners.Add(new Point2f(x, y)); } } } } grayFloat.Dispose(); cornersMat.Dispose(); return corners.ToArray(); } /// /// 分析网格结构 /// private static GridStructureInfo AnalyzeGridStructure(Point2f[] corners) { var info = new GridStructureInfo(); if (corners.Length < 9) // 至少3x3网格 return info; // 估计网格间距 List distances = new List(); for (int i = 0; i < corners.Length; i++) { for (int j = i + 1; j < Math.Min(i + 10, corners.Length); j++) // 限制检查数量 { double dist = Math.Sqrt(Math.Pow(corners[j].X - corners[i].X, 2) + Math.Pow(corners[j].Y - corners[i].Y, 2)); if (dist > 5) // 忽略太近的点 distances.Add(dist); } } if (distances.Count == 0) return info; // 找到最常见的距离作为网格间距 distances.Sort(); double estimatedSpacing = distances[distances.Count / 2]; // 中位数 // 对角落点进行排序(按大致位置) var sortedCorners = corners.OrderBy(p => p.Y).ThenBy(p => p.X).ToArray(); // 尝试分组到行和列 double tolerance = estimatedSpacing * 0.3; info.IsValid = true; info.EstimatedSpacing = estimatedSpacing; info.Corners = sortedCorners; return info; } /// /// 采样棋盘格颜色 /// private static void SampleCheckerboardColors(Mat gray, Point2f[] corners, GridStructureInfo gridInfo, out double blackMean, out double whiteMean, out int sampleCount) { blackMean = 0; whiteMean = 0; int blackCount = 0; int whiteCount = 0; sampleCount = 0; double spacing = gridInfo.EstimatedSpacing; int sampleRadius = (int)(spacing * 0.3); // 对每个角点区域采样 for (int i = 0; i < corners.Length; i++) { int x = (int)corners[i].X; int y = (int)corners[i].Y; // 采样区域 int x1 = Math.Max(0, x - sampleRadius); int y1 = Math.Max(0, y - sampleRadius); int x2 = Math.Min(gray.Cols - 1, x + sampleRadius); int y2 = Math.Min(gray.Rows - 1, y + sampleRadius); if (x2 > x1 && y2 > y1) { Rect roi = new Rect(x1, y1, x2 - x1, y2 - y1); Mat sample = new Mat(gray, roi); Scalar mean = Cv2.Mean(sample); sample.Dispose(); double value = mean.Val0; // 根据位置判断黑白(奇偶性) int gridX = (int)(x / spacing); int gridY = (int)(y / spacing); bool isBlack = (gridX + gridY) % 2 == 0; if (isBlack) { blackMean += value; blackCount++; } else { whiteMean += value; whiteCount++; } sampleCount++; } } if (blackCount > 0) blackMean /= blackCount; if (whiteCount > 0) whiteMean /= whiteCount; // 确保黑色比白色暗 if (blackMean > whiteMean) { // 交换 double temp = blackMean; blackMean = whiteMean; whiteMean = temp; } } /// /// 计算最佳亮度 /// private static int CalculateOptimalBrightness(double blackMean, double whiteMean) { double currentMid = (blackMean + whiteMean) / 2; int optimal = (int)Clamp(128 + (128 - currentMid), 30, 225); return optimal; } public static double Clamp(double value, double min, double max) { return value < min ? min : (value > max ? max : value); } /// /// 计算图像质量评分 /// private static double CalculateQualityScore(double contrast, double blackMean, double whiteMean) { // 对比度评分(0-100) double contrastScore = Math.Min(100, contrast / 2.55); // 亮度范围评分 double blackScore = Math.Max(0, 100 - Math.Abs(blackMean - 30)); double whiteScore = Math.Max(0, 100 - Math.Abs(whiteMean - 220)); double rangeScore = (blackScore + whiteScore) / 2; // 综合评分 double qualityScore = contrastScore * 0.6 + rangeScore * 0.4; return Math.Min(100, Math.Max(0, qualityScore)); } /// /// 一键获取图像质量报告 /// public static ImageQualityReport GetImageQualityReport(Mat image, Rect? roi = null) { var report = new ImageQualityReport(); if (image == null || image.Empty()) return report; // 计算清晰度 report.Sharpness = CalculateImageSharpness(image, FocusMethod.Tenengrad, roi); // 分析棋盘格对比度 var contrastResult = AnalyzeCheckerboardContrast(image, roi); if (contrastResult.Success) { report.HasCheckerboard = true; report.BlackMean = contrastResult.BlackMean; report.WhiteMean = contrastResult.WhiteMean; report.Contrast = contrastResult.Contrast; report.OptimalBrightness = contrastResult.OptimalBrightness; report.QualityScore = contrastResult.QualityScore; } else { // 如果没有检测到棋盘格,使用图像整体对比度 report.HasCheckerboard = false; report.Contrast = CalculateGlobalContrast(image, roi); report.OptimalBrightness = EstimateOptimalBrightness(image, roi); report.QualityScore = Math.Min(100, report.Contrast / 2.0); } // 给出建议 report.Suggestions = GenerateSuggestions(report); return report; } /// /// 计算全局对比度(无棋盘格时使用) /// private static double CalculateGlobalContrast(Mat image, Rect? roi = null) { Mat gray = new Mat(); if (image.Channels() == 3) Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY); else gray = image.Clone(); if (roi.HasValue) { gray = new Mat(gray, roi.Value); } Mat mean = new Mat(); Mat stdDev = new Mat(); Cv2.MeanStdDev(gray, mean, stdDev); double contrast = stdDev.Get(0) * 2; // 标准差的两倍作为对比度估计 gray.Dispose(); mean.Dispose(); stdDev.Dispose(); return contrast; } /// /// 估计最佳亮度(无棋盘格时使用) /// private static int EstimateOptimalBrightness(Mat image, Rect? roi = null) { Mat gray = new Mat(); if (image.Channels() == 3) Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY); else gray = image.Clone(); if (roi.HasValue) { gray = new Mat(gray, roi.Value); } Scalar mean = Cv2.Mean(gray); double avgGray = mean.Val0; // 根据平均灰度调整 int optimal; if (avgGray < 80) optimal = (int)Math.Min(255, avgGray + 80); else if (avgGray > 180) optimal = (int)Math.Max(0, avgGray - 80); else optimal = 128; gray.Dispose(); return optimal; } /// /// 生成调整建议 /// private static List GenerateSuggestions(ImageQualityReport report) { var suggestions = new List(); // 清晰度建议 if (report.Sharpness < 10) suggestions.Add("图像模糊,请调整焦距使图像变清晰"); else if (report.Sharpness < 20) suggestions.Add("清晰度一般,可以继续微调焦距"); else suggestions.Add("图像清晰度良好"); // 对比度建议 if (report.HasCheckerboard) { if (report.Contrast < 50) suggestions.Add("棋盘格对比度过低,建议调整光源或相机参数"); else if (report.Contrast < 100) suggestions.Add("棋盘格对比度适中"); else suggestions.Add("棋盘格对比度很好"); suggestions.Add($"建议亮度值: {report.OptimalBrightness}"); } else { if (report.Contrast < 30) suggestions.Add("图像整体对比度较低"); else suggestions.Add("图像对比度正常"); } // 质量评分建议 if (report.QualityScore < 60) suggestions.Add($"图像质量需要改进,当前评分: {report.QualityScore:F1}/100"); else if (report.QualityScore < 80) suggestions.Add($"图像质量良好,当前评分: {report.QualityScore:F1}/100"); else suggestions.Add($"图像质量优秀,当前评分: {report.QualityScore:F1}/100"); return suggestions; } } // 辅助数据结构 public class CheckerboardContrastResult { public bool Success { get; set; } public double BlackMean { get; set; } public double WhiteMean { get; set; } public double Contrast { get; set; } public int OptimalBrightness { get; set; } public double QualityScore { get; set; } } public class GridStructureInfo { public bool IsValid { get; set; } public double EstimatedSpacing { get; set; } public Point2f[] Corners { get; set; } } public class ImageQualityReport { public double Sharpness { get; set; } public bool HasCheckerboard { get; set; } public double BlackMean { get; set; } public double WhiteMean { get; set; } public double Contrast { get; set; } public int OptimalBrightness { get; set; } public double QualityScore { get; set; } public List Suggestions { get; set; } = new List(); } }