热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

本篇文章主要介绍了PHP图像识别技术原理与实现,这个平时做的密码验证有异曲同工之处,有需要的可以了解一下。

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

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

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

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

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

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

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

好了,下面是原作代码。

/**
 * 电话号码识别.
 * @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 .= "" . $v . "";
          } else {
            $c .= $v;
          }
        }
        $c .= "
"; } } 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 "===============横向数据==============


"; $gjPhone->drawWH($horData); // 画出纵向数据 $verData = $gjPhone->magVerData($horData); echo "


===============纵向数据==============


"; $gjPhone->drawWH($verData); // 输出电话 $phOne= $gjPhone->showPhone($verData); echo "


===============电话==============


" . $phone;

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


推荐阅读
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
author-avatar
李-小-霞_973
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有