Node.js脚本项目合集(三):Node.js+Tesseract.js多线程实现ocr文字识别


前言

在这里插入图片描述

上一期Node.js+Tesseract.js单线程实现ocr文字识别通过单线程(即创建一个worker)来借助tesseract.js实现单张图片ocr文字识别。实际的开发中往往会效率低下,识别一张图片都需要20s左右。上一期还有一个问题,每次识别的图片都需要修改代码进行传参,这期为了方便演示,我们将多线程实现ocr文字识别的代码用koa包成http请求,通过传参的方式来进行功能调用。

一、准备工作以及介绍

1、什么是koa

koa是一个Node.js的一个开源web框架,其项目地址koa,官网地址koa官网,这里我们需要koa做一个简单的get请求,方便传参并执行脚本。

2、tesseract / tesseract.js相关

可直接去上一期安装下载,博客链接:Node.js+Tesseract.js单线程实现ocr文字识别

3、创建项目文件

(1)新建lang-data文件夹,将要识别的语言包(.gz后缀名)放到lang-data中(这里我们用4.0_best版本语言包下载4.0_best),运行代码,通过给loadLanguage方法传参(通过语言包code并且用加号“+”拼接的方式引入多国语言)将语言包解析成.traineddata文件。
(2)新建images文件夹,将要识别的图片放到该文件夹下。
(3)新建moreThreads.js文件,用来执行Node.js代码

4、node环境准备

(1)node版本16.0.0以上
(2)需要用到tesseract.js模块、fs模块、moment模块、koa相关模块和path模块

5、postman工具

通过postman工具进行接口测试,并测试实际执行效率情况

二、项目代码

1.VScode中launch.json相关配置

(1) VScode通过launch.json运行:本地使用Visual Studio Code这个工具生成launch.json来进行本地debug,可以直接添加配置。
代码如下(示例):

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "moreThreads",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\moreThreads.js"
        }
    ]
}

2.代码部分

(1)利用koa新增moreThreadsOCR的get请求接口:利用CommonJs方式引用koa和koa-router,新增moreThreadsOCR的get请求接口,并通过3002端口号进行监听,这里我们将workerNum(worker数量)、fileDir(放图片的文件路径)、language(语言类型)通过接口传参的方式接收。

代码如下(示例):

const Koa = require('koa')
const Router = require('koa-router')
const router = new Router()
const app = new Koa()
const path = require('path')
const moment = require('moment')
const { createWorker, createScheduler } = require('tesseract.js');
const fs = require('fs');

router.get('/moreThreadsOCR', async (ctx) => {
  // 1、开始获取参数
  const { workerNum, fileDir, language } = ctx.query;
  //	......业务代码......
  // 2、将输出结果返回
  ctx.body = res;
})

app.use(router.routes(), router.allowedMethods())
app.listen(3002);

(2)检查图片文件夹的格式: 将传参的文件夹格式中所有的文件遍历一下,并将符合图片后缀的数据添加到filesDirArr数组中

  // 2、将图片文件夹中的图片遍历出来
  const filesName = fs.readdirSync(fileDir, {withFileTypes: 'png'})
  const filesDirArr = filesName.reduce((p, c) =>{
    if (new RegExp(/\.(png|jpg|gif)$/, 'g').test(c.name)){
      p.push(`${fileDir}\\${c.name}`)
    }
    return p;
  }, []);

(3)通过Tesseract.js异步多线程执行ocr识别: 首先,通过createScheduler方法创建一个调度任务,然后通过传参workerNum数量生成worker数(这里边的worker数就好比工人,生成的越多,后边在执行图片文字识别的线程就越多,效率越快。但是workNum也不是越多越好,因为生成worker的时候需要消耗cpu分配资源,具体数量多少合适后边会给大家详细测试)

  // 3、起调度方法,并获得指定数量的worker
  const scheduler = createScheduler();
  for (let i = 0; i < workerNum; i++) {
    const worker = createWorker({
      langPath: path.join(__dirname, './lang-data'),
      cachePath: path.join(__dirname),
      logger: m => console.log(`${moment().format('YYYY-MM-DD HH:mm:ss')}-${JSON.stringify(m)}`)
    })
    await worker.load()
    await worker.loadLanguage(language)
    await worker.initialize(language)
    scheduler.addWorker(worker)
  }

