前端如何做OCR验证码识别

902 阅读4分钟

目前最流行的开源OCR软件是tesseract,是一个google支持的开源ocr项目tesseract,现已有大神用JavaScript将tesseract集成tesseractjs的npm包,那我们这里直接用nodejs+tesseractjs来实现ocr功能。

安装 npm i tesseract.js

tesseract.js使用的语言包下载地址各国语言包,按需下载即可,也支持自己训练数据模型。

程序入口,引入需要用的包

const express = require('express');
const tesseract = require('tesseract.js');
const images = require('images')
const app = express();
const bodyParser = require("body-parser");
const sharp = require('sharp');
const Jimp = require('jimp');

先写一个接口,接受图片base64

app.post('/ocr', async(req, res) => {
    var buff = new Buffer.from(req.body.ImageBase64, 'base64');
    let sharpImg = buff
    images(buff).size(90).save('./test.png')
})

保存上传的图片,也可直接使用buff进行识别

image.png

使用tesseract直接识别

 tesseract.recognize('./test.png', 'eng').then(function(p) {
      console.log(p.data.text);
 })

由于该验证码有黑色边框,导致识别出空字符串,我们先将图像做边框裁剪处理

 // 调用sharp库进行切割和保存
  const { width, height } = await sharp(sharpImg).metadata();
  const top = 2;
  const bottom = 2;
  const left = 2;
  const right = 2;
  const croppedWidth = width - left - right;
  const croppedHeight = height - top - bottom;
  if (croppedWidth <= 0 || croppedHeight <= 0) {
    throw new Error('切割后的图像尺寸无效!');
  }
  await sharp(sharpImg)
  .extract({
    top: top,
    left: left,
    width: croppedWidth,
    height: croppedHeight
  })
  .toBuffer((err, buffer) => {
    if (err) {
      console.error(err);
      return;
    }
    // 在这里可以使用保存的 Buffer 对象进行后续操作
    // 例如,将其发送到客户端或保存到文件等
    sharpImg = buffer
  });

sharpImg为图片路径或buff等格式,这样我们获取到去除黑色边框的切割图片

image.png

识别结果还是为空字符串,由于噪点密集,造成识别率很低,接下来我们将图片转换为灰度图像,并且进行降噪和二值化处理。

降噪处理是图像处理中的一种基本操作,主要目的是消除图像中的噪声(包括因传感器、照明等原因引入的噪声)或细节,以便更好地突出图像中的关键特征。常见的降噪算法包括均值滤波、中值滤波、高斯滤波等。例如,使用中值滤波器可以将每个像素的值替换为其邻域值的中值,从而滤除噪声。

二值化处理也是一种常用的图像处理操作,它将一张灰度图像转换为一个二值图像,即只有黑色和白色两种颜色的图像。在图像识别、字符识别等领域,二值化处理常用于分离文本、检测边缘、识别目标等应用场景中。常见的二值化算法包括全局阈值法、局部阈值法、自适应阈值等。例如,在全局阈值法中,通过将图像中所有像素的灰度值与一个预先定义的阈值进行比较,将大于阈值的像素设为白色,小于等于阈值的像素设为黑色,从而实现二值化处理。

 // 加载图像
  await Jimp.read(sharpImg)
  .then(image => {
    // 转换为灰度图像
    image.greyscale();
    // 二值化处理
    image.scan(0, 0, image.bitmap.width, image.bitmap.height, (x, y, idx) => {
      const red = image.bitmap.data[idx + 0];
      const green = image.bitmap.data[idx + 1];
      const blue = image.bitmap.data[idx + 2];
      // const avg = (red + green + blue) / 3;
      // const threshold = 158; // 阈值
      if (red < 30 && green < 30 && blue < 30) {
        image.bitmap.data[idx + 0] = 255;
        image.bitmap.data[idx + 1] = 255;
        image.bitmap.data[idx + 2] = 255;
      }
    });
    // 保存处理后的图像
    image.write('./deal.png');
    return image.getBufferAsync(Jimp.MIME_PNG);
  }).then(buffer => {
    // 在这里可以使用保存的 Buffer 对象进行后续操作
    // 例如,将其发送到客户端或保存到文件等
    sharpImg = buffer
  })
  .catch(err => {
    console.error(err);
  });

这样我们的图像过滤了多余影响:

image.png

进行ocr识别:

image.png

识别成功率跟图像复杂度有关系,你也可以进行模型训练达到提高识别成功率的效果。

最终代码

app.post('/ocr', async(req, res) => {
  var buff = new Buffer.from(req.body.ImageBase64, 'base64');
  let sharpImg = buff
  // 调用函数进行切割和保存
  const { width, height } = await sharp(sharpImg).metadata();
  const top = 2;
  const bottom = 2;
  const left = 2;
  const right = 2;
  const croppedWidth = width - left - right;
  const croppedHeight = height - top - bottom;
  if (croppedWidth <= 0 || croppedHeight <= 0) {
    throw new Error('切割后的图像尺寸无效!');
  }
  await sharp(sharpImg)
  .extract({
    top: top,
    left: left,
    width: croppedWidth,
    height: croppedHeight
  })
  .toBuffer((err, buffer) => {
    if (err) {
      console.error(err);
      return;
    }
    // 在这里可以使用保存的 Buffer 对象进行后续操作
    // 例如,将其发送到客户端或保存到文件等
    sharpImg = buffer
  });
  // 加载图像
  await Jimp.read(sharpImg)
  .then(image => {
    // 转换为灰度图像
    image.greyscale();
    // 二值化处理
    image.scan(0, 0, image.bitmap.width, image.bitmap.height, (x, y, idx) => {
      const red = image.bitmap.data[idx + 0];
      const green = image.bitmap.data[idx + 1];
      const blue = image.bitmap.data[idx + 2];
      if (red < 30 && green < 30 && blue < 30) {
        image.bitmap.data[idx + 0] = 255;
        image.bitmap.data[idx + 1] = 255;
        image.bitmap.data[idx + 2] = 255;
      }
    });
    // 保存处理后的图像
    image.write('./ecardDeal.png');
    return image.getBufferAsync(Jimp.MIME_PNG);
  }).then(buffer => {
    // 在这里可以使用保存的 Buffer 对象进行后续操作
    // 例如,将其发送到客户端或保存到文件等
    sharpImg = buffer
  })
  .catch(err => {
    console.error(err);
  });
  tesseract.recognize(sharpImg, 'glg').then(function(p) {
    console.log('progress', p.data.text);
  }).then(function(result) { 
    console.log('err', result);
  });
})

qrcode_for_gh_fec3ec2339fe_258.jpg gh_8f1288aa3f96_258(1)(1).jpg

码字不易,欢迎体验智能小工具