字符图像识别——数字字母混合

须要识别的验证码图像,其中包含 4 个字符(数字字母)python

验证码

验证码图片来源:http://my.cnki.net/elibregist...网络

思路

  1. 灰度化:将图像转为灰度图像,即一个像素只有一种色阶(有 256 种不一样灰度),值为 0 表示像素最黑,值为 255 表示像素最白。
  2. 二值化:将图像转为黑白图像,即一个像素只有黑白两种状态,不是黑就是白,没有灰色,值为 0 表示像素最黑,值为 1 表示像素最白.
  3. 图像转字符串:利用工具将图像中的字符串识别出来

前面两步都是对图像进行识别前处理,目的是提升计算机识别的准确度,毕竟计算机自己不能理解图像,一个像素值的微小变化都有可能致使错误识别机器学习

代码

import tesserocr
from PIL import Image

image = Image.open("87FW.jpg")
# 灰度化
image = image.convert("L")
# 二值化,传入的是数字 1,默认阈值是 127。通常不推荐使用,由于不够灵活
# image = image.convert("1")

# 另外一种二值化。自定义灰度,将灰度值在 115 以上的设置 1(白色),其它设为 0(黑色),至关于将阈值设置成了 115
table = [1] * 256
for i in range(256):
    table[i] = 0
    if i > 115:
        break

image = image.point(table, "1")

print(tesserocr.image_to_text(image))

打印:工具

87FW

所谓的阈(yu)值是指将不一样的像素值分开的那个临界值学习

上面的代码没有保存图片,为了直观得看到通过不一样的处理后图像的区别,下面展现的是两张图像分别是灰度处理和二值化(阈值 115)后的图像测试

灰度处理

二值化(阈值 115)

下面将每种不一样阈值的图像保存至本地,主要代码以下:优化

...
image = Image.open("87FW.jpg")
image = image.convert("L")
table = [1] * 256
for i in range(256):
    table[i] = 0
    image.point(table, "1").save(f"87FW_{i}.jpg")

阈值为 0 表明将全部像素处理成白色(没有黑色);阈值为 255 表明将全部像素处理成黑色。spa

不一样阈值的图片

能够发现阈值设置得越低,白色越多,能看获得的验证码(黑色)就少了,由于大部分灰度都处理成白色;反之,若阈值设置越大,黑色越多,更多的干扰像素处理成和验证码同样的黑色。.net

如下是将上面不一样阈值的图片制做成的一个 gif 动态图像,能够看到若是阈值设定在 0 至 255 这个过程当中,验证码会呈现出不一样效果code

阈值递增的动态图像

阈值是一个很难把控的关键,阈值设置大或小都会影响识别的准确性,如下是遍历全部阈值,测试阈值在哪一个区间能够识别出正确的验证码。注:因为没有作优化,整个过程会比较慢

>>> for i in range(256):
...     if tesserocr.image_to_text(Image.open(f"87FW_{i}.jpg")).strip()=="87FW":
...             print(i, end=" ")
...
109 110 112 113 114 115 116 117 118 119 120 122 123 124 169 170 171 172 173

在 256 个阈值中只有 19 个(不足 7.42%)阈值能够正确识别出验证码,仔细察觉能够发现阈值区间被分红了多个,分别是 109~1十、112~120、122~12四、169~173,说明阈值区间不必定具备连续性。更糟糕的是,不一样的验证码图片,能准确识别出其中验证码的阈值的数量、区间范围、区间数等都极可能不一样。固然还有不少问题,好比选择一个“不恰当”的阈值致使图像处理过分,只识别出其中 3 个字符,不要试图随机添加一个字母或数字,由于须要考虑具体是哪一个位置的字符没识别出来,这样瞎猜几乎是很难一次就命中的,好点的作法是:当识别出来的字符不足时能够尝试换一个阈值处理图像,因此能识别出验证码是几率事件。毕竟在正常的人机识别中,识别一个验证码一般只有一次机会,识别错了就会出现新的验证码,没有换阈值再从新试一次的机会,不过好在一般阈值的范围都是能够缩小的,好比能够忽略小于 70 和大于 200 的这些图像处理过分的阈值(正常人都很难识别是什么数字、字母),这样能命中的几率就会大大提升。

image.convert("1") 的默认阈值是 127,在上面 19 个能够准确识别验证码的阈值中没有 127,这也就是为何直接使用 image.convert("1") 方法二值化的图像没法被准确识别出其中的验证码

上面的验证码还算容易处理的,若是干扰像素的灰度值与验证码灰度差异比较大,可用上面的方法;但若是遇到干扰线条的灰度与验证码差很少、验证码重叠等状况,上面对图像仅作简单处理的方法就很难奏效了。这时就须要用到机器学习技术对识别器进行训练,据说识别率几乎 100%!

参考资料:

  • 《Python3网络爬虫开发实战》——8.1 图形验证码的识别