此破解的驗證碼隻适合,字元顔色單一,且字元分割較明顯的驗證碼
破解驗證碼屬于機器學習分支的内容, 破解驗證碼方式有多重,這裡使用向量空間搜尋的方法
資料集來自于實驗樓。由版權原因請自行到https://www.shiyanlou.com/courses/364下載下傳,讀者可配合實驗内容進行了解
圖檔二值化即,将圖檔變成隻有黑白兩種顔色, 其他大部分在代碼中已解釋
#coding:utf-8
"""
此程式隻适合紅色220跟227畫出來的字元,其他顔色不支援
2018年5月2日21:55:57
"""
from PIL import Image
import hashlib
import time
import math
import os
class VectorCompare:
"""
2 篇文檔所使用的相同的單詞越多,那這兩篇文章就越相似!但是這單詞太多怎麼辦,就由我們來選擇幾個關鍵單詞,選擇的單詞又被稱作特征,
每一個特征就好比空間中的一個次元(x,y,z 等),一組特征就是一個矢量,每一個文檔我們都能得到這麼一個矢量,隻要計算矢量之間的夾角就能得到文章的相似度了。
"""
#計算矢量大小, 即二範數
def magnitude(self, concordance):
total = 0
for word, count in concordance.items():
total += count ** 2
return math.sqrt(total)
#計算矢量之間的cos值, 即計算餘弦相似度
def relation(self, concordance1, concordance2):
relevance = 0
topvalue = 0
for word, count in concordance1.items():
if word in concordance2:
topvalue += count * concordance2[word]
return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))
def sort_his():
"""将圖檔排序, 友善選取顔色,去除噪聲"""
his = im.histogram()
values = {}
for i in range(256):
values[i] = his[i]
hisSort = sorted(values.items(), key = lambda x: x[1], reverse = True)
#擷取前10的顔色以及數量
for j, k in hisSort[:10]:
print(j, k)
def binaryzation(im):
"""将圖檔二值化"""
#獲得一張全白的背景,即與im同樣大小,模式為P,像素全為255的圖檔
im2 = Image.new("P", im.size, 255)
#擷取圖像矩陣的行列
for y in range(im.size[1]):
for x in range(im.size[0]):
#擷取像素的RGB值
pix = im.getpixel((x, y))
# if pix == 219:#用219畫出來不是字元的輪廓
#220跟227是紅色,即想要的顔色(通過嘗試前面多種顔色的效果,對比輸入輸出圖檔)
if pix == 220 or pix == 227:
#im2的白色背景上畫像素,顔色為0(黑色)
im2.putpixel((x, y), 0)
return im2
def cutImg(im2):
"""将圖檔進行縱向切割,得到每個字的圖檔"""
inletter = False
foundletter = False
start = 0 #每個字元的橫坐标開始位置
end = 0 #每個字元的橫坐标結束位置
letters = []
for x in range(im2.size[0]):
for y in range(im2.size[1]):
pix = im2.getpixel((x, y))
#每列從左往右掃找到第一個不是白色像素的位置
if pix != 255:
inletter = True
#如果找到了不是白色像素的位置,記錄每行的x
if foundletter == False and inletter == True:
foundletter = True
start = x
#z找到一列都為白色像素的為止,作為字元結尾
if foundletter == True and inletter == False:
foundletter = False
end = x
letters.append((start, end))
inletter = False
count = 0
imgList = []
for xStart, xEnd in letters: #letter裡存放每個字元的開始與結束的橫坐标
#crop參數為(Xstart,Ystart,Xend,Yend),然後對圖檔進行切割
imgList.append(im2.crop(( xStart, 0, xEnd, im2.size[1])))
"""
測試代碼:
count = 0
for xStart, xEnd in letters:
m = hashlib.md5()
im3 = im2.crop(( xStart, 0, xEnd, im2.size[1]))
m.update("%s%s"%(time.time(), count))
im3.save("./%s.gif"%(m.hexdigest()))
count += 1
"""
return imgList
#将圖檔轉換為矢量
def buildvector(im):
d1 = {}
count = 0
for i in im.getdata():
d1[count] = i
count += 1
return d1
def loadSet():
"""加載訓練集"""
#答案
iconset = ['0','1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
#加載訓練集
imageset = []
for letter in iconset:
for img in os.listdir('./iconset/%s/' % (letter)):
temp = []
if img != "Thumbs.db" and img != ".DS_Store":
temp.append(buildvector(Image.open("./iconset/%s/%s" % (letter,img))))
imageset.append({letter:temp})
return imageset
def testImg():
#建立一個向量類
v = VectorCompare()
imageset = loadSet()
#打開測試圖檔
im = Image.open("captcha.gif")
#将圖檔轉換為8位像素模式,即平常RGB(0, 255)
im.convert("P")
'''
測試: 列印圖的顔色直方圖(每位對應像素出現多少次)
print(im.histogram())
sort_his()
'''
#将圖檔二值化
im2 = binaryzation(im)
#将二值化的圖檔進行切割,得到單個字元
imgList = cutImg(im2)
for img in imgList:
m = hashlib.md5()
guess = []
#周遊清單裡的所有字典
for image in imageset:
#擷取字典的鍵值,
for x, y in image.items():
if len(y) != 0:
guess.append( ( v.relation(y[0], buildvector(img)), x) )
guess.sort(reverse=True)
print("",guess[0])
if __name__ == '__main__':
testImg()