首先推荐几篇有关验证码识别的文章,觉得不错

php实现验证码的识别(初级篇)

关于bp神经网格识别验证码

一、思路

碰见一个验证码,如果我们想要识别它,我们需要的是做什么呢?
我们先观察几个验证码............


我们用人眼去观察,会很显然的认出验证码所包含的字符,那么人眼的“识别机理”是什么呢?

大概是验证码图片字符的背景的颜色区别吧,试想,如果字符和背景没有颜色区别,我们能够判断验证码吗,很显然不能。

所以,我们就可以从人出发。

先从图片的颜色着手,即图片的RGB信息。

RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。

定义函数取得RGB信息

 //代码本来是一个类,现在拆开来写的,有可能有不严谨的地方,大家可以看得懂就好了

 /*
*取得图片路径和图片尺寸
*/
$this->ImagePath = $Image;
$this->ImageSize = getimagesize($Image); /*
*获取图像标识符,保存到ImageInfo,只能处理bmp,png,jpg图片
*ImageCreateFromBmp是我自己定义的函数,最后会给出
*/
function getInfo(){
$filetype = substr($this->ImagePath,-3);
if($filetype == 'bmp'){
$this->ImageInfo = $this->ImageCreateFromBmp($this->ImagePath);
}elseif($filetype == 'jpg'){
$this->ImageInfo = imagecreatefromjpeg($this->ImagePath);
}elseif($filetype == 'png'){
$this->ImageInfo = imagecreatefrompng($this->ImagePath);
}
} /*获取图片RGB信息*/
function getRgb(){
$rgbArray = array();
$res = $this->ImageInfo;
$size = $this->ImageSize;
$wid = $size['0'];
$hid = $size['1'];
for($i=0; $i < $hid; ++$i){
for($j=0; $j < $wid; ++$j){
$rgb = imagecolorat($res,$j,$i);
$rgbArray[$i][$j] = imagecolorsforindex($res, $rgb);
}
}
return $rgbArray;
}

二、二值化

因为人眼可以分别出验证码,所以验证码的RGB信息就会有一定的特点,这时候需要我们观察一下,直接打印RGB数组是不好观察的…………,好多数啊

php实现验证码的识别(初级篇)中,作者的判断依据是

无论验证数字颜色如何变化,该数字的 RGB 值总有一个值小于 125

我们先获取他的灰度,再判断

 /*
*获取灰度信息
*/
function getGray(){
$grayArray = array();
$size = $this->ImageSize;
$rgbarray = $this->getRgb();
$wid = $size['0'];
$hid = $size['1'];
for($i=0; $i < $hid; ++$i){
for($j=0; $j < $wid; ++$j){
$grayArray[$i][$j] = (299*$rgbarray[$i][$j]['red']+587*$rgbarray[$i][$j]['green']+144*$rgbarray[$i][$j]['blue'])/1000;
}
}
return $grayArray;
}

然后我们根据灰度信息,打印图片,注意不是打印灰度信息

 /*根据灰度信息打印图片*/
function printByGray(){
$size = $this->ImageSize;
$grayArray = $this->getGray();
$wid = $size['0'];
$hid = $size['1'];
for($k=0;$k<25;$k++){
echo $k."\n";
for($i=0; $i < $hid; ++$i){
for($j=0; $j < $wid; ++$j){
if($grayArray[$i][$j] < $k*10){
echo '■';
}else{
echo '□';
}
}
echo "|\n";
}
echo "---------------------------------------------------------------------------------------------------------------\n";
} }

注意到,从$grayArray[$i][$j] < 80就会有显然的输出,我们观察选择了一个恰当的阈值,得到一个0101的数组,我们已经将我们的图片转化为了字符(1)和背景(0),即二值化。

 /*
*根据自定义的规则,获取二值化二维数组
*@return 图片高*宽的二值数组(0,1)
*/
function getErzhi(){
$erzhiArray = array();
$size = $this->ImageSize;
$grayArray = $this->getGray();
$wid = $size['0'];
$hid = $size['1'];
for($i=0; $i < $hid; ++$i){
for($j=0; $j <$wid; ++$j){
if( $grayArray[$i][$j] < 90 ){
$erzhiArray[$i][$j]=1;
}else{
$erzhiArray[$i][$j]=0;
}
}
}
return $erzhiArray;
}

