改进阴影检测:用原始灰度值判断而非仅依赖背景估计
背景估计受大核模糊影响,阴影处的值可能被周围亮区域拉高。 改为同时检测原始灰度和背景估计,低于中位数45%/50%的区域 识别为阴影,并用渐变过渡避免硬边。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -374,43 +374,50 @@ public static class DocumentScanner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增益:让95百分位的墨迹深度映射到接近纯黑
|
// 阴影识别 + 墨迹映射
|
||||||
// 用非线性映射(幂函数)让浅墨迹也能变深
|
|
||||||
//
|
//
|
||||||
// 阴影识别:拍照时文档边缘常有阴影,特征是:
|
// 阴影特征:原始灰度值本身很低(暗区域)
|
||||||
// - 原始灰度很低(整体偏暗)
|
// 文字特征:原始灰度虽然比背景低,但绝对值不会太低(白纸上的黑字)
|
||||||
// - 背景估计也偏低(因为阴影区域大面积偏暗)
|
//
|
||||||
// - 但 bg - gray 的差值也可能很大
|
// 策略:用原始灰度值判断,如果太暗就认为是阴影
|
||||||
// 区分方法:如果背景估计值本身就很低(< 阈值),说明是阴影区域,
|
// 同时用背景估计值辅助:如果背景本身就暗,也是阴影
|
||||||
// 应该输出白色而非黑色
|
|
||||||
//
|
//
|
||||||
// 同时计算背景亮度的中位数,作为"正常纸面亮度"参考
|
// 计算灰度中位数作为参考
|
||||||
int[] bgHist = new int[256];
|
int[] grayHist = new int[256];
|
||||||
for (int i = 0; i < bgData.Length; i++)
|
for (int i = 0; i < grayData.Length; i++)
|
||||||
{
|
{
|
||||||
bgHist[bgData[i]]++;
|
grayHist[grayData[i]]++;
|
||||||
}
|
}
|
||||||
int bgCum = 0;
|
int grayCum = 0;
|
||||||
int bgMedian = 128;
|
int grayMedian = 128;
|
||||||
for (int i = 0; i < 256; i++)
|
for (int i = 255; i >= 0; i--)
|
||||||
{
|
{
|
||||||
bgCum += bgHist[i];
|
grayCum += grayHist[i];
|
||||||
if (bgCum >= totalPixels / 2)
|
if (grayCum >= totalPixels / 2)
|
||||||
{
|
{
|
||||||
bgMedian = i;
|
grayMedian = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 阴影阈值:背景亮度低于中位数的60%视为阴影区域
|
// 阴影阈值:原始灰度低于中位数的45%,或背景估计低于中位数的50%
|
||||||
int shadowThresh = (int)(bgMedian * 0.6);
|
int shadowGrayThresh = (int)(grayMedian * 0.45);
|
||||||
|
int shadowBgThresh = (int)(grayMedian * 0.50);
|
||||||
|
|
||||||
for (int i = 0; i < grayData.Length; i++)
|
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];
|
int ink = bgData[i] - grayData[i];
|
||||||
|
|||||||
Reference in New Issue
Block a user