From a65c86bb2440bb2706a619f8892dab3e3b8633f6 Mon Sep 17 00:00:00 2001 From: sinvo Date: Thu, 19 Mar 2026 16:36:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=98=B4=E5=BD=B1=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=EF=BC=9A=E7=94=A8=E5=8E=9F=E5=A7=8B=E7=81=B0=E5=BA=A6?= =?UTF-8?q?=E5=80=BC=E5=88=A4=E6=96=AD=E8=80=8C=E9=9D=9E=E4=BB=85=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E8=83=8C=E6=99=AF=E4=BC=B0=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景估计受大核模糊影响,阴影处的值可能被周围亮区域拉高。 改为同时检测原始灰度和背景估计,低于中位数45%/50%的区域 识别为阴影,并用渐变过渡避免硬边。 Co-Authored-By: Claude Opus 4.6 --- CamScanner.cs | 55 +++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/CamScanner.cs b/CamScanner.cs index 00b2d70..4cd09bf 100644 --- a/CamScanner.cs +++ b/CamScanner.cs @@ -374,43 +374,50 @@ public static class DocumentScanner } } - // 增益:让95百分位的墨迹深度映射到接近纯黑 - // 用非线性映射(幂函数)让浅墨迹也能变深 + // 阴影识别 + 墨迹映射 // - // 阴影识别:拍照时文档边缘常有阴影,特征是: - // - 原始灰度很低(整体偏暗) - // - 背景估计也偏低(因为阴影区域大面积偏暗) - // - 但 bg - gray 的差值也可能很大 - // 区分方法:如果背景估计值本身就很低(< 阈值),说明是阴影区域, - // 应该输出白色而非黑色 + // 阴影特征:原始灰度值本身很低(暗区域) + // 文字特征:原始灰度虽然比背景低,但绝对值不会太低(白纸上的黑字) + // + // 策略:用原始灰度值判断,如果太暗就认为是阴影 + // 同时用背景估计值辅助:如果背景本身就暗,也是阴影 // - // 同时计算背景亮度的中位数,作为"正常纸面亮度"参考 - int[] bgHist = new int[256]; - for (int i = 0; i < bgData.Length; i++) + // 计算灰度中位数作为参考 + int[] grayHist = new int[256]; + for (int i = 0; i < grayData.Length; i++) { - bgHist[bgData[i]]++; + grayHist[grayData[i]]++; } - int bgCum = 0; - int bgMedian = 128; - for (int i = 0; i < 256; i++) + int grayCum = 0; + int grayMedian = 128; + for (int i = 255; i >= 0; i--) { - bgCum += bgHist[i]; - if (bgCum >= totalPixels / 2) + grayCum += grayHist[i]; + if (grayCum >= totalPixels / 2) { - bgMedian = i; + grayMedian = i; break; } } - // 阴影阈值:背景亮度低于中位数的60%视为阴影区域 - int shadowThresh = (int)(bgMedian * 0.6); + // 阴影阈值:原始灰度低于中位数的45%,或背景估计低于中位数的50% + int shadowGrayThresh = (int)(grayMedian * 0.45); + int shadowBgThresh = (int)(grayMedian * 0.50); for (int i = 0; i < grayData.Length; i++) { - // 阴影检测:背景估计值很低 → 阴影区域 → 输出白色 - if (bgData[i] < shadowThresh) + // 阴影检测:原始灰度很低 或 背景估计很低 → 阴影 → 白色 + if (grayData[i] < shadowGrayThresh || bgData[i] < shadowBgThresh) { - resultData[i] = 255; - continue; + // 渐变过渡,避免硬边 + // 越暗越白,用线性插值 + int darker = Math.Min(grayData[i], bgData[i]); + int thresh = Math.Max(shadowGrayThresh, shadowBgThresh); + if (darker < thresh) + { + double fade = (double)darker / thresh; // 0=极暗→全白, 1=阈值边界 + resultData[i] = (byte)(255 - (int)(fade * 40)); // 边界处约215,逐渐到255 + continue; + } } int ink = bgData[i] - grayData[i];