(4)将待识别的图片通过Pormise.all同步输出结果: 异步调用,将这些文件通过Pormise.all同步输出结果,返回OCRres数组,这个数组里每一个text则是最终识别的文本。执行完毕后终止调度任务,防止内存溢出。

  // 4、异步调用执行
  const OCRres = await Promise.all(filesDirArr.map((fileDirItem) => (
    scheduler.addJob('recognize', fileDirItem)
  )))
  // 5、终止调度任务
  await scheduler.terminate();

(5)处理识别结果,返回结果内容: 整理数据结果,返回按照每个文字和文字的坐标的结果输出,其中words中text表示文字、box表示该文字的坐标,confidence表示相似程度的百分比

  // 6、处理识别结果,并返回结果内容
  const res = OCRres.reduce((p, c) => {
    const item = {
      text: c.data.text,
      words: [],
    }
    c.data.words.map((m) => {
      const wordsItem = {text:'', box:{}, confidence:''};
      wordsItem.box = m.bbox;
      wordsItem.text = m.text;
      wordsItem.confidence = m.confidence;
      item.words.push(wordsItem);
    })
    p.push(item);
    return p;
  }, []);

(6)综上,完整代码块如下:

const Koa = require('koa')
const Router = require('koa-router')
const router = new Router()
const app = new Koa()
const path = require('path')
const moment = require('moment')
const { createWorker, createScheduler } = require('tesseract.js');
const fs = require('fs');


router.get('/moreThreadsOCR', async (ctx) => {
  // 1、开始获取参数
  const { workerNum, fileDir, language } = ctx.query;

  // 2、将图片文件夹中的图片遍历出来
  const filesName = fs.readdirSync(fileDir, {withFileTypes: 'png'})
  const filesDirArr = filesName.reduce((p, c) =>{
    if (new RegExp(/\.(png|jpg|gif)$/, 'g').test(c.name)){
      p.push(`${fileDir}\\${c.name}`)
    }
    return p;
  }, []);

  // 3、起调度方法,并获得指定数量的worker
  //    备注:这里的workNum不是越多越好,因为启动越多的worker,在createWorker的时候就需要遍历,并挨个创建worker
  //        这就好比你创建很多的worker工人,但是图片没有那么多,前期投入很大,但是没有那么多活需要做,所以尽量保证worker数跟cpu数一样,发挥最大作用 
  const scheduler = createScheduler();
  for (let i = 0; i < workerNum; i++) {
    const worker = createWorker({
      langPath: path.join(__dirname, './lang-data'),
      cachePath: path.join(__dirname),
      logger: m => console.log(`${moment().format('YYYY-MM-DD HH:mm:ss')}-${JSON.stringify(m)}`)
    })
    await worker.load()
    await worker.loadLanguage(language)
    await worker.initialize(language)
    scheduler.addWorker(worker)
  }

  // 4、异步调用,将这些文件通过Pormise.all同步输出结果,返回OCRres数组,这个数组里每一个text则是最终识别的文本
  // 这里箭头后面紧跟这一个"()"指的是自调用表达式,包围一些需要通过运算得出结果的代码(其中包围的代码scheduler.addJob('recognize', fileDirItem)会执行一次)。函数表达式可以自调用(即自动运行一次)。如果表达式后面紧跟(),会自动调用。不能自调用声明的函数,通过添加括号,来说明他是一个函数表达式。
  const OCRres = await Promise.all(filesDirArr.map((fileDirItem) => (
    scheduler.addJob('recognize', fileDirItem)
  )))

  // 下面是错误写法,
  // 原因:箭头函数表达式(ES6)的返回值
  //      箭头函数表达式x => x,表示function(x) {return x;}。
  //      但如果返回值是object类型,则不能为x => {name:'JT'},,需要改为x => ({name:'JT'})。
  //      这里scheduler.addJob是一个函数,其该函数返回的是RecognizeResult为promise对象,所以应该在他的外部再加一个括号可作为匿名函数
  // const OCRres = await Promise.all(filesDirArr.map((fileDirItem) =>{
  //   scheduler.addJob('recognize', fileDirItem)
  // }))

  console.log(OCRres);
  // 5、终止调度任务
  await scheduler.terminate();

  // 6、处理识别结果,并返回结果内容
  const res = OCRres.reduce((p, c) => {
    const item = {
      text: c.data.text,
      words: [],
    }
    c.data.words.map((m) => {
      const wordsItem = {text:'', box:{}, confidence:''};
      wordsItem.box = m.bbox;
      wordsItem.text = m.text;
      wordsItem.confidence = m.confidence;
      item.words.push(wordsItem);
    })
    p.push(item);
    return p;
  }, []);
  ctx.body = res;
})

