【Go 语言社区】HTML5 canvas验证码识别

canvas 的历史这个 HTML 元素是为了客户端矢量图形而设计的。它自己没有行为,但却把一个绘图 API 展现给客户端 JavaScript 以使脚本能够把想绘制的东西都绘制到一块画布上。canvas 标记由 Apple 在 Safari 1.3 Web 浏览器中引入。对 HTML 的这一根本扩展的原因在于,HTML 在 Safari 中的绘图能力也为 Mac OS X 桌面的 Dashboard 组件所使用,并且 Apple 希望有一种方式在 Dashboard 中支持脚本化的图形。Firefox 1.5 和 Opera 9 都跟随了 Safari 的引领。这两个浏览器都支持 canvas 标记。我们甚至可以在 IE 中使用 canvas 标记,并在 IE 的 VML 支持的基础上用开源的 JavaScript 代码(由 Google 发起)来构建兼容性的画布。 参见:http://excanvas.sourceforge.net/。

canvas 的标准化的努力由一个 Web 浏览器厂商的非正式协会在推进,目前 canvas 已经成为 HTML 5 草案中一个正式的标签。 参见:URL验证码生成规律首页我们得观察验证码图片生成规律,通过多次刷新出不同图片来辨别;本文中的验证码由四个数字组成,且每个数字除了颜色随机改变,形状和位置是固定不变的。 上图效果实现:打开Photoshop,把网页中的验证码图片拖进来,然后再拖出几根参考线,让每两根参考线包围一个数字,一开始肯定有偏差,再次刷新页面,按住shift键把验证码图片拖到photoshop中,微调参考线的位置,经过多次操作;根据在横向上来看,验证码图片中的第一个数字是从6px开始的,然后每个数字占了9px,中间间隔是8px.于是我们可以得出一个公式,x=17*i+6,i表示数字的索引[0,1,2,3],x表示横坐标纵向上比较简单,y坐标的范围是恒定的,9px到14px.图像处理首先,我并没有专业的图像处理知识,所以下面说的专业词汇肯定是有问题的,以理解为主.由于数字和背景都是随机颜色的,那么我们生成的模板字符串岂不是每次都会变吗.的确是这样的,由于canvas在获取某个像素点的像素值时,返回的是rgba值.也就是一共有四个值.我们需要使用一个公式,把rgba颜色转换成灰度值:gray = r*0.3 + g*0.59 + b*0.11,灰度值的范围是0~255,我这里把128看成临界点,也就是把0~128看成是暗,用0表示,128~255看成是明,用1表示,我把明暗简写为ld(Light and Dark).也就是公式,ld = gray>128?1:0.为什么通过明暗值能把数字和背景色区分开来呢,因为这种验证码在进行灰度化以后,背景明显是属于亮的,偏白色,而数字是属于暗的,偏黑色.所以能够区分.通过photoshop灰度化的效果 再进行50%阀值 生成模板既然每个数字的形状和位置都是一定的,那我们就能把0-9这10个数字的像素信息存储下来作为模板,在识别验证码时,取出验证码图片中的数字依次对比.如果相等说明就是这个数字.下面是我写的生成模板的代码.

var image = document.querySelector("#validate");                 //获取到验证码图片

var canvas = document.createElement('canvas');                        //新建一个canvas

var ctx = canvas.getContext("2d");                                    //获取2D上下文

var numbers = [];                                                     //存储数字模板的数组

canvas.width = image.width;                                           //设置canvas的宽度

canvas.height = image.height;                                         //设置canvas的高度

document.body.appendChild(canvas);                                    //将canvas添加进文档

ctx.drawImage(image, 0, 0);                                           //将验证码绘制到canvas上

for (var i = 0; i < 4; i++) {                                         //循环四次,识别四个数字

    var pixels = ctx.getImageData(17 * i + 6, 6, 9, 14).data;         //按照公式获取到每个数字上的像素点

    var ldString = "";                                                //用来存储明暗值的字符串

    for (var j = 0, length = pixels.length; j < length; j += 4) {                 //每次循环取四个值,分别是一个像素点的r,g,b,a值

        ldString = ldString + (+(pixels[j] * 0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 128));     //灰度化+二值化,但我们并没有真正的处理图像

    }                

    console.log(ldString);                 //输出存储着明暗值的字符串

}

