这种简单的,只有数字和字母的验证码是非常不安全的,使用python语音就可以进行批量的识别提取,而且实际上使用的函数较少,思路也很简单。那么话不多说,我们就来看看这项工作是如何完成的。
一:package的简介和安装
Pillow,是python中强大的处理图形的包,这期我们主要用到其中的image包来对验证码的图像进行处理。需要注意的是早期的pillow包中是没有image的,如果你出现这种情况可以使用pip uninstall Pillow和pip install Pillow重新安装。
os,一般是默认安装的,其作用是用来判读和操作文件夹及其中文件的,作用我们接下来会讲到。
——《皮皮侠》
二:提取验证码的思路
想要做到的是根据一幅验证码的图片,直接输出它对应的数字和字母。乍一看似乎和图片文字识别很相似,但需要注意的是,大多数验证码只有数字和字母,很多时候我们可能需要大量对验证码进行识别,如果套用文字识别的方法效率是及其低下的。那么我们的做法是什么呢?
首先要知道,一张图片是由一个一个的像素块组成的,而构成的验证码字母和数字的这部分像素和整个图片的像素一定会有明显不同。只需要找到构成的验证码字母和数字的这部分像素,就能得到它们的位置坐标(见图2)。
接下来,根据位置坐标切割图片,就可以得到一个字符的图片。这个时候切割下来的图片包含的信息已经很多了,和真正的数字字母基本吻合。
最后。将切割下来的这部分图片和我们准备好的标准的图片进行对比,根据某种规则判断相似度,我们就可以得到最后的验证码。
图表 2(图中数字7的颜色明显不同)
三:实际操作和代码展示
from PIL import Image
import os
import math #加载需要的package
im = Image.open("F:\captcha.jpg")
#打开图片captcha.jpg
#这里的操作是将图片转化为八位像素格式。这种格式下图片的颜色只有256种,便于操作
im2 = Image.new("P",im.size,255)#先设一个全白的图作为底
im.convert("P")
temp = {}
#这个循环的含义是,(y,x)遍历图中每个像素点,如果该点的像素值为220或者227,就将这个点的像素值赋给im2对应位置的像素点,这样的工作实际是简化像素点,避免之后的比对干扰项太多。220和227是构成验证码字母和数字的这部分像素。
for x in range(im.size[1]):
for y in range(im.size[0]):
pix = im.getpixel((y,x))
temp[pix] = pix
if pix == 220 or pix == 227:
im2.putpixel((y,x),0)
inletter = False
#找出每个字母开始位置
foundletter=False
#找出每个字母结束位置
#找出图片中所有分离图像的开始结束位置。遍历图片中的像素点,当每出现一个非白色点,记为该字符开始位置;当新的一列出现全白色点,那么记为结束位置。
start = 0
end = 0
letters = []
for y in range(im2.size[0]):
for x in range(im2.size[1]):
pix = im2.getpixel((y,x))
if pix != 255:
inletter = True
if foundletter == False and inletter == True:
foundletter = True
start = y
if foundletter == True and inletter == False:
foundletter = False
end = y
letters.append((start,end))
inletter=False
图三:识别验证码
至此,我们已经完成了大部分工作。接下来要做的就是图片的比对。对机器来说,最直接简单的比对就是比较两个数是否接近。这里我们采用向量空间图像识别的方法。
class VectorCompare:#计算矢量大小
def magnitude(self,concordance):
total = 0
for word,count in concordance.items():
total += count ** 2
return math.sqrt(total)
#计算矢量间夹角
def relation(self,concordance1, concordance2):
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 buildvector(im):
d1 = {}
count = 0
for i in im.getdata():
d1[count] = i
count += 1
return d1
v = VectorCompare()
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('F:/iconset/%s/'%(letter)):#注意这里的iconset不是上面的集合,而是一个包含10个数字和26个字母图片的文件夹,使用os函数定位读取
temp = []
if img != "Thumbs.db" and img != ".DS_Store":
count = 0
for letter in letters:
im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))
guess = []
for image in imageset:
for x,y in image.items():
if len(y) != 0:
guess.append( ( v.relation(y[0],buildvector(im3)),x) )
guess.sort(reverse=True)
print("",guess[0])
count += 1
#最后输出的结果是:
到此我们的工作就结束了,原理是不是非常简单呢。其实这个代码还可以优化,在取得图片的像素值后,可以通过代码实现自动识别需要的,构成验证码的那部分像素值,而不是通过人工自己去判断,有兴趣的同学可以自己操作哦。
参考文献
1:代码和测试集文件
https://pan.baidu.com/s/1OccTpSlqTHCVi0FWaTTFzw
提取码:iaj6
2:部分代码参考
https://www.shiyanlou.com/courses/364
3:向量空间识别文献参考
http://ondoc.logand.com/d/2697/pdf

