Tesseract 是什么

Tesseract 是 Google 维护的开源 OCR 引擎,最早源自 HP,2005 年开源后逐步成为离线 OCR 的事实标准。.NET 生态下最常用的封装是同名 NuGet 包 Tesseract(历史上由 charlesw 维护,后由社区接手),它把 Tesseract C++ 库和 C# 绑定打包在一起,并提供 TesseractEngine、Pix、Page 等高层 API。

它适合的场景:

  • 本地离线 OCR,无外部 API 调用与数据外传
  • 扫描件、截图、票据等规整印刷体
  • 对识别成本敏感或不能上云的业务

不适合的场景在文末"能力边界"单独列。

环境与 NuGet 包

推荐用 .NET 6+ 控制台或类库承载。新建项目:

dotnet new console -n OcrDemo
cd OcrDemo
dotnet add package Tesseract

Tesseract NuGet 自带原生依赖,Windows 下无需再装额外运行时;Linux 下需 libtesseract-dev 与 libleptonica-dev,macOS 走 Homebrew 即可。

不要再去 charlesw 的旧仓库下载预编译包。NuGet 上 Tesseract 5.x 已经把原生库打包进 runtimes/ 目录,按目标平台自动选择。

tessdata 语言包

Tesseract 不内置任何语言模型。识别中文必须手动下载 chi_sim.traineddata:

  • 去 tesseract-ocr/tessdata 找 chi_sim.traineddata(简体)
  • 如果需要更准但更慢的版本,去 tessdata_best 仓库取同一文件名
  • 项目根目录建 tessdata/,把文件放进去,复制到输出目录

在 csproj 里设置复制规则:

<ItemGroup>
  <None Update="tessdata\chi_sim.traineddata">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

多语言混排时把多个 .traineddata 文件放同一目录,构造引擎时写 "chi_sim+eng" 这种组合串即可。

最小可运行示例

以下为可直接编译运行的现代写法:

using Tesseract;

var imagePath = args.Length > 0 ? args[0] : "test.jpg";

using var engine = new TesseractEngine(@"./tessdata", "chi_sim", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);

Console.WriteLine($"Mean confidence: {page.GetMeanConfidence():P2}");
Console.WriteLine(page.GetText());

// 遍历器 5.x 改用 IEnumerable API
foreach (var block in page iterate blocks)
{
    Console.WriteLine("<BLOCK>");
    foreach (var para in block iterate paragraphs)
    {
        foreach (var line in para iterate lines)
        {
            foreach (var word in line iterate words)
            {
                Console.Write($"{word.Text} ");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
    }
}
迭代器 API 在 5.x 中已改为基于 IEnumerable 的扩展方法。4 层 do { } while (...) 写法属于 3.x 时代,5.x 编译不会通过。文档示例一定要按当前主版本号对照。

调优与图像预处理

OCR 准确率的最大杠杆不是引擎参数,是**输入图像质量**。建议在调用 Tesseract 之前先做预处理:

  • DPI 提升到 300 以上。手机截图、远景拍摄往往低于 150 DPI,先放大再识别。
  • 灰度 + 二值化。用 Leptonica(Tesseract 已带)或 OpenCV 走一遍自适应阈值。
  • 去噪与去边框。手机拍文档经常带阴影,先做顶帽/白顶变换。
  • 校正倾斜。霍夫变换或投影法估角度,再仿射旋转回正。

Leptonica 端做这些比较顺手:

using var src = Pix.LoadFromFile(imagePath);
using var gray = src.ConvertRGBToGray();
using var binarized = gray.BinarizeOtsu();
using var page = engine.Process(binarized);

引擎与页面分割模式

引擎构造的第三个参数决定走哪条路径:

  • EngineMode.Default:Tesseract 自动选择 LSTM 或 Legacy
  • EngineMode.LstmOnly:仅 LSTM(适合 4.x+ 训练数据,几乎所有现代语言包)
  • EngineMode.TesseractOnly:仅 Legacy(仅在识别老格式时考虑)

页面分割模式(PSM)通过 engine.SetVariable 配置:

engine.SetVariable("page_separator", "\n");
engine.DefaultPageSegMode = PageSegMode.Auto;

常见取值:

  • PageSegMode.Auto:默认,带方向检测的整页识别
  • PageSegMode.SingleBlock:已知是单块文本,关闭额外版面分析
  • PageSegMode.SingleLine:只识别一行
  • PageSegMode.SingleChar:只识别一个字符(验证码场景)

只识别图片中的某个区域

整张图是截图、只有左下角二维码旁边有编号时,可以裁矩形区域:

using var page = engine.Process(img, new Rect(x, y, width, height));

矩形坐标是像素单位,原点 (0, 0) 在左上角。这能显著减少误识别,也跳过版面分析。

可信度阈值

结果里每段都有自己的均值可信度;做批量识别时建议过滤掉低于 0.6 的段,再走人工兜底:

foreach (var block in page iterate blocks)
{
    if (block.Confidence < 60) continue;
    // 进一步处理
}

能力边界

这些场景 Tesseract 表现差或根本不能做:

  • GIF 动图:只取首帧,识别无意义。需要先 ffmpeg -i x.gif -vsync 1 frame_%04d.png 抽帧,再逐帧识别。
  • 手写体:在中文场景识别率"低得可怜",Tesseract 本质仍是印刷体模型。手写场景考虑 PaddleOCR 或云 OCR。
  • 低分辨率 / 强压缩:JPEG 块状噪点会让 LSTM 严重退化,先做超分或换源图。
  • 表格与多列复杂版面:默认会按阅读顺序混排,需要先版面分析或切列再识别。
  • 公式与代码:默认 Tesseract 不识别 LaTeX,需要专用模型。

选型准则

  1. 数据不能出本地、对成本敏感、印刷体为主 → Tesseract 5.x + 预处理。
  2. 需要中文手写或公式识别 → PaddleOCR、MathPix 或云 OCR。
  3. 需要方向检测、版面分析 → Tesseract 4.x+ 的 LSTM + 外部版面分析(LayoutParser)。
  4. 任何场景都应先做图像预处理,再谈调引擎参数。
参考资料