Tesseract.js FAQ 深度解读:项目边界、框架集成、oem/psm 差异排查与 .traineddata 缓存机制
本文基于 Tesseract.js 官方 FAQ 文档(docs/faq.md)展开,结合仓库源码逐项还原官方问答背后的实现细节。读完本篇,你将明确 Tesseract.js 的能力边界(不支持 PDF、不支持手写体)、在框架中遇到 Cannot find module 错误的修复方法、与 Tesseract CLI 结果不一致时的三步排查法,以及语言模型 .traineddata 文件在浏览器与 Node.js 下的下载与缓存原理。
项目定位与维护边界
Tesseract.js 是 Tesseract OCR 引擎的 JavaScript/WebAssembly 移植版本,其工作方式是通过封装 tesseract.js-core 这个 WebAssembly 版本来把 Tesseract 引擎带入浏览器与 Node.js。官方 FAQ 对项目范围有明确界定:
- 本项目不会以任何方式修改底层 Tesseract 识别引擎。因此,如果你遇到的 bug 是由 Tesseract 引擎本身导致的,可以在本仓库开 Issue 以便提醒其他用户,但修复工作不在本仓库的职责范围内。
- 如果你希望某个 Tesseract bug 得到修复,应先在 Tesseract 主项目(CLI 版本)中确认行为一致,再到该主项目的仓库中提交 Issue。
这一点与 README.md 中 "Project Scope" 一节相互印证:Tesseract.js 明确声明不支持 PDF 文件,也不会修改 Tesseract 识别模型来提升准确率;对范围外功能有需求的用户,README 建议考虑其衍生的 Scribe.js 库。
框架兼容性:为什么 React Native 不行
FAQ 的结论是:Tesseract.js 支持所有同时支持 JavaScript 和 WebAssembly 的框架,已知不支持的常见框架只有一个——React Native,因为它不支持 WebAssembly。
理解 worker 架构才能理解集成问题
Tesseract.js 的 "worker" 在浏览器中运行于独立的 Web Worker、在 Node.js 中运行于独立的 worker thread,这是一份使用不同入口点的独立代码。当 Tesseract.js 独立使用时,这个入口点应当被自动识别;但各框架的构建系统(webpack、Vite、rollup 等)会复制、移动、重命名文件,从而破坏 Tesseract.js 对文件位置的假设,导致主线程找不到 worker 代码。
从源码结构看,两个运行环境各自维护一份默认的 workerPath:
- Node.js 端 src/worker/node/defaultOptions.js:
workerPath指向包内src/worker-script/node/index.js的本地绝对路径; - 浏览器端 src/worker/browser/defaultOptions.js:
workerPath默认指向 jsdelivr CDN 上的worker.min.js。
src/utils/resolvePaths.js 则负责在运行前对 corePath、workerPath、langPath 三个路径选项做统一解析(浏览器环境下会基于 window.location.href 把相对路径解析为绝对 URL)。当框架打包工具把 node_modules 里的文件搬走后,上述自动推断就会失效。
Cannot find module 的修复方法
如果你能正常运行 examples 目录中的示例,但放进自己的框架/项目就报 cannot find module,说明主线程找不到 worker 代码。官方给出的解法是手动设置 workerPath 参数,指向本地副本:Node.js 指向 worker-script/node/index.js,浏览器指向 worker.min.js。例如(来自 FAQ 中一位 Node.js 用户的实际解决配置):
const worker = await createWorker("eng", 1, {workerPath: "./node_modules/tesseract.js/src/worker-script/node/index.js"});
按你系统的实际安装路径调整文件路径即可。
PDF 与手写体:两类明确不支持的输入
PDF 文件:两条可行路线
Tesseract.js 不支持 PDF 文件。如果需要识别 PDF,FAQ 给出两条路线:
- 使用 Scribe.js:它是构建在 Tesseract.js 之上的库,额外提供了原生 PDF 支持,包括对 PDF 跑 OCR,以及从文本原生(text-native)PDF 中直接提取文字——后者相比跑 OCR 显著更快、更准确。
- 把 PDF 渲染成图片再识别:这是用 Tesseract.js 本身处理 PDF 的唯一方式。用第三方库把
.pdf渲染为一系列.png图片,再用 Tesseract.js 识别。可参考的库及许可证:- PDF.js(Apache-2.0 许可证)
- muPDF(AGPL-3.0 许可证)
手写体:模型层面就不支持
FAQ 的回答是否定的:Tesseract OCR 模型建立在只对印刷体文本成立的假设之上,任何选项组合都无法显著提升手写识别表现。除非你的手写工整到接近印刷体,否则结果都会很差。这是引擎模型层面的限制,无法通过 Tesseract.js 的配置绕过。
配置参数:默认值已是最优,深入配置请回引擎查文档
FAQ 的核心建议是:默认设置对大多数用户就是最优结果,不建议盲目调参。如果确实想实验,Tesseract 提供了大量可配置项——其中绝大多数记录在 Tesseract 主项目的文档中,而不在本仓库。如前所述,核心识别引擎继承自主 Tesseract 项目,Tesseract.js 中所有 Tesseract 配置项的行为与主项目完全一致。所以针对具体调参问题(比如"如何让噪声去除更强/更弱"、"车牌识别用什么参数"),到 Tesseract 的官方文档与讨论区找答案,比只翻本仓库更有效。
仓库内维护了两组与 FAQ 排查主题直接相关的常量,值得展开:
oem(OCR Engine Mode,识别引擎模式),定义于 src/constants/OEM.js:
| 常量 | 值 | 含义 |
|---|---|---|
TESSERACT_ONLY |
0 | 仅使用传统(Legacy)引擎 |
LSTM_ONLY |
1 | 仅使用 LSTM 神经网络引擎(Tesseract.js 默认) |
TESSERACT_LSTM_COMBINED |
2 | LSTM + Legacy 组合,Legacy 作为回退 |
DEFAULT |
3 | 引擎自动选择 |
psm(Page Segmentation Mode,页面切分模式),定义于 src/constants/PSM.js,取值 0–13,其中与下文排查直接相关的是:
| 常量 | 值 | 含义 |
|---|---|---|
PSM_AUTO |
3 | 自动页面切分(CLI 默认) |
PSM_SINGLE_BLOCK |
6 | 假定单块版式文本(Tesseract.js 默认) |
为什么 Tesseract.js 和 Tesseract CLI 的结果不一样
官方立场是:只要设置、语言数据和版本三者完全一致,Tesseract.js 应当产生与 Tesseract CLI 完全相同的结果。如果你观察到差异且差异有实质影响,请按以下三步排查。
第一步:确认参数完全一致
最容易被忽略的是两者的默认值本来就不同,必须在两边手动显式设置 oem 和 psm 后再对比:
oem默认值不同:Tesseract.js 默认oem = 1(仅 LSTM 模型),Tesseract CLI 默认oem = 2(LSTM 加 Legacy 回退);psm默认值不同:Tesseract.js 默认psm = 6(PSM_SINGLE_BLOCK),Tesseract CLI 默认psm = 3(PSM_AUTO)。
确认默认值对齐后,再逐一核对你自己设置的所有其他参数是否两边完全相同。
第二步:确认语言数据完全一致
这是差异最隐蔽的来源。FAQ 说明 Tesseract.js 按 oem 取值使用两套不同的默认语言文件:
- 以
oem = 0或2运行时,默认使用 Tesseract 主项目tessdata仓库的4.0.0版本语言文件; - 以
oem = 1运行时,默认使用4.0.0_best_int版本语言文件——它们由tessdata_best仓库的语言文件**整型化(integerizing)**而成,理论上等价于使用tessdata中由"整型化后的 tessdata_best 加上 Legacy 模型数据"合并生成的 LSTM 语言文件。
这一点可以在 src/worker-script/index.js 中得到源码级印证:未显式指定 langPath 时,默认下载路径正是按 lstmOnly(即 oem 是否为 1)在 @tesseract.js-data/<lang>/4.0.0 与 4.0.0_best_int 两个目录之间切换。换句话说,仅改变 oem 参数,实际加载的语言数据文件就换了一套,这本身就足以造成识别结果差异——所以对比 CLI 时"语言数据一致"必须逐字节确认,而不只是语言代码相同。
第三步:确认版本完全一致
不同 Tesseract 版本可能产生不同识别结果。Tesseract.js 实际使用的确切 Tesseract 版本,可通过 tesseract.js-core 仓库 third_party 目录下的 tesseract 子模块(submodule)确认。
FAQ 还给出了后续的反馈规则:如果发现 Tesseract.js 与 CLI 在设置、语言数据、版本都相同的情况下结果仍不同,请开 Issue 并提供可复现示例;如果发现更新的 Tesseract 版本结果显著更好,也可以开 Issue,维护方会优先把 Tesseract 升级到最新版;反之如果更旧的版本更好,那属于上游回归,应提给 Tesseract 主项目——Tesseract.js 不会回退到旧版本。
.traineddata 语言模型如何下载与缓存
FAQ 对 "Tesseract.js 如何下载并保存 *.traineddata" 的官方回答是:下载语言模型时,Tesseract.js 先检查 *.traineddata 是否已存在(浏览器:IndexedDB;Node.js:通过 fs 检查你执行命令的目录下)。若不存在,则从 tessdata 源拉取 *.traineddata.gz,解压(ungzip)后存入 IndexedDB 或本地文件系统。你可以手动删除缓存文件,它下次会重新下载。
结合源码可以还原完整链路:
- 缓存命中检查:src/worker-script/index.js 先以
${cachePath || '.'}/${lang}.traineddata为键调用readCache尝试读缓存;命中则直接使用dataFromCache,未命中则进入下载分支。 - 环境相关的缓存适配器:
- 浏览器端 src/worker-script/browser/cache.js 基于
idb-keyval,把readCache/writeCache/deleteCache/checkCache全部映射到 IndexedDB; - Node.js 端 src/worker-script/node/cache.js 则基于
fs的readFile/writeFile/unlink/access,缓存放于进程工作目录。
- 浏览器端 src/worker-script/browser/cache.js 基于
- 下载与解压:未命中时按
langPath(未设置则用默认 CDN 路径)请求${lang}.traineddata.gz(gzip 选项在 src/worker-script/index.js 中默认为true,即默认下载压缩文件)。拿到字节流后通过魔数检测 gzip 头(0x1F 0x8B,见 src/worker-script/index.js),是压缩文件就走adapter.gunzip解压。 - 回写缓存:新下载的数据(
newData且cacheMethod为'write'、'refresh'或未设置)会经writeCache写回 IndexedDB 或本地文件;写失败只记录日志而不中断识别流程。
这个机制解释了 FAQ 中"手动删除即可触发重新下载"的说法:删除的只是缓存文件(IndexedDB 键或本地 .traineddata),下次运行缓存检查失败后会自动走下载分支。
顺带一提,同样走"本地缓存优先、缺失时从 CDN 下载"模式的还有 WASM 核心本身:浏览器端 src/worker-script/browser/getCore.js 未指定 corePath 时默认从 CDN 加载 tesseract.js-core,并会用 wasm-feature-detect 探测设备对 SIMD / relaxed-SIMD 的支持,据此在 tesseract-core.wasm.js、tesseract-core-simd.wasm.js、tesseract-core-lstm.wasm.js 等变体间选择加载(lstmOnly,即 oem = 1 时选择带 -lstm 后缀的更小构建)。
如何训练自己的 .traineddata
FAQ 对这个问题给出的答案是:训练自有语言模型请查阅 Tesseract 主项目的官方训练文档——因为识别模型、训练流程都归属于 Tesseract 引擎本身,Tesseract.js 只负责加载和运行模型文件,不承担模型训练职责(与"不修改底层引擎"的项目边界保持一致)。训练出的 .traineddata 可以通过 langPath(指定文件所在目录)或直接把模型数据对象传给 createWorker 使用;参数细节可参见 docs/api.md。
适用前提小结
- 上述默认
oem/psm值、双套语言数据源与缓存机制均来自当前仓库源码(src/constants/OEM.js、src/constants/PSM.js、src/worker-script/index.js),与 FAQ 描述一致; - 环境要求方面,README.md 声明 Tesseract.js v7 需要 Node.js v16+,v6 需要 Node.js v14+;浏览器端则要求运行环境支持 WebAssembly(这也是 React Native 不受支持的根因);
- 若你的场景涉及 PDF 或更高识别精度诉求,超出本仓库范围的部分(Scribe.js、引擎调参、模型训练)都应回到对应上游项目处理。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00