PHP图像识别技术原理与实现


Posted in PHP onOctober 27, 2016

其实图像识别技术与我们平时做的密码验证之类的没有什么区别,都是事先把要校验的数据入库,然后使用时将录入(识别)的数据与库中的数据做对比,只不过图像识别技术有一部分的容错性,而我们平时的密码验证是要100%匹配。

前几天,有朋友谈到做游戏点击抽奖,识别图片中的文字,当时立马想到的就是js控制或者flash做遮罩层,感觉这种办法是最方便快捷效果好,而且节省服务器资源,但是那边提的要求竟然是通过php识别图像中的文字。

赶巧那两天的新闻有:1、马云人脸识别支付;2、12306使用新的验证码,说什么现在国内的抢票软件都不能用了,发布不到一天就被破解。然后又很凑巧的那天早上看了一篇Java的图像识别技术文章。于是就琢磨着看一下PHP的图像识别技术。

其实所谓的图像识别,已经不是什么新技术了,起码我找到的资料都是很早之前的了。只不过我一直没涉及到这方面的工作,就一直没看过。

先说下这次实验的需求:有一张图片,里面三个位置分别有三个数字,要求取出相应位置的数字的值。(眼尖的同学可能会看出下面的代码是我拿的别人的,没错,的确是我直接copy别人并删减的,毕竟我对这些也是浅尝辄止,最后会贴出原作者的初始代码)

PHP图像识别技术原理与实现

class gjPhone
{

  protected $imgPath; // 图片路径
  protected $imgSize; // 图片大小
  protected $hecData; // 分离后数组
  protected $horData; // 横向整理的数据
  protected $verData; // 纵向整理的数据
  function __construct ($path)
  {
    $this->imgPath = $path;
  }

  public function getHec ()
  {
    $size = getimagesize($this->imgPath);
    $res = imagecreatefrompng($this->imgPath);
    for ($i = 0; $i < $size[1]; ++ $i) {
      for ($j = 0; $j < $size[0]; ++ $j) {
        $rgb = imagecolorat($res, $j, $i);
        $rgbarray = imagecolorsforindex($res, $rgb);
        if ($rgbarray['red'] < 125 || $rgbarray['green'] < 125 ||
             $rgbarray['blue'] < 125) {
          $data[$i][$j] = 1;
        } else {
          $data[$i][$j] = 0;
        }
      }
    }
    $this->imgSize = $size;
    $this->hecData = $data;
  }

  public function magHorData ()
  {
    $data = $this->hecData;
    $size = $this->imgSize;
    $z = 0;
    for ($i = 0; $i < $size[1]; ++ $i) {
      if (in_array('1', $data[$i])) {
        $z ++;
        for ($j = 0; $j < $size[0]; ++ $j) {
          if ($data[$i][$j] == '1') {
            $newdata[$z][$j] = 1;
          } else {
            $newdata[$z][$j] = 0;
          }
        }
      }
    }
    return $this->horData = $newdata;
  }

  public function showPhone ($ndatas)
  {
    error_reporting(0);
    $phone = null;
    $d = 0;
    foreach ($ndatas as $key => $val) {
      if (in_array(1, $val)) {
        foreach ($val as $k => $v) {
          $ndArr[$d] .= $v;
        }
      }
      if (! in_array(1, $val)) {
        $d ++;
      }
    }
    foreach ($ndArr as $key01 => $val01) {
      $phone .= $this->initData($val01);
    }
    return $phone;
  }

  /**
   * 初始数据
   */
  public function initData ($numStr)
  {
    $result = null;
    $data = array(
        '1' => '00000000111000000000000001110000000001001000100000000010100011000000000011000110000000000110000100000000010110011000000',
        '5' => '00000000001000000000000000010000000000100100100000000000101001110000000000100000110000000011000000100000001101000010000',
        '10' => '00000011100011100000000011001100100100100010010001000110000100100010001100001001000100011000010010001001001001100010100'
    );
    foreach ($data as $key => $val) {
      similar_text($numStr, $val, $pre);
      if ($pre > 95) { // 相似度95%以上
        $result = $key;
        break;
      }
    }
    return $result;
  }
}

