天天看點

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

php實作驗證碼的識别(中級篇)(轉)

在上篇文章 <php實作驗證碼的識别 (初級篇 ) > 中,講了如何識别簡單的驗證,這裡的簡單隻的是驗證碼有數字和字母組成,格式統一,每次 出現位置固定。這篇文章将繼續深入研究識别驗證碼,這次識别的目标是,驗證碼有字元和數字組成,驗證碼存在旋轉(可能左右都旋轉),位置不固定,存在字元 與字元之間的粘連,且驗證碼有更強的幹擾素。這篇文章講解的方法,并不是萬能的解決方案,并且提供代碼不能直接解決你的問題,這裡僅僅是方法,具體需求讀 者自己解決,需要說明的是,識别驗證碼與具體的程式設計語言無關,這裡隻是使用 php 語言實作,使用這裡介紹的方法,你可以使用任何語言實作。

 這篇文章逐漸講解識别驗證碼過程中的各個步驟。

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

 如上圖,随後的講解我們都圍繞此圖展開。

 一:拿到一個驗證碼的,第一眼我們首先要做的工作是,二值化。把驗證碼的部分用 1 表示,背景部分用 0 表示出來,識别方法很簡單,我們列印出驗證碼正張圖檔的 RGB ,然後分析其規律即可,通過 RGB 碼,我們很容易分辨出上面這張圖檔的 R 值大于 120 , G 和 B 的值小于 80 ,是以依據這個規則我們很容易把上面的圖檔二值化。再看初級篇中識别的兩張圖

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)
php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

  剛看上去,感覺很複雜。驗證碼的圖檔每次背景色都不相同,且不是單色,各個驗證碼數字的顔色每次也各不相同。貌似很難二值化,其實我們列印出其 RGB 值很容易就發現。無論驗證數字顔色如何變化,該數字的 RGB 值總有一個值小于 125 ,是以通過如下判斷

$rgbarray['red'] < 125 || $rgbarray['green']<125|| $rgbarray['blue'] < 125

我們就很容易分辨出哪裡是數字,哪裡是背景。

  我們能夠找到這些規律的因素是,在制作驗證碼的幹擾素時,為了使幹擾素不影響數字的顯示效果,必須使用幹擾素的 RGB 和數字 RGB 互相獨立,互不幹擾。隻要懂得這個規律,我們就很容易實作二值化。

  我們找到的 120 , 80 , 125 等門檻值,可能和實際的 RGB 有出入,是以,有時二值化後,會有部分地方出現 1 ,對于驗證碼上固定位置顯示數字,這種幹擾沒有太大意義。但是對于驗證碼位置不确定的圖檔來說,在我們切割字元時,很可能造成幹擾。是以,在二值化後要進行去噪出來。

  二:接下來我們進行第二個步驟,出噪。出燥的原理很簡單,就是把孤立的有效的值去掉,如果噪點比較高,要求的效率也比較高的話,這裡面也有很多工作要做。幸好這裡我們不要求這麼高深,我們使用最簡單的方法就可以,如果一個點為 1 則判斷這個點的上下左右上左上右下左下右 8 個方位上數字是否為 1 ,如果不為 1 ,就認為是一個燥點,直接設定為 1 即可。

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

如上圖所示,我們使用此方法很容易發現紅色方框部分的 1 為燥點,直接設定為 1 即可。

在判斷時我們使用了一個技巧,有時候的噪點可能是兩個連續的 1 ,是以我們

view plain copy to clipboard print ?

  1. $num  = 0;  
  2. if ( $data [ $i ][ $j ] == 1)  
  3. {  
  4.     // 上   
  5.     if (isset( $data [ $i -1][ $j ])){  
  6.         $num  =  $num  +  $data [ $i -1][ $j ];  
  7.     }  
  8.     // 下   
  9.     if (isset( $data [ $i +1][ $j ])){  
  10.         $num  =  $num  +  $data [ $i +1][ $j ];  
  11.     }  
  12.     // 左   
  13.     if (isset( $data [ $i ][ $j -1])){  
  14.         $num  =  $num  +  $data [ $i ][ $j -1];  
  15.     }  
  16.     // 右   
  17.     if (isset( $data [ $i ][ $j +1])){  
  18.         $num  =  $num  +  $data [ $i ][ $j +1];  
  19.     }  
  20.     // 上左   
  21.     if (isset( $data [ $i -1][ $j -1])){  
  22.         $num  =  $num  +  $data [ $i -1][ $j -1];  
  23.     }  
  24.     // 上右   
  25.     if (isset( $data [ $i -1][ $j +1])){  
  26.         $num  =  $num  +  $data [ $i -1][ $j +1];  
  27.     }  
  28.     // 下左   
  29.     if (isset( $data [ $i +1][ $j -1])){  
  30.         $num  =  $num  +  $data [ $i +1][ $j -1];  
  31.     }  
  32.     // 下右   
  33.     if (isset( $data [ $i +1][ $j +1])){  
  34.         $num  =  $num  +  $data [ $i +1][ $j +1];  
  35.     }  
  36. }  
  37. if ( $num  == 0){  
  38.     $data [ $i ][ $j ] = 0;  
  39. }  

