圖像識別聽起來很高深,但其核心邏輯與傳統密碼驗證並無二致:預先建立特徵庫 → 採集待識別資料 → 比對相似度。差別在於圖像識別引入容錯機制(如相似度 95% 即判定成功),而密碼驗證要求 100% 精確匹配。
早期某專案需識別圖片中固定位置的三位數字(如抽獎頁面的獎項數字)。最初考慮用 JS/Flash 遮罩層前端處理,但需求方強制要求 PHP 後端識別。恰逢當時新聞熱議馬雲人臉支付、12306 驗證碼被破解,遂深入研究 PHP 圖像識別實作。
將 PNG 圖片載入 GD 資源,遍歷每個像素,根據 RGB 閾值(如 < 125 判定為前景)轉為 0/1 矩陣:
<?php
$res = imagecreatefrompng($path);
$size = getimagesize($path);
for ($y = 0; $y < $size[1]; ++$y) {
for ($x = 0; $x < $size[0]; ++$x) {
$rgb = imagecolorat($res, $x, $y);
$rgba = imagecolorsforindex($res, $rgb);
$data[$y][$x] = ($rgba['red'] < 125 || $rgba['green'] < 125 || $rgba['blue'] < 125) ? 1 : 0;
}
}
?>
掃描二值矩陣,按行/列投影找出字符邊界,切分單一字符區塊並正規化尺寸(如 17×11 像素)。
預先將 0-9 數字的標準字形轉為 0/1 字串存入陣列。識別時,將切分出的字符字串與模板庫逐一計算 similar_text() 相似度,超過 95% 即判定為該數字:
<?php
$templates = [
'1' => '00000000111000000000000001110000000001001000100000000010100011000000000011000110000000000110000100000000010110011000000',
'5' => '00000000001000000000000000010000000000100100100000000000101001110000000000100000110000000011000000100000001101000010000',
// ... 其餘數字模板
];
function recognize($charStr, $templates) {
foreach ($templates as $digit => $tpl) {
similar_text($charStr, $tpl, $percent);
if ($percent > 95) return $digit;
}
return '?';
}
?>
<?php
class DigitRecognizer {
private $imgPath, $imgSize, $binData;
public function __construct(string $path) { $this->imgPath = $path; }
// 二值化
public function binarize(): void {
$res = imagecreatefrompng($this->imgPath);
$this->imgSize = getimagesize($this->imgPath);
for ($y = 0; $y < $this->imgSize[1]; ++$y) {
for ($x = 0; $x < $this->imgSize[0]; ++$x) {
$rgb = imagecolorat($res, $x, $y);
$rgba = imagecolorsforindex($res, $rgb);
$this->binData[$y][$x] = ($rgba['red'] < 125 || $rgba['green'] < 125 || $rgba['blue'] < 125) ? 1 : 0;
}
}
}
// 切割並識別指定 X 座標區域
public function recognizeAt(int $x, int $y, int $w, int $h): string {
$canvas = imagecreatetruecolor($w, $h);
$white = imagecolorallocate($canvas, 255, 255, 255);
imagefilledrectangle($canvas, 0, 0, $w, $h, $white);
$src = imagecreatefrompng($this->imgPath);
imagecopy($canvas, $src, 0, 0, $x, $y, $w, $h);
$tmp = sys_get_temp_dir() . '/ocr_' . uniqid() . '.png';
imagepng($canvas, $tmp);
$this->imgPath = $tmp;
$this->binarize();
// 這裡簡化:直接將二值矩陣展平為字串比對
$str = '';
foreach ($this->binData as $row) $str .= implode('', $row);
return $this->matchTemplate($str);
}
private function matchTemplate(string $str): string {
// 同前方模板比對邏輯
}
}
?>
此類基於模板比對的識別,原理上可破解早期固定字體、無干擾線的驗證碼(如舊版 12306)。現代防禦手段包括:
圖像識別技術本身中性,關鍵在於對抗雙方的技術迭代。開發者應採用成熟驗證碼服務(如 Google reCAPTCHA、Geetest)而非自建弱防禦方案。
文末附上原作者 gjPhone 類完整實現(含縱向投影、調試輸出等),供深入研究參考。