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];