在上一篇文章 中,我们使用sklearn对验证码进行了识别,为了提高识别率,今天来进行进一步优化。
观察验证码后,发现还可以对其进行旋转处理,这个验证码旋转角度在-30~30之间,那么如何判断旋转角度呢?这里我使用最简单粗暴的判断方式——如果旋转后的字符宽度小于旋转之前,则认为是合理的旋转。但这里还有一个问题需要处理,上一篇文章中我们为了简便直接根据固定的宽度对字符进行了分割,但是分割后字符在小图片中的位置不是固定的,需要手动将其放在中心位置。
首先判断每个字符的边界:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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
参数下面会说。接下来创建一个新的图片,并把提取出来的字符放到图片中心:
1 2 3 4 5 6 7 8 9 10 11 12 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库对图片旋转后使用了黑色对边界进行填充,而我们的原始图片是白底黑字,所以还需要对图片进行反相操作,以免旋转造成的干扰:
1 2 3 4 5 def opposite (img) : tmp = np.array(img) tmp = 255 - tmp return Image.fromarray(tmp)
上面get_border
函数中f
参数的作用就是如果传入一张反相处理后的图片,则设其为True
来获取边界。接下来旋转图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 ): tmp = new3.rotate(ro) l, t, r, b = get_border(tmp, f=True ) if r - l <= w and abs(ro) > 9 : w = r - l result.append(ro) final_ro = result[-1 ] if result else 0 return opposite(new3.rotate(final_ro))
关于binarizing
函数请看上一篇文章。旋转图片如下: 可以看出,逆时针旋转27度时,字符就“立”起来了。当我们把字符旋转之后,再使用相同的代码进行预测,正确率如下:
1 2 3 knn score: 0.9 bayes score: 0.705 description tree score: 0.755
正确率提升了不少。