此破解的验证码只适合,字符颜色单一,且字符分割较明显的验证码
破解验证码属于机器学习分支的内容, 破解验证码方式有多重,这里使用向量空间搜索的方法
数据集来自于实验楼。由版权原因请自行到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()