var image = document.querySelector("#validate");                 //获取到验证码图片var canvas = document.createElement('canvas');                        //新建一个canvasvar ctx = canvas.getContext("2d");                                    //获取2D上下文var numbers = [];                                                     //存储数字模板的数组canvas.width = image.width;                                           //设置canvas的宽度canvas.height = image.height;                                         //设置canvas的高度document.body.appendChild(canvas);                                    //将canvas添加进文档ctx.drawImage(image, 0, 0);                                           //将验证码绘制到canvas上for (var i = 0; i < 4; i++) {                                         //循环四次,识别四个数字    var pixels = ctx.getImageData(17 * i + 6, 6, 9, 14).data;         //按照公式获取到每个数字上的像素点    var ldString = "";                                                //用来存储明暗值的字符串    for (var j = 0, length = pixels.length; j < length; j += 4) {                 //每次循环取四个值,分别是一个像素点的r,g,b,a值        ldString = ldString + (+(pixels[j] * 0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 128));     //灰度化+二值化,但我们并没有真正的处理图像    }                    console.log(ldString);                 //输出存储着明暗值的字符串}复制代码识别验证码var image = document.querySelector("#validate");       //如果要用在greasemonkey脚本里,可以把下面的代码放在image的onload事件里         var canvas = document.createElement('canvas');                var ctx = canvas.getContext("2d");                //模板,依次是0-9十个数字对应的明暗值字符串var numbers = [                          "001111100011111110111000110110000111110000111010000011110000011110000011110000011111000011110000111111000110011111110001111100","000001100000011100000111100011111100011101101000001110100001100000001100000000100000001100000001100000001100000011100000001100","001111100011111110111001111110000011010010011000000111000001110000011110000111100001111000011110000111100000111111111101111101","011111000111111100110101110110000110000001110000011100000111100000111110000000110000000110110000110110001110111111100011111000","000000110000001100000011110000111110000110110001101110011100110011000110110000110111111111111111111000000110000000110000000110","011111110011111110011000000011000000111000000111111100111111110110000111000000001000000011110000111110000111111111110011111100","001111100011111110011000111110000011110000100111111100111111110111000111110000011110000011110000011111100111011111110001111100","111111111111111111000000110000001100000011100000011000000111000001110000000110000010110000001100000001100000001100000001100000","001110100011111110111000110110000110111001110011101110011111100111111110110000111110000011110000011111000111011111110001111100","001111100011111110111000110010000011110000011110000011100000111111101111011111111000110111010000111110000110011111010011011100"];//存放识别后的验证码var captcha = "";                        canvas.width = image.width;canvas.height = image.height;document.body.appendChild(canvas);ctx.drawImage(image, 0, 0);for (var i = 0; i < 4; i++) {    var pixels = ctx.getImageData(17 * i + 6, 6, 9, 14).data;    var ldString = "";    for (var j = 0,length = pixels.length; j < length; j += 4) {        ldString = ldString + (+(pixels[j] * 0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 140));    }    var comms = numbers.map(function (value) {                      //为了100%识别率,这里不能直接判断是否和模板字符串相等,因为可能有个别0被计算成1,或者相反        return ldString.split("").filter(function (v, index) {            return value[index] === v        }).length    });    captcha += comms.indexOf(Math.max.apply(null, comms));          //添加到识别好的验证码中}console.log(captcha);复制代var image = document.querySelector("#validate");                 //获取到验证码图片

var canvas = document.createElement('canvas');                        //新建一个canvas

var ctx = canvas.getContext("2d");                                    //获取2D上下文

var numbers = [];                                                     //存储数字模板的数组

canvas.width = image.width;                                           //设置canvas的宽度

canvas.height = image.height;                                         //设置canvas的高度

document.body.appendChild(canvas);                                    //将canvas添加进文档

ctx.drawImage(image, 0, 0);                                           //将验证码绘制到canvas上

for (var i = 0; i < 4; i++) {                                         //循环四次,识别四个数字

    var pixels = ctx.getImageData(17 * i + 6, 6, 9, 14).data;         //按照公式获取到每个数字上的像素点

    var ldString = "";                                                //用来存储明暗值的字符串

    for (var j = 0, length = pixels.length; j < length; j += 4) {                 //每次循环取四个值,分别是一个像素点的r,g,b,a值

        ldString = ldString + (+(pixels[j] * 0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 128));     //灰度化+二值化,但我们并没有真正的处理图像

    }                

    console.log(ldString);                 //输出存储着明暗值的字符串

}

识别验证码

var image = document.querySelector("#validate");       //如果要用在greasemonkey脚本里,可以把下面的代码放在image的onload事件里         

