中文點選驗證碼之自動識別
某次測試中遇到了漢字點選的驗證碼,看著很簡單,嘗試了一下發現有兩種簡單的識別方法,終於有空給重新整理一下,分享出來。
0×01 驗證碼的獲取
首先獲取驗證碼。由於網站比較特殊,就不以他們的為例,自己生成驗證碼吧。這個不是重點,這裡直接貼程式碼了。
<?php function createImage($word,$imagePath,$type, $imageName){ $fontPath = 'msyh.ttc';//字型 $fontSize = 20* 0.75; foreach ($wordas $v ) { $fontarea=imagettfbbox($fontSize,0 , $fontPath, $v); $textWidth =$fontarea[2] -$fontarea [0]; $textHeight =$fontarea[1] -$fontarea [7]; $tmp['text'] = $v; $tmp['size'] = $fontSize; $tmp['width'] = $textWidth; $tmp['height'] = $textHeight; $textArr[] =$tmp; } list( $imageWidth, $imageHeight,$imageType) =getimagesize( $imagePath); for( $i=0;$i <count($textArr );$i++){ list($x, $y) =randPosition($textArr,$imageWidth,$imageHeight, $textArr[$i][ 'width'], $textArr[$i]['height'],$i,$type); $textArr[$i]['x'] =$x; $textArr[$i]['y'] =$y; } unset( $v); //建立圖片的例項 $image =imagecreatefromstring(file_get_contents( $imagePath)); //字型顏色 $color =imagecolorallocate($image,0,0,0); //繪畫文字 foreach( $textArras $v){ imagefttext ($image, $v ['size'], 0 , $v['x' ], $v['y'], $color,$fontPath,$v ['text']); } if (imagepng( $image,$imageName )){ echo $imageName." \n"; } } functionrandPosition ($textArr,$imgW, $imgH ,$fontW,$fontH,$i,$type){ switch($type) { case 0:// 生成mp $x =rand ($i* 60, ($i +1)*60-$fontW -3); $y =rand (40, 80); break; case1: //生成ap $x = ($i )*25+ 5; $y =25; default: break; } $return =array( $x,$y); return$return ; } $ap_imagePath='ap_bg.png' ; $mp_imagePath='mp_bg.png' ; $ap_imageName="ap_" .time (). ".png" ; $mp_imageName="mp_" .time (). ".png" ; $ap_word=array (' 請 ' ,' 依 ' ,' 次 ' ,' 點 ' ,' 擊 ' ,' 圖 ' ,' 中 ' ,' 的 ' ,' 獵 ' ,' 戶 ' ,' 室 ' ); $mp_word=array (' 獵 ' ,' 戶 ' ,' 實 ' ,' 驗 ' ,' 室 ' ); createImage ( $ap_word ,$ap_imagePath , 1 ,$ap_imageName ); createImage ( $mp_word ,$mp_imagePath , 0 ,$mp_imageName ); ?>
執行後生成這樣兩張圖片。
ap_XXXXX.png
mp_XXXXX.png
ap_XXXXX.png是說明需要點選的文字,mp_XXXXX.png是需要點選的圖片。
0×02 驗證碼識別
對於這種簡單的點選驗證碼,可以有兩種很容易的識別方式(機器學習算麻煩的,這裡就不列出了。嗯,對,我也不會)。一種是opencv的影象模板匹配,另外一種是OCR識別。
1. opencv的影象模板匹配
第一種方式,使用opencv的影象模板匹配。模板匹配是一種在較大影象中搜索和查詢模板影象位置的方法,opencv2和opencv3中提供了一個專門用於模板匹配的函式matchTemplate()。它是在輸入影象上滑動模板影象(如在2D卷積中),並比較模板影象下的輸入影象的模板和補丁。在OpenCV中實現了六種比較方法(這裡用到的是cv2.TM_CCOEFF_NORMED),它返回一個灰度影象,其中每個畫素表示該畫素的鄰域與模板匹配的程度。
獲得結果後,可以使用cv.minMaxLoc()函式查詢最大/最小值的位置。將其作為矩形的左上角,並將(w,h)作為矩形的寬度和高度,那個矩形就是模板區域。
我們進行使用模板匹配來識別這種驗證碼時,首先先將“模板”找出來,這裡我們需要匹配的是“獵”、“戶”、“室”這三個字。將這三個字所在的圖片進行擷取,然後使用matchTemplate()函式在mp中進行匹配。
首先擷取第一個字“獵”。
擷取之後,就可以在mp中進行匹配。
這裡得到了最大和最小位置。我們使用最大位置,然後將最大值作為閾值。獲取模板的尺寸,然後在 mp 中用矩形(紅色區域)畫出匹配的區域。如下所示。
同理,用黃色和藍色矩形將“戶”、“室”所在的區域畫出來。
點選時傳送所選區域中間的座標即可,這裡就不再給出例項了。這種方法雖然簡單,但是對於字型不一的就不能很正確的標記出來。
修改生成圖片的程式碼,將mp中的文字的字型設定為隨機。修改的程式碼如下:
switch($type) { case0: //mp文字隨機大小 $fontSize=rand (20,30) *0.75 ; break; case1 ://ap文字固定 $fontSize=20 *0.75; break; }
ap生成的結果還是和之前一樣, mp 的圖片如下:
使用同樣的程式碼來匹配。
此時匹配的結果就有些慘不忍睹了。所以就換另外一種識別方式-ocr 識別。
2.OCR識別
這裡採用的是騰訊雲的OCR-通用印刷體識別( ofollow,noindex" target="_blank">https://cloud.tencent.com/document/product/866/17600 )。
參考文件。輸入mp圖片,返回的是json。
檢視json內容,發現包含了圖片中的文字、位置和大小等。
同理ap中內容也可以獲取。
此時匹配的時候直接就是匹配文字了。首先獲取ap中後三個文字,然後與mp中返回的內容匹配,獲取其位置和大小,然後再畫矩形即可。
由於比較簡單,這裡直接貼結果。
匹配相當完美。
0X03 總結
本文用了兩種方法來自動識別漢字點選驗證碼,第一種採用的是opencv的模板匹配,這種方法雖然也可以匹配到,但這種方法缺點就是對於字型形狀差異較大的驗證碼識別率較低。而第二種方法就比較快捷方便了,而且識別度高,比較推薦第二種方法。
當然這兩種方法對於簡單、“正規”的驗證碼可以,遇到複雜的、“扭曲的”驗證碼就不行了。這時候就要用到機器學習了,而本文只是簡單的“識別”,將機器學習用到這裡,就有些大材小用了。
相關程式碼: Python/tree/master/captcha/Pointselection" rel="nofollow,noindex" target="_blank">https://github.com/fupinglee/MyPython/tree/master/captcha/Pointselection
0×04 參考
[1] http://bluewhale.cc/2017-09-22/use-python-opencv-for-image-template-matching-match-template.html
[2] https://cloud.tencent.com/document/product/866/17600