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