三、去除噪点

但是我们发现有一些小点影响了我们的判断

我们可以注意到这些事干扰噪点,但是如果我们是机器的话,我们如何判断这些点是否是字符呢?

所以接下来,我们需要将这些字符去除。

我们判断,如果一个黑点的上下左右的八个点全部是白,我们就认为它是噪点,并予以清除,赋为白

 /*
*二值化图片降噪
*@param $erzhiArray二值化数组
*/
function reduceZao($erzhiArray){
$data = $erzhiArray;
$gao = count($erzhiArray);
$chang = count($erzhiArray['0']); $jiangzaoErzhiArray = array(); for($i=0;$i<$gao;$i++){
for($j=0;$j<$chang;$j++){
$num = 0;
if($data[$i][$j] == 1)
{
// 上
if(isset($data[$i-1][$j])){
$num = $num + $data[$i-1][$j];
}
// 下
if(isset($data[$i+1][$j])){
$num = $num + $data[$i+1][$j];
}
// 左
if(isset($data[$i][$j-1])){
$num = $num + $data[$i][$j-1];
}
// 右
if(isset($data[$i][$j+1])){
$num = $num + $data[$i][$j+1];
}
// 上左
if(isset($data[$i-1][$j-1])){
$num = $num + $data[$i-1][$j-1];
}
// 上右
if(isset($data[$i-1][$j+1])){
$num = $num + $data[$i-1][$j+1];
}
// 下左
if(isset($data[$i+1][$j-1])){
$num = $num + $data[$i+1][$j-1];
}
// 下右
if(isset($data[$i+1][$j+1])){
$num = $num + $data[$i+1][$j+1];
}
} if($num < 1){
$jiangzaoErzhiArray[$i][$j] = 0;
}else{
$jiangzaoErzhiArray[$i][$j] = 1;
}
}
}
return $jiangzaoErzhiArray; }

我们发现噪点消失了。

四、分割

这个时候,我们就需要对单一数字字母进行操作了,我们先将数字提取出来。

有些验证码字符相连,特别难!!!

我们分别从左到右,从右到左,从上到下,从下到上,进行扫描,去除白点,找到边框。

 /*
*归一化处理,针对一个个的数字,即去除字符周围的白点
*@param $singleArray 二值化数组
*/
function getJinsuo($singleArray){
$dianCount = 0;
$rearr = array(); $gao = count($singleArray);
$kuan = count($singleArray['0']); $dianCount = 0;
$shangKuang = 0;
$xiaKuang = 0;
$zuoKuang = 0;
$youKuang = 0;
//从上到下扫描
for($i=0; $i < $gao; ++$i){
for($j=0; $j < $kuan; ++$j){
if( $singleArray[$i][$j] == 1){
$dianCount++;
}
}
if($dianCount>1){
$shangKuang = $i;
$dianCount = 0;
break;
}
}
//从下到上扫描
for($i=$gao-1; $i > -1; $i--){
for($j=0; $j < $kuan; ++$j){
if( $singleArray[$i][$j] == 1){
$dianCount++;
}
}
if($dianCount>1){
$xiaKuang = $i;
$dianCount = 0;
break;
}
}
//从左到右扫描
for($i=0; $i < $kuan; ++$i){
for($j=0; $j < $gao; ++$j){
if( $singleArray[$j][$i] == 1){
$dianCount++;
}
}
if($dianCount>1){
$zuoKuang = $i;
$dianCount = 0;
break;
}
}
//从右到左扫描
for($i=$kuan-1; $i > -1; --$i){
for($j=0; $j < $gao; ++$j){
if( $singleArray[$j][$i] == 1){
$dianCount++;
}
}
if($dianCount>1){
$youKuang = $i;
$dianCount = 0;
break;
}
}
for($i=0;$i<$xiaKuang-$shangKuang+1;$i++){
for($j=0;$j<$youKuang-$zuoKuang+1;$j++){
$rearr[$i][$j] = $singleArray[$shangKuang+$i][$zuoKuang+$j];
}
}
return $rearr;
}

然后从左到右扫描,找到字符的分割

