关于javascript进行的简单的验证码识别

  • 示例验证码:bilibili直播间领瓜子算式验证码(120px*40px)

getCaptcha (1).jpg

经过分析,该验证码满足三个特征

  1. 长度固定,左边两个数字,然后一个符号,右边一个数字,总长度4位。
  2. 字符有限,0-9共10个数字,再加上一个加号和一个减号。
  3. 样式单一,没有字体变化,干扰元素极少,字符之间有一定的空隙。

这篇博客中,它使用了提取各个数字的区块的识别方法,但是该方法只能应用在字符位置固定且等宽的情况,在bilibili这个验证码中,字符的初始位置并不固定,不能通过公式进行切割,字符的宽度也不相等,所以只能另外寻找方法

根据上面的规律和这次验证码的特殊性,可以确定出识别方案:

  1. 先通过二值化,将锯齿像素排除,将图片转为特定的二值化后的矩阵
  2. 依据空白,将字符分割开来
  3. 将分割开的字符分别确定其特征
分割开的字符,二值化后可以产生白色像素和黑色像素,在这里用1表示黑色像素,这样的话我们就可以成功表示出二值化后的像素矩阵

代码如下:

            var ctx = $("#helper_canvas")[0].getContext("2d");
            var pixels = ctx.getImageData(0,0,120,40).data;
            //console.log(pixels);
            var pix = []; //定义一维数组
            var j = 0;
            var i=0;
            var n=0;
            for(i=1;i<=40;i++)
            {
                pix[i] = []; //将每一个子元素又定义为数组
                for(n=1;n<=120;n++)
                {
                    let c = 1;
                    if(pixels[j]-(-pixels[j + 1])-(- pixels[j + 2]) >200){
                        c=0;
                    }
                    j = j+4;
                    pix[i][n]=c; //此时pix[i][n]可以看作是一个二级数组
                }
            }
            //我们得到了二值化后的像素矩阵pix[40][120]

通过这个二值矩阵,我们将每一列求和,为0的位置就是该列全都是空白,因此可以求出一个和的数组lie[120],该数组记录了每一列上黑色像素的个数。
于是通过切割0的部分,我们可以将其各个字符的分割出来,并且去获得每一个字符的像素总数,第一列的像素数和最后一列的像素数。

代码如下:

    

    var lie = [];
    lie[0]=0;
    for(i=1;i<=120;i++){
        lie[i] = 0;
        for(n=1;n<=40;n++){
            lie[i] = lie[i]+pix[n][i];
        }
    }
    var ta = [];
    n=0;
    for(i=1;i<=120;i++){
        if(lie[i]>0&&lie[i-1]===0){
            n++;
            ta[n] = new Object();
            ta[n].fi = lie[i];
            ta[n].total = 0;
        }
        if(lie[i]>0){
            ta[n].total = ta[n].total+lie[i];
        }
        if(lie[i-1]>0&&lie[i]===0){
            ta[n].la = lie[i-1];
        }
    }

通过该代码,我们可以依据二值化矩阵得出每一个字符的三个属性,包括:像素个数,第一列像素个数,和最后一列像素个数
通过总像素个数,我们已经能够区分大部分的字符,但是0、6、9三个字符的像素个数接近,只能通过第一列像素个数和最后一列像素个数进行区分。

在对十几张验证码进行统计以后,我们可以大概得出如下的识别函数:

  function get_word(a){
        if(a.total<=50) return "-";
        if(a.total>120&&a.total<135) return "+";
        if(a.total>155&&a.total<162) return 1;
        if(a.total>189&&a.total<195) return 7;
        if(a.total>228&&a.total<237) return 4;
        if(a.total>250&&a.total<260) return 2;
        if(a.total>286&&a.total<296) return 3;
        if(a.total>303&&a.total<313) return 5;
        if(a.total>335&&a.total<342) return 8;
        if(a.total>343&&a.total<350){
            if(a.fi>24&&a.la>24) return 0;
            if(a.fi<24&&a.la>24) return 9;
            if(a.fi>24&&a.la<24) return 6;

        }

    }

通过该识别函数,即可得出四个字符,最后进行一下算式加减即可完成识别
最终效果:1.gif

本项目地址:bilibili自动领瓜子