向量空间识别验证码

Posted by mjTree on April 11, 2024

更新于:2024-04-11 22:00

一、验证码识别

平时在登录一些网站账号时,会让进行验证码输入的操作,这步操作是为了防止机器人或者爬虫之类非人类角色的登录,避免服务器资源的浪费。但是早在几年前,破解验证码的方案就已经非常成熟了,网上还有很多开源免费的工具服务等。本篇文章要介绍一种识别验证码算法,但不涉及什么深度学习的CV算法,算法核心是采用了相似度对比逻辑,完成对验证码的识别。

二、向量空间逻辑识别

VerificationCode

向量空间识别逻辑:对于上面这张验证码,首先对其进行二值化处理,将图片变成黑白照片,然后把里面字母和数字切割成一张张图标,之后再提取每张图标中像素点的数值并存储为一个向量,类似<1,2,3,4>这样的值。最后再拿去和训练集(做好标签的验证码图标)去对比进行相似度计算。计算两个向量的夹角cos值,当cos值越大则说明夹角越小,两者越相似,最后选取值最大的对应的图标名作为识别出来的结果。

1、二值化

在进行二值化操作时,需要设定界限值,就是把像素点置黑或者置白。上面验证码图片中除了必要的红色还有其他杂色,因此需要统计一下图片中各类颜色点的数目。

from PIL import Image

im = Image.open('0q1d10.gif')
'''将图片转换为8位像素模式'''
im.convert('P')
imList = im.histogram()
values = {}

for i in range(256):
    values[i] = imList[i]
for j,k in sorted(values.items(), key=lambda x:x[1],reverse=True)[:10]:
    print(j,k)

通过上面代码可以得到图片中最多的十种颜色,其中 220 与 227 是我们需要的红色和灰色,由此我们就找到了界限值。然后就可以进行二值化操作。

from PIL import Image

im = Image.open('0q1d10.gif')
'''将图片转换为8位像素模式'''
im.convert('P')
im2 = Image.new('P', im.size, 255)
temp = {}
'''构造黑白二值图片'''
for x in range(im.size[1]):
    for y in range(im.size[0]):
        pix = im.getpixel((y,x))
        temp[pix] = pix
        '''220和227是需要的红色和灰色'''
        if pix==220 or pix==227:
            im2.putpixel((y,x),0)
im2.show()

2、图片切割

此时验证码已经被二值化了(像素点值只有0或255),我们需要把 0、Q、1、b、1、0 这六个字符切割出来,再分别拿去和训练集中数据进行对比。而切割思路就是从左向右走,两层for循环即可,先列再行的遍历方式。

'''获取每个字符开始和结束的列序号'''
inletter = False
foundletter = False
start = 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
print(letters)

代码思路:向右走首先碰到 0 的最左端的黑色像素,然后插个小旗子标记开始位置,之后接着向右边走,走到刚好过了 0 的最右端黑色像素的白色像素点,插上另一个小旗子标记结束位置。方法相同接着遇到 Q、1、D、1、0 这后面五个字符并且插好小旗子记录列标的位置,切割的时候就开始列标的左上端和结束列标的右下端两个点开始切割。

im = im.crop((letter[0],0,letter[1],im2.size[1]))

这里有个疑问就是为什么字符头顶和脚底的白色区域切割掉,如果要去除的话需要写三层循环来操作。这也是这个项目的缺点,就是只能针对某一个应用的验证码去进行识别。要是处理其他应用的验证码时,会因为尺寸问题似的在相似度计算时误差很大,这里暂时先不考虑这个问题。

3、图形向量化

对切割完成的小图标需要对其数值化,因此我们需要把图片型数据变成数值型数据让计算机去进行操作。向量化过程就是图片的像数值存储在python字典类型当中便于后期操作。

'''将图片转换为矢量'''
def buildvector(im):
    d1 = {}
    count = 0
    for i in im.getdata():
        d1[count] = i
        count += 1
    return d1

4、向量相似度计算

这里建立一个类对象方便后面代码的调用,关于向量空间的验证码识别不用觉得很厉害的样子,你可以认为就是向量<1,2>和<2,1>的夹角cos值计算就行了。顺便说一下它和很火热的机器学习、深度学习的图像识别的对比。

它不需要大量的训练迭代;不用担心过拟合现象;可以随时添加移除错误的数据查看效果;易理解和实现;提供分级结果;可以查看最接近的多个匹配;对于无法识别的东西只要加入到搜索引擎中,马上就能识别了。缺点就是分类的速度比神经网络慢很多,它不能找到自己的方法解决问题还有上面提到的。

import math
class VectorCompare:
    '''计算矢量大小'''
    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):
        topvalue = 0
        for word, count in concordance1.items():
            if concordance2.get(word):
                topvalue += count * concordance2[word]
        return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))

想了解更多向量空间搜索引擎的原理可以参考这篇 英文文献完整代码 在此,数据集 可以在此获取。

三、小结

本篇文章介绍了一种简单思路的识别验证码逻辑,通过对验证码图片的二值化后的像素点转换成向量值,计算向量的cos值进行验证码内容识别。

猎隼

猎隼🦅
    猎隼(拉丁学名:Falco cherrug),季候鸟,大型猛禽。主要以鸟类和小型动物为食。分布广泛,中国和中欧、
    北非、印度北部、蒙古常见。中国国家二级重点保护动物。猎隼体重510-1200克,体长278-779毫米。猎隼是体大
    且胸部厚实的浅色隼。颈背偏白,头顶浅褐。头部对比色少,眼下方具不明显黑色线条,眉纹白。上体多褐色而略具
    横斑,与翼尖的深褐色成对比。尾具狭窄的白色羽端。下体偏白,狭窄翼尖深色,翼下大覆羽具黑色细纹。翼比游隼
    形钝而色浅。幼鸟上体褐色深沉,下体满布黑色纵纹。叫声似游隼但较沙哑。

保护级度:
    EN——濒危物种,国家二级重点保护动物。

老博客原文链接