nodejs的一种图像识别方法

2023/2/21 tesseract

原文地址 (opens new window)

# nodejs图片验证码识别

由于微信小程序云开发使用npm中的gm图像处理工具有些困难,因为gm需要在linux环境下额外安装native的包。所以我使用了nodejs canvas来处理图像然后放入tesseractjs中进行识别。场景是用于带有图片验证码登录的爬虫。

nodejs识别图像验证码主要分为3步

  1. 图片灰度化
  2. 灰度图片二值化
  3. nodejs保存图片数据为图片文件
  4. 用ocr工具识别图片

# 图片灰度化


灰度化,在 RGB 模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。图片灰度化的细节不会详细讲,可以自行百度。

在图片灰度化之前要安装canvas,引入canvas用于处理图像文件,由于二维码图片尺寸固定,本例中的二维码图片是92*34,在创建canvas 时就固定了长和宽。

const {  createCanvas,  loadImage} = require('canvas');
const canvas = createCanvas(92, 34);
const ctx = canvas.getContext('2d');
loadImage('./1.jpg').then(image => { 
   ctx.drawImage(image, 0, 0, 92, 34)  
   ProcessToGrayImage(ctx);
   //灰度化  
   OTSUAlgorithm(120); 
   //二值化  
   fs.writeFileSync('./2.jpg',dataURLtoFile(canvas.toDataURL()),{flag:'w'})
   })
1
2
3
4
5
6
7
8
9
10
11

# 灰度化函数

function ProcessToGrayImage(ctx) {  
  //取得图像数据  // 
  var imgData = ctx.getImageData(10, 10, 50, 50); 
   var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);  
   //这个循环是取得图像的每一个点,在计算灰度后将灰度设置给原图像​  
   for (var x = 0; x < canvasData.width; x++) {   
     //alert("x="+x);    
     for (var y = 0; y < canvasData.height; y++) {      
      //alert("y="+y);      
      // Index of the pixel in the array  
         var idx = (x + y * canvas.width) * 4;// The RGB values     
          var r = canvasData.data[idx + 0];      var g = canvasData.data[idx + 1];      var b = canvasData.data[idx + 2];     
           //更新图像数据      
           var gray = CalculateGrayValue(r, g, b);      
           canvasData.data[idx + 0] = gray;     
            canvasData.data[idx + 1] = gray;     
             canvasData.data[idx + 2] = gray;    } 
              }  
             ctx.putImageData(canvasData, 0, 0);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# nodejs保存图片数据为图片文件

这里的dataURLtoFile是将图片base64数据转化为二进制图片数据方便写入文件中。参考了思否qylcx7758大佬的回答。

const atob = require('atob');
function dataURLtoFile(dataurl, filename) {  
  var arr = dataurl.split(','), 
  mime = arr[0].match(/:(.*?);/)[1],     
   bstr = atob(arr[1]), n = bstr.length, 
   u8arr = new Uint8Array(n);  
   while(n--){      
    u8arr[n] = bstr.charCodeAt(n);  
    }  
    return u8arr;
    }
1
2
3
4
5
6
7
8
9
10
11

atob() 函数能够解码通过base-64编码的字符串数据。相反地,btoa() 函数能够从二进制数据“字符串”创建一个base-64编码的ASCII字符串。由于nodejs中没有atob(浏览器有),所以要从npm中下载才能够使用atob的功能。

# 图片二值化

图像二值化( Image Binarization)就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。因为ocr功能往往会被噪点所影响,所以图片二值化设置合理的阈值可以取消掉图片中的浅色小点,提高ocr识别精度,本例中的阈值为120。

# 二值化函数

二值化的阈值可以用概率计算出一个合适的值,但在本例中不尽人意,在本文中使用手动设置阈值为120,比较方便快速的过滤了图片中的灰色小点。

