From f17fd5cb857ea42587fc2c5a6c4db1b834425019 Mon Sep 17 00:00:00 2001 From: sinvo Date: Thu, 19 Mar 2026 17:06:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9D=A1=E5=B8=A6=E7=8A=B6?= =?UTF-8?q?=E6=8A=98=E7=97=95=E6=A3=80=E6=B5=8B=EF=BC=9A=E6=8C=89=E5=88=97?= =?UTF-8?q?/=E8=A1=8C=E6=8A=95=E5=BD=B1=E8=AF=86=E5=88=AB=E7=AA=84?= =?UTF-8?q?=E6=9D=A1=E6=9A=97=E5=B8=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 深色窄折痕的边缘方差高(亮暗跳变),双级方差检测不到。 新增条带检测:按列/行统计暗像素比例,超过40%的列/行 标记为折痕条带,条带内低方差像素推白,高方差像素(文字)保留。 Co-Authored-By: Claude Opus 4.6 --- CamScanner.cs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/CamScanner.cs b/CamScanner.cs index 447d216..cb038aa 100644 --- a/CamScanner.cs +++ b/CamScanner.cs @@ -461,13 +461,81 @@ public static class DocumentScanner { if (inkData[i] > 10 && varDataS[i] < 5 && varDataL[i] < 12) { - // 两级方差都低 → 折痕/阴影 double suppressRatio = (double)varDataS[i] / 5.0; int origVal = resultData[i]; resultData[i] = (byte)(origVal + (int)((255 - origVal) * (1.0 - suppressRatio))); } } + // --- 条带状折痕检测 --- + // 折痕特征:垂直或水平方向上,某列/行有大量连续暗像素 + // 文字不会形成这种长条状连续暗区域 + // + // 对每列统计暗像素比例,高比例的列标记为折痕条带 + int inkThreshForStripe = 30; // 墨迹强度>30才算暗像素 + double stripeRatioThresh = 0.4; // 一列中40%以上是暗像素就是条带 + int stripeHalfWidth = 3; // 条带半宽(左右各扩展3像素) + + // 垂直条带检测(按列扫描) + bool[] vStripe = new bool[w]; + for (int x = 0; x < w; x++) + { + int darkCount = 0; + for (int y = 0; y < h; y++) + { + if (inkData[y * w + x] > inkThreshForStripe) darkCount++; + } + vStripe[x] = ((double)darkCount / h) > stripeRatioThresh; + } + + // 水平条带检测(按行扫描) + bool[] hStripe = new bool[h]; + for (int y = 0; y < h; y++) + { + int darkCount = 0; + for (int x = 0; x < w; x++) + { + if (inkData[y * w + x] > inkThreshForStripe) darkCount++; + } + hStripe[y] = ((double)darkCount / w) > stripeRatioThresh; + } + + // 扩展条带宽度并抑制 + for (int i = 0; i < resultData.Length; i++) + { + int x = i % w; + int y = i / w; + + bool inStripe = false; + // 检查是否在垂直条带范围内 + for (int dx = -stripeHalfWidth; dx <= stripeHalfWidth; dx++) + { + int nx = x + dx; + if (nx >= 0 && nx < w && vStripe[nx]) { inStripe = true; break; } + } + // 检查是否在水平条带范围内 + if (!inStripe) + { + for (int dy = -stripeHalfWidth; dy <= stripeHalfWidth; dy++) + { + int ny = y + dy; + if (ny >= 0 && ny < h && hStripe[ny]) { inStripe = true; break; } + } + } + + if (inStripe && inkData[i] > 5) + { + // 在条带内:检查这个像素是否真的是文字 + // 文字在条带内的特征:小核方差高(笔画边缘锐利) + if (varDataS[i] < 10) + { + // 方差不高 → 不是文字,是折痕本身 → 推白 + resultData[i] = 255; + } + // 方差高 → 是文字,保留不动 + } + } + // --- 边缘阴影修复:从边缘向内扫描连续暗像素 --- byte[] shadowFlag = new byte[w * h]; int darkThresh = (int)(paperBright * 0.55);