很久很久之前(大概在五年前),我就在想,能不能在前端用js识别图片验证码,直到现在,这个功能也未实现,因为做了一半发现实现不了,就放弃了,这里只是记录一下当时的思路和方向;
后来整理到 Github 上了,源代码 和 DEMO 可点击查看;
当时的验证码比较简单,干扰因素比较少,字符也基本没有重叠,所以就想用Canvas对图片进行处理,与标准字符进行比较,找出最像的字符:
- 将图片转成黑白;
- 去除噪点;
- 找到字符边界,拆分出单个字符;
- 对字符进行分割;
- 计算出每个分割出的区域中,有效像素的占比=有效的像素数/总的有效像素数;
- 与26个英文字母大小写、数字的有效像素占比进行比较,得出最接近的值;
像 0、O、o,以及8,x 这种对称的字符应该很难识别;
后来发现,字体、字号大小、加粗与否、去除噪点的算法、旋转角度等,都会造成误差,影响精度;实际中,暂不考虑旋转角度和字体,如果字体固定、字符不旋转的情况下能识别出来,再加角度、换字体进行计算、比较就可以了,虽然计算量很大,但只计算一次就够了,把数据存储起来;
将图片转成黑白
将图片绘制到 Canvas 中,获取每个像素点的信息,将非白色的点统一转成黑色;
去除噪点
当时在网上找的实例图片,噪点都是一个个像素点,所以我的策略也比较简单,就是找孤立的像素点,然后删掉(改成白色);
找字符边界
去除噪点后,从左向右,找第一个出现黑色点的列(字符的左边界),然后再向后找,找到之后第一个纯白色点的列(字符的右边界),第一个字符就找到了;
然后循环上面的操作,找到图片中所有的字符;
*实际操作中,先按一张图片中只有一个字符来处理的;
分割
将找到的字符二维数组数据进行拆分,按照整除的原则(单数的话手动+1变成双数),比如 8行6列的数组,可以按照将行分成4组、列分成3组,这样就得到了12个分割区;比如下面是A的拆分:
分割的组数量并非越大越精确,因为验证码字符本身有效字符就比较少,如果再切分的太细,反而会放大误差;
计算
计算出每个分割区中有效像素的占比,有效像素的占比=有效的像素点数/总的有效点数;
比较
将26个英文字母大小写、数字绘制到 Canvas 中,也按照相同的分割规则,计算每个分割区的有效像素占比(这个占比可以只计算一次,然后存储起来,之后直接拿来用),然后与目标值进行比较,得出最接近的值;