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
函数中f
参数的作用就是如果传入一张反相处理后的图片,则设其为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
函数请看上一篇文章。旋转图片如下:
可以看出,逆时针旋转27度时,字符就“立”起来了。当我们把字符旋转之后,再使用相同的代码进行预测,正确率如下:
knn score: 0.9
bayes score: 0.705
description tree score: 0.755
正确率提升了不少。