首先推荐几篇有关验证码识别的文章,觉得不错
php实现验证码的识别(初级篇)
关于bp神经网格识别验证码
一、思路
碰见一个验证码,如果我们想要识别它,我们需要的是做什么呢? 我们先观察几个验证码............
我们用人眼去观察,会很显然的认出验证码所包含的字符,那么人眼的“识别机理”是什么呢? 大概是验证码图片字符的背景的颜色区别吧,试想,如果字符和背景没有颜色区别,我们能够判断验证码吗,很显然不能。
所以,我们就可以从人出发。
先从图片的颜色着手,即图片的RGB信息。
RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
定义函数取得RGB信息
1 //代码本来是一个类,现在拆开来写的,有可能有不严谨的地方,大家可以看得懂就好了 2 3 /* 4 *取得图片路径和图片尺寸 5 */ 6 $this->ImagePath = $Image; 7 $this->ImageSize = getimagesize($Image); 8 9 /*10 *获取图像标识符,保存到ImageInfo,只能处理bmp,png,jpg图片11 *ImageCreateFromBmp是我自己定义的函数,最后会给出12 */13 function getInfo(){14 $filetype = substr($this->ImagePath,-3);15 if($filetype == 'bmp'){16 $this->ImageInfo = $this->ImageCreateFromBmp($this->ImagePath);17 }elseif($filetype == 'jpg'){18 $this->ImageInfo = imagecreatefromjpeg($this->ImagePath); 19 }elseif($filetype == 'png'){20 $this->ImageInfo = imagecreatefrompng($this->ImagePath); 21 }22 }23 24 /*获取图片RGB信息*/25 function getRgb(){26 $rgbArray = array();27 $res = $this->ImageInfo;28 $size = $this->ImageSize;29 $wid = $size['0'];30 $hid = $size['1'];31 for($i=0; $i < $hid; ++$i){32 for($j=0; $j < $wid; ++$j){33 $rgb = imagecolorat($res,$j,$i);34 $rgbArray[$i][$j] = imagecolorsforindex($res, $rgb);35 }36 }37 return $rgbArray;38 }
二、二值化
因为人眼可以分别出验证码,所以验证码的RGB信息就会有一定的特点,这时候需要我们观察一下,直接打印RGB数组是不好观察的…………,好多数啊
在php实现验证码的识别(初级篇)中,作者的判断依据是
无论验证数字颜色如何变化,该数字的 RGB 值总有一个值小于 125
我们先获取他的灰度,再判断
1 /* 2 *获取灰度信息 3 */ 4 function getGray(){ 5 $grayArray = array(); 6 $size = $this->ImageSize; 7 $rgbarray = $this->getRgb(); 8 $wid = $size['0']; 9 $hid = $size['1'];10 for($i=0; $i < $hid; ++$i){11 for($j=0; $j < $wid; ++$j){12 $grayArray[$i][$j] = (299*$rgbarray[$i][$j]['red']+587*$rgbarray[$i][$j]['green']+144*$rgbarray[$i][$j]['blue'])/1000;13 }14 }15 return $grayArray;16 }
然后我们根据灰度信息,打印图片,注意不是打印灰度信息
1 /*根据灰度信息打印图片*/ 2 function PRintByGray(){ 3 $size = $this->ImageSize; 4 $grayArray = $this->getGray(); 5 $wid = $size['0']; 6 $hid = $size['1']; 7 for($k=0;$k<25;$k++){ 8 echo $k."\n"; 9 for($i=0; $i < $hid; ++$i){10 for($j=0; $j < $wid; ++$j){11 if($grayArray[$i][$j] < $k*10){12 echo '■';13 }else{14 echo '□';15 }16 }17 echo "|\n";18 }19 echo "---------------------------------------------------------------------------------------------------------------\n";20 }21 22 }
注意到,从$grayArray[$i][$j] < 80就会有显然的输出,我们观察选择了一个恰当的阈值,得到一个0101的数组,我们已经将我们的图片转化为了字符(1)和背景(0),即二值化。
1 /* 2 *根据自定义的规则,获取二值化二维数组 3 *@return 图片高*宽的二值数组(0,1) 4 */ 5 function getErzhi(){ 6 $erzhiArray = array(); 7 $size = $this->ImageSize; 8 $grayArray = $this->getGray(); 9 $wid = $size['0'];10 $hid = $size['1'];11 for($i=0; $i < $hid; ++$i){12 for($j=0; $j <$wid; ++$j){13 if( $grayArray[$i][$j] < 90 ){14 $erzhiArray[$i][$j]=1;15 }else{16 $erzhiArray[$i][$j]=0;17 }18 }19 }20 return $erzhiArray;21 }
三、去除噪点
但是我们发现有一些小点影响了我们的判断
我们可以注意到这些事干扰噪点,但是如果我们是机器的话,我们如何判断这些点是否是字符呢?
所以接下来,我们需要将这些字符去除。
我们判断,如果一个黑点的上下左右的八个点全部是白,我们就认为它是噪点,并予以清除,赋为白
1 /* 2 *二值化图片降噪 3 *@param $erzhiArray二值化数组 4 */ 5 function reduceZao($erzhiArray){ 6 $data = $erzhiArray; 7 $gao = count($erzhiArray); 8 $chang = count($erzhiArray['0']); 9 10 $jiangzaoErzhiArray = array();11 12 for($i=0;$i<$gao;$i++){13 for($j=0;$j<$chang;$j++){14 $num = 0; 15 if($data[$i][$j] == 1) 16 {17 // 上 18 if(isset($data[$i-1][$j])){ 19 $num = $num + $data[$i-1][$j]; 20 } 21 // 下 22 if(isset($data[$i+1][$j])){ 23 $num = $num + $data[$i+1][$j]; 24 } 25 // 左 26 if(isset($data[$i][$j-1])){ 27 $num = $num + $data[$i][$j-1]; 28 } 29 // 右 30 if(isset($data[$i][$j+1])){ 31 $num = $num + $data[$i][$j+1]; 32 } 33 // 上左 34 if(isset($data[$i-1][$j-1])){ 35 $num = $num + $data[$i-1][$j-1]; 36 } 37 // 上右 38 if(isset($data[$i-1][$j+1])){ 39 $num = $num + $data[$i-1][$j+1]; 40 } 41 // 下左 42 if(isset($data[$i+1][$j-1])){ 43 $num = $num + $data[$i+1][$j-1]; 44 } 45 // 下右 46 if(isset($data[$i+1][$j+1])){ 47 $num = $num + $data[$i+1][$j+1]; 48 } 49 }50 51 if($num < 1){ 52 $jiangzaoErzhiArray[$i][$j] = 0; 53 }else{54 $jiangzaoErzhiArray[$i][$j] = 1; 55 }56 }57 }58 return $jiangzaoErzhiArray; 59 60 }
我们发现噪点消失了。
四、分割
这个时候,我们就需要对单一数字字母进行操作了,我们先将数字提取出来。
有些验证码字符相连,特别难!!!
我们分别从左到右,从右到左,从上到下,从下到上,进行扫描,去除白点,找到边框。
1 /* 2 *归一化处理,针对一个个的数字,即去除字符周围的白点 3 *@param $singleArray 二值化数组 4 */ 5 function getJinsuo($singleArray){ 6 $dianCount = 0; 7 $rearr = array(); 8 9 $gao = count($singleArray);10 $kuan = count($singleArray['0']);11 12 $dianCount = 0;13 $shangKuang = 0;14 $xiaKuang = 0;15 $zuoKuang = 0;16 $youKuang = 0;17 //从上到下扫描18 for($i=0; $i < $gao; ++$i){19 for($j=0; $j < $kuan; ++$j){20 if( $singleArray[$i][$j] == 1){21 $dianCount++;22 }23 }24 if($dianCount>1){25 $shangKuang = $i;26 $dianCount = 0;27 break;28 }29 }30 //从下到上扫描31 for($i=$gao-1; $i > -1; $i--){32 for($j=0; $j < $kuan; ++$j){33 if( $singleArray[$i][$j] == 1){34 $dianCount++;35 }36 }37 if($dianCount>1){38 $xiaKuang = $i;39 $dianCount = 0;40 break;41 }42 }43 //从左到右扫描44 for($i=0; $i < $kuan; ++$i){45 for($j=0; $j < $gao; ++$j){46 if( $singleArray[$j][$i] == 1){47 $dianCount++;48 }49 }50 if($dianCount>1){51 $zuoKuang = $i;52 $dianCount = 0;53 break;54 }55 }56 //从右到左扫描57 for($i=$kuan-1; $i > -1; --$i){58 for($j=0; $j < $gao; ++$j){59 if( $singleArray[$j][$i