var canvas = document.createElement('canvas');                

var ctx = canvas.getContext("2d");                

//模板,依次是0-9十个数字对应的明暗值字符串

var numbers = [                          

"001111100011111110111000110110000111110000111010000011110000011110000011110000011111000011110000111111000110011111110001111100",

"000001100000011100000111100011111100011101101000001110100001100000001100000000100000001100000001100000001100000011100000001100",

"001111100011111110111001111110000011010010011000000111000001110000011110000111100001111000011110000111100000111111111101111101",

"011111000111111100110101110110000110000001110000011100000111100000111110000000110000000110110000110110001110111111100011111000",

"000000110000001100000011110000111110000110110001101110011100110011000110110000110111111111111111111000000110000000110000000110",

"011111110011111110011000000011000000111000000111111100111111110110000111000000001000000011110000111110000111111111110011111100",

"001111100011111110011000111110000011110000100111111100111111110111000111110000011110000011110000011111100111011111110001111100",

"111111111111111111000000110000001100000011100000011000000111000001110000000110000010110000001100000001100000001100000001100000",

"001110100011111110111000110110000110111001110011101110011111100111111110110000111110000011110000011111000111011111110001111100",

"001111100011111110111000110010000011110000011110000011100000111111101111011111111000110111010000111110000110011111010011011100"];

//存放识别后的验证码

var captcha = "";                        

canvas.width = image.width;

canvas.height = image.height;

document.body.appendChild(canvas);

ctx.drawImage(image, 0, 0);

for (var i = 0; i < 4; i++) {

    var pixels = ctx.getImageData(17 * i + 6, 6, 9, 14).data;

    var ldString = "";

    for (var j = 0,length = pixels.length; j < length; j += 4) {

        ldString = ldString + (+(pixels[j] * 0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 140));

    }

    var comms = numbers.map(function (value) {                      //为了100%识别率,这里不能直接判断是否和模板字符串相等,因为可能有个别0被计算成1,或者相反

        return ldString.split("").filter(function (v, index) {

            return value[index] === v

        }).length

    });

    captcha += comms.indexOf(Math.max.apply(null, comms));          //添加到识别好的验证码中

}



console.log(captcha);

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-01-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

android绘制虚线

有的时候我们需要一种虚线效果,比如图片的边框,愤怒的小鸟的飞翔路径,那么怎么绘制这些虚线呢?方法很多,目前我觉得好的有两种: 一、自己创建模式,一个点一个点的绘...

29960
来自专栏HTML5学堂

原生JS | 导航底部横线跟随鼠标缓动

HTML5学堂(码匠):在上周当中,我们用jQuery实现了 - 在导航底部存在一条横线,跟随着鼠标缓动到相应导航项 - 的特效,今天我们来讲讲原生JS的实现方...

80580
来自专栏用户2442861的专栏

python数字图像处理(12):基本图形的绘制

skimage.draw.set_color(img, coords, color)

23420
来自专栏前端萌媛的成长之路

CSS选择器的优先级

19940
来自专栏进击的君君的前端之路

CSS理解之border

19630
来自专栏王金龙的专栏

svg.js教程及使用手册详解(二)

上篇简要介绍了svg.js的基本信息和基本用法,这篇开始详细讲解svg.js的用法。

52740
来自专栏Android知识点总结

开源计划之--Android绘图库--LogicCanvas

Painter采用单例模式 优化原型模式,各Shape采用深拷贝来解决构造较长、繁琐的情况 比较new 对象和拷贝的效率问题,拷贝一点。具体见文:来谈谈Ja...

19030
来自专栏Java帮帮-微信公众号-技术文章全总结

【Java案例】打印五环

案例描述 在屏幕上画出奥运五环旗,如图1.7所示。 ? 图1.7 奥运五环旗 案例分析 观察奥运五环旗的图案,直观的感觉,由五个圆组成,每个圆的颜色不一样...

45750
来自专栏Google Dart

Flutte部件目录-基本部件(二) 顶

支持以下图像格式:JPEG,PNG,GIF,GIF动画,WebP,WebP动画,BMP和WBMP

19420
来自专栏程序员的诗和远方

一个比想象中更骚气的圆-svg实现

之前写了一篇Canvas画图-一个比想象中更骚气的圆(渐变圆环),其实SVG也可以实现类似的效果,而且两者api惊人的相似。 关于SVG SVG是一种矢量...

50970

扫码关注云+社区

领取腾讯云代金券