Tesseract.js 图像输入格式全解:recognize/detect 支持哪些图片与数据类型,底层如何加载
2026/9/6 15:05:21 网站建设 项目流程

Tesseract.js 图像输入格式全解:recognize/detect 支持哪些图片与数据类型,底层如何加载

【免费下载链接】tesseract.jsPure Javascript OCR for more than 100 Languages 📖🎉🖥项目地址: https://gitcode.com/GitHub_Trending/te/tesseract.js

本篇技术指南基于 Tesseract.js 仓库的官方文档 Image Format,系统梳理recognizedetect等主函数的image参数支持的全部图片格式(bmp、jpg、png、pbm、webp、gif)与数据类型(base64 字符串、Buffer、File/Blob、DOM 元素、本地路径),并结合src/workersrc/worker-script中的实际加载代码,讲清每种输入在浏览器和 Node 两种环境下是如何被解析、转换并送入 Tesseract OCR 内核的。读完后你可以针对任意运行环境选择最合适的图像输入方式,并在遇到“格式报错”时快速定位问题出在格式层还是数据类型层。

一、支持的图片格式总览

Tesseract.js 的主入口函数(如recognizedetect)都接收一个image参数。官方文档明确列出的支持格式为:

bmp、jpg、png、pbm、webp、gif(仅限非动画 GIF)

这 6 种格式并非只是文档声明,而是由测试套件逐一回归验证的。tests/constants.mjs 中定义了测试用的格式列表:

export const FORMATS = ['png', 'jpg', 'bmp', 'pbm', 'webp', 'gif'];

tests/recognize.test.mjs 会对每种格式执行完整的识别流程,并断言输出文本与预期一致:

describe('should read bmp, jpg, png and pbm format images', () => { FORMATS.forEach((format) => ( it(`support ${format} format`, async () => { await worker.reinitialize('eng'); const { data: { text } } = await worker.recognize(`${IMAGE_PATH}/simple.${format}`); expect(text).to.be(SIMPLE_TEXT); }) )); });

测试图片位于 tests/assets/images/ 目录,同名图片simple.*以全部 6 种格式各存一份(如 simple.png、simple.bmp、simple.gif 等),保证“同一内容、不同编码”下识别结果一致。这从侧面印证了格式声明的可复现性。

注意 GIF 的限制:文档标注 gif 仅支持非动画GIF。Tesseract.js 只提取单帧图像交给 OCR 内核,动画 GIF 的多帧序列并不在支持范围内。

二、数据类型支持矩阵:环境决定你能传什么

格式解决的是“文件编码”问题,数据类型解决的是“数据怎么拿进来”的问题。官方文档将二者分开列示,并给出了一张按运行环境划分的支持矩阵:

