php识别验证码(二)
在 php 识别验证码(一)当中说了基本的识别验证码的方法,及其关键的代码,但是最后我们留下了一个问题,就是对于有旋转的验证码的识别率及其低下的问题,下面来解决这个问题。
标准化
解决类似于下面图片这种有旋转字符的验证码,我们首先要给出一种标准,让所有的分割后的图片都按照一个标准来摆放,最后对比的时候才能够统一
如何标准化?
我们对一个字符进行旋转,然后计算这个字符的宽度,当宽度最小时,我们认为它是一个标准字符,这样我们就能够得到比较好的结果了(通过观察这种验证码,发现一般只要左右旋转三十度即可)
然后仅仅进行旋转还不够,为了能够和模板也就是特征值库进行对比,我们还需要,统一字符长度。也就是二值化之后的字符串长度,这时就需要画一张长宽固定的图片,把我们原来的图片按照比例拉伸压缩复制过去
/**
* 图像标准化,将旋转的图像标准化
* @author mohuishou<1@lailin.xyz>
* @param $img
* @return resource 标准的图像资源句柄
*/
public function imageStandard($img){
$min_w=999;
$oimg=$img;
$c=imagecolorallocate($img, 255, 255, 255);
for($i=-30;$i<30;$i++){
$simg=imagerotate($img,$i,$c);
// //计算字符宽度
$simg_hash_data=$this->getWidth($simg);
$w=count($simg_hash_data);
if($w<$min_w){
$oimg_hash_data=$simg_hash_data;
$min_w=$w;
}
}
$out_img_w=count($oimg_hash_data);
$out_img_h=count($oimg_hash_data[0]);
$out_img = imagecreatetruecolor($out_img_w,$out_img_h);//创建一幅真彩色图像
$bg=imagecolorallocate($out_img, 255, 255, 255);//背景色画为白色
imagefill($out_img, 0,0, $bg);
//一列一列的进行画图
foreach ($oimg_hash_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,$key, $c);
}
}
// imagepng($out_img,'./0.png');
$hash_img = imagecreatetruecolor(self::HASH_W, self::HASH_H);
imagecopyresized($hash_img, $out_img, 0, 0, 0, 0, self::HASH_W,self::HASH_H,$out_img_w,$out_img_h);
return $hash_img;
}
/**
* 获取图像的宽度
* @author mohuishou<1@lailin.xyz>
* @param $img 图像资源句柄
* @return int
*/
public function getWidth($img){
//根据资源句柄获取整个图像的高与宽
$img_w=imagesx($img);
$img_h=imagesy($img);
//图像二值化
for($i = 0; $i <$img_h; $i++) {
for ($j = 0; $j <$img_w; $j++) {
$rgb = imagecolorat($img,$j,$i);
if($rgb==0){
$data[$i][$j]=1;
}else{
$data[$i][$j]=0;
}
}
}
//去掉零行
$data=$this->removeZero($data);
//按列取图像获取宽度
for($i=0;$i<$img_w;$i++){
$column=array_column($data,$i);
if(implode("",$column)!=0){
$data1[]=$column;
}
}
//返回
return $data1;
}
看上方的代码发现会有一个去除零行的操作,也就是去除空白行,这一步操作的目的主要是为了出掉上下左右无用的空白部分,在我们进行模板对比的时候更加的精确
建立特征值库
之前提到了很多和模板或者是特征值库进行比较,那我们如何来建立这个库呢?下面说一下最简单的两种方法
- 第一种,就是把你需要的数字可能还有 26 个字母,都先识别一遍然后存到一个文件当中,使用的时候包含这个文件进行对比就行了
- 第二种,也差不多,可以写一个 study 文件,让上面的步骤稍微智能一点,直接输入之后,自动储存到数据库中
下面是我写的一个针对联通优选在沃的一个小的脚本,需要注意的是对于这种通过链接来识别的,一定要先将链接的图片保存下来,因为链接没获取一次图片就会改变一次
<?php
/**
* Created by mohuishou<1@lailin.xyz>.
* User: mohuishou<1@lailin.xyz>
* Date: 2016/5/1 0001
* Time: 20:44
*/
require_once 'Image.class.php';
require_once 'DB.class.php';
$db=new \ImageOCR\DB();
if(isset($_POST['send'])&&$_POST['send']=="send"){
$image=new \ImageOCR\Image("./img/inImgTemp.png");
$code=$_POST['code'];
$code_arr=str_split($code);
for($i=0;$i<$image::CHAR_NUM;$i++){
$hash_img_data=implode("",$image->splitImage($i));
$db->add($code_arr[$i],$hash_img_data);
}
echo "<script>location.href='./study.php';</script>";
}else{
$image=new \ImageOCR\Image("http://www.169ol.com/Mall/Code/getCode&1462104790492");
imagepng($image->_in_img,"./img/inImgTemp.png");
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Study</title>
</head>
<body>
<form action="" method="post">
<img src="img/inImgTemp.png">
<input type="text" name="code">
<input name="send" type="submit" value="send" />
</form>
</body>
</html>
开源代码
觉得不错,给个 star 呗
感谢
结束语
目前的识别效率可以达到 50%以上,基本可以投入正常使用,但是还有很大的进步空间,正在做下一步的优化,针对识别结果对特征库进行自动优化,自动去除使用率较低的特征值,自动保存,识别成功的特征值等等
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!