ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Tesseract OCR离线安装与批量识别实战:中文语言包配置与预处理技巧

Tesseract OCR离线安装与批量识别实战:中文语言包配置与预处理技巧

简介:Tesseract OCR 安装包与中文语言包面向需要在本地或 Python 项目中实现文字识别的开发者、测试人员及自动化办公用户,解决图片文字提取、批量文档数字化等场景下的环境搭建与中文识别问题。压缩包共 723 个文件,约 36.01MB,以 274 个 h 头文件、271 个 cpp 源码文件为核心,辅以 am、java、xml、html、sh、cmake、yml 等构建与配置脚本,以及 traineddata、tif、png、jpg 等语言模型与测试图像,另含 man 手册、readme、license 等说明文档,结构接近完整源码发行版。资源已获 941 人学习下载,适合希望深入理解 OCR 引擎组成或直接部署识别环境的读者。通过该包可获取可编译的 Tesseract 源码、简体中文语言数据及配套命令行工具,便于在 Python 中调用识别图片文字,并支持按需扩展其他语种,为二次开发与排错提供完整参考。

1. 从一张糊掉的发票说起:这个离线 OCR 包到底能扛什么活

上个月帮朋友处理一批扫描件,几百张发票和合同混在一起,文件名全是IMG_20240311_001.jpg这种。他第一反应是找在线识别服务,我直接拦住了——票据里有金额、抬头、税号,往别人的接口上送,心里不踏实。于是翻出硬盘里存了很久的tesseract-ocr安装包和中文语言包.zip,解压、配环境变量、跑脚本,一个下午把活干完了。

这个压缩包解决的就是这件事:在完全离线的 Windows 机器上,把图片里的中英文文字抠出来,变成可搜索、可复制的文本。它包含 Tesseract 引擎本体和chi_sim(简体中文)等语言包,装完不依赖网络,适合内网办公、批量票据归档、老照片文字提取这类场景。适合谁?不想把敏感图片传到云端的人、需要批量处理几百上千张图的运维或行政、以及想在自己程序里嵌一个免费 OCR 能力的开发者。如果你只是偶尔识别一张图,在线工具更省事;但只要涉及批量、隐私、自动化,这个包就是绕不开的起点。

2. 装之前先搞懂:Tesseract 的识别链路和语言包选择

2.1 它到底怎么把像素变成文字的

Tesseract 的流程不是「看一眼就认字」,而是一条固定流水线:先对输入图像做二值化,把灰度图变成黑白;再做连通域分析,找出一个个可能是文字的区块;接着进行行分割和字符切分,把区块拆成单行、单字;最后用训练好的模型做特征匹配,输出文本。这条链路里,二值化的质量直接决定后面所有环节的上限。一张光照不均、背景带噪点的图,二值化后文字和背景糊在一起,后面模型再强也救不回来。

所以理解这条链路的意义在于:当识别结果乱七八糟时,你要能判断问题出在哪一环。是图像太脏(预处理问题),还是语言包没加载(配置问题),还是版面太复杂(分割问题)。很多人一上来就怪引擎不行,其实八成是喂进去的图没处理好。

2.2 语言包怎么选、放哪里

压缩包里通常有tesseract.exe主程序和tessdata目录,语言包就是tessdata下的.traineddata文件。简体中文对应chi_sim.traineddata,繁体是chi_tra,英文是eng。关键点:语言包必须和主程序版本匹配。Tesseract 4.x 用的是 LSTM 模型,3.x 用的是旧版特征模型,把 4.x 的语言包丢给 3.x 主程序,会直接报错或者识别出一堆乱码。

放置位置有两种常见做法。一是放在安装目录的tessdata文件夹里,程序默认去那找;二是单独放一个目录,通过环境变量TESSDATA_PREFIX指过去。我一般用第二种,因为换版本、加语言包时不用动安装目录,干净。

