python验证码识别实战2

上一篇文章中,我们使用sklearn对验证码进行了识别,为了提高识别率,今天来进行进一步优化。

观察验证码后,发现还可以对其进行旋转处理,这个验证码旋转角度在-30~30之间,那么如何判断旋转角度呢?这里我使用最简单粗暴的判断方式——如果旋转后的字符宽度小于旋转之前,则认为是合理的旋转。但这里还有一个问题需要处理,上一篇文章中我们为了简便直接根据固定的宽度对字符进行了分割,但是分割后字符在小图片中的位置不是固定的,需要手动将其放在中心位置。

首先判断每个字符的边界:

def get_border(img, f=False):
    """获取字符边界"""
    t = 999
    b = 0
    l = 999
    r = 0
    flag = 255 if f else 0
    w, h = img.size
    pixdata = img.load()
    for y in range(h):
        for x in range(w):
            if pixdata[x, y] == flag and y < t:
                t = y
            if pixdata[x, y] == flag and y > b:
                b = y
            if pixdata[x, y] == flag and x < l:
                l = x
            if pixdata[x, y] == flag and x > r:
                r = x
    return l, t, r, b

由于图片大小是30×30,所以我取999作为上、左边界初始值。上面代码中的f参数下面会说。接下来创建一个新的图片,并把提取出来的字符放到图片中心:

def resize_img(img):
    """把提取出来的字符放到新图片中央,方便旋转"""
    i_w = 30
    i_h = 30
    w, h = img.size
    image = Image.new('RGB', (i_w, i_h), (255, 255, 255))
    l = (i_w - w) // 2
    r = l + w
    t = (i_h - h) // 2
    b = t + h
    image.paste(img, (l, t, r, b))
    return image

由于pillow库对图片旋转后使用了黑色对边界进行填充,而我们的原始图片是白底黑字,所以还需要对图片进行反相操作,以免旋转造成的干扰:

def opposite(img):
    # 反相处理,注意传入的是灰度二值化后的图片
    tmp = np.array(img)
    tmp = 255 - tmp
    return Image.fromarray(tmp)

上面get_border函数中参数的作用就是如果传入一张反相处理后的图片,则设其为True来获取边界。接下来旋转图片:

def rotate_img(imgname):
    """将初步切分的图片提取字符并修正旋转"""
    img = Image.open(imgname)
    img = binarizing(img,180)
    border = get_border(img)
    new = img.crop(border)
    new2 = resize_img(new)
    new2 = binarizing(new2,180)
    result = []
    o_l, o_t, o_r, o_b = get_border(new2)
    w = o_r - o_l
    new3 = opposite(new2)
    for ro in range(-30, 31, 3):
        # 一般旋转角度在-30到30之间,可以通过修改步长来精细控制
        tmp = new3.rotate(ro)
        l, t, r, b = get_border(tmp, f=True)
        if r - l <= w and abs(ro) > 9:
            # 如果旋转后字符宽度变小了,则认为需要旋转。并且角度要大于9
            w = r - l  # 这句话保证最合适的旋转度数在最后,即最窄
            result.append(ro)
    final_ro = result[-1] if result else 0
    return opposite(new3.rotate(final_ro))

关于binarizing函数请看上一篇文章。旋转图片如下: img 可以看出,逆时针旋转27度时,字符就“立”起来了。当我们把字符旋转之后,再使用相同的代码进行预测,正确率如下:

knn score: 0.9
bayes score: 0.705
description tree score: 0.755

正确率提升了不少。