用php读取验证码图片里的字符

听说过可以用程序读取验证码里的字符,一直不知道实现方法,这两天找到一个php写的类,可以读取比较规整的验证码图片里的数字,分享一下,后面有附件下载。

先讲一下原理,gd库里有个函数imagecolorat(resource,x,y),可以获取指定像素的RGB值,关键点就在这里,如果验证码图片做的比较简单,有固定RGB的背景,比如白色,字体大小和位置又相对固定,就可以分析出来。

一张图片从像素的角度看,实际上就是一个二维数组,每个像素点都有RGB值,过滤掉背景点(置0),留下字符所占位的像素点(置1),这样就可以得到一个只有0|1两个值的二维数组,如果打印这个二维数组,就会发现1组成的形状正好是图片里的字符,接下来要做的就是,按照字符所在位置和宽高,从上往下,从左到右,读取每个字符的特征码,如果只有数字的话,特征码就只有0-9,加上字母的话,也就是30来个特征码,不算多,拿图片里读取出来的特征码和已存在的特征码进行比对,就能得到具体的字符了。

下面这片由0,1组成的二维数组,其图片内容是¥198.00,转换成二维数组,一目了然。

001111011110000000000000000000000000000000000000000000000000
000111001100000110000011110000111100000000111100001111000000
000011101000001110000110011001100110000001100110011001100000
000011101000000110000110011001100110000001100110011001100000
000001110000000110000110011000111100000001100110011001100000
000001110000000110000011111001100110000001100110011001100000
000011111000000110000000011001100110000001100110011001100000
000001110000000110000000110001100110011001100110011001100000
000001111000001111000011100000111100011000111100001111000000

关键代码解析:

将图片转换成二维数组

$res = imagecreatefrompng($this->imgPath);
$size = getimagesize($this->imgPath);
$data = array();
for ($i = 0; $i < $size[1]; ++$i) {
for ($j = 0; $j < $size[0]; ++$j) {
$rgb = imagecolorat($res, $j, $i);//逐个读取像素RGB值
$rgbarray = imagecolorsforindex($res, $rgb);// 将RGB值转换成10进制数组
if ($rgbarray[‘red’] < 125 || $rgbarray[‘green’] < 125
|| $rgbarray[‘blue’] < 125) {// 125,125,125以上的像素点都认为是背景色,置0
$data[$i][$j] = 1;
} else {
$data[$i][$j] = 0;
}
}
}

按照指定高宽和间距读取图片上字符特征码

for ($i = 0; $i < $numCount; ++$i) {
$x = ($i * ($this->imgInfo[‘wordWidth’] + $this->imgInfo[‘wordSpacing’])) + $this->imgInfo[‘offsetX’] + $DotPlus;
$y = $this->imgInfo[‘offsetY’];

array_push($data, ”);
for ($h = $y; $h < ($this->imgInfo[‘offsetY’] + $this->imgInfo[‘wordHeight’]); ++$h) {
for ($w = $x; $w < ($x + $this->imgInfo[‘wordWidth’]); ++$w) {
$data[$i] .=$this->dataArray[$h][$w];
}
}

// 这里有专门针对小数点的处理,因为小数点占的宽度和其他数字不同

if (!in_array($data[$i], $this->keys)) {
$DotPlus = 4 – $this->imgInfo[‘wordWidth’];
$data[$i] = $this->keys[‘.’];
}
}

下面是附件,实际上这是一个分析京东商品价格图片的php,京东商品价格是用图片显示的,但是这些价格图片非常规整,数字的宽高和间距以及第一个数字距左上的距离都是固定,而且里面没有干扰像素,读取就成为可能了。

readimg

从这段php程序也可以看出,好的验证码,必须要具备的一些要素:

1、干扰像素必须要有

2、背景和字符的颜色,也就是RGB,不能固定

3、字符的大小和位置不能固定,最好有些字符能紧挨在一起,让程序分离不出来

4、字符的范围要广,纯数字肯定是不行的,如果只是针对中文用户,最好放中文进去

5、人眼能识别,不能搞成程序设别不了,人眼也识别不了

从程序的角度来看,要能分辨出干扰像素,要能准确定位字符,采取模糊匹配特征码,比如一个字符有N个像素,能匹配到M个像素,确定字符,按照M/N大者来,个别字符可能要另外处理,比如0和o,可能很像。

发表评论