PHP识别24位BMP的验证码

网络整理 - 08-09
PHP识别24位BMP的验证码

今天接到个活,就是识别一个简单的验证码,这个验证码异常的简单,具有很少的干扰信息,所以只要拿到信息进行比较.很容易就可以识别,但是一切不会那么一帆风顺的..也许是对于我来说吧.我觉得我干什么都十分曲折...

这个验证码图片是用ASP生成的..理所当然成了BMP格式.

但是对于PHP来说.这是个软伤.因为PHP的GD库根本就没把BMP纳入在内.虽然有wbmp但是还是不一样的.

所以就google了一下.结果又让我一阵喜悦.竟然有个现成的识别256色BMP的类..十分激动..有这个还不是白拿钱的事啊!!

结果又是令人失望的..256色的BMP跟24位色的根本是两个概念..这就意味这我得重新写了..

看来没有现成的类库和函数.只能自己写了.用最最原始的fopen.分析BMP头.一个一个字节的读了.

所有又去google了BMP的24位色的头分析..那么长的文章...立马就失望了..

所以无聊就去翻手册..眼睁睁的看着有imagecreatefromgif imagecreatefromjpeg imagecreatefrompng imagecreatefromwbmp等等.就是没有imagecreatefrombmp

于是突发奇想.去google一下imagecreatefrombmp会是什么结果..果然不出我所料.已经有达人写出了这个函数..

这个函数的过程是这样的..用fopen打开文件.一个字节一个字节的读出来..先读BMP头.然后逐行读像素.再用imagecreatetruecolor新建立个图像.把刚读出来的像素逐个再画到刚才创建的图片上.
最后返回这个图像的resource .

估计大家跟我一样.一开始就被这么长的函数吓到了.其实不用怕.分析一下.我们是要分析24位色的BMP.所以上面函数的大部分语句我们都不用.if($biBitCount==24)以下的才是关键..

$B=freadbyte($f);

$G=freadbyte($f);

$R=freadbyte($f);

这三行就是读取像素了..我们需要的就是把每个像素放到数组中.这样就容易被操作了.

$array[] = sprintf("%03d",$R).sprintf("%03d",$G).sprintf("%03d",$B);

这样我们就把每个像素放到$array这个数组中了.但是这样还不够.因为我们得识别像素.所以我是这样想的.把白色的(255255255)改为0.其他颜色改为1.这是因为我这个验证码比较简单.而且干扰颜色非常少..

最后形成的$array就类似这样的了

0001111000   0001111000   0001111000   0000101000

0010000100   0010000100   0010000100   0000100000

0010000100   0010000100   0010000100   0000100000

0010110100   0000000100   0000000100   0000100000

0010110100   0000101000   0000000100   0000100000

0010110100   0000110000   0011000100   0000010000

0010110100   0000001000   0010111000   0000010000

0010000100   0010000100   0010000000   0010001000

0011000100   0010000110   0010000000   0010001000

0001111000   0001111000   0011111100   0011111100

当然 之所以形成这个样子是我输出的时候处理过的.
很容易看的清楚.这是0357..

有的人会问为什么是倒的?这是因为BMP在存储图像的时候就是倒着存放的.比较BT.
不过这个不必担心.我们也不用给他费劲正过来.因为我们是要做比较.只要这个对比码也是倒着的就可以了.

为了更清楚的说明白这个数组是怎么存储内容的.我给大家详细的描述一下.就按照上面的例子.

数组下标0 1 2 3 4 5 6 7 8 9   10.....                                                       ...  39

存储内容0 0 0 1 1 1 1 0 0 0   0 0 0 1 1 1 1 0 0 0    0 0 0 1 1 1 1  0 0 0     0 0 0 0 1  0 1 0 0  0  第一行

数组下标40 41 ....                                                                           ...79

存储内容0  0 1 0 0 0 0 1 0 0    0 0 1 0 0 0 0 1 0 0    0 0 1 0 0 0 0 1 0 0    0 0 0 0 1 0 0 0 0 0    第二行

为了省略我就不写了..很容易看出来.这是个一维的数组.一共有399个元素..但是为了识别验证码.我们得把其中的每个数字取出来.

0-9 40-49 80-89 ...为一组

10-19 50-59 90-99 ...为一组

20-29 60-69 100-109...为一组

30-39 70-79 110-119..为一组

你可以自己写个算法..

这样整理出来后..是一个具有4个元素的数组.
每个元素都有100位0和1的数字.

元素取出来了.要跟谁比较呢??对.我们得找个参照物才行..
找参照物就是体力活了..
返回到上面的过程..不断的刷新验证码..直到把0-9都出现了.针对每个.记下他的样子比如说0是这样

0001111000

0010000100

0010000100

0010110100

0010110100

0010110100

0010110100

0010000100

0011000100

0001111000

变成字符串.0就是这样了0001111000001000010000100001000010110100001011010000101101000010110100001000010000110001000001111000
也就是上面的按照顺序放在一行.

0-9都记下来后.我们可以放到一个数组中,就叫$key吧.

这样的话跟我们生成出来的元素就一一对应了.

下面就要开始比较了.

循环是免不了的.比较的技巧是用similar_text和参照物逐个比较.把相思百分比放到一个数组中.
然后把这个数组中最大的那个百分比取出来.所对应的索引值(此索引值不是自动生成的)就是我们识别出来的数字了.
下面的代码值描述重要的部分

然后取出$maxArr这个最大的索引值.就是我们识别出来的数字了.

到此验证码算是识别完成了.

但是部署给用户的时候.发现他的机器上用fopen打开远程URL.只有30%的几率能获取到.其他的情况都是HTTP Request failed...
更改了很多方法还是不成功.结果我的解决办法是.
用CURL把验证码保存到本地.然后fopen打开本地图片就100%OK啦..

至于详细的代码我就不贴了.因为代码是卖给人家的.为了保障人家的利益.所以大家也不要开口跟我要了..思路我已经给出来了.相信你是可以写出来的.