function OTSUAlgorithm(threshold) {var imageInfo = GetGrayImageInfo(ctx);  
if (imageInfo == null) {    
  console.warn("图像还没有转化为灰度图像!");    
  return; 
   } 
   //获取图像信息  
   var canvasData = imageInfo[0];   
   //下面执行二值化过程  
   for (i = 0; i < canvasData.width; i++) {   
     for (j = 0; j < canvasData.height; j++) {     
      //取得每一点的位置      
      var ids = (i + j * canvasData.width) * 4;     
      //取得像素的R分量的值      
      var r = canvasData.data[ids];      
      //与阀值进行比较,如果小于阀值,那么将改点置为0,否则置为255     
       var gray = r > threshold ? 255 : 0;
      canvasData.data[ids + 0] = gray;
      canvasData.data[ids + 1] = gray;
      canvasData.data[ids + 2] = gray;    
       }  
      }  
    //显示二值化图像  //
    var newImage = document.getElementById('myCanvas').getContext('2d'); 
    ctx.putImageData(canvasData, 0, 0);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 完整的生成二值化图像的代码

const fs = require('fs');
const atob = require('atob');
const {  createCanvas,  loadImage} = require('canvas')
const canvas = createCanvas(92, 34);
const ctx = canvas.getContext('2d')
console.log(ctx)function ProcessToGrayImage(ctx) {  
  //取得图像数据  // 
  var imgData = ctx.getImageData(10, 10, 50, 50);  
  var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);  
  //这个循环是取得图像的每一个点,在计算灰度后将灰度设置给原图像​  
  for (var x = 0; x < canvasData.width; x++) {    
    //alert("x="+x);    
    for (var y = 0; y < canvasData.height; y++) {      
      //alert("y="+y);      // Index of the pixel in the array     
       var idx = (x + y * canvas.width) * 4;// The RGB values      
       var r = canvasData.data[idx + 0];      
       var g = canvasData.data[idx + 1];      
       var b = canvasData.data[idx + 2];      
       //更新图像数据      
       var gray = CalculateGrayValue(r, g, b);      
       canvasData.data[idx + 0] = gray;      
       canvasData.data[idx + 1] = gray;      
       canvasData.data[idx + 2] = gray;    
       }  
       }  
       ctx.putImageData(canvasData, 0, 0);}
       //一维OTSU图像处理算法
       function OTSUAlgorithm(threshold) {var imageInfo = GetGrayImageInfo(ctx);  
       if (imageInfo == null) {    
        console.warn("图像还没有转化为灰度图像!");    
        return;  
        }  
        //获取图像信息  
        var canvasData = imageInfo[0];//下面执行二值化过程   
        for (i = 0; i < canvasData.width; i++) {    
          for (j = 0; j < canvasData.height; j++) {      
            //取得每一点的位置      
            var ids = (i + j * canvasData.width) * 4;      
            //取得像素的R分量的值      
            var r = canvasData.data[ids];      
            //与阀值进行比较,如果小于阀值,那么将改点置为0,否则置为255      
            var gray = r > threshold ? 255 : 0;      
            canvasData.data[ids + 0] = gray;      
            canvasData.data[ids + 1] = gray;      
            canvasData.data[ids + 2] = gray;    
          }  
        }  
            //显示二值化图像  // 
            var newImage = document.getElementById('myCanvas').getContext('2d');  ctx.putImageData(canvasData, 0, 0);}//获取图像的灰度图像的信息
            function GetGrayImageInfo(ctx) {  
              // 
              var canvas = document.getElementById('myCanvas');  
              // 
              var ctx = canvas.getContext('2d');  
              var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);  
              if (canvasData.data.length == 0) {    return null;  }  
              return [canvasData, ctx];}
              //计算图像的灰度值,公式为:Gray = R*0.299 + G*0.587 + B*0.114function CalculateGrayValue(rValue, gValue, bValue) {  return parseInt(rValue * 0.299 + gValue * 0.587 + bValue * 0.114);}
              function dataURLtoFile(dataurl, filename) {  
                var arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]), n = bstr.length, 
                u8arr = new Uint8Array(n); 
                 while(n--){      
                  u8arr[n] = bstr.charCodeAt(n);  
                  }  
                  return u8arr;
                }
                  ​loadImage('./1.jpg').then(image => {  
                    ctx.drawImage(image, 0, 0, 92, 34)  
                    ProcessToGrayImage(ctx);  
                    OTSUAlgorithm(120);  
                    fs.writeFileSync('./2.jpg',dataURLtoFile(canvas.toDataURL()),{flag:'w'}) 
                    console.log('<img src="' + canvas.toDataURL() + '" />')
                  }
            )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

# 用tesseract.js识别二值化后的图像

Tesseract (/'tesərækt/) 这个词的意思是”超立方体”,指的是几何学里的四维标准方体,又称”正八胞体”,是一款被广泛使用的开源 OCR 工具。 Tesseract 已经有30 年历史,开始它是惠普实验室于1985年开始研发的一款专利软件,到1995年一件成为OCR业界内最准确的识别引擎之一。

代码不多,从官方实例上面拷贝下来的

const { createWorker } = require('tesseract.js');const worker = createWorker({  logger: m => {}});
(async () => {  
  await worker.load();  
  await worker.loadLanguage('eng');  
  await worker.initialize('eng');  
  const { data: { text } } = await worker.recognize('./2.jpg');  
  console.log(text);  
  await worker.terminate();})();
1
2
3
4
5
6
7
8
9

# 参考文章

  1. Rush的技术总结 (opens new window)
  2. node-canvas (opens new window)
  3. js 如何将base64编码转图片格式 (opens new window)

原文地址 (opens new window)

最后更新时间: 2023/2/23 18:24:24