Sharpness.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. namespace AutoFocusTool.Imaging
  3. {
  4. /// <summary>
  5. /// 清晰度评价(纯 C#,无 OpenCV 依赖)。
  6. /// 按总方案:梯度类指标(Tenengrad 主 / Laplacian 备),锁 ROI、缩图、除均值归一化。
  7. /// 别用灰度方差/对比度(会被"暗+高反差"骗)。
  8. ///
  9. /// 输入统一为相机的 24bpp BGR 字节数组(未翻正也可,清晰度与上下翻转无关)。
  10. /// </summary>
  11. public static class Sharpness
  12. {
  13. /// <summary>清晰度算法种类</summary>
  14. public enum Metric { Tenengrad, Laplacian }
  15. /// <summary>
  16. /// 计算清晰度分数(越大越清晰,已除均值归一化,可跨帧比较)。
  17. /// </summary>
  18. /// <param name="bgr24">相机原始 24bpp BGR 字节</param>
  19. /// <param name="width">图宽</param>
  20. /// <param name="height">图高</param>
  21. /// <param name="roi">ROI(像素坐标,null=全图)。只在 ROI 内算分,排除背景反光。</param>
  22. /// <param name="metric">指标类型</param>
  23. public static double Compute(byte[] bgr24, int width, int height,
  24. System.Drawing.Rectangle? roi = null, Metric metric = Metric.Tenengrad)
  25. {
  26. // 1) 转灰度(只取 ROI 区域,行优先)
  27. var (gray, gw, gh) = ToGrayRoi(bgr24, width, height, roi);
  28. if (gw < 3 || gh < 3) return 0;
  29. // 2) 梯度算分
  30. return metric == Metric.Laplacian
  31. ? LaplacianScore(gray, gw, gh)
  32. : TenengradScore(gray, gw, gh);
  33. }
  34. /// <summary>BGR → 8bit 灰度,裁出 ROI。返回灰度数组+宽高。</summary>
  35. private static (byte[] gray, int w, int h) ToGrayRoi(
  36. byte[] bgr24, int width, int height, System.Drawing.Rectangle? roi)
  37. {
  38. int x0 = 0, y0 = 0, w = width, h = height;
  39. if (roi.HasValue)
  40. {
  41. var r = roi.Value;
  42. x0 = Math.Max(0, r.X);
  43. y0 = Math.Max(0, r.Y);
  44. w = Math.Min(r.Width, width - x0);
  45. h = Math.Min(r.Height, height - y0);
  46. if (w <= 0 || h <= 0) { x0 = 0; y0 = 0; w = width; h = height; }
  47. }
  48. var gray = new byte[w * h];
  49. int stride = width * 3;
  50. for (int y = 0; y < h; y++)
  51. {
  52. int srcRow = (y0 + y) * stride;
  53. int dstRow = y * w;
  54. for (int x = 0; x < w; x++)
  55. {
  56. int p = srcRow + (x0 + x) * 3;
  57. // BGR: 0.114B + 0.587G + 0.299R
  58. byte b = bgr24[p], g = bgr24[p + 1], rr = bgr24[p + 2];
  59. gray[dstRow + x] = (byte)((b * 29 + g * 150 + rr * 77) >> 8);
  60. }
  61. }
  62. return (gray, w, h);
  63. }
  64. /// <summary>
  65. /// Tenengrad:Sobel 梯度幅值平方和,除以像素数与均值灰度(归一化)。
  66. /// </summary>
  67. private static double TenengradScore(byte[] g, int w, int h)
  68. {
  69. double sumSq = 0;
  70. long sumGray = 0;
  71. for (int i = 0; i < g.Length; i++) sumGray += g[i];
  72. double mean = (double)sumGray / g.Length;
  73. if (mean < 1e-6) mean = 1e-6;
  74. for (int y = 1; y < h - 1; y++)
  75. {
  76. int row = y * w;
  77. for (int x = 1; x < w - 1; x++)
  78. {
  79. int i = row + x;
  80. // Sobel Gx
  81. int gx = (g[i - w + 1] + 2 * g[i + 1] + g[i + w + 1])
  82. - (g[i - w - 1] + 2 * g[i - 1] + g[i + w - 1]);
  83. // Sobel Gy
  84. int gy = (g[i + w - 1] + 2 * g[i + w] + g[i + w + 1])
  85. - (g[i - w - 1] + 2 * g[i - w] + g[i - w + 1]);
  86. sumSq += (double)gx * gx + (double)gy * gy;
  87. }
  88. }
  89. int n = (w - 2) * (h - 2);
  90. if (n <= 0) return 0;
  91. // 除以像素数(消除 ROI 尺寸影响)+ 除以均值平方(消除亮度影响)
  92. return sumSq / n / (mean * mean);
  93. }
  94. /// <summary>
  95. /// Laplacian 方差:拉普拉斯响应的方差,除均值归一化。备用指标。
  96. /// </summary>
  97. private static double LaplacianScore(byte[] g, int w, int h)
  98. {
  99. long sumGray = 0;
  100. for (int i = 0; i < g.Length; i++) sumGray += g[i];
  101. double mean = (double)sumGray / g.Length;
  102. if (mean < 1e-6) mean = 1e-6;
  103. int n = (w - 2) * (h - 2);
  104. if (n <= 0) return 0;
  105. double sum = 0, sumSq = 0;
  106. for (int y = 1; y < h - 1; y++)
  107. {
  108. int row = y * w;
  109. for (int x = 1; x < w - 1; x++)
  110. {
  111. int i = row + x;
  112. int lap = 4 * g[i] - g[i - 1] - g[i + 1] - g[i - w] - g[i + w];
  113. sum += lap;
  114. sumSq += (double)lap * lap;
  115. }
  116. }
  117. double m = sum / n;
  118. double variance = sumSq / n - m * m;
  119. return variance / (mean * mean);
  120. }
  121. }
  122. }