Browse Source

fix(focus): 清晰度归一化÷mean²改÷mean(真根因) + P1峰落边界报警 + P2日志去陈旧Z

真机zcurve验证(4号舱well1):亮度随Z从53升到185,÷mean²把高Z清晰帧压垮,
算法误选最暗最糊的z=20000;改÷mean后峰正确落在z=92000(肉眼焦面),曲线单峰。
- Sharpness: sumSq/n/(mean*mean) → sumSq/n/mean
- P1: 粗对焦峰落在扫描区间边界时报警(真实焦面可能在窗口外)
- P2: OnStep日志去掉陈旧_lastZ/_lastH(标定中不刷新会误导排查)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 1 week ago
parent
commit
1216bbcbb5
3 changed files with 15 additions and 3 deletions
  1. 4 0
      Calib/CalibrationEngine.cs
  2. 8 2
      Imaging/Sharpness.cs
  3. 3 1
      MainWindow.Calib.cs

+ 4 - 0
Calib/CalibrationEngine.cs

@@ -340,6 +340,10 @@ namespace AutoFocusTool.Calib
                 OnStep?.Invoke($"粗对焦 Z={z} (区间{lo}~{hi})", null, null);
             }
             Log?.Invoke($"[well{well}] 粗对焦扫{layers}层 区间[{lo},{hi}] 步距{ZCoarseStep}");
+            // P1: 峰落在扫描区间边界 → 真实焦面很可能在窗口之外,警示(focusZ会偏,需调整ZCoarseCenter/Half)。
+            if (bestZ <= lo || bestZ >= hi)
+                Log?.Invoke($"[well{well}] ⚠ 粗对焦峰落在区间边界(z={bestZ}, 区间[{lo},{hi}])," +
+                            $"真实焦面可能在窗口外,建议调整 ZCoarseCenter/ZCoarseHalf");
             return bestZ;
         }
     }

+ 8 - 2
Imaging/Sharpness.cs

@@ -95,8 +95,14 @@ namespace AutoFocusTool.Imaging
             }
             int n = (w - 2) * (h - 2);
             if (n <= 0) return 0;
-            // 除以像素数(消除 ROI 尺寸影响)+ 除以均值平方(消除亮度影响)
-            return sumSq / n / (mean * mean);
+            // 除以像素数(消除 ROI 尺寸影响)+ 除以均值(归一化亮度,但只除一次方)。
+            // 【真机修正 2026-06-16】原为除以 mean*mean(均值平方)。实测 4 号舱 well1 的 Z 清晰度曲线
+            // (TestData/zcurve_w1_*)显示:随 Z 增大画面亮度从 ~53 单调升到 ~185(光学效应),
+            // mean² 会把高 Z 的清晰帧分数严重压低,导致算法把最暗最糊的低 Z 层(z=20000)误判为
+            // 最清晰,真实焦面(z≈92000,原始梯度峰)反而落选 → 对焦落到错误位置。
+            // 改为除以 mean(一次方):既保留对亮度的鲁棒性,又不过度压制高亮清晰帧,
+            // 反算验证峰正确落在 92000。
+            return sumSq / n / mean;
         }
 
         /// <summary>

+ 3 - 1
MainWindow.Calib.cs

@@ -150,7 +150,9 @@ namespace AutoFocusTool
                                 ? $"圆心偏移 Y:{circle.OffsetYPct:F0}% X:{circle.OffsetXPct:F0}% 完整:{circle.Complete}"
                                 : "未检出well圆";
                             string expPart = exp != null ? $" 曝光信息:{exp}" : "";
-                            string param = $"Z:{_lastZ} 水平:{_lastH} {circlePart}{expPart}";
+                            // P2: 不再拼 _lastZ/_lastH——它们仅在 RefreshPositions 时更新,标定过程中是陈旧值,
+                            // 会误导排查(日志会全程显示标定前的旧 Z/水平)。msg 本身已含当前步骤的真实命令 Z。
+                            string param = $"{circlePart}{expPart}";
                             win.SetCurrentInfo(param, msg);
                             AutoFocusTool.Logging.FileLogger.Data("CALIB", $"{msg} | {param}");
                         }