app.use(router.routes(), router.allowedMethods())
app.listen(3002);

3.完整项目地址

https://github.com/lp970703/node_job/tree/master/tesseractOCR_js

三、思考与分析

1、测试worker数对识别效率的影响

  • worker数主要跟cpu核心数有一定关系,通常cpu几核那么worker数就是多少。目前我们测试的这台电脑cpu为16核。
    在这里插入图片描述
  • 下图为我们批量ocr识别10张图片的输出结果时间,单线程用时2分31秒,而设置到8个或者16个worker数时(与CPU核数一致)用时仅为48s左右,而当worker数设置为32个时,所用时间又变大到1分零2秒
    一个worker数
    8个worker数
    16个worker数
    32个worker数

2、对返回结果box文字坐标位置的理解

  • Tesseract坐标规则:从图片的左上角点为原点,横坐标自左向右x依次变大,纵坐标自上而下y依次变大。x0y0指的是文字中左上角的点,x1y1指的是文字中右下角的点。下图图一为返回的“颜色”这两个字的通过Tesseract返回的坐标位置,图二为用工具实际测出的位置(会出现略微的误差)。
    颜色位置
    被识别图片的工具测量

总结

以上就是通过Node.js+tesseract.js多线程实现ocr文字识别,后续还会更新将图片中的文字用方框框出、自己训练ocr语言模型来应对复杂的文字识别等脚本。本人还开放了其他关于nodejs实用脚本,感兴趣可以去本人github地址:node_job中学习。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue是一个流行的JavaScript框架,用于构建用户界面。Tesseract.js是一个基于JavaScriptOCR(光学字符识别)库,用于从图像中提取文本。 如果你想在Vue项目中使用Tesseract.js,首先你需要安装Tesseract.js库。你可以通过npm或yarn来安装,具体命令如下: 使用npm: ``` npm install tesseract.js ``` 使用yarn: ``` yarn add tesseract.js ``` 安装完成后,你可以在Vue组件中引入并使用Tesseract.js。以下是一个简单的示例: ```vue <template> <div> <input type="file" @change="handleFileChange" /> <button @click="extractText">提取文本</button> <div>{{ extractedText }}</div> </div> </template> <script> import Tesseract from 'tesseract.js'; export default { data() { return { extractedText: '', }; }, methods: { handleFileChange(event) { const file = event.target.files[0]; this.image = URL.createObjectURL(file); }, async extractText() { const { data: { text } } = await Tesseract.recognize(this.image); this.extractedText = text; }, }, }; </script> ``` 在上面的示例中,我们创建了一个包含一个文件输入框和一个按钮的Vue组件。当用户选择文件后,我们会使用Tesseract.js来提取图像中的文本,并将提取的文本显示在页面上。 请注意,上述代码只是一个简单的示例,你可能需要根据你的具体需求进行适当的修改和调整。另外,为了使Tesseract.js能够正常工作,你可能还需要提供一些语言数据文件。 希望这能帮到你!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值