From cf7e6677337161faa1e55ad90698191066b838a6 Mon Sep 17 00:00:00 2001 From: sinvo Date: Fri, 20 Mar 2026 08:44:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=BE=B9=E7=BC=98=E9=98=B4?= =?UTF-8?q?=E5=BD=B1=E6=B8=85=E9=99=A4=EF=BC=9A=E4=BB=8E=E5=9B=9B=E8=BE=B9?= =?UTF-8?q?=E5=90=91=E5=86=85=E6=89=AB=E6=8F=8F=E6=9A=97=E5=83=8F=E7=B4=A0?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E6=8E=A8=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 除法归一化对均匀阴影免疫,但边缘深色阴影的bg也被拉低, 除法后比值偏低变灰色。在最终LUT之后加入边缘扫描: 从上下左右四边向内逐列/行扫描原始灰度,低于纸面亮度60% 的连续暗像素设为纯白,遇到亮像素停止。最大扫描深度1/4。 Co-Authored-By: Claude Opus 4.6 --- CamScanner.cs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/CamScanner.cs b/CamScanner.cs index 551dccf..f8ad70e 100644 --- a/CamScanner.cs +++ b/CamScanner.cs @@ -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);