Tesseract.js OCR 中文识别

项目里需要从图片中提取文字,最好不用后端服务。调研了一圈,最后选了 Tesseract.js。

为什么选 Tesseract.js

主要几个原因:

  1. 纯前端,不需要后端支持
  2. 支持中文识别(虽然准确率一般,但够用)
  3. 能返回每个字的位置信息,我需要这个来定位文本

其他的方案要么不支持中文,要么要收费,要么只能用后端。

基础使用很简单
javascript 复制代码
import Tesseract from "tesseract.js";

const result = await Tesseract.recognize(image, "chi_sim+eng", {
  logger: (m) => console.log(m),
});

console.log(result.data.text); // 识别的文本

chi_sim 是简体中文,eng 是英文。+ 号表示同时识别两种语言。

第一次运行会自动下载语言包,大概 20MB 左右,会有点慢。之后就会缓存了。

但有个坑:词语位置信息

我需要的不只是文本,还要每个词在图片上的位置,这样才能定位到原文。

Tesseract.js 可以返回 words 数组,每个词有 bbox 信息:

javascript 复制代码
const words = result.data.words.map((w) => ({
  bbox: w.bbox, // [x0, y0, x1, y1]
  text: w.text,
}));

bbox 是一个四元组 [x0, y0, x1, y1],表示词语的左上角和右下角坐标。

问题来了:怎么找到特定文本的位置?

比如我要找"工程工作说明书"这几个字,怎么从 words 数组里定位?

我写了个简单的算法:

javascript 复制代码
function getBBoxesForText(words, target) {
  const targetArr = target.split("");
  
  for (let i = 0; i < words.length; i++) {
    let match = true;
    
    // 逐字匹配
    for (let j = 0; j < targetArr.length; j++) {
      if (!words[i + j] || words[i + j].text !== targetArr[j]) {
        match = false;
        break;
      }
    }
    
    if (match) {
      // 合并 bbox
      const bboxes = words.slice(i, i + targetArr.length).map((w) => w.bbox);
      const x0 = Math.min(...bboxes.map((b) => b.x0));
      const y0 = Math.min(...bboxes.map((b) => b.y0));
      const x1 = Math.max(...bboxes.map((b) => b.x1));
      const y1 = Math.max(...bboxes.map((b) => b.y1));
      return { x0, y0, x1, y1 };
    }
  }
  
  return null;
}

这个算法就是逐字匹配,然后把匹配到的字的 bbox 合并成一个大的矩形。

但是准确率一般

Tesseract.js 对中文的识别准确率确实不如百度、腾讯这些商用 OCR。我用几十张图片测试,大概 70-80% 的准确率。

常见的错误:

  1. 形状相似的字混淆(比如"己"和"已")
  2. 印刷质量差的时候识别不出来
  3. 复杂排版的时候位置不准

但对我们的场景够用了,因为我们主要用来辅助定位,不是精确提取。

性能优化:图片预处理

我后来发现,如果先对图片做一下预处理,准确率会高很多。

比如:

  • 转灰度
  • 二值化
  • 去噪

但我没在前端做,因为会影响性能。如果图片质量差,我会建议用户重新上传清晰一点的。

还有个坑:logger 太吵

Tesseract.js 识别过程中会不断输出日志,比如进度、状态等等。

javascript 复制代码
const result = await Tesseract.recognize(image, "chi_sim+eng", {
  logger: (m) => console.log(m), // 这个会输出一堆
});

我后来改成只在关键节点输出:

javascript 复制代码
const result = await Tesseract.recognize(image, "chi_sim+eng", {
  logger: (m) => {
    if (m.status === 'recognizing text') {
      console.log(`识别进度: ${Math.round(m.progress * 100)}%`);
    }
  },
});

这样清爽多了。

最后封装了一下

为了复用,我把 OCR 功能封装成工具函数:

javascript 复制代码
// OCRProcessor.js
export async function recognizeImage(image) {
  const result = await Tesseract.recognize(image, "chi_sim+eng", {
    logger: (m) => {
      if (m.status === 'recognizing text') {
        console.log(`识别进度: ${Math.round(m.progress * 100)}%`);
      }
    },
  });
  
  if (!result.data || !result.data.text) {
    throw new Error("OCR识别失败");
  }
  
  const words = result.data.words.map((w) => ({
    bbox: w.bbox,
    text: w.text,
  }));
  
  return { text: result.data.text, words };
}

export function getBBoxesForText(words, target) {
  // ... 算法逻辑
}

使用起来就很简单:

javascript 复制代码
const { text, words } = await recognizeImage(imageUrl);
const bbox = getBBoxesForText(words, "工程工作说明书");

if (bbox) {
  console.log("找到了位置:", bbox);
  // 在这个位置画标注框
}
几个踩坑总结
  1. 中文准确率一般:别指望 100% 准确,大概 70-80%
  2. 第一次会下载语言包:20MB 左右,要等一会儿
  3. logger 很吵:记得过滤一下
  4. words 数组很好用:如果要定位文本位置,一定要用这个
  5. 图片质量很重要:模糊、倾斜的图片识别率很差
相关推荐
AI人工智能+8 分钟前
从像素到数据:浅析计算机视觉与自然语言处理驱动的毕业证书识别
深度学习·计算机视觉·自然语言处理·ocr·毕业证书识别
\xin13 分钟前
pikachu自编CSRF(GET),CSRF(POST),CSRF(token)
前端·csrf
是大强16 分钟前
前端一个项目用node20 一个项目用node14 怎么切换
前端
不老刘22 分钟前
Git Cherry-Pick:微前端架构下的“精准医疗”与最佳实践
前端·git
LIO43 分钟前
ESLint 极简指南:让代码既规范又一致
前端·eslint
明月_清风1 小时前
前端工程化七连问:从紧急修复到版本控制,一文打通工程化任督二脉
前端·前端工程化
用户6757049885021 小时前
不装插件不写代码!教你一招搞定网页长截图!清晰且高效!
前端·chrome
tjl521314_211 小时前
01C++ 分离编译与多文件编程
前端·c++·算法
sayamber1 小时前
vLLM 容器化部署实战:如何在云服务器上跑起高并发大模型推理服务
前端
LIO1 小时前
Pinia 极简指南:Vue 3 官方状态管理库
前端·vue.js