文件作用常见坑
tesseract.exe主程序,命令行入口路径带空格要加引号
chi_sim.traineddata简体中文识别模型版本不匹配直接报错
eng.traineddata英文识别模型中英混排时建议一起加载
tessdata目录语言包存放位置权限不足会导致读取失败

2.3 环境变量配置的两种方式

第一种是图形界面:右键「此电脑」→ 属性 → 高级系统设置 → 环境变量,在系统变量里新建TESSDATA_PREFIX,值填tessdata目录的完整路径。第二种是命令行临时设置,适合写脚本时用:

:: Windows 下临时设置,只对当前命令行窗口生效 set TESSDATA_PREFIX=D:\ocr\tessdata :: 验证是否生效 tesseract --list-langs

--list-langs会列出当前能找到的所有语言包。如果输出里没有chi_sim,说明路径没配对,或者语言包文件名拼错了。这一步是后面所有操作的前提,别跳过。

3. 命令行跑通第一张图:参数怎么调、结果怎么看

3.1 最小可用命令

装好之后,先拿一张清晰的图试手。假设图片叫test.png,想识别中文:

:: 基本用法:输入图 输出文件 -l 语言 tesseract test.png output -l chi_sim :: 中英混排,同时加载两个语言包,用加号连接 tesseract test.png output -l chi_sim+eng :: 直接输出到终端,不写文件 tesseract test.png stdout -l chi_sim

第一条命令会生成output.txt。-l参数指定语言,多个语言用+连接。注意语言加载顺序会影响结果:chi_sim+eng表示以中文为主、英文为辅,反过来则英文优先。中英混排的票据,我一般用chi_sim+eng,因为中文识别对英文数字的兼容性更好。

3.2 页面分割模式(PSM)的选择

Tesseract 默认假设输入是「一页排版规整的文档」,但实际图片可能是单行文字、单个字、竖排、或者带大量留白的截图。这时候要用--psm参数告诉它版面结构:

:: psm 6:假设是一整块统一文本,适合大多数截图 tesseract test.png output -l chi_sim --psm 6 :: psm 7:假设是单行文字,适合裁剪出来的标题 tesseract title.png output -l chi_sim --psm 7 :: psm 3:默认模式,全自动分割,适合排版规整的文档 tesseract doc.png output -l chi_sim --psm 3

--psm 6是我用得最多的,因为大部分截图和票据都是「一块一块」的文本,自动分割反而容易把相邻区块切错。--psm 7处理单行标题很准,但喂整页图进去会只识别第一行。选错 PSM 的典型症状是:明明图很清楚,结果只出来几个字,或者顺序全乱。

3.3 输出格式与置信度

除了纯文本,Tesseract 还支持多种输出格式,通过-c参数控制:

:: 输出 TSV,包含每个词的坐标和置信度 tesseract test.png output -l chi_sim tsv :: 输出 PDF,文字层可搜索 tesseract test.png output -l chi_sim pdf :: 输出 hOCR,保留版面结构 tesseract test.png output -l chi_sim hocr

TSV 格式特别有用,它每个词一行,带conf字段(置信度 0-100)。批量处理时,我习惯先跑 TSV,把conf低于 60 的行筛出来人工复核,剩下的直接入库。这比全量人工校对省一半时间。

提示:conf为 -1 的行通常是分割产生的空块,直接过滤掉,不要当成低置信度文字处理。

4. 批量处理与预处理:让识别率从六成到九成

4.1 用 Python 批量调用

命令行单张跑没问题,但几百张图就得写脚本。Python 里用subprocess调 Tesseract 是最稳的方式,不依赖额外的 Python 包:

