注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
想做一个针对联通优选在沃自动签到的小程序,但是登陆需要验证码,找了一下现有的一些博客或者是开源的验证码识别的代码,没有一个比较满意的,so,自己写了一个。
需要识别的验证码
第一种:
第二种
分析
像上图的验证码一般来说的话,我们需要识别的是 4 个数字,但是验证码为了防止自动识别程序添加了许多的干扰项,例如背景色
、雪花
、干扰线
等等
去除背景
我们通过分析该验证码图片可以知道,数字的 rgb 值一般处在 180~190 以下,而背景色和雪花的 rgb 值一般处在 200 以上,so,我们只要在处理图片的时候只取 190 以下的 rgb 值保存就 ok 了,这样就可以去除掉绝大部分的干扰项
二值化
把干扰信息去除之后,只留下二进制(也就是 0 和 1 表示)的点阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public function imageHash(){ for($i = 0; $i < $this->_image_h; $i++) { for ($j = 0; $j < $this->_image_w; $j++) { $rgb = imagecolorat($this->_in_img,$j,$i); $rgb_array = imagecolorsforindex($this->_in_img, $rgb); if($rgb_array['red']<190&&$rgb_array['green']<190&&$rgb_array['blue']<190){
$data[$i][$j]=1; }else{
$data[$i][$j]=0; } }
}
}
|
点阵化之后的截图
去噪点
图像二值化之后可能还存在很多噪点,噪点的特点一般是孤立无援的,这时候我们只需要判断以这个 1 点为中心的点,周围的 1 点个数小于一个阈值(我把它设为 3),就确认它为噪点并将之去除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
public function removeHotSpots($hash_data){ for($i = 0; $i < $this->_image_h; $i++) { for ($j = 0; $j < $this->_image_w; $j++) { if($hash_data[$i][$j]){ if($this->isHotSpots($i,$j,$hash_data)) $hash_data[$i][$j]=0; } } } return $hash_data; }
public function isHotSpots($i,$j,$hash_data){ if($i == 0 || $j == 0 || $i == ($this->_image_h - 1) || $j == ($this->_image_w - 1)) return true;
$points[0]=$hash_data[$i-1][$j-1]; $points[1]=$hash_data[$i-1][$j]; $points[2]=$hash_data[$i-1][$j+1]; $points[3]=$hash_data[$i][$j-1]; $points[4]=$hash_data[$i][$j]; $points[5]=$hash_data[$i][$j+1]; $points[6]=$hash_data[$i+1][$j-1]; $points[7]=$hash_data[$i+1][$j]; $points[8]=$hash_data[$i+1][$j+1];
$count=0;
foreach ($points as $v){ if($v){ $count++; } }
return $count<4; }
|
去除噪点之后
去除干扰线
通过去除噪点之后的图像,我们可以发现,大部分的干扰线,其实已经被去除掉了,所以我们可以将,去除噪点的方法当做一个滤镜,多过滤几次,干扰线基本上可以去除完毕(这里测试去除三次的效果是最好的)
分割验证码
这个验证码的每个数字其实都是等分的,所以我们可以采用等分的方法去分割它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public function splitImage($n){ $data=[]; $a=$this->_image_w/self::CHAR_NUM; for($i=$n*$a;$i<($n+1)*$a;$i++){ $column=array_column($this->_hash_data,$i); if(implode("",$column)!=0){ $data[]=$column; } }
$out_img_w=count($data)+4; $out_img_h=count($data[0])+4;
$out_img = imagecreatetruecolor($out_img_w,$out_img_h); $bg=imagecolorallocate($out_img, 255, 255, 255); imagefill($out_img, 0,0, $bg);
foreach ($data as $k=>$v){ foreach ($v as $key=> $val){ $color=255; if($val) $color=0; $c = imagecolorallocate($out_img, $color, $color, $color); imagesetpixel($out_img, $k+2,$key+2, $c); } }
return $out_img; }
|
保存&对比识别
最后将分割好的图片二值化,通过一个数组保存下来,作为一个标准,然后后之后需要验证的验证码,通过和之前的数组求交集,比较最相近的数字得出结果即可
更多
其实到这一步之后已经能够识别一些验证码了,对于第二种验证码已经可以达到很好的识别效果,但是对于第一种验证码的识别率很低很低,大概只有5%-15%
,基本不能投入使用,下一篇文章针对这种验证码,怎么更好的识别,主要是标准化
和特征库的建立
关注我获取更新
猜你喜欢