☰
FluentRead 本地图片 OCR 技术解析:Tesseract.js 资源打包、WASM 加载与语言包管理
2026/9/28 2:42:22 网站建设 项目流程
  • 前端
  • AI 应用
  • 本地部署

【免费下载链接】FluentRead

An open-source browser extension for bilingual translation. 一款开源的浏览器双语翻译插件。

项目地址:https://gitcode.com/gh_mirrors/fl/FluentRead
点击查看免费下载

FluentRead 是一款开源的浏览器双语翻译插件,其图片翻译、圈选翻译(本地 OCR 模式)等能力依赖 public/fluent-read-ocr/NOTICE.md 所声明的 Tesseract.js OCR 资产。本文以该 NOTICE 文档为核心,结合扩展内真实源码与测试,系统讲解 Tesseract.js 6.0.1 及其 core 6.1.2 如何被本地化打包、经 Offscreen 文档与 Web Worker 加载执行,以及 OCR 语言包(eng / chi_sim / jpn 等)如何按需下载、缓存与清除——帮助读者理解这套"零 CDN 运行时依赖"的本地文字识别基础设施的原理与工程实现。

一、NOTICE 文档声明了什么:扩展内置的三类 OCR 资产

public/fluent-read-ocr/NOTICE.md全文虽短,却精确界定了 FluentRead 内置 OCR 资产的组成与来源:

  • Tesseract.js 6.0.1(Apache-2.0 许可):面向浏览器的 Tesseract 封装库,提供createWorker等高层 API;
  • tesseract.js-core 6.1.2(Apache-2.0 许可):Tesseract 的 WebAssembly 内核;
  • Tesseract 语言数据包(eng、chi_sim、jpn,MIT 许可):traineddata识别模型数据。

NOTICE 同时强调了一个关键的工程决策:

The worker, WebAssembly files, and language data are loaded from this extension's own resources. No OCR code is downloaded from a third-party CDN at runtime.

即Worker 脚本、WebAssembly 文件和语言数据一律从扩展自身资源加载,运行时绝不从第三方 CDN 下载任何 OCR 代码。这句话决定了整个 OCR 模块的架构取向,也解释了public/fluent-read-ocr/目录为何只包含两个文件:

  • core/tesseract-core-simd-lstm.wasm.js:SIMD + LSTM 版本的 WASM 内核(含 asm 化的 .wasm 二进制);
  • worker/worker.min.js:Tesseract.js 的 worker 脚本。

注意:目录下只有 SIMD + LSTM 一种 core 变体。其原因在 ocrRuntime.ts 的注释中说明——扩展支持的 Chrome/Edge(Offscreen 需 109+)与 Firefox 140+ 均支持 WASM SIMD,且引擎只使用 LSTM 识别引擎,因此不再打包永远不会被选中的非 SIMD 变体,缩小扩展体积。

二、本地资源如何被定位:chrome.runtime.getURL 与扩展资产路径

Worker 与 core 之所以能以"扩展自身资源"身份加载,关键在于 ocrRuntime.ts 中的extensionAsset辅助函数:

function extensionAsset(path: string): string { const getRuntimeUrl = chrome.runtime.getURL as (assetPath: string) => string; return getRuntimeUrl(`/fluent-read-ocr/${path}`); }

它以chrome.runtime.getURL把public/fluent-read-ocr/下的相对路径解析为chrome-extension://<extension-id>/fluent-read-ocr/...形式的内置 URL,随后传入 Tesseract.js 的createWorker配置:

createWorker(languages.split('+'), 1, { workerPath: extensionAsset('worker/worker.min.js'), // 本地 Worker 脚本 corePath: extensionAsset('core/tesseract-core-simd-lstm.wasm.js'), // 本地 WASM 内核 cachePath: 'fluent-read-image-ocr', workerBlobURL: false, // 不使用 Blob Worker logger: message => { if (message.status === 'recognizing text') onProgress(message.progress, message.userJobId); }, }, OCR_INIT_CONFIG)

几个配置项的含义与工程动机:

  • workerPath/corePath:强制指向扩展内部资源,替代 Tesseract.js 默认的 CDN 地址,从根上兑现 NOTICE 的"零运行时 CDN"承诺;
  • workerBlobURL: false:Tesseract.js 默认会把 worker 包装成 Blob URL,而 Offscreen 页面拥有扩展源,直接加载本地 worker 可规避 Blob Worker 的 CSP/源限制;
  • cachePath: 'fluent-read-image-ocr':指定语言包在 IndexedDB 中的缓存键前缀;
  • OCR_INIT_CONFIG = {tessedit_load_sublangs: ''}:关闭隐式子语言加载。这是针对 6.1.2 内置 core 的一个已知缺陷规避——该版本在遍历语言 vector 时会追加 CJK 模型声明的竖排子语言,导致迭代器失效并误读为「;」;由于所需语言已由getOcrLanguages显式传入,初始化时关闭隐式子语言即可保留已下载的主模型(上游同类问题见 tesseract-ocr/tesseract#4002)。

测试 imageOcrRuntime.test.ts 直接验证了这条资源链路:mock 的chrome.runtime.getURL把路径解析为chrome-extension://test/fluent-read-ocr/worker/worker.min.js与.../core/tesseract-core-simd-lstm.wasm.js,并断言createWorker以['jpn', 'eng']、1(单语言模型)和上述配置被调用。

三、识别在哪里跑:Offscreen Document + Web Worker 的隔离架构

本地 OCR 并不在页面上下文执行,而是运行在扩展的隔离环境。从 docs/architecture.md 可以看到整体运行时布局:Chrome/Edge MV3 使用原生 Offscreen Document,Firefox MV2 使用后台页面中的隐藏扩展 iframe,两者加载同一个offscreen.html,复用同一份 OCR、图片/区域绘制等逻辑。

识别调用链大致如下:

  1. 内容脚本或后台发起图片识别请求,经由 offscreenAdapter.ts 适配为平台 Offscreen 消息;
  2. offscreenRuntime.ts 在具备 Canvas/DOM 的隔离文档中完成图片解码、尺寸校验与调用编排;
  3. ocrRuntime.ts 创建 Tesseract.js Worker,真正的识别循环跑在worker.min.js启动的 Web Worker 中;
  4. ocrWorkerRuntime.ts 负责与引擎无关的 Worker 生命周期与串行任务队列。

Offscreen 页面与后台/内容脚本之间只通过类型化消息通信,识别产生的位图、Canvas 等临时资源在完成或失败后即被释放,见 offscreenRuntime.ts 的模块边界说明。

3.1 Worker 复用与串行队列:识别不会互相"踩踏"

ocrWorkerRuntime.ts 是整套并发控制的实现核心,它保证了:

  • 同语言 Worker 复用:连续识别同语言图片时不会重复创建 Worker、重复初始化语言模型(getWorker会比对workerLanguages);
  • 串行执行:所有操作挂在一条"尾链"(operationTail)上排队,避免一次请求的setParameters/recognize被另一次请求交叉覆盖;
  • 语言切换先终止旧 Worker:等待正在进行的识别结束后才terminate旧 Worker 并创建新语言 Worker,切换过程还带"所有权代际"(workerOwnershipGeneration)校验,防止旧切换恢复后误覆盖新请求安装的实例;
  • 任务隔离的进度上报:通过jobId关联进度,只发布当前任务的真实百分比,丢弃非法(NaN/Infinity)、倒退、重复和迟到的进度值。

识别时会对 Worker 设置两个 Tesseract 参数:

worker.setParameters({ tessedit_pageseg_mode: pageSegmentationMode, // 默认 PSM.SPARSE_TEXT(11) preserve_interword_spaces: '1', })
  • tessedit_pageseg_mode:页面分割模式。普通图片使用PSM.SPARSE_TEXT(稀疏文本),圈选翻译在空结果时才以PSM.SINGLE_BLOCK(6,单块)重试一次,见 ocrRuntime.ts;
  • preserve_interword_spaces: '1':保留词间空格,避免识别文本粘连。

以上语义均有 ocrWorkerRuntime.test.ts 的测试覆盖,例如"复用同语言 Worker 和稀疏文本参数,连续识别不重复跨 Worker 初始化"(第 69-84 行)、"等待正在进行的识别结束后才终止 Worker 并切换语言"(第 86-107 行)。

3.2 取消语义:排队取消与执行中取消

取消(AbortSignal)被区分为两种情形,ocrWorkerRuntime.ts 对此有精细处理:

  • 排队中的请求被取消:只结束自己的等待,不阻塞后续任务,尾链继续等待前一任务完成,避免后来的请求越过正在进行的 OCR;
  • 执行中的请求被取消:立即释放当前 Worker(terminateCurrentWorker),因为其内部状态已不可信,同时保证取消不会阻塞串行尾链。

两条路径都以AbortError(name: 'AbortError',提示"图片 OCR 请求已取消")拒绝调用方,且会接住底层迟到的 Promise 拒绝,避免遗留未处理 Promise。

四、识别前后的图像处理:解码、缩放、坐标还原与结果缓存

ocrRuntime.ts 在调用 Worker 前后还承担了完整的图像管线:

4.1 图片解码与预缩放

loadOcrImage负责把 data URL 解码为HTMLImageElement,并设置15 秒解码超时(OCR_IMAGE_DECODE_TIMEOUT_MS = 15_000),超时、数据无法解码、请求被取消都会给出明确错误并释放src。解码完成后立刻清空source.src释放像素,不等远端识别结束(第 143-147 行)。

prepareOcrImage则根据识别场景(image整图 /area圈选)把图片缩放/加边后绘制到 Canvas 再编码为 PNG:

  • 整图识别直接使用getOcrImageSize的目标尺寸;
  • 圈选识别使用getAreaOcrImageSize,小图会有界放大并加白边(padding),以提升低分辨率选区的识别率;
  • 绘制使用高画质插值(imageSmoothingQuality = 'high'),绘制/编码完成后立即将 Canvas 宽高置 0 释放像素,"不与 OCR WebAssembly 长期并存"(第 136-140 行)。

4.2 坐标映回与结果缓存

Tesseract 返回的是归一化/缩放后的 block 结构,restoreOcrLineCoordinates会把行级bbox依据sourceWidth/sourceHeight、目标尺寸与 padding 映回原图坐标,供翻译绘制层直接在原图上定位。

完成的结果会进入一个有界 LRU 缓存(completedRecognitionCache):

  • 上限3 张图 / 12 MB(MAX_CACHED_OCR_IMAGES = 3、MAX_CACHED_OCR_BYTES = 12 * 1024 * 1024),超出时淘汰最旧条目;
  • 返回给调用方的是深拷贝(copyOcrLines),隔离调用方对缓存内容的修改;
  • 缓存命中不重放旧进度、不重复创建 Image/Canvas;
  • 清除语言包后缓存整体清空,保证重新识别(见 imageOcrRuntime.test.ts 与 第 32-41 行)。

五、语言包体系:八种语言、按需下载与 IndexedDB 缓存

NOTICE 声明的三个内置语言包(eng、chi_sim、jpn)只是"默认随扩展声明"的最小集合。实际的语言包体系由 ocrLanguages.ts 定义,共8 种可下载语言:

代码语言说明预估大小推荐
chi_sim简体中文识别简体中文界面、截图和图片文字约 20 MB✅
chi_tra繁體中文识别繁体中文界面、截图和图片文字约 20 MB✅
engEnglish识别英文和拉丁字母文字约 11 MB✅
spaEspañol识别西班牙语图片文字约 11 MB❌
jpn日本語识别日文图片和漫画文字约 16 MB✅
kor한국어识别韩语图片文字约 2 MB❌
fraFrançais识别法语图片文字约 1 MB❌
rusРусский识别俄语图片文字约 5 MB❌

(数据来自 ocrLanguages.ts 的IMAGE_OCR_LANGUAGE_PACKS,"约 XX MB" 为源码声明的下载体积,实际以网络传输为准。)

5.1 源语言 → 语言包的映射规则

getRequiredImageOcrLanguages 把用户选择的源语言映射为需要加载的 Tesseract 语言代码:

  • 中文按脚本(getChineseScript)细分:简体(Hans)→chi_sim + eng,繁体(Hant)→chi_tra + eng;
  • 明确的英/日/西/韩/法/俄语言 → 对应代码(英文只加载eng,其余语言加载[代码, 'eng'],英文作为拉丁兜底);
  • 自动源语言→ 加载推荐组合['chi_sim', 'chi_tra', 'eng', 'jpn'],避免默认配置把繁体识别成简体后丢失脚本信息。

5.2 语言包下载、缓存与清除

语言包不随扩展打包(这与 NOTICE 声明的"语言数据从扩展自身资源加载"并不冲突——NOTICE 针对的是扩展内core与worker两个文件,而 traineddata 本身过大且可裁剪,因此按需下载)。下载与缓存流程在 ocrRuntime.ts 注释中有清晰说明:

不再把 traineddata 打进扩展;Tesseract.js 会从 jsDelivr 按需下载,并将解压后的语言包缓存到 Offscreen Document 的 IndexedDB。

  • 下载入口:downloadImageOcrLanguages→ocrWorkerRuntime.ensureLanguages,通过getWorker(normalized)触发 Tesseract.js 的语言包获取流程,支持AbortSignal取消;客户端侧的语言包准备还有 300 秒超时保护(见 client.ts);
  • 缓存位置:IndexedDB 的keyval-store/keyval仓库(Tesseract.js 内部 idb-keyval 的默认库),键形如fluent-read-image-ocr/<lang>.traineddata;
  • 清除入口:removeImageOcrLanguages→clearModels→ ocrModelCache.ts 的removeOcrModelFiles,按cachePath精确删除指定语言的traineddata键,等待事务提交后返回,同时清空完成结果缓存;只删除明确指定的模型键,不清空数据库。

下载状态由后台 ocrLanguageRepository.ts 持久化,键为fluentReadImageOcrLanguages;用户也可以从图片翻译设置中的语言包卡片上手动"清除模型"释放磁盘占用。

六、隐私边界:识别本地完成,像素不上传

本地 OCR 在隐私上有一层重要承诺(见 docs/guide/privacy.md 与 docs/guide/image-translation.md):

文字识别在浏览器本地完成,识别出的文字交给所选翻译服务;图片像素不会作为文本翻译请求上传。

换言之,图片数据只在本机(Offscreen + Worker + WASM)中流转,识别出的文本才可能发送给用户配置的翻译服务;语言包首次下载需要网络,属于资源获取而非图片上传。圈选翻译还支持"优先使用模型识图":模型支持图片输入时直接把裁剪后的选区图片交给模型识别,这条路径不加载 OCR 语言包、不执行本地识别(见 offscreenRuntime.ts)。

七、WASM 诊断与打包验证

core/tesseract-core-simd-lstm.wasm.js这类 vendor WASM 在生产中常产生难以排查的 stderr 输出,FluentRead 用 scripts/wasm/diagnostics.js 对内核的打印逻辑做了插桩适配,并由 wasmDiagnostics.test.ts 守护:

  • 将引擎的printErr重定向到扩展的日志通道(测试断言插桩后代码包含n=b.printErr||fluentReadWasmStderr,见 第 63-64 行);
  • 保留原始严重级别、去除终端颜色,把"已知无害"的 LSTM 旧参数警告(如language_model_ngram_on、chop_enable等)和分辨率估算提示降为 debug,而Error opening data file eng.traineddata、Aborted(out of memory)、Error loading model等真实错误保持可见(第 27-48 行)。

这套诊断管道让内置 WASM 的报错既能被开发/用户看到,又不会让已知噪音干扰日志,是"扩展自带 WASM 资产"工程化的一个典型配套。

八、小结:从 NOTICE 到可运行本地 OCR 的完整链路

public/fluent-read-ocr/NOTICE.md虽只有寥寥几行,但它所描述的三类资产(Tesseract.js 6.0.1、tesseract.js-core 6.1.2、traineddata 语言包)和"零运行时 CDN"原则,在源码中被落实为一套完整链路:

  1. 资源定位:chrome.runtime.getURL指向扩展内置的worker.min.js与tesseract-core-simd-lstm.wasm.js(ocrRuntime.ts);
  2. 隔离执行:Offscreen Document + Web Worker 承载识别,串行队列保证并发安全(ocrWorkerRuntime.ts);
  3. 图像管线:解码超时、缩放加边、坐标还原、有界结果缓存(ocrRuntime.ts);
  4. 语言包管理:8 种语言按需下载至 IndexedDB,支持定向清除(ocrLanguages.ts、ocrModelCache.ts);
  5. 隐私与可观测:识别留在本地、像素不上传,WASM stderr 经插桩分级输出。

相关代码路径汇总:NOTICE 声明、OCR 运行时、Worker 运行时、语言包目录、语言包缓存清除、Offscreen 编排、运行时测试、OCR 运行时测试、WASM 诊断测试。读者可按上述路径逐一深入,或直接参考 图片翻译指南 体验该能力的实际使用方式。

  • 前端
  • AI 应用
  • 本地部署

【免费下载链接】FluentRead

An open-source browser extension for bilingual translation. 一款开源的浏览器双语翻译插件。

项目地址:https://gitcode.com/gh_mirrors/fl/FluentRead
点击查看免费下载

相关推荐

上一篇:从0到1开发Fabulous插件:VS Code扩展开发完整教程
下一篇:gh_mirrors/rs/rsschool-app的移动端兼容性测试:确保跨设备体验一致

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询