提到图片 OCR 识别,一般可能都会想到著名的 tesseract-ocr。
在 Node.js 程序中处理如验证码识别一类的文本或物体识别时,需要以 Node.js API 的方式调用。一般来说有三种可实现该需求的方式:包装本地安装的 OCR 应用程序(如 tesseract-ocr)以提供 Node.js 调用接口、通过 http 请求方式调用第三方在线服务以及使用支持 node.js 的第三方依赖库(如 tesseract.js)。
包装本地 OCR 应用程序 API
这种方案要求首先在本地机器安装并配置要包装的 OCR 应用。然后使用 node.js child_process
模块的 exec、spawn
等方法调用 OCR 应用,并将执行后的结果处理后返回。
npm 上能找到的大多为基于 tesseract-ocr
实现的包装,主要区别在于包装后的 API 易用性。相关实现参考:
- https://github.com/tesseract-ocr/tesseract
- https://github.com/nicolaspearson/node.ts.ocr
使用第三方在线服务 API
BAT 都提供有相关的在线服务 API,这些在线 API 的用法一般也都比较简单,少量的调用基本都是免费的。另外一些在线打码平台也会提供相关收费 API 服务。下面以百度智能云为例作简单示例介绍。
使用百度账号登陆百度 AI 智能云平台,并新建一个应用,然后进入应用管理可得到 APPId、key、和 secret。
百度智能云控制台:https://console.bce.baidu.com/ai/#/ai/ocr/overview/index
百度智能云平台提供了相当多的人工智能相关的 API 开放服务。以下为 node.js 调用百度智能云 OCR API 实现图片文本识别的一个示例:
// 需先安装其 SDK 包: npm -i badidu-aip-sdk // const AipOcrClient = require('baidu-aip-sdk').ocr; const AipOcrClient = require('baidu-aip-sdk/AipOcr'); const fs = require('fs'); const APP_ID = '14406xxx'; const API_KEY = 'Fz7XXXXXXXXXXX'; const SECRET_KEY = 'XEXXXXXXXXXXXXXX'; const client = new AipOcrClient(APP_ID, API_KEY, SECRET_KEY); const ocrTextTest = async () => { const options = { detect_direction: true }; // 识别本地图片 const image = fs.readFileSync('./donate_wx.png').toString('base64'); let result = client.generalBasic(image, options); console.log(result.words_result); // 识别在线图片 const result = await client.generalBasicUrl('https://lzw.me/wp-content/uploads/2017/02/donate_wx.png'); console.log(result.words_result); } ocrTextTest();
使用 tesseract.js
库实现 OCR 识别
tesseract.js 本质上也是基于 tesseract-ocr
的封装,但其不同的是它借助 emscripten
将 C++ 开发的 tesseract-ocr
编译为 WebAssembly
实现能力支持,现代浏览器均支持 WebAssembly
,故其也可直接在浏览器中应用。
以下对使用 tesseract.js
实现 OCR 文本识别作简要介绍。
安装 tesseract.js
依赖
npm i tesseract.js # or yarn add tesseract.js
下载训练数据
tesseract.js
默认从其网站下载训练数据,但由于训练数据包比较大,可能需要比较久的时间。我们可以先使用迅雷等工具将其下载下来,然后通过 langPath
参数指定训练数据的位置。
下载地址示例: https://tessdata.projectnaptha.com/4.0.0_fast/chi_sim.traineddata.gz
下载其他语言类型的训练数据,只需将示例地址中的 chi_sim
改为要下载的语言标识即可。
当然你也可以从其默认训练数据的仓库下载(可能会因 git 拉取太久而失败),所有默认支持的的语言也都可以从这里知晓。
默认训练数据仓库地址为:https://github.com/naptha/tessdata
基于 tesseract.js
的图片文本识别示例
const Tesseract = require('tesseract.js'); const path = require('path'); const worker = Tesseract.createWorker({ logger: m => console.log(m), errorHandler: err => console.log('[error:]', err), // 使用离线训练数据 langPath: path.resolve(__dirname, './tessdata/4.0.0_best'), }); const ocrTest = async () => { await worker.load(); await worker.loadLanguage('chi_sim'); await worker.initialize('chi_sim'); await worker.setParameters({ // 验证码只为数字的情况下,设定白名单 // tessedit_char_whitelist: '0123456789', tessedit_pageseg_mode: tessedit_pageseg_mode: Tesseract.PSM.AUTO, }); // const image = require('fs').readFileSync('./donate_wx.png'); const image = 'https://lzw.me/wp-content/uploads/2017/02/donate_wx.png'; const { data: { text } } = await worker.recognize(image); console.log(text); await worker.terminate(); }); ocrTest();
以上示例为对文本对识别。
自行训练自定义数据
默认支持的训练数据相对比较弱,对于文字比较清晰的图片来说一般没有问题,但对于存在干扰的验证码这种需求来说,则需要通过自行训练来提升准确率。
tesseract.js
使用的训练数据只是将 tesseract-ocr
gzip 压缩了一下而已,训练自定义的数据需要安装 tesseract-ocr
以及其训练数据相关工具。
具体的方法可参考官方相关仓库和文档:
- 安装 tesseract-OCR:https://github.com/tesseract-ocr/tessdoc/blob/master/Installation.md
- https://github.com/tesseract-ocr/tesstrain
- https://github.com/tesseract-ocr/tessdoc/blob/master/Compiling.md
另外也有一些可参考的教程:
- https://zhuanlan.zhihu.com/p/103714876
- https://github.com/kekxv/TesseractTrain
- https://www.bbsmax.com/A/6pdDb7pDJw/
- https://www.bbsmax.com/A/8Bz8KNOkdx/
- https://www.ershicimi.com/p/9bf5ec129082ac6800b1cb6f56c6f71c
- https://blog.csdn.net/sylsjane/article/details/83751297
- https://blog.csdn.net/holmofy/article/details/80867243
- https://blog.csdn.net/makesibushuohua/article/details/52058310
获取用于训练的图片
以验证码为例,下面为获取一定数量的验证码作为训练集。由于训练的图片只能为 png 格式,使用了 sharp 库将下载得到的图片转换为 png 格式。sharp 安装过程中需要从 GitHub 仓库下载二进制资源,可能会因为无法下载而安装失败,我们执行如下命令设置从淘宝镜像下载相关资源:
npm config set sharp_binary_host "https://npm.taobao.org/mirrors/sharp" npm config set sharp_libvips_binary_host "https://npm.taobao.org/mirrors/sharp-libvips"
代码示例:
import fs from 'fs'; import http from 'http'; import https from 'https'; import sharp from 'sharp'; const downloadImage = (imageUrl: string): Promise<buffer> => { return new Promise((resolve, reject) => { (imageUrl.startsWith('https') ? https : http).get(imageUrl, (res) => { const chunks: Buffer[] = []; res.on('data', (chunk) => chunks.push(chunk)); res.on('end', (error: Error) => { if (error) return reject(error); const size = chunks.map(d => d.length).reduce((val, total) => val + total, 0); resolve(Buffer.concat(chunks, size)); }); }); }); }; const getCodeImgUrl = () => `https://lzw.me/getcaptcha?theme=light&_pc=${Date.now()}`; export const saveImages = async (total = 100, nameBase = 10000, baseDir = './images/captcha/') => { if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true }); console.log('等待下载的图片数量:', total); await downloadImage(getCodeImgUrl()).then(buf => { return sharp(buf).png().toFile(path.resolve(baseDir, `${nameBase++}.png`)); }); await new Promise(rs => setTimeout(() => rs(null), Math.ceil(Math.random() * 700 + 300))); if (--total) await saveImages(total, nameBase, baseDir); }; saveImages(10, 100, './images/captcha_1/');
然后可以人肉识别,将验证码图片重命名为其对应的验证码值,以便用于后续的训练,有重名的可以加上后缀如 -1
。
除了 之外,还有 opencv:
- https://www.npmjs.com/package/opencv
- https://www.npmjs.com/package/opencv-wasm
相关参考
- https://github.com/tesseract-ocr/tesseract
- https://github.com/naptha/tesseract.js/
- https://github.com/emscripten-core/emscripten
- https://github.com/UB-Mannheim/tesseract/wiki
- https://www.npmjs.com/package/captcha-cv-ocr