首页
/ tesseract.js OCR 实战指南:从基础识别、区域裁剪到多 Worker 并行调度

tesseract.js OCR 实战指南:从基础识别、区域裁剪到多 Worker 并行调度

2026-09-05 12:22:31作者:殷蕙予

本文基于仓库文档 docs/examples.md,系统讲解 tesseract.js 的八类核心用法:基础识别、进度日志、多语言加载、字符白名单、页面分割模式(PSM)、PDF 输出、局部图像识别(rectangle)以及基于 Scheduler 的多 Worker 并行加速。读完本文,你既能直接复制运行这些示例,也能通过源码路径理解每个参数在 tesseract.js 内部的真实作用机制。

Tesseract.js 在浏览器中运行的 OCR 演示

前置说明:示例依赖哪些 API

所有示例都围绕 tesseract.js 包导出的两个核心构造函数展开。查看仓库入口 src/index.js 可以看到,包对外暴露的能力包括 createWorkercreateScheduler,以及常量对象 languagesOEMPSMsetLogging。文档开头也提示可以查看 examples 目录,其中 examples/node/examples/browser/ 分别存放 Node 端和浏览器端的可直接运行脚本。

createWorker 的完整签名定义在 src/createWorker.js

module.exports = async (langs = 'eng', oem = OEM.LSTM_ONLY, _options = {}, config = {}) => {

从源码结构看,四个参数依次为:语言(字符串或数组)、OCR 引擎模式(默认 OEM.LSTM_ONLY,即纯 LSTM 引擎,常量定义见 src/constants/OEM.js)、运行时选项(如 loggercorePathlangPath 等)和额外的 Tesseract 配置。需要特别注意的是,文档示例中的顶层 await createWorker(...) 写法要求代码运行在支持顶层 await 的 ESM 环境(例如 Node 下的 .mjs 文件);而仓库自带的 Node 示例 examples/node/recognize.js 采用 async IIFE 包裹的写法,两种风格等价,可任选其一。

另外,worker.recognize 的第三个参数 output 默认值为 { text: true }(见 src/createWorker.js),即默认只返回文本结果;要获取 PDF 等其他输出格式,需要显式传入第三个参数,后文 PDF 小节会用到这一点。

基础用法:创建 Worker 并识别一张图片

文档给出的最小可用示例:

const { createWorker } = require('tesseract.js');

const worker = await createWorker('eng');

(async () => {
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
  console.log(text);
  await worker.terminate();
})();

结合源码可以补充几点文档未展开的细节:

  • await createWorker('eng') 返回的不是一个"空壳",而是一个已经加载完成的 worker。src/createWorker.js 中,createWorker 在内部按顺序执行 loadInternal → loadLanguageInternal → initializeInternal 三个作业后才 resolve,即 Tesseract WASM 内核与语言模型(eng 的 traineddata)的下载、解压、初始化都已在后台 worker 中完成。文档也因此在注释中标明 "workers now come pre-loaded"。
  • worker.recognize(image) 的入参可以是 URL、本地文件路径或 ImageData/Buffer 等,Node 端的加载逻辑由 src/worker/node/loadImage.js 处理;仓库示例 examples/node/recognize.js 就演示了传入本地路径 tests/assets/images/cosmic.png 的用法。
  • await worker.terminate() 用于释放 worker 线程资源(Node 端 worker 基于 worker_threads 派生,见 src/worker-script/node/index.js),批量任务结束后应显式调用。

观察识别进度:logger 选项

const { createWorker } = require('tesseract.js');

const worker = await createWorker('eng', 1, {
  logger: m => console.log(m), // Add logger here
});

(async () => {
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
  console.log(text);
  await worker.terminate();
})();

这里 createWorker 的第二个参数 1OEM.LSTM_ONLY(与默认值相同,写出只是为了显式指定引擎模式)。

进度回调的触发链路在源码中非常清晰:src/createWorker.js 在构造时从 _options 中单独拆出 loggererrorHandler;当后台 worker 回传的消息 status === 'progress' 时,onMessage 处理器 会调用 logger({ ...data, userJobId: jobId }),把内核上报的 statusprogress 等字段连同作业 ID 一起交给你的回调。这就是长任务中打印加载/识别进度条的标准方式。

多语言识别:用 "+" 分隔或数组

const { createWorker } = require('tesseract.js');

const worker = await createWorker(['eng', 'chi_tra']);

(async () => {
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
  console.log(text);
  await worker.terminate();
})();

两种等价写法都受支持。从 src/createWorker.js 可以看到 const currentLangs = typeof langs === 'string' ? langs.split('+') : langs;,即传入 'eng+chi_tra' 这样的字符串会被按 + 拆分,与数组形式完全一致。可用的语言代码(如 chi_tra 繁体中文、jpnfra 等)汇总在 src/constants/languages.jsdocs/tesseract_lang_list.md 中。

限制可识别字符:tessedit_char_whitelist

const { createWorker } = require('tesseract.js');

const worker = await createWorker('eng');

(async () => {
  await worker.setParameters({
    tessedit_char_whitelist: '0123456789',
  });
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
  console.log(text);
  await worker.terminate();
})();

setParameters 会把参数打包成一个 action: 'setParameters' 的作业发往后台 worker(见 src/createWorker.js),worker 脚本侧通过 api.SetVariable 将其应用到 Tesseract 内核。典型场景是识别纯数字的车牌、表格编号等,白名单能显著降低误识别。需要注意 tessedit_char_whitelist 会同时约束大小写字母,若只要数字,就不要把字母也加进白名单。

页面分割模式:tessedit_pageseg_mode 与 PSM 常量

原文档建议参考 Tesseract 上游源码中 publictypes.h 的 PSM 定义;在本仓库中,这些模式已被整理为可直接导入的字符串常量 src/constants/PSM.js

常量 含义
PSM.OSD_ONLY '0' 仅运行方向与脚本检测
PSM.AUTO_OSD '1' 自动 + OSD
PSM.AUTO_ONLY '2' 自动,但无 OSD
PSM.AUTO '3' 默认,完全自动
PSM.SINGLE_COLUMN '4' 均匀的单栏文本
PSM.SINGLE_BLOCK_VERT_TEXT '5' 竖向文本块
PSM.SINGLE_BLOCK '6' 单个文本块
PSM.SINGLE_LINE '7' 单行文本
PSM.SINGLE_WORD '8' 单个单词
PSM.CIRCLE_WORD '9' 环形单词
PSM.SINGLE_CHAR '10' 单个字符
PSM.SPARSE_TEXT '11' 页面上散布的文本
PSM.SPARSE_TEXT_OSD '12' 散布文本 + OSD
PSM.RAW_LINE '13' 单行,不经过 Tesseract 内部分析

文档示例使用 PSM.SINGLE_BLOCK,适用于图中只有一整块文字、不需要版面分析的场景:

const { createWorker, PSM } = require('tesseract.js');

const worker = await createWorker('eng');

(async () => {
  await worker.setParameters({
    tessedit_pageseg_mode: PSM.SINGLE_BLOCK,
  });
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
  console.log(text);
  await worker.terminate();
})();

在 worker 脚本 src/worker-script/index.js 中可以看到,tessedit_pageseg_mode 最终通过 api.SetVariable('tessedit_pageseg_mode', String(psmInit)) 生效;同时识别结束后的 api.RestoreParameters()L435-L437)会把本次临时改动的参数还原,避免污染后续作业。

