程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> PHP圖像識別技術原理與實現

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 .= "<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;

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved