Skip to Content
全部文章Node服务端前端实现图片OCR: 选中、复制图片里文本

前端实现图片OCR: 选中、复制图片里文本

为什么要复制图片文字?

有一个比较好用的产品,可能部分人使用过,TAPD ,一款敏捷项目管理工具,类似于禅道之类的在线应用,今天重点不是说项目管理工具, 今天主要是说说TAPD中有一项比较好用的功能,放大一张图片,可以直接复制图片中的文字,这个功能对于产品经理给出的需求,开发人员拿到里面的文案就非常好用。

现在企业微信、微信中已经具备这个功能了,但是对于Web来说,支持这个功能是更加复杂的,因为在Web中OCR天生就比较弱,node单线程,本身解释型语言性能也不算高。

既然TAPD已经有了,今天就来尝试动手写功能这个功能,所以有了此篇文章,最后我的demo我会放到github上,大家可以随意fork、测试。

运行图

本地OCR

本地使用Tessract.js识别文字,可以看到我们双击其中一处文字,上面输入框会显示我们选中的文字,可以用于测试对比识别效果。

1

也可以打开文字显示,看看整体的识别、位置还原程度

1

腾讯云OCR

通过腾讯云OCR服务识别效果是明显好于本地Tessract.js的,双击其中一处文字,查看上面文本框 1

同样也可以打开文字显示,看看整体的识别、位置还原程度

1

实现思路

实现的思路很简单,分为两个部分

  1. 图片识别文字
  2. 前端在图片上对应位置放置文字

对于本地ocr来说,只需要使用tesseract.js识别图片即可,对于腾讯云来说,需要使用腾讯云的OCR服务,然后把识别结果返回给前端,前端再把识别结果展示出来。

本地ocr不论是从识别速度、准确率都是不如氪金腾讯云OCR的,还是那句话:加钱,世界触手可得!

为了更好的体验,我把tessract.js运行在服务端中,没有直接在react前端项目使用这个包,所以不论是本地、还是腾讯ocr都是通过我的node服务端来调用的。 他们的前端完全一样,有兴趣可以在最后的github地址中查看。

本地ocr

本地实现的逻辑是,拿到图片后,先做灰度化和二值化处理,然后交给tesseract.js识别文字,最后把识别结果返回给前端。

腾讯云ocr

逻辑就很简单了,直接http服务传入图片,拿到返回的识别结果。

最终返回前端的结果

格式如下:

export interface ConvertedItem { // 文本 text: string; // 左上角坐标 start: [number, number]; // 右下角坐标 end: [number, number]; } const result:ConvertedItem[] = [{...},{...}];

示例

{ "data": [ { "text": "民 刁查看器 加 控制台 品 调试器 人 网络 《】样式编辑器 《Am 性能 » 乓 … X", "start": [ 22, 23 ], "end": [ 1272, 55 ] }, { "text": "W 。 过滤URL 1 十 Q © 禁用缓存 ”不节流? ”洽", "start": [ 26, 81 ], "end": [ 1274, 113 ] }, ... { "text": "炎 <b>ayaeaice< azas% doateivic/0>123123 23:20:27.627", "start": [ 24, 850 ], "end": [ 1019, 875 ] } ] }

前端拿到这些数据就可以把文字显示到对应的位置了。

核心代码实现

前端核心代码

前端就是依赖上面的数据结构,在把文字显示到对应的位置上:

<div className="image-container"> <img id="ocr-image" src={selectedImage!} alt="请先选择图片" /> {ocrResults.map((result, index) => { const { text, start: leftTop, end: rightBottom } = result; const height = rightBottom[1] - leftTop[1]; return ( <div key={index} className="ocr-text" style={{ color:`rgb(199, 10, 10${showOCRTxt?',1':',0'})`, left: `${leftTop[0]}px`, top: `${leftTop[1]}px`, fontSize: `${height*0.7}px`, width: `${rightBottom[0] - leftTop[0]}px`, height: `${rightBottom[1] - leftTop[1]}px` }}> {text} </div> ); })} </div>

服务端核心代码

本地Tessract.js实现OCR

本地OCR最好将图片先预处理一下,二值化和灰度化,让图片更便于识别。

async function multiThreadOCR(imagePath: string, numThreads: number): Promise<ConvertedItem[] | undefined> { const scheduler = createScheduler(); const workers: Worker[] = []; try { for (let i = 0; i < numThreads; i++) { const worker = await createWorker("chi_sim+eng", OEM.DEFAULT, { // logger: (m) => console.log(m) }); worker.setParameters({ preserve_interword_spaces: '1' }); scheduler.addWorker(worker); workers.push(worker); } const res = await scheduler.addJob('recognize', imagePath, {}, { hocr: true, blocks: true, layoutBlocks: true, }); const { data: { lines } } = res; const result: ConvertedItem[] = []; lines.forEach((line: OcrLine) => { const { text, bbox } = line; const { x0, y0, x1, y1 } = bbox; result.push({ text: text.replace('\n', ''), start: [x0, y0], end: [x1, y1] }); }); return result; } catch (error) { console.error('识别过程中出现错误:', error); } finally { for (const worker of workers) { await worker.terminate(); } } } app.post('/ocr', async (req: express.Request, res: express.Response) => { // 二值化和灰度化 const grayscaleResult = await binarizeImage(await grayscaleImage(req.body.image)); if (grayscaleResult) { multiThreadOCR(grayscaleResult, req.body.threadNum).then((data) => { if (data) { res.json({ data }); } else { res.status(500).send('识别过程中出现错误'); } }).catch(err => { console.error(err); res.status(500).send('识别过程中出现错误'); }); } else { res.status(500).send('图像预处理出错'); } });

腾讯云OCR

腾讯云每个账号每个月都会给一些免费的OCR额度,如果用量不大的话,也是不需要自己花什么钱的。

const OcrClient = tencentcloud.ocr.v20181119.Client; const clientConfig: ClientConfig = { credential: { secretId: process.env.secretId, secretKey: process.env.secretKey, }, region: "", profile: { httpProfile: { endpoint: "ocr.tencentcloudapi.com", }, }, }; app.post('/tencentocr', async (req: express.Request, res: express.Response) => { const client = new OcrClient(clientConfig); const params = { "ImageBase64": req.body.image }; client.GeneralAccurateOCR(params).then( (data) => { res.json({ // 把腾讯云返回的数据结构转换成和我们本地OCR一样的结构 data: convertData(data) }); }, (err) => { console.error("error", err); res.status(500).send('识别过程中出现错误'); } ); });

存在的问题

不管是本地识别还是腾讯云识别,因为不是个性化场景打磨的识别算法,所以可定制化程度几乎没有,这也就无可避免的出现识别效果可能不是很友好,存在如下比较麻烦的问题:

  • 本地OCR文字识别率有些许问题,用的tesseract.js
  • 文字在图片上为的位置还原有问题,主要是识别文字字号及中间空白的宽度困难

Demo可以优化的点

  1. 建立一个标准,对图片从大小、分辨率来划分界限,小图片可以使用本地OCR速度很快1秒多出结果,大一些的走腾讯云
  2. 优化文字在图片上的还原,尽可能保持重叠。
  3. 本地ocr可以研究tessract的words数据,本demo用的是lines数据,相对来说words数据粒度更细。也可以看看hocr的输出,是否还原效果会更好。
  4. 对于图片组件的封装,完全复刻Tapd的图片缩放、拖拽,并保持OCR数据。

项目地址

本文所用Demo项目源码地址:https://github.com/gtjyj/img-ocr-text-copy

欢迎大家查看源码,fork修改。

最后编辑于

hi