返回三维数组,每一维就是一个字符。

 /*
*切割成三维数组,每个小数字在一个数组里面
*只适用四个数字一起的数组
*@param 经过归一化处理的二值化数组
*/
function cutSmall($erzhiArray){
$doubleArray = array();
$jieZouyou = array(); $gao = count($erzhiArray);
$kuan = count($erzhiArray['0']); $jie = 0;
$s = 0;
$jieZouyou[$s] = 0;
$s++;
//从左到右扫描 for($i=0; $i < $kuan;){
for($j=0; $j < $gao; ++$j){
$jie = $jie + $erzhiArray[$j][$i];
}
//如果有一列全部是白,设置$jieZouyou,并且跳过中间空白部分
if($jie == 0){
$jieZouyou[$s] = $i+1;
do{
$n = ++$i;
$qian = 0;
$hou = 0;
for($m=0; $m < $gao; ++$m){
$qian = $qian + $erzhiArray[$m][$n];
$hou = $hou + $erzhiArray[$m][$n+1];
}
$jieZouyou[$s+1] = $n+1;
}
//当有两列同时全部为白,说明有间隙,循环,知道间隙没有了
while($qian == 0 && $hou == 0);
$s+=2;
$i++;
}else{
$i++;
} $jie = 0;
}
$jieZouyou[] = $kuan;
//极端节点数量,(应该是字符个数)*2
$jieZouyouCount = count($jieZouyou); for($k=0;$k<$jieZouyouCount/2;$k++){
for($i=0; $i < $gao; $i++){
for($j=0; $j < $jieZouyou[$k*2+1]-$jieZouyou[$k*2]-1; ++$j){
$doubleArray[$k][$i][$j] = $erzhiArray[$i][$j+$jieZouyou[$k*2]];
}
} }
return $doubleArray;
}

五、倾斜调整

我们发现第三个9有一点倾斜,

我们需要将倾斜的图片“正”过来

人怎么处理的呢,先眼睛观察“倾斜了多少度”,然后把图片扭过来多少度,并且观察->负反馈->大脑传递扭转角度时刻在发生,最后图片就“正”过来了。

人是怎么观察“倾斜”的,以上面的“2”做例子,可能是右上方(左下方)的黑色比左上方(右下方)的多?

我们建立X轴正向向下,Y轴向右的直角坐标系

我们计算每一层的黑点的分布中点坐标,得到一系列离散点,计算这些点所在的直线(线性回归方程的计算,),公式y = b*x+a,

竟然有用到这个公式的一天!!!

大概就是一条倾斜的直线了,通过直线计算直线倾斜角度,然后转这么多的角度,图片应该就“正”了吧。

其中a,b的计算如下

 /*
*定义求线性回归A和B的函数
*@param $zuobiaoArray坐标的三维数组
*/
function getHuigui($zuobiaoArray){
$y8 = 0;
$x8 = 0;
$x2 = 0;
$xy = 0;
$geshu = count($zuobiaoArray);
for($i=0;$i<$geshu;$i++){
$y8 = $y8+$zuobiaoArray[$i]['y'];
$x8 = $x8+$zuobiaoArray[$i]['x'];
$xy = $xy+$zuobiaoArray[$i]['y']*$zuobiaoArray[$i]['x'];
$x2 = $x2 + $zuobiaoArray[$i]['x']*$zuobiaoArray[$i]['x'];;
}
$y8 = $y8/$geshu;
$x8 = $x8/$geshu; $b = ($xy-$geshu*$y8*$x8)/($x2-$geshu*$x8*$x8);
$a = $y8-$b*$x8;
$re['a'] = $a;
$re['b'] = $b;
return $re;
//y = b * x + a
}

怎么转角?

1、可以直接对图片进行操作,但是发现有比较大的失真,就没有继续了。
2、或者,对黑点白点的坐标进行操作……

这就是三角函数了,好长时间不碰三角函数,都差点忘记了。

