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