| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- using TeamAAS_VP.Enums;
- using TeamAAS_VP.Models;
- using OpenCvSharp;
- using System;
- using TeamAAS_VP.Resources.Languages;
- namespace TeamAAS_VP.Services
- {
- /// <summary>
- /// 图像分析服务
- /// </summary>
- public class ImageAnalysisService
- {
- /// <summary>
- /// 分析图像清晰度和对比度
- /// </summary>
- public AnalysisResult AnalyzeImage(Mat image, FocusMethod method, Rect? roi = null)
- {
- var result = new AnalysisResult { Timestamp = DateTime.Now };
- if (image == null || image.Empty())
- {
- result.Message = Lang.图像为空;
- return result;
- }
- try
- {
- Mat analyzeMat = image;
- // 如果指定了ROI,则裁剪图像
- if (roi.HasValue && roi.Value.Width > 0 && roi.Value.Height > 0)
- {
- var roiRect = roi.Value;
- // 确保ROI在图像范围内
- roiRect.X = Math.Max(0, Math.Min(roiRect.X, image.Width - 1));
- roiRect.Y = Math.Max(0, Math.Min(roiRect.Y, image.Height - 1));
- roiRect.Width = Math.Min(roiRect.Width, image.Width - roiRect.X);
- roiRect.Height = Math.Min(roiRect.Height, image.Height - roiRect.Y);
- if (roiRect.Width > 0 && roiRect.Height > 0)
- {
- analyzeMat = new Mat(image, roiRect);
- }
- }
- // 转换为灰度图
- Mat gray = new Mat();
- if (analyzeMat.Channels() == 3)
- {
- Cv2.CvtColor(analyzeMat, gray, ColorConversionCodes.BGR2GRAY);
- }
- else
- {
- gray = analyzeMat.Clone();
- }
- // 计算清晰度
- result.Sharpness = CalculateSharpness(gray, method);
- // 计算对比度
- result.Contrast = CalculateContrast(gray);
- // 计算质量评分 (0-100)
- result.QualityScore = CalculateQualityScore(result.Sharpness, result.Contrast, method);
- result.Message = Lang.分析完成;
- gray?.Dispose();
- if (roi.HasValue && analyzeMat != image)
- {
- analyzeMat?.Dispose();
- }
- }
- catch (Exception ex)
- {
- result.Message = string.Format(Lang.分析错误0,ex.Message);
- }
- return result;
- }
- /// <summary>
- /// 计算清晰度
- /// </summary>
- private double CalculateSharpness(Mat gray, FocusMethod method)
- {
- switch (method)
- {
- case FocusMethod.Laplacian:
- return CalculateLaplacianVariance(gray);
- case FocusMethod.Sobel:
- return CalculateSobelVariance(gray);
- case FocusMethod.Variance:
- return CalculateImageVariance(gray);
- case FocusMethod.Tenengrad:
- return CalculateTenengrad(gray);
- case FocusMethod.FrequencyDomain:
- return CalculateFrequencyDomain(gray);
- default:
- return CalculateLaplacianVariance(gray);
- }
- }
- /// <summary>
- /// Laplacian方差法
- /// </summary>
- private double CalculateLaplacianVariance(Mat gray)
- {
- using (var laplacian = new Mat())
- {
- Cv2.Laplacian(gray, laplacian, MatType.CV_64F);
- Cv2.MeanStdDev(laplacian, out _, out Scalar stddev);
- return stddev.Val0 * stddev.Val0;
- }
- }
- /// <summary>
- /// Sobel方差法
- /// </summary>
- private double CalculateSobelVariance(Mat gray)
- {
- using (var sobelX = new Mat())
- using (var sobelY = new Mat())
- using (var sobel = new Mat())
- {
- Cv2.Sobel(gray, sobelX, MatType.CV_64F, 1, 0, 3);
- Cv2.Sobel(gray, sobelY, MatType.CV_64F, 0, 1, 3);
-
- Cv2.Magnitude(sobelX, sobelY, sobel);
-
- return Cv2.Mean(sobel).Val0;
- }
- }
- /// <summary>
- /// 图像方差法
- /// </summary>
- private double CalculateImageVariance(Mat gray)
- {
- Cv2.MeanStdDev(gray, out _, out Scalar stddev);
- return stddev.Val0 * stddev.Val0;
- }
- /// <summary>
- /// Tenengrad算子
- /// </summary>
- private double CalculateTenengrad(Mat gray)
- {
- using (var sobelX = new Mat())
- using (var sobelY = new Mat())
- {
- Cv2.Sobel(gray, sobelX, MatType.CV_64F, 1, 0, 3);
- Cv2.Sobel(gray, sobelY, MatType.CV_64F, 0, 1, 3);
- double sum = 0;
- for (int y = 0; y < gray.Rows; y++)
- {
- for (int x = 0; x < gray.Cols; x++)
- {
- double gx = sobelX.At<double>(y, x);
- double gy = sobelY.At<double>(y, x);
- double magnitude = Math.Sqrt(gx * gx + gy * gy);
-
- // 只计算超过阈值的梯度
- if (magnitude > 50)
- {
- sum += magnitude * magnitude;
- }
- }
- }
- return sum / (gray.Rows * gray.Cols);
- }
- }
- /// <summary>
- /// 频域分析法
- /// </summary>
- private double CalculateFrequencyDomain(Mat gray)
- {
- try
- {
- // 转换为浮点型
- Mat floatMat = new Mat();
- gray.ConvertTo(floatMat, MatType.CV_32F);
- // 执行DFT
- Mat dft = new Mat();
- Cv2.Dft(floatMat, dft, DftFlags.ComplexOutput);
- // 计算幅度谱
- Mat[] planes = Cv2.Split(dft);
- Mat magnitude = new Mat();
- Cv2.Magnitude(planes[0], planes[1], magnitude);
- // 计算高频能量占比
- int centerX = magnitude.Cols / 2;
- int centerY = magnitude.Rows / 2;
- int radius = Math.Min(centerX, centerY) / 3;
- double totalEnergy = Cv2.Sum(magnitude).Val0;
-
- // 屏蔽低频区域
- Cv2.Circle(magnitude, new Point(centerX, centerY), radius, Scalar.All(0), -1);
-
- double highFreqEnergy = Cv2.Sum(magnitude).Val0;
- floatMat?.Dispose();
- dft?.Dispose();
- foreach (var plane in planes) plane?.Dispose();
- magnitude?.Dispose();
- return totalEnergy > 0 ? (highFreqEnergy / totalEnergy) * 100 : 0;
- }
- catch
- {
- return 0;
- }
- }
- /// <summary>
- /// 计算对比度
- /// </summary>
- private double CalculateContrast(Mat gray)
- {
- try
- {
- // 使用标准差作为对比度指标
- Cv2.MeanStdDev(gray, out Scalar mean, out Scalar stddev);
-
- // 归一化对比度 (0-100)
- double contrast = (stddev.Val0 / 128.0) * 100;
-
- return Math.Min(100, contrast);
- }
- catch
- {
- return 0;
- }
- }
- /// <summary>
- /// 计算质量评分
- /// </summary>
- private double CalculateQualityScore(double sharpness, double contrast, FocusMethod method)
- {
- // 根据不同的算法,设置不同的归一化参数
- double normalizedSharpness = 0;
- switch (method)
- {
- case FocusMethod.Laplacian:
- normalizedSharpness = Math.Min(100, (sharpness / 50.0) * 100);
- break;
- case FocusMethod.Sobel:
- normalizedSharpness = Math.Min(100, (sharpness / 30.0) * 100);
- break;
- case FocusMethod.Variance:
- normalizedSharpness = Math.Min(100, (sharpness / 5000.0) * 100);
- break;
- case FocusMethod.Tenengrad:
- normalizedSharpness = Math.Min(100, (sharpness / 1000.0) * 100);
- break;
- case FocusMethod.FrequencyDomain:
- normalizedSharpness = sharpness;
- break;
- }
- // 综合清晰度和对比度
- double score = (normalizedSharpness * 0.7 + contrast * 0.3);
-
- return Math.Min(100, Math.Max(0, score));
- }
- /// <summary>
- /// 获取ROI区域图像
- /// </summary>
- public Mat GetRoiImage(Mat image, Rect roi)
- {
- if (image == null || image.Empty())
- return null;
- try
- {
- // 确保ROI在图像范围内
- roi.X = Math.Max(0, Math.Min(roi.X, image.Width - 1));
- roi.Y = Math.Max(0, Math.Min(roi.Y, image.Height - 1));
- roi.Width = Math.Min(roi.Width, image.Width - roi.X);
- roi.Height = Math.Min(roi.Height, image.Height - roi.Y);
- if (roi.Width > 0 && roi.Height > 0)
- {
- return new Mat(image, roi).Clone();
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(string.Format(Lang.获取ROI图像错误0,ex.Message));
- }
- return null;
- }
- }
- }
|