定义函数

 /*
*定义转化坐标的函数
*@param $x x坐标即$i
*@param $y y坐标,即j
*@param $b 线性回归方程的b参数
*/
function getNewZuobiao($x,$y,$b){
if($x == 0){
if($y>0){
$xianJiao = M_PI/2;
}elseif($y<0){
$xianJiao = -M_PI/2;
}else{
$p['x'] = 0;
$p['y'] = 0;
return $p;
}
}else{
$xianJiao = atan($y/$x);
}
$jiao =$xianJiao-atan($b);
$chang = sqrt($x*$x+$y*$y);
$p['x'] = $chang*cos($jiao);
$p['y'] = $chang*sin($jiao);
return $p;
}

转角吧

 /*
*对【单个】数字的二值化二维数组进行倾斜调整
*@param $singleArray 高*宽的二值数组(0,1)
*/
function singleSlopeAdjust($singleErzhiArray){
$slopeArray = array();
$gao = count($singleErzhiArray);
$chang = count($singleErzhiArray['0']); //初始化$slopeArray
for($i=0;$i<$gao*4;$i++){
for($j=0;$j<$chang*4;$j++){
$slopeArray[$i][$j] = 0;
}
} //初始化中心坐标(是数组的下标)
$centerXfoalt = ($gao-1)/2;
$centerYfoalt = ($chang-1)/2;
$centerX = ceil($centerXfoalt);
$centerY = ceil($centerYfoalt); //初始化图片倾斜诶角度
/*斜率的计算!!!!!,回归方程*/
//从上到下扫描,计算中点,求得一串坐标($i,$ava)
for($i=0;$i<$gao;$i++){
$Num = 0;
$Amount = 0;
for($j=0;$j<$chang;$j++){
if($singleErzhiArray[$i][$j] == 1){
$Num = $Num+$j;
$Amount++;
}
}
if($Amount == 0){
$Ava[$i] = $chang/2;
}else{
$Ava[$i] = $Num/$Amount;
}
} //计算线性回归方程的b与a
$zuo = array();
for($j=0;$j<count($Ava);$j++){
$zuo[$j]['x'] = $j;
$zuo[$j]['y'] = $Ava[$j];
}
$res = $this->getHuigui($zuo);
$zuoB = $res['b']; for($i=0;$i<$gao;$i++){
for($j=0;$j<$chang;$j++){
if($singleErzhiArray[$i][$j] == 1){
$splodeZuobiao = $this->getNewZuobiao($i,$j,$zuoB);
$splodeX = $splodeZuobiao['x'];
$splodeY = $splodeZuobiao['y'];
$slopeArray[$splodeX+$gao][$splodeY+$chang] = 1;
}
}
} //将预处理的数组空白清理
$slopeArray = $this->getJinsuo($slopeArray);
return $slopeArray;
}

看到正了一些

六、统一大小

上文中因为各种操作,每个字符大小不一,我们需要统一大小

七、特征值的建立

有很多方法

1、逐像素特征提取法
这是一种最简单的特征提取方法。它可以对图像进行逐行逐列的扫描,当遇到黑色像素时取其特征值为1,遇到白色像素时取其特征值为0,这样当扫描结束后就获得一个维数与图像中的像素点的个数相同的特征向量矩阵。

这种方法提取的信息量最大,但是它的缺点也很明显,就是适应性不强。

2、骨架特征提取法

两幅图像由于它们的线条的粗细不同,使得两幅图像差别很大,但是将它们的线条进行细化后,统一到相同的宽度,如一个像素宽时,这是两幅图像的差距就不那么明显。利用图形的骨架作为特征来进行数码识别,就使得识别有了一定的适应性。一般使用细化的方法来提取骨架,细化的算法有很多,如Hilditch算法、Rosenfeld算法等。对经过细化的图像利用EveryPixel函数进行处理就可以得到细化后图像的特征向量矩阵。骨架特征提取的方法对于线条粗细不同的数码有一定的适应性,但是图像一旦出现偏移就难以识别。

3、微结构法

微结构法将图像分为几个小块,统计每个小块的像素分布。本文提取出汉字的39个特征,存储在数组f[0]~f[38]中。具体算法可分为四步:

步骤一:把字符平均分成9份,如图4.1所示,给每一份编号如图4.2,统计每一份内黑色像素的个数,存储在数字tz[0]~tz[9]中,统计在行方向和列方向上每一份内的黑色像素个数和与之相邻的一份内黑色像素个数的比值作为一个特征,例如:行方向上提取特征f[0]=tz[1]/
tz[0],f[1]=tz[2]/ tz[1],f[2]=tz[0]/ tz[2],…,f[8]=tz[6]/ tz[8];列方向上f[9]=tz[3]/
tz[0],f[10]=tz[6]/ tz[3],f[11]=tz[0]/ tz[6],…,f[17]=tz[2]/ tz[8],共18个特征。