$imgurl = 'jd.png';
list ($width, $heght, $type, $attr) = getimagesize($imgurl);
$new_w = 17;
$new_h = 11;
$thisimage = imagecreatetruecolor($new_w, $new_h); // $new_w, $new_h 为裁剪后的图片宽高
$background = imagecolorallocate($thisimage, 255, 255, 255);
imagefilledrectangle($thisimage, 0, 0, $new_w, $new_h, $background);
$oldimg = imagecreatefrompng($imgurl); // 载入原始图片
                    
// 首先定位要取图的位置(这里可以通过前端js或者其他手段定位,由于我这是测试,所以就ps定位并写死了)
$weizhi = array(
    '1' => 165,
    '5' => 308,
    '10' => 456
);

foreach ($weizhi as $wwzz) {
  $src_y = 108;
  imagecopy($thisimage, $oldimg, 0, 0, $wwzz, $src_y, $new_w, $new_h); // $src_y,$new_w为原图中裁剪区域的左上角坐标拷贝图像的一部分将src_im图像中坐标从src_x,src_y开始,宽度为src_w,高度为src_h的一部分拷贝到dst_im图像中坐标为dst_x和dst_y的位置上。
  $tem_png = 'tem_1.png';
  imagepng($thisimage, __DIR__ . '/' . $tem_png); // 通过定位从原图中copy出想要识别的位置并生成新的缓存图,用以后面的图像识别类使用。
  
  $gjPhone = new gjPhone($tem_png); // 实例化类
  $gjPhone->getHec(); // 进行图像像素分离
  $horData = $gjPhone->magHorData(); // 将分离出是数据转成01表示的图像、这里可以根据自己喜好定
  $phone = $gjPhone->showPhone($horData); // 将转换好的01表示的数据与库中的数据进行匹配,匹配度95以上就算成功,库这里由于是做测试就直接写了数组
  echo '| ' . $phone . ' | ';
}

如此看来,其实12306验证码被破解也算是有情可原了,也没必要那么的口诛笔伐了罢。只要不断的抓验证码图片并转成自己程序可读的数据存入库里,然后验证的时候进行匹配就可以了。那么阿里的人脸识别支付原理也算是理解了,只不过他们做的可能会很精细。

前端时间有看到阿里云的一个验证码形式,刚开始感觉可能会好点,现在看来,只要有心,其实也是可以破解的啊。

PHP图像识别技术原理与实现

好了,下面是原作代码。

/**
 * 电话号码识别.
 * @author by zsc for 2010.03.24
 */
class gjPhone
{

  protected $imgPath; // 图片路径
  protected $imgSize; // 图片大小
  protected $hecData; // 分离后数组
  protected $horData; // 横向整理的数据
  protected $verData; // 纵向整理的数据
  function __construct ($path)
  {
    $this->imgPath = $path;
  }

  /**
   * 颜色分离转换...
   *
   * @param unknown_type $path      
   * @return unknown
   */
  public function getHec ()
  {
    $size = getimagesize($this->imgPath);
    $res = imagecreatefrompng($this->imgPath);
    for ($i = 0; $i < $size[1]; ++ $i) {
      for ($j = 0; $j < $size[0]; ++ $j) {
        $rgb = imagecolorat($res, $j, $i);
        $rgbarray = imagecolorsforindex($res, $rgb);
        if ($rgbarray['red'] < 125 || $rgbarray['green'] < 125 ||
             $rgbarray['blue'] < 125) {
          $data[$i][$j] = 1;
        } else {
          $data[$i][$j] = 0;
        }
      }
    }
    $this->imgSize = $size;
    $this->hecData = $data;
  }

  /**
   * 颜色分离后的数据横向整理...
   *
   * @return unknown
   */
  public function magHorData ()
  {
    $data = $this->hecData;
    $size = $this->imgSize;
    $z = 0;
    for ($i = 0; $i < $size[1]; ++ $i) {
      if (in_array('1', $data[$i])) {
        $z ++;
        for ($j = 0; $j < $size[0]; ++ $j) {
          if ($data[$i][$j] == '1') {
            $newdata[$z][$j] = 1;
          } else {
            $newdata[$z][$j] = 0;
          }
        }
      }
    }
    return $this->horData = $newdata;
  }

