深度解析

OCR 技术深解:计算机如何学会从图像中读取文字

koboshiCo-founder
·5 分钟阅读
OCR 技术深解:计算机如何学会从图像中读取文字
概述

对计算机而言,一张文档照片只是一堆彩色像素网格。将这些像素转换为可搜索、可编辑的文字,需要一套涵盖图像预处理、版面分析和字符识别的完整流水线——每个环节背后都有数十年的研究积累。本文将从大津二值化一路讲到 Vision Transformer,带你走完 OCR 的全流程。

用十六进制编辑器打开任意一张图片。一张路牌的照片,一张收据,一张 PDF 的截图。偏移量为零时看到的是格式头。再往下几千字节,就到了像素数据:RGB 模式下每个像素三个字节,一个数字网格,其中 (128, 52, 19) 可能是一堵砖墙,而 (240, 238, 220) 可能是纸张。在这个网格中的某处,有一块写着"STOP"的停车标志,白字红底,字体是 Highway Gothic。人看到的是四个字母。计算机看到的是一个 47 × 19 的偏红色像素区域,其中 R 通道接近 200,G 和 B 低于 40。将这块区域映射为字符串"STOP",这就是光学字符识别(OCR),自 1929 年 Gustav Tauschek 为第一台阅读机申请专利以来,它一直是一个开放的研究问题。

OCR 的难度是欺骗性的,因为阅读本身太容易了。一个六岁的孩子能识别出几十种字体里的字母 A,不同大小、光照不均、轻微旋转、部分遮挡、用铅笔写的——全都能认出来。而计算机需要的是一条流水线:清理图像、找到文字区域、分割字符或字形序列、然后逐一分类。每个阶段都有各自的失败模式,而失败会层层累积。

OCR 为什么这么难

核心问题并不是分类。将一张预分割好的 28 × 28 字符图像分到 62 个类别(A–Z、a–z、0–9)中,这个任务 90 年代的 LeNet 就能做到 99% 的准确率。真正的难点在于分类器之前的所有步骤。

字体差异。 字母"g"至少有四种常见的结构变体:单层式(手写体)、双层式(大多数衬线字体)、Helvetica 的环尾式,以及 Futura 的开尾式。一个用 Tahoma 训练的模型在遇到 Univers 字体时会以可测量的频率出错。实际场景中的 OCR 要覆盖数百种字体,再加上手写——而手写根本没有一致的笔画拓扑结构。

几何畸变。 文档照片很少是平板扫描的效果。用手机拍一张收据会引入透视倾斜、弯曲和不均匀的缩放。靠近书脊的文字会弯曲。白板照片会拍进摄影师的影子。10 度的倾斜就能让 Tesseract 的准确率从约 97% 降到 85% 以下(基于标准基准测试)。去倾斜算法(霍夫线检测、Radon 变换、投影轮廓分析)能挽回一部分损失,但无法全部挽回。

光照与噪声。 不均匀的光照把统一背景变成了渐变,破坏了全局阈值化。JPEG 压缩伪影在锐利边缘周围产生振铃效应——在 OCR 的语境下,这意味着字母笔画周围产生振铃。低光传感器的椒盐噪声用深色像素填满空白区域,看起来像标点符号。复印件再复印会让细笔画黏连在一起,把"rn"变成"m",把"cl"变成"d"。

版面复杂性。 文档包含分栏、图注、表格、页眉、脚注和侧边栏。文字环绕图片流动。有些语言从右向左书写。有些在同一段落中混合方向。识别阅读顺序(哪块文字在前,哪块在后)是一个独立于识别之外的版面分析问题,顺序搞错了,即使每个字符都分类正确,输出也会被打乱。

上下文歧义。 仅凭像素图案,"0"(数字零)、"O"(大写字母 O)和"o"(小写字母 o)在许多无衬线字体中是完全一样的。"1"、"l"、"I"和"|"共享同一根竖线。分辨率增加了另一个维度:在 12 像素高度下,"e"和"c"的区别仅在于一根三像素宽的横线。人类读者靠上下文来分辨这些。机器需要语言模型——无论是统计的还是学习的——来做出同样的判断。

传统 OCR 流水线

在深度学习出现之前,OCR 是一系列手工设计的阶段。每个阶段由不同研究团队独立开发,并针对某种特定的退化场景进行调优。这条流水线大致如下:

原始图像 → 预处理 → 二值化 → 去倾斜 → 版面分析
  → 字符分割 → 特征提取 → 分类 → 后处理(语言模型) → 文本输出

预处理与二值化

彩色图像先转为灰度。降噪在阈值化之前平滑信号。中值滤波处理椒盐噪声。高斯模糊处理传感器噪声。当需要保留边缘时则使用双边滤波。

二值化将灰度图像转为白色背景上的黑色文字。标准算法是大津算法(Otsu's method,1979 年发表):它穷举搜索能最小化前景与背景像素分布类内方差的阈值。大津算法假设图像具有双峰直方图(文字像素聚集在某一个强度值,背景像素聚集在另一个),这在受控光照下的平板扫描上效果很好,但在有阴影的手机照片上会失败。局部自适应方法(Sauvola、Niblack)根据每个像素邻域的统计量计算不同的阈值,能处理不均匀光照,但代价是边缘附近会产生伪影。

去倾斜与版面分析

文档倾斜通过对边缘像素做霍夫变换,或通过投影轮廓分析来检测——在角度范围内旋转文档,选择水平投影峰值最尖锐的角度。获知倾斜角度后,用仿射变换将图像旋转回来。

版面分析将页面分割为文字块。经典方法是运行连通分量分析(CCA)来找出前景像素的连通区域,按邻近度将其分组为词,再将词分组为行,将行分组为块。XY-cut 算法通过沿水平和垂直投影寻找空白间隙来递归地切分页面,构建出区域树。现代实现采用混合方式:用 CCA 做初始区域检测,再用学习模型将每个区域分类为文字、图片、表格或分隔线。

字符分割

对于机器印刷的文本,分割意味着将词图像切割为独立的字符。垂直投影轮廓(每列前景像素计数的直方图)在字符中心处产生峰值,在字符边界处产生谷值。这个方法在两个字符粘连,或一个字符包含不连续的部分("i"、"j"、":"、"%")时会失效。一种处理方案是先过度分割(切得过细),再根据分类器置信度进行合并。另一种方案是完全跳过分割,直接识别整个词——这正是现代基于序列的方法所做的。

特征提取与分类

有了字符图像后,需要用数值来描述它,以便送入分类器。深度学习之前的特征包括:

  • 原始像素值,展开为一维向量(简单,但对平移和缩放很脆弱)
  • 分区法(Zoning):将字符边界框划分为 N × N 的网格,统计每个单元格的前景像素数,用这些计数作为特征
  • 梯度特征(HOG):方向梯度直方图捕获边缘方向信息,对小幅平移较为鲁棒
  • 尺度不变特征变换(SIFT):检测并描述对尺度、旋转和光照不变的关键点

分类器通常是带有 RBF 核的支持向量机(SVM),针对每种字体用数万张标注字符图像进行训练。一套调优得当的 SVM + HOG 流水线在干净印刷文本上可以达到约 98% 的单字符准确率。但同样的流水线在面对噪声、倾斜或手写输入时,准确率会跌到 70–80%。

Tesseract:从 HP 实验室到 LSTM

Tesseract 是开源 OCR 引擎的标杆。它始于 1980 年代 HP 实验室 Bristol 的一个博士项目,2005 年开源,2006 年起由 Google 维护。其架构清晰地分为两个时代。

版本 3:经典流水线

Tesseract 3 实现了传统流水线并做了若干创新。其版面分析使用了一种制表符检测算法,通过对齐词的边界框来找到列边界——这是在 90 年代 HP 扫描典型文档时调出来的实用启发式方法。字符分割使用了切分器/关联器(chopper/associator)逻辑:切分器将词图像过度分割为候选切割点,关联器利用分类器置信度和词典来决定哪些切割点应该合并。

分类器是一个双通道系统。第一通道(静态分类器)将分割后的连通区域与原型(聚类后的训练样本)进行最近邻匹配。第二通道(自适应分类器)在文档本身上进行微调,学习该图像中使用的具体字体。自适应分类器大约需要一页文字才能变得有效,这就是为什么 Tesseract 3 在路牌和图注等单次图像上表现不佳的原因。

Tesseract 3 以 .traineddata 文件的形式发布:这些归档文件包含原型聚类、字符集定义、词频词典,以及一张映射易混淆字符对的 unichar 歧义表。训练一种新语言需要给 Tesseract 提供文本图像及其对应的真值转录、框出每个字符,然后运行训练工具。每种语言需要数小时的处理时间。

版本 4/5:LSTM 取代流水线

Tesseract 4(2018 年)用单个 LSTM 神经网络替换了整条识别路径。LSTM 对文本行图像的垂直切片序列进行操作,每个切片宽度为一个像素,高度为整行文本。每个切片被送入堆叠的双向 LSTM 层。输出是一系列字符概率,使用连接时序分类(CTC)损失函数将可变长度的输出序列与真值文本对齐,无需预分割的字符位置。

LSTM 模型无需切分器/关联器的机制,就能处理变宽字符、粘连字符和字体差异。它仍然依赖 Tesseract 遗留的版面分析来查找文本行,但行识别是完全端到端的神经网络。在 ICDAR 2017 基准测试上,Tesseract 4 在印刷英文上达到了 4.4% 的字符错误率(CER),而 Tesseract 3 为 7.2%。训练过程也从遗留的逐字框选工具切换为将文本行与转录文本配对。仍然是监督学习,但每行只需一个标签,而不是每个字符一个。

.traineddata 格式被扩展为在单个文件中同时保存 LSTM 模型权重(数 MB)和遗留数据(词典、unichar 表)。对于拉丁字母语言(约 100 个字符类别),在现代 GPU 上从头训练一个快速 LSTM 模型大约需要 24 小时;CJK 语言由于字符集更大,需要更长的时间。

准确率特征

Tesseract 5 在版本 4 的基础上扩大了训练语料并优化了默认参数,但架构没有变化。准确率高度依赖输入条件:

输入条件Tesseract 4 CERTesseract 5 CER
干净 300 DPI 扫描,英文,单栏2.1%1.8%
150 DPI 手机照片,英文5.8%4.9%
干净扫描,混合字体7.3%6.1%
历史文档(不规则字体)18.4%16.2%
手写(IAM 数据集)28.7%26.3%

LSTM 模型消除了版本 3 的大部分字体敏感问题,但引擎在以下场景仍然会退化:低分辨率(低于 200 DPI 时,LSTM 的像素切片输入会丢失笔画细节)、手写文本(模型是在印刷字体上训练的;手写具有根本不同的笔画统计特征)、复杂版面(版面分析仍然是遗留代码路径,而非神经网络路径)。

现代深度学习方法

学术上,OCR 大约在 2019 年超越了 CTC + LSTM 范式。目前文献中有三种架构占据主导地位。

CRNN + CTC

卷积循环神经网络(CRNN)由 Shi 等人于 2015 年发表,它将 CNN 特征提取器与 RNN 序列模型和 CTC 解码配对使用。CNN 从图像中提取空间特征,本质上是在学习 Tesseract 中 LSTM 需要手工获取的东西(垂直切片表示)。RNN 对提取出的特征之间的序列依赖关系进行建模。CTC 处理对齐问题。CRNN-CTC 成为标准基线:端到端可训练,泛化能力好,推理速度快(GPU 上约每行 20 ms)。

基于注意力的编码器-解码器

基于注意力的模型借鉴了机器翻译中的 seq2seq 范式。CNN 或视觉编码器生成图像的特征图。RNN 解码器一次一个字符地生成输出文本,每一步都关注特征图的相关空间区域。注意力机制消除了 CTC 的单调约束:解码器可以在语言模型预期出现重复模式时跳回图像的更早部分,但这种灵活性也为不可辨认的输入带来了幻觉风险。基于注意力的解码器在干净印刷英文上达到约 1.5–3% 的 CER,在长序列和不规则版面上优于纯 CTC 模型。

Vision Transformer(TrOCR、Donut)

TrOCR(Microsoft,2021 年)将 Transformer 架构应用于 OCR。图像被分割为补丁(patch),由 ViT(Vision Transformer)编码器编码,文本由 Transformer 文本解码器自回归地解码。这是一个标准多模态模型的架构,专门为 OCR 而训练。TrOCR 在印刷文本上取得了最先进的结果(干净扫描上低于 1% CER),无需任何 CNN 预处理、显式语言模型或字符级分割。

Donut(NAVER,2022 年)将这一方法扩展到文档理解:输入是整张文档图像,输出是直接从视觉特征中提取的结构化 JSON,完全跳过了 OCR → NLP 的流水线。这模糊了 OCR 与文档解析之间的界限,对实际应用来说意义重大:提取发票总额、护照号码或研究论文中的表格,变成了单次模型调用,而不是 OCR + 正则 + 启发式规则。

性能对比(印刷英文,干净 300 DPI)

方法CER推理速度(行/秒)所需训练数据
Tesseract 3(经典)7.2%~2(CPU)~10 万字符图像
Tesseract 5(LSTM)1.8%~15(CPU)~50 万文本行
CRNN + CTC2.5%~120(GPU)~200 万文本行
Attention seq2seq1.8%~60(GPU)~200 万文本行
TrOCR(ViT + decoder)0.8%~20(GPU)~1000 万文本行
Donut(ViT + decoder)1.2%~15(GPU)~1200 万文档图像

在基准测试上,Tesseract 与最佳 Transformer 模型之间的差距是真实存在的。在实践中,这种差距会缩小,因为大多数部署系统喂给 Tesseract 的都是干净、光线充足、300 DPI 的输入,在这种输入下,1.8% 的 CER 意味着每 55 个字符中有一个错误。对于搜索索引来说可以接受,对于法律文档的转录来说则不尽然。

非英文 OCR:为什么更难

英文 OCR 已经是一个已解决的问题。26 个大写字母、26 个小写字母、10 个数字和少量标点——总共约 70 个类别,一个多分类问题,90 年代的 LeNet 就绰绰有余。世界上的其他书写系统则没有那么宽容。

CJK:字符集爆炸

中文、日文和韩文(CJK)仅从字符集规模上来说就是最难啃的 OCR 挑战。简体中文约有 3,500 个常用字,通用文本中超过 6,000 个。繁体中文还要再加一千多个变体。日文将两种音节文字(平假名、片假名:各 46 个字符)与约 2,000 个常用汉字以及拉丁字母数字混合在同一句子中。韩文(谚文)在音系上是规则的(24 个基本字母组合成音节块),但视觉上非常密集:单个音节块最多可包含六个基本 jamo 组件,挤在约两个拉丁字符并排的空间中。

CJK OCR 系统不能将识别视为 70 路分类问题。日语至少要 4,000 路分类,简体中文要 6,000 路,繁体中文则超过 10,000 路。仅 softmax 输出层的参数量就超过一整个拉丁字母 OCR 模型。训练数据需求随字符集规模而增长:要达到可接受的准确率,每类约需 100 个标注样本,因此中文训练集起步就是 60 万张文本行图像。

Tesseract 提供了 CJK 的 traineddata 文件(chi_simchi_trajpnkor),但它们比拉丁模型大得多。chi_sim.traineddata 约 50 MB,而 eng.traineddata 约 15 MB,且由于输出空间更大,识别速度也更慢。Tesseract 5 在干净印刷 CJK 文本上的准确率约为 2–5% CER,大约是在同等条件下的英文错误率的 2–3 倍。

阿拉伯文与从右向左的书写系统

阿拉伯文除了字符集问题(28 个字母,每个字母根据在词中的位置有不同的上下文形态)之外,还增加了两个难题。第一,它从右向左书写,所以 OCR 引擎必须检测文本方向并反转输出顺序。第二,阿拉伯文是连写的——词内字母通过基线笔画相互连接,因此分割天然比拉丁和西里尔字母的分离式字符形态更难。一个阿拉伯文词图像是一个连续的连通区域;基于字符的分割实际上是不可能的,这也是为什么 LSTM/CTC 方法(在词图像上操作而无需字符分割)对阿拉伯文 OCR 的突破比对英文 OCR 更大。

希伯来文、乌尔都文、波斯文和普什图文共享其中部分挑战的组合。Tesseract 提供了阿拉伯文 traineddata,但在干净印刷文本上的准确率约为 5–8% CER,大约是同等条件下英文的 4 倍。

印度系文字:连字问题

天城文(印地语、马拉地语、尼泊尔语)和其他婆罗米系文字代表了一种独特的挑战:书写单位不是字符,而是连字(conjunct)——一个辅音、一个元音修饰符、有时再加一个辅音在视觉上融合成的簇。Unicode 天城文区块有 128 个码点,但可能的连字数量超过 1,000 个。"Shirorekha"(连接词内字符的水平顶线)使分割更难,因为顶线将相邻字符合并为一个视觉单元。

Tesseract 的印地语 traineddata(hin)覆盖了常见的连字,但在印刷文本上的准确率比英文差 3–4 倍。泰米尔文、泰卢固文、孟加拉文及其他印度文字的覆盖更不完善,有些甚至完全没有官方的 traineddata 文件。根本原因是训练数据量:印地语有数百万张高质量的标注文本行图像,而卡纳达语的可用数据集只有数万张。

竖排文本与混合方向版面

日文和繁体中文有时采用竖排,行从上到下,列从右到左。Tesseract 的版面分析假设文本是水平排列的;竖排文本需要预先旋转或使用单独的引擎。同一页上同时出现水平和竖排文本(在日本报纸和漫画中很常见)完全打破了单一方向假设,需要在识别之前进行区域级别的方向检测。

小语种与训练数据

对于使用人数少于 1,000 万的语言,高质量的 OCR 训练数据几乎不存在。Unicode 联盟已经编码了 150 多种文字系统,但 Tesseract 大约只为 120 种语言提供了 traineddata,其中许多是用标准字体在干净背景上生成的合成文本。合成数据适用于常见字体下的印刷文本,但对于实际文档中出现的字体、纸张和印刷质量则会失效。一个在 300 DPI 下用合成 Arial 训练的模型,无法读取 1970 年代打字机敲出的阿姆哈拉语文档。

浏览器端 OCR:Tesseract.js

在 WebAssembly 面世之前,在浏览器中运行 OCR 是不现实的。Tesseract.js 通过 Emscripten 将 Tesseract 5(LSTM)编译为 WebAssembly,用 JavaScript API 封装 C++ 引擎,并为每个识别任务运行一个 Web Worker。引擎、语言数据和 Worker 都从静态资源加载。图像数据无需往返服务器。

API 接口很简洁:创建一个指定一种或多种语言代码的 Worker,传入图像,返回识别出的文本及每个字符的置信度分数。多个 Worker 可以并行运行,受限于可用的 CPU 核心数。消费级笔记本上通常可以同时跑 4 个识别任务,最新的手机上可以跑 6 到 8 个。

性能受三个因素制约。第一,WebAssembly 运行速度约为原生的 50–70%;在原生 Tesseract 中耗时 100 ms 的文本行,在浏览器中大约需要 160 ms。第二,traineddata 文件通过网络加载。eng.traineddata 约 15 MB,chi_sim.traineddata 约 50 MB,两者都需要完全下载后才能开始识别。第三,图像解码(JPEG/PNG/WebP/HEIC 到原始像素数据)使用浏览器内置的解码器,速度很快,但会因格式和设备而异。

隐私优势是结构性的。客户端 OCR 意味着图像永不离开设备。护照照片、银行对账单、医疗记录——像素在浏览器中解码,相当于 Textract 的功能在 Web Worker 中运行,结果是一段用户可以复制或保存的文字。没有数据中心处理该图像。这不是一个功能,而是服务器的缺失——这与承诺"我们不会看"的隐私政策在本质上是不同的。

我们的 Image to Text 工具运行的就是这套技术栈:Tesseract.js 配合 LSTM 识别、Web Worker 并行处理,以及 12 种语言的 traineddata(英文、西班牙文、法文、德文、葡萄牙文、意大利文、简体中文与繁体中文、日文、韩文、印地文和俄文)。拖入一张照片,选择语言,获得文字。该工具读取 JPEG、PNG、WebP、BMP 和 GIF 输入,应用分辨率感知预缩放(最长边超过 3,000 像素的图像会被缩小,以保证识别延迟处于合理范围),并输出每张图片的纯文本,附带每字符置信度。

图像格式的选择在实践中影响 OCR 质量。来自 iPhone 的 HEIC 照片,在转换为 JPEG 后进行 OCR 时,会在文字边缘周围产生压缩伪影,降低识别准确率。先将 HEIC 转为 PNG(无损,无量化伪影)则可以保留 LSTM 所依赖的锐利边缘。我们的 HEIC to JPGHEIC to PNG 转换器直接在浏览器中完成这一预处理步骤。同样,在低光环境下拍摄的照片,在阈值化之前转换为能保留完整色调范围的格式会更有优势:JPG to PNG 可以避免 JPEG 源文件重新编码时产生的二次压缩伪影。

浏览器 OCR 技术栈在吞吐量上无法与 GPU 加速的服务器端模型竞争。一台在 32 个 CPU 核心上运行原生 C++ Tesseract 的服务器,总能跑赢一个浏览器标签页。它的优势在于隐私、零基础设施和每次请求零成本。对于扫描几页文件、一张收据或几个路牌来说,200 ms 和 50 ms 之间的延迟差异是察觉不到的。

OCR 的未来走向

OCR 大约从 2022 年起不再是一个独立的研究领域,而是融入了更广阔的文档 AI 和多模态模型空间。三个转变正在发生。

多模态大语言模型作为零样本 OCR。 GPT-4V、Claude 和 Gemini 可以在没有显式 OCR 训练的情况下从图像中读取文字。给模型一张文档照片并索要文字,它就会返回。不是因为它有一个专门的 OCR 头,而是因为文字识别能力从数十亿图像-文本对的训练中涌现出来。质量并不均匀。在干净印刷英文上,GPT-4V 达到约 1% CER,与微调后的 TrOCR 相当。在中文收据的低分辨率手机照片上,它可以胜过 Tesseract,因为模型利用了上下文(它知道收据长什么样,以及应该期待哪些数字),而不是仅仅依赖像素级证据。在手写文字上,它比微调后的识别器差,因为训练分布中没有包含足够的手写图像。

权衡是成本和延迟。调用一次 GPT-4V 进行 OCR,根据 token 数量计算,每页成本 1–3 美分,耗时 1–3 秒。Tesseract 在本地运行,每页 100–500 ms,成本为零。扫描一份 200 页的文档,差值是 2–6 美元和数分钟的墙上时间,对比零成本和不到一分钟。对于一次性使用(一张收据、一张白板照片),多模态模型更简单且往往更准确。

端侧推理。 支撑服务器端 OCR 的模型正在缩小。ONNX Runtime Web 在浏览器中运行量化后的 CRNN 和小型 ViT 模型,在使用 WebGPU 时每行文本 50–100 ms。Apple 的 Vision 框架在 iOS 和 macOS 上内置了一个紧凑的 OCR 模型,通过 VNRecognizeTextRequest 即可访问,无需网络请求。"服务器 GPU 模型"和"浏览器模型"之间的差距正在缩小,因为两者都趋向同一个架构甜点:一个小型 ViT 编码器(约 2,000–5,000 万参数)加上轻量解码器,量化为 INT8 或 FP16。

文档理解,而非仅仅是转录。 OCR 的输出是一个字符串。大多数实际任务涉及从该字符串中提取结构化信息:发票号、日期、总额、姓名、地址。传统方法将 OCR 与正则表达式、命名实体识别和模式映射串联使用。每个阶段都是一个独立的模型,有各自的失败模式。端到端文档理解模型(Donut、LayoutLMv3、Pix2Struct)跳过 OCR,将文档图像直接映射为结构化输出。一个在收据上微调的 Donut 模型不会先生成文本转录再解析它,而是直接从像素输入生成 {"total": "42.50", "date": "2026-07-27", "vendor": "..."}。OCR 这一步骤——本文的主题——变成了一个不可见的实现细节。

这并不意味着 OCR 被淘汰。它变成了一个构件。这条流水线仍然需要处理边缘情况:在昏暗餐厅里斜着拍的收据、泛黄纸张上 50 年前的打字文档、没有哪个大型多模态模型训练过的语言的标志。针对这些特定分布训练的专业 OCR 引擎,在速度和性能上将超越通用模型多年,因为一个在整个互联网上训练的通用模型,只将其微乎其微的一小部分能力分配给了阅读 1970 年代阿姆哈拉文打字机输出。

对于世界上的大多数语言和大大多数真实成像条件来说,OCR 并不是一个已解决的问题。它只在一个最窄、资金最充足的子集上被解决了:干净、300 DPI、英文、单栏、机器印刷的文本。其余的,仍然开放。

更多推荐阅读