数据类型浏览器Node说明
base64 编码的字符串(匹配data:image\/([a-zA-Z]*);base64,([^"]*)正则)即 Data URL,形如data:image/png;base64,iVBORw0KGgo...
Buffer文档口径;Node 侧源码对Buffer有显式分支(见下文)
FileBlob对象浏览器专有
imgcanvasDOM 元素浏览器专有;源码还支持video(取 poster)与OffscreenCanvas
本地图片路径字符串Node 专有,如'tests/assets/images/cosmic.png'

两个关键约束:

  1. 格式与数据类型必须同时满足:官方文档特别强调“images must be a supported image formatanda supported data type”。例如“一个包含 PNG 的 Buffer”是受支持的;而“一个包含原始像素数据的 Buffer”不受支持——Tesseract.js 不接收裸像素数组,图像必须携带自身的容器编码(或经过 DOM 元素/文件对象间接携带)。
  2. 字符串语义随环境不同而不同:同样传入字符串,浏览器先判断它是否为 base64 Data URL,否则当作 URL 去fetch;Node 则先判断是否为 URL,其次判断是否为 base64 Data URL,最后才当作本地文件路径读取。同一个字符串在两个环境下的解析优先级完全不同,跨端移植代码时需留意。

三、浏览器侧加载链路:src/worker/browser/loadImage.js

浏览器环境下image参数的实际处理逻辑集中在 src/worker/browser/loadImage.js。该文件是一个约 70 行的异步函数,按输入类型分派处理,最终统一返回Uint8Array。逐分支拆解:

3.1 undefined 与 base64 Data URL

if (typeof image === 'undefined') { return 'undefined'; } if (typeof image === 'string') { // Base64 Image if (/data:image\/([a-zA-Z]*);base64,([^"]*)/.test(image)) { data = atob(image.split(',')[1]) .split('') .map((c) => c.charCodeAt(0)); } else { const resp = await fetch(image); data = await resp.arrayBuffer(); } }
  • 传入undefined时直接返回字符串'undefined'(供上层判断,不会进入 OCR);
  • 字符串命中 Data URL 正则(src/worker/browser/loadImage.js#L38)时,截取base64,之后的部分,用atob解码并逐字符转为 charCode 数值数组;
  • 字符串未命中正则则被当作 URL,通过fetch拉取远程图片再转为ArrayBuffer。这意味着在浏览器中传一个本地文件路径字符串是不会生效的——那属于 Node 专有行为。

3.2 DOM 元素:IMG、VIDEO、CANVAS

} else if (typeof HTMLElement !== 'undefined' && image instanceof HTMLElement) { if (image.tagName === 'IMG') { data = await loadImage(image.src); } if (image.tagName === 'VIDEO') { data = await loadImage(image.poster); } if (image.tagName === 'CANVAS') { await new Promise((resolve) => { image.toBlob(async (blob) => { data = await readFromBlobOrFile(blob); resolve(); }); }); } }
  • <img>:递归调用自身去加载image.src,因此 img 的 src 可以是 URL 或 Data URL,复用同一套解析逻辑(src/worker/browser/loadImage.js#L47-L49);
  • <video>:官方文档只提到imgcanvas,但从源码看video元素同样被支持——取其poster属性指向的静态图进行识别(src/worker/browser/loadImage.js#L50-L52);
  • <canvas>:通过canvas.toBlob将画布内容编码为 Blob,再交给下面的readFromBlobOrFile读取。这实际上是对文档“img or canvas element”声明的落地实现,也解释了为何测试里 canvas 用例要先用drawImage把图画上(见 tests/recognize.test.mjs#L216-L246)。

3.3 OffscreenCanvas、File 与 Blob

} else if (typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) { const blob = await image.convertToBlob(); data = await readFromBlobOrFile(blob); } else if (image instanceof File || image instanceof Blob) { data = await readFromBlobOrFile(image); }

File/Blob走统一的readFromBlobOrFile(src/worker/browser/loadImage.js#L10-L21),内部使用FileReader.readAsArrayBuffer,并在onerror中抛出File could not be read! Code=${code}。这是文件选择框(<input type="file">)场景的入口——examples/browser/basic-scheduler.html 中evt.target.files得到的就是File对象,直接喂给scheduler.addJob('recognize', files[i])OffscreenCanvas支持则主要服务于 Web Worker 内部环境(无 DOM 的画布),通过convertToBlob转码。

最终函数以return new Uint8Array(data)收口(src/worker/browser/loadImage.js#L68),保证进入 worker 脚本的永远是纯字节序列。

四、Node 侧加载链路:src/worker/node/loadImage.js

Node 环境的实现位于 src/worker/node/loadImage.js,分派顺序与浏览器不同,这是跨端行为差异的根源:

if (typeof image === 'string') { if (isURL(image) || image.startsWith('moz-extension://') || image.startsWith('chrome-extension://') || image.startsWith('file://')) { const resp = await fetch(image); data = await resp.arrayBuffer(); } else if (/data:image\/([a-zA-Z]*);base64,([^"]*)/.test(image)) { data = Buffer.from(image.split(',')[1], 'base64'); } else { data = await readFile(image); } } else if (Buffer.isBuffer(image)) { data = image; }

要点:

  • 字符串的三级判定(src/worker/node/loadImage.js#L24-L32):先按 URL 判断(含moz-extension://chrome-extension://file://前缀,说明其亦考虑了扩展类运行场景),再按 base64 Data URL 判断,最后兜底为本地文件路径,用util.promisify(fs.readFile)读取。这就是文档所说“For Node only: string containing a path to local image”的实现;
  • Buffer 直通(src/worker/node/loadImage.js#L33-L35):Buffer.isBuffer命中后不做任何转换,最终统一转Uint8Array返回;
  • 依赖内置fetch或回退node-fetch(src/worker/node/loadImage.js#L5-L6),与 Node 版本相关。

Node 端的典型用法可参考 examples/node/recognize.js:

const [,, imagePath] = process.argv; const image = path.resolve(__dirname, (imagePath || '../../tests/assets/images/cosmic.png')); (async () => { const worker = await createWorker('eng', 1, { logger: (m) => console.log(m), }); const { data: { text } } = await worker.recognize(image); // 直接传本地路径 console.log(text); await worker.terminate(); })();

五、字节进入 OCR 内核前的最后一环:setImage.js

loadImage只是把输入归一化为Uint8Array;真正把这些字节交给 Tesseract 内核(tesseract.js-core,基于 Leptonica 读图)的是 src/worker-script/utils/setImage.js。这一小段代码解释了若干格式上的“为什么”:

// Check for bmp magic numbers (42 and 4D in hex) const isBmp = (image[0] === 66 && image[1] === 77) || (image[1] === 66 && image[0] === 77); const exif = parseInt(image.slice(0, 500).join(' ').match(/1 18 0 3 0 0 0 1 0 (\d)/)?.[1], 10) || 1; if (isBmp) { const buf = Buffer.from(Array.from({ ...image, length: Object.keys(image).length })); const bmpBuf = bmp.decode(buf); TessModule.FS.writeFile('/input', bmp.encode(bmpBuf).data); } else { TessModule.FS.writeFile('/input', image); } const res = api.SetImageFile(exif, angle); if (res === 1) throw Error('Error attempting to read image.');

从这段源码可以读出三个实现事实:

  1. BMP 有专门的重编码路径:Leptonica 只支持“部分” BMP 文件(源码注释引用了上游问题讨论),因此 Tesseract.js 用 bmp-js 先把 BMP 解码再重新编码,转换为核心可稳定处理的 BMP 形态后,才写入虚拟文件系统(src/worker-script/utils/setImage.js#L13-L30)。这解释了为何测试必须覆盖simple.bmp——BMP 是六格式中最需要特殊处理的一个;
  2. EXIF 方向被主动读取setImage从图像头部前 500 字节中匹配 JPEG EXIF 方向标签,解析出exif角度后传给api.SetImageFile(exif, angle)。这与 tests/recognize.test.mjs#L53-L65 中simple-90.jpgsimple-180.jpgsimple-270.jpg三组旋转图片测试相互印证——手机照片携带旋转元数据时,识别结果仍然正确;
  3. 读图失败的显式报错SetImageFile返回 1 时抛出Error attempting to read image.(src/worker-script/utils/setImage.js#L32-L33)。如果你在recognize时看到该错误,说明字节流不是内核能解析的图像容器——通常正是“原始像素数据”一类不满足“格式 + 数据类型”双重要求的输入。

六、简化接口与完整接口共用同一套输入规则

除了createWorker后调用worker.recognize(image)之外,Tesseract.js 还保留了一组一次性接口:src/Tesseract.js 导出的recognize(image, langs, options)detect(image, options)内部创建临时 worker、执行识别后自动terminate()

const recognize = async (image, langs, options) => { const worker = await createWorker(langs, 1, options); return worker.recognize(image) .finally(async () => { await worker.terminate(); }); }; const detect = async (image, options) => { const worker = await createWorker('osd', 0, options); return worker.detect(image) .finally(async () => { await worker.terminate(); }); };

两者接受的imageworker.recognize完全同源,同样受本文第一、二节所列格式与数据类型矩阵约束。tests/recognize.test.mjs#L67-L77 中即有用 base64 Data URL 调用简化接口Tesseract.recognize(image, undefined, OPTIONS)的回归用例,验证了文档中“main Tesseract.js functions (ex. recognize, detect) take an image parameter”的说法。

七、常见踩坑与排查清单

结合文档声明与源码实现,把容易出错的场景整理成一张排查表:

现象根因依据
浏览器里传本地路径字符串,识别失败浏览器侧非 Data URL 的字符串一律走fetch(image),本地路径不是合法 URLsrc/worker/browser/loadImage.js#L42-L45
Node 里传<img>元素或 Buffer 之外的对象报错DOM 元素分支只存在于浏览器版 loadImage;Node 版只识别字符串与 Buffersrc/worker/node/loadImage.js#L24-L35
传入裸像素数组/RGBA 数据报Error attempting to read image.原始像素数据不携带容器编码,不满足“受支持格式”要求;内核SetImageFile返回 1src/worker-script/utils/setImage.js#L32-L33、docs/image-format.md
动画 GIF 识别结果不符合预期仅支持非动画 GIF,文档明确标注 [non-animated]docs/image-format.md
某些 BMP 文件读图失败Leptonica 对 BMP 支持不完整,需经 bmp-js 重编码路径处理;若仍失败属内核读图失败src/worker-script/utils/setImage.js#L18-L30
canvas 用例识别不到内容测试中 canvas 需先drawImage绘入图像再传入,空画布自然无文字tests/recognize.test.mjs#L222-L232
想从<video>抽帧识别源码实际取video.poster静态图,而非任意帧src/worker/browser/loadImage.js#L50-L52

八、格式与数据类型速查

将全部结论浓缩为一张速查表(环境列与 docs/image-format.md 保持一致,源码补充列标注了实现出处):

输入浏览器Node实现位置
data:image/*;base64,...字符串browser/loadImage.js#L36-L41、node/loadImage.js#L28-L29
图片 URL 字符串✅(fetch)✅(含扩展/file:// 前缀)browser/loadImage.js#L43-L45、node/loadImage.js#L25-L27
本地路径字符串node/loadImage.js#L30-L32
Buffer✅(文档口径)node/loadImage.js#L33-L35
File/Blobbrowser/loadImage.js#L64-L66
<img>/<canvas>browser/loadImage.js#L46-L60
<video>(取 poster)/OffscreenCanvas✅(源码扩展支持)browser/loadImage.js#L50-L63

支持的容器格式统一为bmp、jpg、png、pbm、webp、gif(非动画),并有 tests/constants.mjs#L17 的FORMATS列表与 tests/recognize.test.mjs 的多组回归用例(各格式、base64、Buffer、DOM 元素、旋转 EXIF 图片)作为可执行验证。

九、小结

Tesseract.js 对image参数的契约可以概括为一句话:容器格式六选一,数据通道看运行环境,且两者缺一不可。浏览器端的优势是输入形态多样(Data URL、Blob、DOM 画布),Node 端的优势是可直接吃本地路径与 Buffer;两条链路最终都归一为Uint8Array,经 src/worker-script/utils/setImage.js 做 BMP 重编码与 EXIF 方向解析后写入内核虚拟文件系统完成识别。开发时只要对照本文的速查表确认“我的数据是什么类型、我在哪个环境跑”,再配合Error attempting to read image.等错误信号,基本可以覆盖所有图像输入相关的排障场景。

【免费下载链接】tesseract.jsPure Javascript OCR for more than 100 Languages 📖🎉🖥项目地址: https://gitcode.com/GitHub_Trending/te/tesseract.js

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

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

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

立即咨询