1. 为什么我又把OCR这件事翻出来折腾了一遍
日常办公里最让人抓狂的场景之一,就是拿到一份扫描版PDF或者一张截图,里面的文字明明看得清清楚楚,但就是选不中、复制不了。想改一个字,得对着屏幕手敲;想引用一段话,得逐字校对。这种时候,一个靠谱的OCR工具就是救命稻草。
我最早接触OCR是好几年前,那时候用的还是某商业软件的试用版,识别几页就提示要付费,而且中文识别率感人,标点符号经常乱飞。后来陆续试过在线OCR服务、手机App、各种桌面端工具,要么有页数限制,要么要上传到别人服务器,要么识别完排版全乱。直到我开始用开源OCR方案,才算真正把这件事掌握在自己手里。
这篇文章要聊的,就是怎么用开源OCR工具把图片和PDF里的文字快速、准确地提取出来。核心关键词就几个:OCR、开源、PDF、图片、文字提取。不管你是完全没接触过OCR的新手,还是用过一些工具但觉得不够顺手的老用户,下面这些内容都能直接拿去用。我会从方案选型讲到实操步骤,再到踩过的坑和排查技巧,尽量把每个环节都说透。
先说结论:目前开源OCR领域最成熟、最省心的组合,是Tesseract OCR引擎加上一个顺手的调用方式(命令行或者Python脚本),如果对中文识别要求更高,可以上PaddleOCR。这两个都是完全开源、本地运行、不依赖网络的方案。下面我会把两条路线都讲清楚,你根据自己的需求选。
2. 开源OCR方案怎么选:Tesseract还是PaddleOCR
2.1 两个主流开源OCR引擎的定位差异
Tesseract的历史可以追溯到上世纪八十年代,后来由惠普开源,再后来由Google接手维护。它的最大优势是成熟稳定、语言包丰富、跨平台支持好。Windows、macOS、Linux上都能跑,安装包也不大。缺点是默认状态下对中文的识别效果一般,尤其是排版复杂或者字体特殊的场景,需要做一些预处理和参数调优。
PaddleOCR是百度飞桨团队开源的一套OCR工具库,底层用的是深度学习模型。它在中文识别上的表现明显优于Tesseract,尤其是对自然场景文字、倾斜文字、手写体的识别能力更强。而且它自带方向分类、版面分析等功能,开箱即用的效果就很好。缺点是对硬件有一定要求,虽然CPU也能跑,但如果有GPU会快很多,另外安装过程比Tesseract稍微复杂一点。
我自己的使用策略是这样的:如果只是偶尔提取一些印刷体文档、截图里的文字,Tesseract足够了,装起来快,用起来简单。如果需要批量处理大量中文文档,或者对识别准确率要求很高,那就上PaddleOCR。两者并不冲突,可以都装着,按场景切换。
2.2 不同场景下的选型建议
为了让你更直观地做决定,我整理了一个对比表格:
| 对比维度 | Tesseract OCR | PaddleOCR |
|---|---|---|
| 中文识别准确率 | 中等,需调优 | 高,开箱即用 |
| 英文识别准确率 | 高 | 高 |
| 安装难度 | 低 | 中等 |
| 运行速度(CPU) | 快 | 中等 |
| 运行速度(GPU) | 不支持 | 快 |
| 语言包支持 | 100+种语言 | 80+种语言 |
| 版面分析 | 弱 | 强 |
| 手写体识别 | 差 | 较好 |
| 适合场景 | 印刷体文档、截图 | 复杂版面、批量处理 |
如果你只是想把一张截图里的几行字提出来,Tesseract命令行一行命令就搞定。如果你要处理几百页的扫描PDF,而且里面有表格、多栏排版,PaddleOCR会更省心。
还有一个选择维度是是否需要保留排版。Tesseract默认输出的是纯文本,段落结构会丢失。PaddleOCR可以输出带坐标信息的结构化结果,方便你后续还原版面。如果你只是要文字内容,不需要保留格式,两者都行。如果你要把识别结果还原成Word或者保持原来的段落结构,PaddleOCR更有优势。
2.3 为什么我不推荐在线OCR服务
顺便说一句,很多人第一反应是用在线OCR服务,觉得方便。但有几个问题:第一,隐私。你把合同、身份证、内部文档上传到别人服务器,风险不用我多说。第二,限制。免费版通常有页数或文件大小限制,批量处理很麻烦。第三,稳定性。服务说关就关,接口说变就变,你依赖它就有断档的风险。开源本地方案虽然前期配置花点时间,但一次搞定之后就是一劳永逸。
3. Tesseract OCR从安装到出结果:完整实操流程
3.1 Windows下的安装与配置
Windows上安装Tesseract最简单的方式是下载官方编译好的安装包。你可以在GitHub的UB-Mannheim仓库找到最新的Windows安装程序。下载后双击安装,注意在安装向导里勾选Additional language data,把中文语言包一起装上。默认安装路径是C:\Program Files\Tesseract-OCR,记住这个路径,后面配置环境变量要用。
安装完成后,需要把Tesseract的安装目录添加到系统环境变量Path里。具体操作:右键“此电脑”->属性->高级系统设置->环境变量->在系统变量里找到Path->编辑->新建->填入C:\Program Files\Tesseract-OCR->确定保存。然后打开命令提示符,输入tesseract --version,如果能看到版本号输出,说明安装配置成功。
语言包方面,中文需要chi_sim(简体)和chi_tra(繁体)两个文件。如果你安装时没勾选,可以单独下载语言包文件,放到Tesseract安装目录下的tessdata文件夹里。下载地址在Tesseract的GitHub仓库的tessdata目录下,找到对应的.traineddata文件下载即可。
3.2 macOS和Linux下的安装
macOS上用Homebrew安装最方便:
brew install tesseract brew install tesseract-lang第二条命令会安装所有语言包,包括中文。安装完成后同样用tesseract --version验证。
Linux(以Ubuntu为例):
sudo apt update sudo apt install tesseract-ocr sudo apt install tesseract-ocr-chi-sim sudo apt install tesseract-ocr-chi-tra如果你需要其他语言,把chi-sim换成对应的语言代码即可。
3.3 命令行直接提取图片文字
安装配置好之后,最基本的用法是这样的:
tesseract input.png output -l chi_sim这条命令的意思是:识别input.png里的文字,结果保存到output.txt,使用简体中文语言包。如果你同时有中英文混排的内容,可以这样写:
tesseract input.png output -l chi_sim+engTesseract会自动在两种语言之间切换识别。实测下来,中英文混排的场景用chi_sim+eng比单用chi_sim效果好很多,因为纯中文语言包对英文字母的识别率会下降。
对于PDF文件,Tesseract本身不直接支持PDF输入,需要先把PDF转成图片。可以用ImageMagick或者pdftoppm来做这件事:
pdftoppm -png -r 300 input.pdf page这条命令会把PDF的每一页转成300 DPI的PNG图片,文件名依次是page-1.png、page-2.png等等。然后对每一张图片跑Tesseract就行了。300 DPI是我实测下来比较合适的值,再低会影响识别率,再高文件会很大但识别率提升有限。
3.4 用Python批量处理图片和PDF
如果你要批量处理,写个Python脚本会方便很多。先安装依赖:
pip install pytesseract pillow pdf2image然后是一个批量处理的脚本示例:
import pytesseract from PIL import Image from pdf2image import convert_from_path import os # Windows下需要指定tesseract路径 # pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe' def ocr_image(image_path, lang='chi_sim+eng'): img = Image.open(image_path) text = pytesseract.image_to_string(img, lang=lang) return text def ocr_pdf(pdf_path, lang='chi_sim+eng', dpi=300): images = convert_from_path(pdf_path, dpi=dpi) all_text = [] for i, img in enumerate(images): text = pytesseract.image_to_string(img, lang=lang) all_text.append(f'--- 第{i+1}页 ---\n{text}') return '\n'.join(all_text) # 批量处理文件夹里的图片 def batch_ocr(folder_path, output_file): results = [] for filename in sorted(os.listdir(folder_path)): if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp')): filepath = os.path.join(folder_path, filename) text = ocr_image(filepath) results.append(f'=== {filename} ===\n{text}') with open(output_file, 'w', encoding='utf-8') as f: f.write('\n\n'.join(results)) if __name__ == '__main__': batch_ocr('./images', './output.txt')这个脚本可以直接拿去用,把图片放在images文件夹里,运行后结果会输出到output.txt。PDF的处理同理,调用ocr_pdf函数就行。
3.5 提升识别率的关键参数调优
Tesseract的默认配置不一定适合所有场景,有几个参数值得调整:
页面分割模式(PSM):这个参数控制Tesseract如何分析页面结构。常用的值有:
--psm 3:默认值,自动分析页面布局--psm 6:假设页面是一整块统一的文本--psm 4:假设页面是单列可变大小的文本--psm 11:稀疏文本,适合从复杂背景中提取零散文字
如果你识别的是截图或者单栏文档,--psm 6通常比默认值效果好。如果是多栏排版,--psm 3更合适。
OCR引擎模式(OEM):--oem 3是默认值,使用基于LSTM的神经网络引擎。除非有特殊需求,否则保持默认就行。
字符白名单:如果你知道图片里只有数字和英文字母,可以限制识别范围来提升准确率:
tesseract input.png output -l eng --psm 6 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz这个技巧在处理票据、验证码之类的场景特别有用。
4. PaddleOCR上手:中文识别效果更好的选择
4.1 安装与环境准备
PaddleOCR的安装分两步:先装PaddlePaddle框架,再装PaddleOCR库。以CPU版本为例:
pip install paddlepaddle pip install paddleocr如果你有NVIDIA显卡并且配置了CUDA,可以安装GPU版本:
pip install paddlepaddle-gpu pip install paddleocr安装完成后,第一次运行时会自动下载模型文件,大约几百MB,需要联网。下载完成后就可以离线使用了。
4.2 三行代码完成图片文字提取
PaddleOCR的Python API设计得很简洁:
from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang='ch') result = ocr.ocr('test.png', cls=True) for line in result[0]: print(line[1][0])use_angle_cls=True表示启用方向分类,可以自动纠正旋转的文字。lang='ch'指定中文模型。result里包含了每个文本框的坐标和识别文字,line[1][0]就是识别出来的文本内容。
4.3 批量处理PDF的完整方案
PaddleOCR处理PDF的思路和Tesseract一样,先把PDF转成图片,再逐页识别。下面是一个完整的脚本:
from paddleocr import PaddleOCR from pdf2image import convert_from_path import os ocr = PaddleOCR(use_angle_cls=True, lang='ch') def process_pdf(pdf_path, output_txt): images = convert_from_path(pdf_path, dpi=300) all_text = [] for i, img in enumerate(images): img.save(f'_temp_page_{i}.png') result = ocr.ocr(f'_temp_page_{i}.png', cls=True) page_text = '\n'.join([line[1][0] for line in result[0]]) if result[0] else '' all_text.append(f'--- 第{i+1}页 ---\n{page_text}') os.remove(f'_temp_page_{i}.png') with open(output_txt, 'w', encoding='utf-8') as f: f.write('\n\n'.join(all_text)) print(f'完成,结果已保存到{output_txt}') process_pdf('document.pdf', 'result.txt')这个脚本会逐页处理PDF,把每页的识别结果按页分隔保存。实测下来,PaddleOCR对中文文档的识别准确率明显高于Tesseract,尤其是对宋体、黑体之外的字体,以及稍微模糊的扫描件。
4.4 处理特殊场景的注意事项
PaddleOCR虽然开箱即用效果好,但有几个点需要注意:
内存占用:处理高分辨率图片时,PaddleOCR会占用较多内存。如果遇到内存不足的情况,可以在初始化时设置rec_image_shape参数来限制输入尺寸,或者先把图片缩小再识别。
首次加载慢:第一次运行时会加载模型,可能需要几秒钟。如果你要处理大量文件,建议把OCR对象初始化一次,然后重复使用,不要每次识别都重新创建。
竖排文字:PaddleOCR对竖排中文的支持有限,如果遇到竖排文档,可能需要先用图像处理库把图片旋转90度再识别。
表格识别:PaddleOCR有专门的表格识别模型(PP-Structure),如果你需要提取表格内容并保留结构,可以研究一下这个模块。不过表格识别比纯文字识别复杂得多,准确率也受表格样式影响较大。
5. 图片预处理:让识别率再上一个台阶
5.1 为什么预处理很重要
OCR引擎再强,如果输入的图片质量差,识别结果也好不到哪去。我踩过的坑里,至少有一半的问题出在图片本身:分辨率太低、对比度不够、有噪点、倾斜、阴影等等。花几分钟做预处理,识别率可能从70%提升到95%以上。
5.2 常用预处理操作及代码
用OpenCV和Pillow可以做大部分预处理工作。先安装:
pip install opencv-python pillow灰度化:彩色信息对文字识别没有帮助,转成灰度图可以减少干扰:
import cv2 img = cv2.imread('input.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)二值化:把灰度图转成黑白两色,让文字和背景的对比更明显:
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)Otsu方法会自动计算最佳阈值,适合大多数场景。
去噪:中值滤波可以去掉椒盐噪声:
denoised = cv2.medianBlur(gray, 3)倾斜校正:如果扫描件是歪的,可以用霍夫变换检测直线角度然后旋转:
import numpy as np def deskew(image): coords = np.column_stack(np.where(image > 0)) angle = cv2.minAreaRect(coords)[-1] if angle < -45: angle = -(90 + angle) else: angle = -angle (h, w) = image.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, angle, 1.0) rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE) return rotated放大:如果图片分辨率太低,可以先放大再识别。但要注意,简单的插值放大不会增加信息量,效果有限。更好的做法是用超分辨率模型,不过那就复杂了。实际经验是,如果原图DPI低于150,识别率会明显下降,建议重新扫描或者用更高的DPI重新截图。
5.3 预处理流程的推荐顺序
我一般按这个顺序做预处理:灰度化 -> 去噪 -> 二值化 -> 倾斜校正 -> 缩放。不是每一步都必须做,根据图片实际情况取舍。比如截图通常不需要去噪和倾斜校正,但扫描件往往需要全套。
注意:二值化不是万能的。如果原图背景复杂或者有渐变,强行二值化可能丢失文字细节。这种情况下,保持灰度图直接识别反而效果更好。建议两种方式都试一下,对比结果。
6. 常见问题与排查技巧实录
6.1 识别结果乱码或空白
这是最常见的问题,原因通常有几个:
语言包没装对。检查tessdata目录下是否有chi_sim.traineddata文件。如果没有,去下载对应的语言包放进去。PaddleOCR的话,检查初始化时的lang参数是否正确。
图片路径或格式问题。Tesseract对某些格式支持不好,比如WebP。建议统一转成PNG或TIFF再识别。
图片质量太差。如果图片模糊、对比度低,任何OCR引擎都无能为力。先做预处理,或者重新获取更清晰的图片。
PSM参数不合适。试试不同的--psm值,有时候换个模式结果就完全不一样。
6.2 中文识别率低怎么办
Tesseract的中文识别率确实不如PaddleOCR,但通过以下方法可以改善:
- 使用
chi_sim+eng而不是纯chi_sim - 设置
--psm 6(假设统一文本块) - 做二值化预处理
- 如果还是不行,换PaddleOCR
PaddleOCR中文识别率低的情况比较少见,如果遇到,检查图片是否分辨率太低或者文字太小。PaddleOCR对小于20像素高的文字识别率会下降,可以尝试放大图片。
6.3 处理速度太慢的优化思路
降低DPI:300 DPI是精度和速度的平衡点,如果对精度要求不高,可以降到200 DPI。
限制识别区域:如果图片中只有部分区域有文字,可以先裁剪出文字区域再识别,减少处理面积。
使用GPU:PaddleOCR支持GPU加速,如果有NVIDIA显卡,安装GPU版本后速度可以提升5到10倍。
并行处理:批量处理时可以用多进程,Python的multiprocessing库可以轻松实现。
调整模型:PaddleOCR提供了不同大小的模型,默认用的是中等大小的模型。如果对速度要求高,可以换用轻量级模型。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 输出为空 | 语言包缺失 | 安装对应语言包 |
| 中文变乱码 | 未指定中文语言 | 加-l chi_sim参数 |
| 识别率低 | 图片质量差 | 预处理:灰度、二值化、去噪 |
| 速度慢 | DPI过高或图片过大 | 降低DPI或裁剪区域 |
| 段落结构丢失 | OCR引擎限制 | 用PaddleOCR的结构化输出 |
| PDF无法直接识别 | Tesseract不支持PDF输入 | 先转图片再识别 |
| 竖排文字识别错乱 | 引擎不支持竖排 | 旋转图片后识别 |
| 表格内容混乱 | 未做版面分析 | 使用PP-Structure表格识别 |
6.5 几个我踩过的坑
坑一:路径里有中文或空格。Tesseract命令行对中文路径支持不好,容易报错。解决办法是把文件放到纯英文路径下,或者用Python调用时用os.path处理路径。
坑二:PDF转图片时DPI设太低。我一开始用默认的150 DPI,识别率惨不忍睹。后来改成300 DPI,效果立竿见影。如果原PDF是扫描件,建议用400 DPI。
坑三:忘了关PaddleOCR的日志输出。PaddleOCR默认会打印很多日志信息,批量处理时刷屏很烦。可以在初始化时设置show_log=False来关闭。
坑四:Tesseract的--psm参数位置放错。这个参数必须放在输出文件名之后,放在前面会被当成输入文件处理。正确的顺序是tesseract input output -l chi_sim --psm 6。
坑五:PaddleOCR首次运行下载模型超时。如果网络环境不好,模型下载可能失败。可以手动下载模型文件放到指定目录,或者设置镜像源加速下载。
7. 把OCR集成到日常工作流里
7.1 批量处理文件夹的自动化脚本
如果你经常需要处理大量图片或PDF,可以写一个带监控功能的脚本,自动处理指定文件夹里的新文件:
import time import os from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from paddleocr import PaddleOCR class OCRHandler(FileSystemEventHandler): def __init__(self, output_dir): self.ocr = PaddleOCR(use_angle_cls=True, lang='ch', show_log=False) self.output_dir = output_dir def on_created(self, event): if event.is_directory: return if event.src_path.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')): print(f'检测到新文件:{event.src_path}') result = self.ocr.ocr(event.src_path, cls=True) text = '\n'.join([line[1][0] for line in result[0]]) if result[0] else '' base_name = os.path.splitext(os.path.basename(event.src_path))[0] output_path = os.path.join(self.output_dir, f'{base_name}.txt') with open(output_path, 'w', encoding='utf-8') as f: f.write(text) print(f'识别完成:{output_path}') observer = Observer() observer.schedule(OCRHandler('./output'), './watch', recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()这个脚本会监控watch文件夹,一旦有新图片放进去就自动识别,结果保存到output文件夹。配合截图工具的自动保存功能,可以实现“截图即识别”的体验。
7.2 与笔记软件联动
我自己的做法是:截图后自动OCR,然后把文字追加到当天的笔记文件里。这样平时看到有用的内容,截个图就自动归档了,后面搜索笔记就能找到。实现方式是在上面的脚本里加一段追加写入的逻辑,把识别结果按时间戳追加到指定文件。
7.3 处理特殊格式的注意事项
验证码识别:验证码通常有干扰线、扭曲、粘连,普通OCR效果很差。如果确实需要,可以针对性地做图像预处理(去干扰线、字符分割),但这不是本文的重点,而且验证码识别的合规性需要特别注意,不要用于不当用途。
公式识别:普通OCR对数学公式的识别基本不可用。如果需要提取公式,要用专门的公式识别工具,比如LaTeX-OCR这类项目。
手写体识别:Tesseract对手写体基本无能为力,PaddleOCR稍好但也有限。手写体识别目前仍然是个难题,如果要求高,可能需要用专门的手写识别模型。
多语言混排:中英日韩混排的文档,建议用PaddleOCR的多语言模型,或者分别用不同语言包识别后合并结果。
8. 一些实际使用中的体会
用开源OCR处理文档这件事,我从最开始的手忙脚乱到现在基本能稳定输出,中间经历了大概两三个月的摸索期。最大的感受是:没有万能方案,只有适合当前场景的方案。Tesseract和PaddleOCR各有优劣,关键是根据你的具体需求来选。
另一个体会是,预处理的重要性怎么强调都不为过。同样一张图,做不做二值化,识别率可能差30%以上。花几分钟调一下对比度和阈值,比换引擎的效果还明显。
还有一点,不要追求100%的准确率。OCR本质上是一个概率问题,再好的引擎也会有出错的时候。重要的是建立一个校验机制,比如识别完后快速扫一遍,或者对关键字段做二次确认。对于批量处理,可以先用小样本测试,确认效果后再全量跑。
最后说一个实用技巧:如果你经常需要识别同一种版式的文档(比如某种固定的票据或表格),可以针对这个版式做模板匹配,先定位关键区域再识别,准确率会比整页识别高很多。这个思路在处理结构化文档时特别有效。