使用sklearn來進行驗證碼識別

scikit-learn是一個Python的科學計算庫,其中的很多包都能拿來解決分類的問題。其中的neural_network包是一個簡單的神經網路包,也稱為多層感知器。為什麼筆者嘗試用它來進行驗證碼識別,而不用目前較流行的卷積神經網路來做呢?

使用卷積神經網路來識別驗證碼,確實有很多優勢,尤其是不需要進行分割,甚至去噪。但是他需要有監督的訓練,也就是需要一個前提條件:標註樣本。

想達到一個比較理想的識別效果,標註的樣本數量至少在萬級以上,對於不差錢的小夥伴來說,直接送去打碼平台就好了。如果算上一個性能比較好的計算平台的成本,這部分費用其實不算少了。

如果能設計一個合適的演算法,來有效的分割驗證碼中的字元,那麼識別單個字元的工作將會容易的多,訓練樣本數量要求也降低為幾百組。然後我們只需要調用一個能進行有效分類的包來做這個事情就好了。做一個調包俠實在是輕鬆又愉快。

測試一例:

實例來源於sci-hub

1.去噪:肉眼觀察,干擾線像素和目標字元像素區別明顯,對像素進行取樣(不想裝ps的,直接用QQ截圖,移動滑鼠觀察像素值。),去掉干擾像素,並二值化。

def img_precond(im): im = im.convert("L") table = [] for i in range(256): if i == 122: table.append(0) else: table.append(1) im = im.point(table,1) return im

去除干擾像素後的效果

2.設計切割演算法,觀察目標字元間隔區間,區間內目標像素比較少,而且呈現規律性,依此邏輯寫代碼。

def estimate_cutoff_line(im,x): w = im.size[0] h = im.size[1] threshold = 5 point = 0 for y in range(h): pix = im.getpixel((x,y)) if pix == 0: point = point + 1 if point <= threshold: return True else: return Falsedef get_cutoff_line(im): w = im.size[0] h = im.size[1] slice_array = [] for x in range(10,w-10): if estimate_cutoff_line(im,x): if not estimate_cutoff_line(im,x-1) or not estimate_cutoff_line(im,x+1): slice_array.append(x) i = 0 while True: try: if slice_array[i+1]-slice_array[i] < 20: slice_array[i] = (slice_array[i+1]+slice_array[i])/2 slice_array.remove(slice_array[i+1]) else: i = i + 1 except: break return slice_array

切割效果

3.按照以上切割演算法,收集每個字元的訓練樣本。例如字元a的部分樣本收集如下(筆者測試的每個字元收集數量大概在150左右):

字元a的部分樣本

4.收集好訓練樣本之後,直接以去除干擾且統一大小後的圖片像素為特徵,構建CSV訓練數據:

def imgToCSV(): files = get_lablesdir() features=[] for file in files: temp=[] im=Image.open(file) im = im.resize((60,114)) for x in range(im.width): for y in range(im.height): if im.getpixel((x,y)): temp.append(0) else: temp.append(1) temp.append(ord(file[-22])) features.append(temp) with open(CSV_FILE,w) as csvfile: spamwriter = csv.writer(csvfile, delimiter= ) for i in features: spamwriter.writerow(i)

5.有了訓練數據之後,就可以使用sklearn進行訓練了,訓練過程非常的簡單,也不需要像卷積神經網路那樣耗費很長時間進行迭代訓練。分分鐘就能得到一個訓練好的模型結果。

def train(self,csv_path): train_data = genfromtxt(csv_path, delimiter= ) x=train_data[:,:6840] y=train_data[:,6840] self.clf = neural_network.MLPClassifier(hidden_layer_sizes=(90,90)) self.clf.fit(x, y) return

6.訓練完成後,使用模型進行預測:

def predict(self,im): temp=[] im = im.resize((60,114)) for x in range(im.width): for y in range(im.height): if im.getpixel((x,y)): temp.append(0) else: temp.append(1) return chr(int(self.clf.predict([np.array(temp)])[0]))

經測試,該樣例驗證碼的識別率在30%以上,實際上達到這樣的效果就可以實際應用了。可能有的同學會說,你找的這個驗證碼很容易去除干擾,也很好分割,複雜一點的還能達到這個效果嗎?事實上,去除干擾以及分割確實是達到理想識別效果的一個關鍵點,只要能找到合適的演算法,還是能達到我們想要的結果,比如下面這種:

雖然經過分割演算法後的效果看起來比前面差了很多,丟失了很多特徵像素,但我們此樣本數據構建訓練數據,使用sklearn進行訓練後的識別效果,還是能達到一定實用效果的:

只要我們能找到合適的去干擾以及字元分割的演算法,使用sklearn來進行驗證碼字元識別不失為一個輕量級的實現方法。


推薦閱讀:

鋼鐵直男的救世主來了!讓AI告訴你妹子到底是啥意思
關鍵詞提取Part1(A Quick Review)
一樣的打遊戲,不一樣的酷
機器學習基石筆記14:正則化(Regularization)
複習:PCA有關

TAG:機器學習 | 驗證碼識別 | Python |