生成 PDF 输出

文档在此处指引查看 examples 目录,两个可运行参考分别是浏览器端 examples/browser/download-pdf.html 与 Node 端 examples/node/download-pdf.js。Node 示例的核心代码:

const worker = await createWorker();
const { data: { text, pdf } } = await worker.recognize(image, { pdfTitle: 'Example PDF' }, { pdf: true });
fs.writeFileSync('tesseract-ocr-result.pdf', Buffer.from(pdf));

对照 recognize 的签名 recognize(image, opts, output)src/createWorker.js):第二参数 { pdfTitle: 'Example PDF' } 是 tesseract 侧选项,指定生成的 PDF 标题;第三参数 { pdf: true } 是输出选项,打开后结果对象的 data 中才会包含 pdf 字段(一个 Uint8Array),Node 端再经 Buffer.from 落盘即可。默认 output 只有 { text: true },所以不显式传 { pdf: true } 是拿不到 PDF 的

只识别图像的一部分:rectangle 参数

tesseract.js 支持传入 { left, top, width, height } 形式的矩形,只对该区域做识别。文档给出三个递进的示例。

单个矩形

const { createWorker } = require('tesseract.js');

const worker = await createWorker('eng');
const rectangle = { left: 0, top: 0, width: 500, height: 250 };

(async () => {
  const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png', { rectangle });
  console.log(text);
  await worker.terminate();
})();

