引言:為什么學習這個呢?
這個算是機器學習,最入門的一點東東
這里介紹兩種方法:
1.直接調用第三方庫進行識別,缺點:存在部分圖片無法識別
2.使用knn算法進行對圖片的處理,以及運算進行識別
聲明:本文均在pycharm上進行編輯操作,並本文所寫代碼均是python3進行編寫,如果不能正常運行本文內的代碼,請自己調試環境
另本文所識別的驗證碼類型為如下圖片:
先介紹第一種比較簡單的操作:
1.環境准備:
安裝如下第三方庫
from selenium import webdriver
from PIL import Image
import pytesseract
2.環境介紹
selenium 環境模仿鼠標點動,以及賬號密碼傳遞,等等
pytesseract 識別圖片中字符借用的第三方庫
PIL 對圖片的一些處理的第三方庫
3.具體實現
driver.find_element_by_xpath('地址').click()
點擊網頁中xpath為括號內的位置
driver.find_element_by_xpath('地址').send_keys(傳遞信息)
傳遞相應數據到xpath為括號內的相應位置
ele=driver.find_element_by_xpath('地址')
ele.screenshot('圖片名,以及格式')
找到xpath為括號內的地址,並截取相應位置圖片
4.圖片處理
在獲取相應驗證碼圖片后,往往圖片為彩圖,或者存在噪點,為了減少模型的復雜度,以及減少模型的訓練強度,同時增加識別率,很有必要對圖片進行預處理,使其對機器識別更友好。
具體步驟如下:
1.讀取原始素材
2.將彩圖轉化為黑白圖
3.去噪點
4.1二值化圖片
圖像二值化( Image Binarization)就是將圖像上的像素點的灰度值設置為0或255,也就是將整個圖像呈現出明顯的黑白效果的過程。------來自百度百科
1.RGB彩圖轉為灰度圖
2.將灰度圖轉化為二值圖,即設定二值化閾值,轉化為01圖
1 image = Image.open('a.png') 2 image = image.convert('L') #轉化為灰度圖 3 threshold = 127 #設定的二值化閾值 4 table = [] #table是設定的一個表,下面的for循環可以理解為一個規則,小於閾值的,就設定為0,大於閾值的,就設定為1 5 for i in range(256): 6 if i < threshold: 7 table.append(0) 8 else: 9 table.append(1) 10 image = image.point(table,'1') #對灰度圖進行二值化處理,按照table的規則(也就是上面的for循環)
如下圖:
2.去除噪點
在轉化為二值圖片后,就需要清除噪點。本文選擇的素材比較簡單,大部分噪點也是最簡單的那種 孤立點,所以可以通過檢測這些孤立點就能移除大量的噪點。
關於如何去除更復雜的噪點甚至干擾線和色塊,有比較成熟的算法: 洪水填充法 Flood Fill ,后面有興趣的時間可以繼續研究一下。
轉載自 https://www.cnblogs.com/beer/p/5672678.htm
5.直接借助selenium和pytesseract實現
result = pytesseract.image_to_string(image) # 讀取里面的內容
輸出result,就是圖片的結果.
上述方法的精確度,嗯........
我沒經過專業的測試,但是點着試試,試了二三十次,有那么五六次是錯誤的
所以呢為了提高模型的精確度,下面介紹knn算法
knn:從訓練樣本集中選擇k個與測試樣本“距離”最近的樣本,這k個樣本中出現頻率最高的類別即作為測試樣本的類別。
-
KNN是屬於有監督學習(因為訓練集中每個數據都存在人工設置的標簽——即類別)
-
那是如何進行分類的呢?
其實是用數據之間的歐氏距離來衡量它們的相似程度,距離越短,表示兩個數據越相似。
5.建立樣本集---圖片分割
既然是樣本集,那么肯定要有樣本呀,找相應網站,提交請求,爬取完事,在這不寫這個了
而樣本集的建立,可以數格子,沒錯就是數格子
打開ps,圖片放大到最大,然后數格子,額,這個方法有點low,在線ps:https://www.uupoop.com/
找到左上點,右下點,間距,然后循環切割,保存
1 from PIL import Image 2 3 4 def cut_image(image): 5 box_list = [] 6 # (left, upper, right, lower) 7 for i in range(0, 4): 8 box = (5+i*12+1,5,14+i*12,19+1) 9 box_list.append(box) 10 image_list = [image.crop(box) for box in box_list] 11 return image_list 12 13 14 # 保存 15 def save_images(image_list): 16 index = 1 17 for image in image_list: 18 image.save(str(index) + '.png', 'PNG') 19 index += 1 20 21 22 if __name__ == '__main__': 23 file_path = "地址" # 圖片保存的地址 24 image = Image.open(file_path) 25 26 image_list = cut_image(image) 27 save_images(image_list)
效果圖:
上面方法有點low
所以可以,通過圖片黑色或白色的圖片的連續性,來進行尋找左上點和右下點來確定一個矩形范圍,即切割的圖片的位置,循環切割保存
1 def cut_image(image): 2 """ 3 字符切割,根據黑色的連續性,當某一列出現黑色為標志,當黑色消失為結束點 4 :param image: 完整的驗證碼圖片 5 :return images: 切割好的圖片列表 6 """ 7 # inletter代表當前列是否出現黑點 8 inletter = False 9 # foundletter為False時,未找到字符開始位置;否則,已找到字符開始位置 10 foundletter = False 11 # 記錄所有字符的開始點和結束點 12 letters = [] 13 start = 0 14 end = 0 15 for x in range(image.size[0]): 16 for y in range(image.size[1]): 17 # 當前像素點的狀態(0黑色或1白色) 18 pix = image.getpixel((x,y)) 19 # 出現黑色點時證明有字符出現 20 if pix == 0: 21 inletter = True 22 # 當前列出現黑色點,且未找到字符開始位置,則找當前列為字符開始位置 23 if foundletter == False and inletter ==True: 24 foundletter = True 25 start = x 26 # 當前列為全白,且已有字符開始位置,則該字符結束,記錄字符的范圍 27 if foundletter == True and inletter == False: 28 end = x 29 letters.append((start,end)) 30 foundletter = False 31 inletter = False 32 images = [] 33 # 利用letter的信息切割驗證碼,得到單個字符 34 for letter in letters: 35 img = image.crop((letter[0],0,letter[1],image.size[1])) 36 #img.save(str(letter[0])+'.jpeg')#展示切割效果 37 images.append(img) 38 return images
上面代碼只寫出連續黑的情況,所以在部分要進行修改
6.建立樣本集---分組
將爬取的樣本重復上述操作進行圖片處理和切割
將切割好的圖片,建立文件夾進行分組
7.識別
具體操作步驟如下:
1.預處理圖片
2.將圖片轉化
3.cos求解相似度
1.預處理圖片
上面的樣本切割出是單獨的數字,那么在識別的時候,要對圖片進行處理以及切割,具體操作參考上面的介紹.
2.將圖片轉換
在將圖片切割后,是一個圖片的形式顯示,這樣不便於計算,所以將其轉化為矢量,將二維形式轉化為一維形式
1 def buildvector(image): 2 """ 3 圖片轉換成矢量,將二維的圖片轉為一維 4 :param image: 5 :return: 6 """ 7 result = {} 8 count = 0 9 for i in image.getdata(): 10 result[count] = i 11 count += 1 12 return result
3.cos值求解相似度
求解方程:
即目標值與其中一個樣本值的相似度.
m表示該樣本組的數量,數組c表示目標圖片,數組d表示樣本組中的每一張圖片
另外在此所用的目標圖片和樣本圖片,均已經一維化處理
計算完目標圖片與所有樣本集后進行排序,去相似度最高即為目標圖片所示數字
1 class CaptchaRecognize: 2 def __init__(self): 3 self.letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 4 self.loadSet() 5 6 def loadSet(self): 7 """ 8 將icon中預先准備好的圖片,以向量的形式讀出 9 ps: icon中圖片為驗證碼切割完成后,人工標記的訓練集 10 如果需要增加,只需把切割后的圖片放到其所表示的文件夾下即可 11 :return: 12 """ 13 self.imgset = [] 14 for letter in self.letters: 15 temp = [] 16 # 打開icon下的各個文件,icon文件下是一些已切割的字符圖片 17 for img in os.listdir('./icon/%s'%(letter)): 18 # 將圖片轉成一維向量,放入temp列表中 19 temp.append(buildvector(Image.open('./icon/%s/%s'%(letter,img)))) 20 # 標簽與對應圖片轉換成的向量,以字典形式存到imgset 如:letter為1,temp就是1文件夾下圖片的向量 21 self.imgset.append({letter:temp}) 22 23 def magnitude(self,concordance): 24 """ 25 利用公式求計算矢量大小,詳細公式見README.md 26 :param concordance: 27 :return: 28 """ 29 total = 0 30 for word, count in concordance.items(): 31 # count 為向量各個單位的值 32 total += count ** 2 33 return math.sqrt(total) 34 35 def relation(self, concordance1, concordance2): 36 """ 37 計算矢量之間的 cos 值,詳細公式見README.md 38 :param concordance1: 39 :param concordance2: 40 :return: 41 """ 42 relevance = 0 43 topvalue = 0 44 # 遍歷concordance1向量,word 當前位置的索引,count為值 45 for word, count in concordance1.items(): 46 # 當concordance2有word才繼續,防止索引超限 47 if word in concordance2: 48 #print(type(topvalue), topvalue, count, concordance2[word]) 49 topvalue += count * concordance2[word] 50 #time.sleep(10) 51 return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2)) 52 53 def recognise(self,image): 54 """ 55 識別驗證碼 56 :param image: 驗證碼圖片 57 :return result: 返回驗證碼的值 58 """ 59 # 二值化,將圖片按灰度轉為01矩陣 60 image = convert_image(image) 61 # 對完整的驗證碼進行切割,得到字符圖片 62 images = cut_image(image) 63 vectors = [] 64 for img in images: 65 vectors.append(buildvector(img)) # 將字符圖片轉一維向量,如[0,1,0,1,1,....] 66 result = [] 67 for vector in vectors: 68 guess=[] 69 # 讓字符圖片和訓練集中的 0-9 逐一比對 70 for image in self.imgset: 71 for letter,temp in image.items(): 72 relevance=0 73 num=0 74 # 遍歷一個標簽下的所有圖片 75 for img in temp: 76 # 計算相似度 77 relevance+=self.relation(vector,img) 78 print (vector,img) 79 num+=1 80 # 求出相似度平均值 81 relevance=relevance/num 82 guess.append((relevance,letter)) 83 # 對cos值進行排序,cos值代表相識度 84 guess.sort(reverse=True) 85 result.append(guess[0]) #取最相似的letter,作為該字符圖片的值 86 return result
8.主函數調用
1 if __name__ == '__main__': 2 imageRecognize=CaptchaRecognize() 3 # 設置圖片路徑 4 image = Image.open('3.png') 5 # print(image.mode) 6 7 result = imageRecognize.recognise(image) 8 string = [''.join(item[1]) for item in result] 9 print(result)
9.總結
本文主要是識別簡單的驗證碼圖片,要根據具體情況進行修改,主要提供一個框架,如果所給圖片呈不規則顯示,可能無法識別,這個算是機器學習簡單的入門,對於以上僅為個人看法,如果有別的看法,歡迎私聊!!!