$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 == 0){

$data[$i][$j] = 0;

}

我們計算這個點的 8 個方向上的值之和,最後我們判斷他們的和是否小于特定的門檻值

 三:經過去噪後,我們就得到幹淨的二值化的資料,接下來要做的就是切割字元了。切割字元的方法有很多種,這裡我采用最簡單的一種,先垂直方向切割成為字元,然後在水準方向去掉多于的 0000 ,如下圖

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

第一步切割紅線部分,第二步切割藍線部分,這樣就可以得到獨立的字元了。但是像下面這種情況

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

按上面的方法會把 dw 字元切割成一個字元,這是錯誤的切割,是以這裡我們涉及到粘連字元的切割。

 四:粘連字元切割,制作驗證碼時,規則字元的粘連很容易分割開,如果字元本身有縮放,變形就很難處理,經過分析,我們可以發現,上面的字元粘連屬于很簡 單的方式,隻是規則字元的粘連,是以處理這種情況,我們也使用很簡單的處理方式。當完成分割操作後,我們不能馬上确定分割的部分就為一個字元,要進行驗 證,驗證的關鍵因素就是,切割下來的字元的寬是否大于門檻值,這個門檻值的取舍标準是,一個字元無論怎麼旋轉變形都不會大于這個門檻值,是以,如果我們切割的塊 大于這個門檻值,就可以認為這是一個粘連字元;如果大于兩個門檻值之和,就認為是三個字元粘連,以此類推。知道這個規則後,切割粘連字元也就很簡單了。如果我 們發現是粘連字元塊,直接平分這個塊為兩個或者多個新的塊就可以。當然為了更好的還原字元,我一般都采用平分 +1 , -1 對字元塊的部分進行适當的補充。

 五:經過上面四個步驟,我們就可以提取出比較純的字元塊了,接下來要做就是比對字元了。對于旋轉字元的特征碼建立,有很多種方法,這裡就不做深入研究了。我這裡使用的最簡單的方式,為所有字元的所有情況建立比對庫,是以在我提供的代碼種增加了 study 操作,其目的就是,先有人手工識别圖檔的驗證碼,然後通過 study 方法,寫入特征碼庫。這樣寫入的圖檔資料越多,驗證識别的準确行也就越高。

 好了,經過以上步驟,我們基本上可以識别現在網際網路上大部分的驗證碼,這裡我們都是使用的最簡單的方法,沒有使用任何 OCR 知識。這些方法,應該屬于非 OCR 領域的頂峰了,要想識别更加複雜的驗證碼,那就需要更多的 OCR 知識了。有機會的話,我會在進階篇中一一做介紹。

 下面是一些容易識别的驗證碼,希望引起網站管理者的重視。

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

制作驗證碼的一些建議

 對于識别驗證碼的程式來說,最難得部分是驗證字元的切割和特征碼的建立,而國内很多程式員隻做驗證碼時,總是喜歡在驗證碼加很多幹擾素,幹擾線,影響效果不說,還達不到很好的效果;是以,要想使自己驗證碼難于本識别,隻做下面兩點就夠了

1 :字元粘連,最好所有的字元都有粘連的部分;

2 :不要使用規格字元,驗證碼的各個部分使用不同比例的縮放或者旋轉。

隻要做到這兩點,或者這兩點的變形,識别程式就很難識别。我們看看, yahoo 和 google 的驗證碼就知道,白字黑底,卻很難被識别。

Goole:

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

yahoo:

php實作驗證碼的識别(中級篇)(轉)php實作驗證碼的識别(中級篇)(轉)

源檔案下:點選下載下傳

作者 : 逸學堂( ugg )

轉帖請注明:http://blog.csdn.net/ugg/archive/2009/03/09/3972368.aspx