底层实现位于 src/worker-script/index.jsrecognize 作业中检测到 options.rectangle 为对象时,调用 api.SetRectangle(rec.left, rec.top, rec.width, rec.height) 告知内核只对裁剪区域做识别,而图像数据本身不需要在 JS 层裁切。rectangle 属于一组 tesseract.js 专属选项(tessjsOptionssrc/worker-script/index.js#L340,还包括 pdfTitlepdfTextOnlyrotateAutorotateRadians),它们不会被转发给 Tesseract 内核。

多个矩形(串行)

由于一次 recognize 只能指定一个矩形,多个区域最简单的做法就是循环调用:

const { createWorker } = require('tesseract.js');

const worker = await createWorker('eng');
const rectangles = [
  { left: 0, top: 0, width: 500, height: 250 },
  { left: 500, top: 0, width: 500, height: 250 },
];

(async () => {
  const values = [];
  for (let i = 0; i < rectangles.length; i++) {
    const { data: { text } } = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png', { rectangle: rectangles[i] });
    values.push(text);
  }
  console.log(values);
  await worker.terminate();
})();

多个矩形(用 scheduler 并行识别)

串行循环在区域较多时会明显变慢,此时可以让多个 worker 同时工作:

const { createWorker, createScheduler } = require('tesseract.js');

const scheduler = createScheduler();
const worker1 = await createWorker('eng');
const worker2 = await createWorker('eng');
const rectangles = [
  { left: 0, top: 0, width: 500, height: 250 },
  { left: 500, top: 0, width: 500, height: 250 },
];

(async () => {
  scheduler.addWorker(worker1);
  scheduler.addWorker(worker2);
  const results = await Promise.all(rectangles.map((rectangle) => (
    scheduler.addJob('recognize', 'https://tesseract.projectnaptha.com/img/eng_bw.png', { rectangle })
  )));
  console.log(results.map(r => r.data.text));
  await scheduler.terminate();
})();

用多个 Worker 提速:createScheduler 作业调度

文档的最后一个示例是把同一张图片派发 10 个识别作业,由 2 个 worker 并行消化:

const { createWorker, createScheduler } = require('tesseract.js');

const scheduler = createScheduler();
const worker1 = await createWorker('eng');
const worker2 = await createWorker('eng');

(async () => {
  scheduler.addWorker(worker1);
  scheduler.addWorker(worker2);
  /** Add 10 recognition jobs */
  const results = await Promise.all(Array(10).fill(0).map(() => (
    scheduler.addJob('recognize', 'https://tesseract.projectnaptha.com/img/eng_bw.png')
  )))
  console.log(results);
  await scheduler.terminate(); // It also terminates all workers.
})();

Scheduler 的实现完全位于 src/createScheduler.js,其调度模型可以从源码中逐条确认:

  • addJob(action, ...payload) 会把作业压入 jobQueue,并立即尝试 dequeue()src/createScheduler.js)。dequeue 扫描当前所有 worker,找到第一个空闲者(不在 runningWorkers 中)执行队首作业;作业完成后在 finally 中再次触发 dequeue,形成"完成一个、补一个"的流水线(L20-L30)。
  • 在没有任何 worker 的情况下调用 addJob 会直接抛错:You need to have at least one worker before adding jobsL61-L66)。
  • scheduler.terminate() 会遍历并终止挂名其下的全部 worker,同时清空 jobQueueL68-L73),这解释了文档示例注释中 "It also terminates all workers" 的含义——不需要再逐个 worker.terminate()
  • 调度器还暴露 getQueueLen()getNumWorkers(),可用于监控积压情况。

仓库自带的 examples/node/scheduler.js 与上文示例思路一致:创建 4 个 worker 注册进 scheduler,然后对同一张图派发 4 个并行 recognize 作业,注释中也说明这是为了演示批量任务加速,实际业务中通常是不同的图片。浏览器端的并行示例可参考 examples/browser/basic-scheduler.html,它与 Node 端共用同一套 scheduler API。

小结:示例与仓库文件对照

用法 文档章节 可运行参考 关键源码
基础识别 basic examples/node/recognize.js src/createWorker.js
进度日志 with detailed progress examples/node/recognize.js src/createWorker.js#L219-L221
多语言 multiple languages src/constants/languages.js src/createWorker.js#L33
字符白名单 whitelist char src/createWorker.js#L160-L166
页面分割模式 pageseg mode src/constants/PSM.js src/worker-script/index.js#L395-L397
PDF 输出 pdf output examples/node/download-pdf.jsexamples/browser/download-pdf.html src/createWorker.js#L168-L176
局部识别 part of the image src/worker-script/index.js#L415-L418
多 Worker 并行 multiple workers examples/node/scheduler.js src/createScheduler.js

上述示例在 Node 与浏览器端行为一致:createWorker 内部会根据运行环境选择对应的 worker 启动方式(src/worker/node/ 使用 worker_threadssrc/worker/browser/ 使用 Web Worker),因此文档示例代码可以基本原样迁移到两种环境,只需注意浏览器中图片输入形式与文件系统落盘方式的差异(可进一步参考 docs/api.mddocs/workers_vs_schedulers.md)。

登录后查看全文
热门项目推荐
相关项目推荐