import subprocess import os from pathlib import Path # 配置:tesseract 路径、语言、PSM 模式 TESSERACT = r"C:\Program Files\Tesseract-OCR\tesseract.exe" LANG = "chi_sim+eng" PSM = "6" def ocr_image(img_path, out_dir): """对单张图做 OCR,输出 txt 到指定目录""" img_path = Path(img_path) out_base = Path(out_dir) / img_path.stem cmd = [ TESSERACT, str(img_path), str(out_base), "-l", LANG, "--psm", PSM ] # capture_output 捕获错误信息,方便排查 result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: print(f"失败: {img_path.name} -> {result.stderr}") return None return out_base.with_suffix(".txt") # 批量遍历目录 img_dir = Path(r"D:\scans") out_dir = Path(r"D:\ocr_output") out_dir.mkdir(exist_ok=True) for img in img_dir.glob("*.jpg"): txt = ocr_image(img, out_dir) if txt: print(f"完成: {txt.name}")

这段脚本的核心是subprocess.run调用外部程序,capture_output=True把错误信息抓回来,避免某张图失败导致整个批次静默中断。returncode非零说明 Tesseract 报错了,常见原因是图片路径含中文、文件损坏、或者语言包没加载。参数部分,LANG和PSM抽成常量,方便统一调整;out_base用原文件名做输出名,避免重名覆盖。

4.2 预处理:二值化和去噪

前面说过二值化决定上限。如果原图偏暗、有阴影、或者背景是彩色,直接喂给 Tesseract 效果很差。常见做法是先用 OpenCV 做一轮预处理:

import cv2 import numpy as np def preprocess(img_path): """灰度化 + 自适应二值化 + 去噪""" img = cv2.imread(img_path) # 转灰度,减少通道干扰 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 自适应阈值:应对光照不均,blockSize 取 31,C 取 10 binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 10 ) # 中值滤波去椒盐噪声,核大小 3 denoised = cv2.medianBlur(binary, 3) return denoised # 保存预处理结果,再喂给 Tesseract processed = preprocess(r"D:\scans\dark_invoice.jpg") cv2.imwrite(r"D:\scans\dark_invoice_clean.png", processed)

adaptiveThreshold比全局阈值更适合票据,因为票据常有局部阴影。blockSize=31表示每个像素参考周围 31×31 的区域算阈值,太大丢失细节,太小引入噪声;C=10是从计算结果里减去的常数,用来微调。medianBlur的核大小必须是奇数,3 是最保守的选择,去噪同时不糊字。预处理后另存为 PNG,避免 JPEG 二次压缩产生伪影。

4.3 分辨率与 DPI 的门槛

Tesseract 对输入分辨率有隐性要求:文字高度最好在 30 像素以上。手机拍的图往往够,但扫描件如果按低 DPI 存,字高可能只有十几像素,识别率断崖式下跌。常见做法是把图放大到 300 DPI 等效尺寸:

def upscale_if_needed(img, min_height=30): """如果文字区域太矮,按比例放大""" h, w = img.shape[:2] # 假设文字占图高约 1/20,估算字高 est_char_h = h / 20 if est_char_h < min_height: scale = min_height / est_char_h new_size = (int(w * scale), int(h * scale)) # INTER_CUBIC 放大比线性插值更平滑 img = cv2.resize(img, new_size, interpolation=cv2.INTER_CUBIC) return img

INTER_CUBIC插值在放大时比INTER_LINEAR边缘更锐利,但计算稍慢。放大倍数别超过 3 倍,否则噪点也被放大,反而更糟。这一步的判断逻辑是估算,实际项目中我会先跑几张看置信度,再决定要不要统一放大。

5. 避坑与排查:那些让我返工三次的问题

5.1 报错Error opening data file tessdata/chi_sim.traineddata

现象:命令行一跑就报这个,或者--list-langs里看不到chi_sim。

原因:TESSDATA_PREFIX没设对,或者语言包文件名大小写不对。Windows 下路径分隔符用反斜杠,但环境变量里写正斜杠有时也能认,写错就找不到。