步骤二:把字符横向分成三份,如图4.3所示,统计每一份内的黑色像素个数,每一份内的黑色像素个数与前一份内黑色像素个数的比值作为一个特征,f[18]=tz[10]/
tz[9],f[19]=tz[11]/ tz[10],f[20]=tz[9]/
tz[11];把字符纵向分成三份,如图4.4所示,统计每一份内的黑色像素个数,每一份内的黑色像素个数与前一份内黑色像素个数的比值作为一个特征,f[21]=tz[13]/
tz[12],f[22]=tz[14]/ tz[13],f[23]=tz[12]/ tz[14];共六个特征。

步骤三:如图4.5,在竖直方向上找出三列,统计在该列中跳变点的个数,即相邻点像素值从0变到255的次数,共三个特征,记为f[24],f[25],f[26];在水平方向上找出三行列,统计在该行中跳变点的个数,即相邻点象素值从0变到255的次数,共三个特征,记为f[27],f[28],f[29]。

图4.5

步骤四:把每一份内黑色象素的个数tz[0]~tz[9],作为9个特征,记为:f[30]~f[38]。

这样得到汉字的共39个特征,根据这些特征就可以区分每个车牌汉字,进行识别。

我们使用最简单的逐像素特征提取法。

多多增加数据库,识别率会增加的

八、识别验证码

对于一个新的验证码,进行上文操作,然后对比数据库就可以了

 /*
*进行匹配
*@param $Image 图片路径
*/
public function run($Image){
$data = array('','','','');
$result="";
$bilu = '';
$maxarr = ''; //提取特征
$this->prepare($Image);
$yuanshi = $this->getErzhi();
$yijijiangzao = $this->reduceZao($yuanshi);
$small = $this->cutSmall($yijijiangzao);
for($k=0;$k<4;$k++){
$tianchong = $this->tianChong($small[$k]);
$tiaozhenjiaodu = $this->singleSlopeAdjust($tianchong);
$tongyidaxiao = $this->tongyiDaxiao($tiaozhenjiaodu);
for($i=0;$i<20;$i++){
for($j=0;$j<20;$j++){
$data[$k] .= $tongyidaxiao[$i][$j];
}
}
} // 进行关键字匹配
foreach($data as $numKey => $numString)
{ $max = 0;
$num = 0;
foreach($this->Keys as $value => $key)
{
similar_text($value, $numString,$percent);
if($percent > $max)
{
$max = $percent;
$num = $key;
$zim = $value;
}
if($max>95){
break;
}
}
$result .=$num;
$maxarr[] = $max;
}
// 查找最佳匹配数字
$re = $maxarr;
$re[] = $result;
return $re;
//return $result.'|max|一:'.$maxarr['0'].'|二:'.$maxarr['1'].'|三:'.$maxarr['2'].'|四:'.$maxarr['3'];
}

试试:

PHP之验证码识别的更多相关文章

  1. 字符型图片验证码识别完整过程及Python实现

    字符型图片验证码识别完整过程及Python实现 1   摘要 验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的 防火墙 功能,但是随时OCR技术的发展,验证码暴露出来的安全问题也越 ...

  2. 验证码识别<1>

    1. 引子 前两天访问学校自助服务器()缴纳网费,登录时发现这系统的验证码也太过“清晰”了,突然脑袋里就蹦出一个想法:如果能够自动识别验证码,然后采用暴力破解的方式,那么密码不是可以轻易被破解吗? p ...

  3. 简单的验证码识别(opecv)

    opencv版本: 3.0.0 处理验证码: 纯数字验证码 (颜色不同,有噪音,和带有较多的划痕) 测试时间 :  一天+一晚 效果: 比较挫,可能是由于测试的图片是在太小了的缘故. 原理:  验证码 ...

  4. 利用开源程序(ImageMagick+tesseract-ocr)实现图像验证码识别

    --------------------------------------------------低调的分割线-------------------------------------------- ...

  5. 基于LeNet网络的中文验证码识别

    基于LeNet网络的中文验证码识别 由于公司需要进行了中文验证码的图片识别开发,最近一段时间刚忙完上线,好不容易闲下来就继上篇<基于Windows10 x64+visual Studio2013 ...

  6. Java验证码识别解决方案

    建库,去重,切割,识别. package edu.fzu.ir.test; import java.awt.Color; import java.awt.image.BufferedImage; im ...

  7. 简单验证码识别(matlab)

    简单验证码识别(matlab) 验证码识别, matlab 昨天晚上一个朋友给我发了一些验证码的图片,希望能有一个自动识别的程序. 1474529971027.jpg 我看了看这些样本,发现都是很规则 ...

  8. Python验证码识别处理实例(转载)

    版权声明:本文为博主林炳文Evankaka原创文章,转载请注明出处http://blog.csdn.net/evankaka 一.准备工作与代码实例 1.PIL.pytesser.tesseract ...

  9. 验证码识别--type2

    验证码识别--type2 终于来到了彩色图像,一定有一些特点 这里的干扰项是色彩不是很鲜艳的.灰色的线条,还有单独的干扰点,根据这些特性进行去除 直接ostu的话,有的效果好,有的效果不好   本来是 ...

  10. 验证码识别--type5

    验证码识别--type5 每一种验证码都是由人设计出来.在设计过程中,可能由于多个方面的原因,造成了这样或那样的可以被利用的漏洞.验证码识别,首先需要解决的问题就是发现这些漏洞--然后利用漏洞解决问题 ...

随机推荐

  1. OpenStack Havana 部署在Ubuntu 12.04 Server 【OVS+GRE】(一)——控制节点的安装

      序:OpenStack Havana 部署在Ubuntu 12.04 Server [OVS+GRE] 控制节点: 1.准备Ubuntu 安装好Ubuntu12.04 server 64bits后 ...

  2. 一键清除cvs/svn 目录

    步骤一.编写注册表脚本      新建一个文本文件,把下面的代码COPY进去,保存为delSVNorCVS.reg(可直接从本文附件中下载) Windows Registry Editor Versi ...

  3. [置顶] 【VB.NET2010】在空间上显示提示气泡框的方法

    在VB6中,有ToolTip这个属性,可以设置鼠标悬浮在控件上的时候显示的图像. 而,在VB.NET中,实现这个功能需要使用一个类,ToolTip类. 代码如下(为了立即起效,建议将这些代码放在For ...

  4. BZOJ 1806 IOI2007 Miners 矿工配餐 动态规划

    题目大意:将一个123序列拆分为两个子序列.定义每一个数的贡献值为以这个数结尾的长度最大为3的子串中不同数的数量,求贡献值和的最大值 令f[i][a1][a2][b1][b2]为前i个数分成两组,第一 ...

  5. my.cnf已经存在,影响安装--mysql

    Found existing config file ./my.cnf on the system. Because this file might be in use, it was not rep ...

  6. mysql查看端口

    在你的my.ini(Windows)或my.cfg(Linux) 中就有啊. 或者如果已经连入MySQL可以直接 SQL code ? 1 2 3 4 5 6 7 8 9 mysql> show ...

  7. Dapper Use For Net

    Dapper.Net by example januari 6, 2012 When the team behind StackOverflow released the mini-ORM Dappe ...

  8. tcmalloc源码剖析的资料

    1. https://seanhn.wordpress.com/2011/04/14/exploit-necromancy-in-tcmalloc-reviving-the-4-to-n-byte-o ...

  9. Android(java)学习笔记201:网络图片浏览器的实现(ANR)

    1.我们在Android下,实现使用http协议进行网络通信,请求网络数据.这里是获取网络上的图片信息,让它可以显示在手机上: 但是我们这个手机连接网络是很费时间,如果我们在主线程(UI线程)中写这个 ...

  10. swift 关于delegate

    Cocoa 开发中接口-委托 (protocol-delegate) 模式是一种常用的设计模式,它贯穿于整个 Cocoa 框架中,为代码之间的关系清理和解耦合做出了不可磨灭的贡献. 在 ARC 中,对 ...