  /**
   * 整理纵向数据...
   *
   * @return unknown
   */
  public function magVerData ($newdata)
  {
    for ($i = 0; $i < 132; ++ $i) {
      for ($j = 1; $j < 13; ++ $j) {
        $ndata[$i][$j] = $newdata[$j][$i];
      }
    }
    
    $sum = count($ndata);
    $c = 0;
    for ($a = 0; $a < $sum; $a ++) {
      $value = $ndata[$a];
      if (in_array(1, $value)) {
        $ndatas[$c] = $value;
        $c ++;
      } elseif (is_array($ndatas)) {
        $b = $c - 1;
        if (in_array(1, $ndatas[$b])) {
          $ndatas[$c] = $value;
          $c ++;
        }
      }
    }
    
    return $this->verData = $ndatas;
  }

  /**
   * 显示电话号码...
   *
   * @return unknown
   */
  public function showPhone ($ndatas)
  {
    $phone = null;
    $d = 0;
    foreach ($ndatas as $key => $val) {
      if (in_array(1, $val)) {
        foreach ($val as $k => $v) {
          $ndArr[$d] .= $v;
        }
      }
      if (! in_array(1, $val)) {
        $d ++;
      }
    }
    foreach ($ndArr as $key01 => $val01) {
      $phone .= $this->initData($val01);
    }
    return $phone;
  }

  /**
   * 分离显示...
   *
   * @param unknown_type $dataArr      
   */
  function drawWH ($dataArr)
  {
    if (is_array($dataArr)) {
      foreach ($dataArr as $key => $val) {
        foreach ($val as $k => $v) {
          if ($v == 0) {
            $c .= "<font color='#FFFFFF'>" . $v . "</font>";
          } else {
            $c .= $v;
          }
        }
        $c .= "<br/>";
      }
    }
    echo $c;
  }

  /**
   * 初始数据...
   *
   * @param unknown_type $numStr      
   * @return unknown
   */
  public function initData ($numStr)
  {
    $result = null;
    $data = array(
        0 => '000011111000001111111110011000000011110000000001110000000001110000000001110000000001011000000011011100000111000111111100000001110000',
        1 => '011000000000011000000000111111111111111111111111',
        2 => '001000000011011000000111110000001101110000011001110000011001110000110001111001100001011111100001000110000001',
        3 => '001000000010011000000011110000000001110000000001110000110001110000110001011001110011011111011111000110001100',
        4 => '000000001100000000111100000001111100000011101100000111001100001100001100011000001100111111111111111111111111000000001100000000000100',
        5 => '111111000001111111000001110001000001110001000001110001100001110001100001110000110011110000111111000000001100',
        6 => '000011111000001111111110011000110011110001100001110001100001110001100001110001100001010001110011010000111111000000001100',
        7 => '110000000000110000000111110000111111110001110000110111000000111100000000111000000000111000000000',
        8 => '000100011110011111111111110011100001110001100001110001100001110001100001110011100001011111111111000100011110',
        9 => '001111000000011111100001110000110001110000110001110000110001110000110001011000100001011111100111000111111110000001110000'
    );
    foreach ($data as $key => $val) {
      similar_text($numStr, $val, $pre);
      if ($pre > 95) { // 相似度95%以上
        $result = $key;
        break;
      }
    }
    return $result;
  }
}

$imgPath = "http://bj.ganji.com/tel/5463013757650d6c5e31093e563c51315b6c5c6c5237.png";
$gjPhone = new gjPhone($imgPath);
// 进行颜色分离
$gjPhone->getHec();
// 画出横向数据
$horData = $gjPhone->magHorData();
echo "===============横向数据==============<br/><br/><br/>";
$gjPhone->drawWH($horData);
// 画出纵向数据
$verData = $gjPhone->magVerData($horData);
echo "<br/><br/><br/>===============纵向数据==============< br/><br/><br/>";
$gjPhone->drawWH($verData);

