新增边缘阴影清除:从四边向内扫描暗像素区域推白

除法归一化对均匀阴影免疫,但边缘深色阴影的bg也被拉低,
除法后比值偏低变灰色。在最终LUT之后加入边缘扫描:
从上下左右四边向内逐列/行扫描原始灰度,低于纸面亮度60%
的连续暗像素设为纯白,遇到亮像素停止。最大扫描深度1/4。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 08:44:09 +08:00
parent b150536858
commit cf7e667733

View File

@@ -418,6 +418,77 @@ public static class DocumentScanner
whiteLutMat.Dispose();
sharpened.Dispose();
// --- g: 边缘阴影清除 ---
// 从四边向内扫描原始灰度图,连续暗像素区域标记为阴影→纯白
// 除法归一化对均匀阴影免疫但边缘深色阴影bg也被拉低
// 除法后比值偏低变成灰色,需要额外清除
int cx1 = w / 4;
int cx2 = w * 3 / 4;
int cy1 = h / 4;
int cy2 = h * 3 / 4;
long centerSum = 0;
int centerCount = 0;
for (int py = cy1; py < cy2; py++)
{
for (int px = cx1; px < cx2; px++)
{
centerSum += grayData[py * w + px];
centerCount++;
}
}
int paperBright = (int)(centerSum / Math.Max(centerCount, 1));
int darkThresh = (int)(paperBright * 0.6);
int maxScanDepth = Math.Max(w, h) / 4; // 最多扫描1/4深度
byte[] cleanedData = new byte[w * h];
Marshal.Copy(cleaned.Data, cleanedData, 0, cleanedData.Length);
// 上边缘
for (int x = 0; x < w; x++)
{
for (int y = 0; y < Math.Min(maxScanDepth, h); y++)
{
if (grayData[y * w + x] < darkThresh)
cleanedData[y * w + x] = 255;
else
break;
}
}
// 下边缘
for (int x = 0; x < w; x++)
{
for (int y = h - 1; y >= Math.Max(0, h - maxScanDepth); y--)
{
if (grayData[y * w + x] < darkThresh)
cleanedData[y * w + x] = 255;
else
break;
}
}
// 左边缘
for (int py = 0; py < h; py++)
{
for (int x = 0; x < Math.Min(maxScanDepth, w); x++)
{
if (grayData[py * w + x] < darkThresh)
cleanedData[py * w + x] = 255;
else
break;
}
}
// 右边缘
for (int py = 0; py < h; py++)
{
for (int x = w - 1; x >= Math.Max(0, w - maxScanDepth); x--)
{
if (grayData[py * w + x] < darkThresh)
cleanedData[py * w + x] = 255;
else
break;
}
}
Marshal.Copy(cleanedData, 0, cleaned.Data, cleanedData.Length);
// 转回3通道
Mat output = new Mat();
Cv2.CvtColor(cleaned, output, ColorConversionCodes.GRAY2BGR);