解决:先用echo %TESSDATA_PREFIX%确认变量值,再进那个目录dir看chi_sim.traineddata在不在。注意有些压缩包解压后语言包在tessdata\tessdata双层目录里,路径要多一层。

5.2 识别结果全是乱码或问号

现象:输出一堆锟斤拷或者方块。

原因:语言包版本和主程序不匹配,或者图片编码有问题。Tesseract 4.x 主程序配 3.x 语言包,就会出这种。

解决:确认主程序版本(tesseract --version),去下对应版本的语言包。另外输出文件默认是 UTF-8,如果编辑器用 GBK 打开会显示乱码,那是显示问题不是识别问题,用 VS Code 切编码看看。

5.3 中文识别率极低,英文却正常

现象:同一张图,-l eng出来八九不离十,-l chi_sim一塌糊涂。

原因:中文模型对字体和清晰度更敏感,尤其是宋体、楷体这类笔画复杂的字体。另外图片如果是彩色背景,中文笔画细,二值化时容易被吃掉。

解决:先做预处理,重点保证笔画不断。如果还是差,试试--psm 6强制整块识别,避免自动分割把字切碎。实在不行,把图放大 2 倍再跑。

5.4 批量处理时某几张图卡死

现象:脚本跑到某张图就不动了,也不报错。

原因:Tesseract 遇到超大图或损坏文件时可能挂起,subprocess默认没有超时。

解决:给subprocess.run加timeout参数:

try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) except subprocess.TimeoutExpired: print(f"超时跳过: {img_path.name}") return None

30 秒对单张图足够,超时直接跳过并记录,不让整个批次卡住。

5.5 路径含中文导致失败

现象:命令行手动跑没问题,脚本一跑就报「找不到文件」。

原因:subprocess传参时,中文路径在某些 Windows 编码环境下会乱码。

解决:用Path对象处理路径,或者在subprocess.run里加encoding='utf-8'。更稳的做法是把待处理图片先复制到纯英文路径的临时目录,处理完再挪回去。

6. 进阶技巧:用 TSV 置信度做半自动校对

批量 OCR 最耗时的不是识别,是校对。全量人工看一遍,几百张图能看瞎眼。我的做法是只校对低置信度的部分,高置信度的直接过。具体流程:先跑 TSV 输出,解析conf字段,把低于阈值的词连同坐标提取出来,生成一张「待复核清单」,人工只看这些。

import csv def extract_low_conf(tsv_path, threshold=60): """从 TSV 里筛出低置信度的词""" low_conf_words = [] with open(tsv_path, "r", encoding="utf-8") as f: reader = csv.DictReader(f, delimiter="\t") for row in reader: # 跳过空行和表头 if not row.get("text") or row["text"].strip() == "": continue conf = int(row["conf"]) # conf 为 -1 是分割块,跳过 if conf == -1: continue if conf < threshold: low_conf_words.append({ "text": row["text"], "conf": conf, "left": row["left"], "top": row["top"] }) return low_conf_words # 用法:先跑 tsv 输出,再筛 words = extract_low_conf(r"D:\ocr_output\test.tsv", threshold=60) for w in words: print(f"置信度 {w['conf']}: {w['text']} @ ({w['left']},{w['top']})")

threshold=60是经验值,中文票据一般设 60-70,英文可以放到 50。坐标信息用来在原图上画框,方便人工定位。这套流程跑下来,校对工作量能压到原来的三成左右。

还有一个技巧是多 PSM 投票:同一张图用--psm 3和--psm 6各跑一遍,对比结果,不一致的地方大概率是识别难点,优先复核。代价是耗时翻倍,只对关键票据用。

注意:TSV 的conf是词级置信度,不是字级。一个词里某个字错了,整个词置信度会拉低,但不会精确到哪个字。所以复核时还是要看原图。

从那以后我每次接 OCR 的活,都强制先跑一遍 TSV 看置信度分布,再决定预处理参数和校对策略,不再盲目全量跑完再返工。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进