From e0a0aa893019ec597809b20c69a4a40f289509da Mon Sep 17 00:00:00 2001 From: itcsx Date: Fri, 15 May 2026 11:34:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OcrFinalHelper.cs | 206 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 OcrFinalHelper.cs diff --git a/OcrFinalHelper.cs b/OcrFinalHelper.cs new file mode 100644 index 0000000..18877c0 --- /dev/null +++ b/OcrFinalHelper.cs @@ -0,0 +1,206 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using Tesseract; + +public static class OcrFinalHelper +{ + private static readonly string _tessDataPath = + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tessdata"); + + /// + /// 针对白底黑字/截图优化的中文识别(强化浅灰色文字处理) + /// + public static string RecognizeScreenshot(string imagePath) + { + if (!File.Exists(imagePath)) + return "文件不存在"; + + try + { + using var original = new Bitmap(imagePath); + using var scaled = ScaleImage(original, original.Width * 2, original.Height * 2); + + // 预处理:灰度 → 对比度拉伸 → OTSU 二值化 + using var gray = ToGrayscale(scaled); + using var stretched = AutoContrast(gray); + using var binary = BinarizeOtsu(stretched); + + using var engine = new TesseractEngine( + _tessDataPath, + "chi_sim+eng", + EngineMode.LstmOnly); + + engine.SetVariable("preserve_interword_spaces", "1"); + engine.SetVariable("tessedit_pageseg_mode", "6"); + engine.SetVariable("textord_min_linesize", "2.5"); + + using var pix = BitmapToPix(binary); + using var result = engine.Process(pix); + + return result.GetText()?.Trim() ?? ""; + } + catch (Exception ex) + { + return $"识别失败:{ex.Message}"; + } + } + + #region 预处理 + + private static Bitmap ScaleImage(Bitmap original, int newWidth, int newHeight) + { + var scaled = new Bitmap(newWidth, newHeight); + using (var g = Graphics.FromImage(scaled)) + { + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + g.DrawImage(original, 0, 0, newWidth, newHeight); + } + return scaled; + } + + private static Bitmap ToGrayscale(Bitmap bmp) + { + var gray = new Bitmap(bmp.Width, bmp.Height); + using (var g = Graphics.FromImage(gray)) + { + var cm = new ColorMatrix(new float[][] + { + new float[]{0.299f,0.299f,0.299f,0,0}, + new float[]{0.587f,0.587f,0.587f,0,0}, + new float[]{0.114f,0.114f,0.114f,0,0}, + new float[]{0,0,0,1,0}, + new float[]{0,0,0,0,1} + }); + using (var ia = new ImageAttributes()) + { + ia.SetColorMatrix(cm); + g.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height), + 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia); + } + } + return gray; + } + + /// + /// 强化版对比度拉伸(专门优化:浅灰文字 + 低对比度文字) + /// 不破坏图像、不炸图、Tesseract 可正常识别 + /// + private static Bitmap AutoContrast(Bitmap gray) + { + int min = 255, max = 0; + int width = gray.Width; + int height = gray.Height; + + // 第一步:统计灰度(忽略纯白 255,避免背景干扰文字) + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int val = gray.GetPixel(x, y).R; + if (val == 255) continue; // 跳过纯白背景 + if (val < min) min = val; + if (val > max) max = val; + } + } + + // 如果无有效内容,返回原图 + if (min >= max || max - min < 10) + return (Bitmap)gray.Clone(); + + // 第二步:强化拉伸(专门强化 180~240 浅灰色文字) + Bitmap result = new Bitmap(width, height); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int val = gray.GetPixel(x, y).R; + + // 核心:浅灰色文字强制增强 + if (val >= 180 && val <= 245) + { + val = val - 30; // 把浅灰 → 变深灰 + } + + // 标准对比度拉伸(强化文字边缘) + int newVal = (int)(((val - min) / (double)(max - min)) * 220 + 20); + newVal = Math.Clamp(newVal, 0, 255); + + result.SetPixel(x, y, Color.FromArgb(newVal, newVal, newVal)); + } + } + + return result; + } + /// + /// OTSU 大津法二值化,自动寻找最佳阈值,适应各种灰度文字 + /// + private static Bitmap BinarizeOtsu(Bitmap gray) + { + int[] histogram = new int[256]; + int totalPixels = gray.Width * gray.Height; + + // 统计直方图 + for (int y = 0; y < gray.Height; y++) + { + for (int x = 0; x < gray.Width; x++) + { + int val = gray.GetPixel(x, y).R; + histogram[val]++; + } + } + + // 计算 OTSU 阈值 + double sum = 0; + for (int i = 0; i < 256; i++) + sum += i * histogram[i]; + + double sumB = 0; + int wB = 0, wF = 0; + double maxVariance = 0; + int threshold = 128; // 默认 + + for (int t = 0; t < 256; t++) + { + wB += histogram[t]; // 背景像素数 + if (wB == 0) continue; + wF = totalPixels - wB; // 前景像素数 + if (wF == 0) break; + + sumB += t * histogram[t]; + double mB = sumB / wB; // 背景平均灰度 + double mF = (sum - sumB) / wF; // 前景平均灰度 + + double variance = wB * (double)wF * (mB - mF) * (mB - mF); + if (variance > maxVariance) + { + maxVariance = variance; + threshold = t; + } + } + + // 白底黑字:大于阈值 -> 白,否则 -> 黑 + var bin = new Bitmap(gray.Width, gray.Height); + for (int y = 0; y < gray.Height; y++) + { + for (int x = 0; x < gray.Width; x++) + { + int val = gray.GetPixel(x, y).R; + bin.SetPixel(x, y, val > threshold ? Color.White : Color.Black); + } + } + return bin; + } + + private static Pix BitmapToPix(Bitmap bmp) + { + using var ms = new MemoryStream(); + bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + ms.Position = 0; + return Pix.LoadFromMemory(ms.ToArray()); + } + + #endregion +} \ No newline at end of file