上传文件至 /
This commit is contained in:
parent
ea7e460f24
commit
e0a0aa8930
206
OcrFinalHelper.cs
Normal file
206
OcrFinalHelper.cs
Normal file
@ -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");
|
||||
|
||||
/// <summary>
|
||||
/// 针对白底黑字/截图优化的中文识别(强化浅灰色文字处理)
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强化版对比度拉伸(专门优化:浅灰文字 + 低对比度文字)
|
||||
/// 不破坏图像、不炸图、Tesseract 可正常识别
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
/// <summary>
|
||||
/// OTSU 大津法二值化,自动寻找最佳阈值,适应各种灰度文字
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user