// 输出电话
$phone = $gjPhone->showPhone($verData);
echo "<br/><br/><br/>===============电话==============<br /><br/><br/>" . $phone;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
使用PHP实现蜘蛛访问日志统计
Jul 05 PHP
php读取目录所有文件信息dir示例
Mar 18 PHP
php采用ajax数据提交post与post常见方法总结
Nov 10 PHP
PHP用反撇号执行外部命令
Apr 14 PHP
js代码实现微博导航栏
Jul 30 PHP
在WordPress中使用wp-cron插件来设置定时任务
Dec 10 PHP
PHP实现可自定义样式的分页类
Mar 29 PHP
PHP的PDO操作简单示例
Mar 30 PHP
php生成Android客户端扫描可登录的二维码
May 13 PHP
php结合mysql与mysqli扩展处理事务的方法
Jun 29 PHP
网站被恶意镜像怎么办 php一段代码轻松搞定(全面版)
Oct 23 PHP
微信公众号实现扫码获取微信用户信息(网页授权)
Apr 09 PHP
PHP对称加密函数实现数据的加密解密
Oct 27 #PHP
PHP下的浮点运算不准的解决方法
Oct 27 #PHP
php函数mkdir实现递归创建层级目录
Oct 27 #PHP
PHP实现递归目录的5种方法
Oct 27 #PHP
PHP读取大文件的几种方法介绍
Oct 27 #PHP
php array_multisort 对数组进行排序详解及实例代码
Oct 27 #PHP
PHP中的密码加密的解决方案总结
Oct 26 #PHP
You might like
snoopy 强大的PHP采集类使用实例代码
2010/12/09 PHP
php设计模式  Command(命令模式)
2011/06/17 PHP
php魔术函数__call()用法实例分析
2015/02/13 PHP
PHP中数据类型转换的三种方式
2015/04/02 PHP
PHP中的正则表达式实例详解
2017/04/25 PHP
有效的捕获JavaScript焦点的方法小结
2009/10/08 Javascript
Cookie 小记
2010/04/01 Javascript
JavaScript与Div对层定位和移动获得坐标的实现代码
2010/09/08 Javascript
js跑步算法的实现代码
2013/12/04 Javascript
一款基于jQuery的图片场景标注提示弹窗特效
2015/01/05 Javascript
JS基于面向对象实现的选项卡效果示例
2016/12/20 Javascript
详解nodejs 文本操作模块-fs模块(五)
2016/12/23 NodeJs
Linux系统中利用node.js提取Word(doc/docx)及PDF文本的内容
2017/06/17 Javascript
史上最全JavaScript数组去重的十种方法(推荐)
2017/08/17 Javascript
全站最详细的Vuex教程
2018/04/13 Javascript
一个基于react的图片裁剪组件示例
2018/04/18 Javascript
[15:57]教你分分钟做大人:斧王
2014/10/30 DOTA
Python中if __name__ == &quot;__main__&quot;详细解释
2014/10/21 Python
Django中数据库的数据关系:一对一,一对多,多对多
2018/10/21 Python
Python cv2 图像自适应灰度直方图均衡化处理方法
2018/12/07 Python
python numpy 矩阵堆叠实例
2020/01/17 Python
一些关于python 装饰器的个人理解
2020/08/31 Python
python实现控制台输出颜色
2021/03/02 Python
基于PyInstaller各参数的含义说明
2021/03/04 Python
介绍一下EJB的分类及其各自的功能及应用
2016/08/23 面试题
网页设计个人找工作求职信
2013/11/28 职场文书
项目总经理岗位职责
2014/02/14 职场文书
中职生自荐信范文
2014/06/15 职场文书
管理岗位竞聘演讲稿
2014/08/18 职场文书
2014年乡镇党建工作总结
2014/11/11 职场文书
2014年防汛工作总结
2014/12/08 职场文书
撤诉状格式范本
2015/05/19 职场文书
小学运动会宣传稿
2015/07/23 职场文书
升职自荐书
2019/05/09 职场文书
小学五年级(说明文3篇)
2019/08/13 职场文书
【海涛解说】暗牧也疯狂,牛蛙成配角
2022/04/01 DOTA