垂直投影法分割验证码

垂直投影法分割验证码

背景:

在在上一篇的文章中,我们获得了能够破解验证码的完整的步骤的程序,虽然很简单,但是整体的框架已经设计完毕,接下来只要对其中的算法进行改进即可。
老规矩,先附上缺的上一次的链接地址:
神经网络3.0(验证码识别)
其中是上一次的代码和五张测试用的图片。(为什么只有五张原因见上一次的文章结尾)至于百度云盘的代码可以从上一次的文章末尾找,随意下载,永久有效。
因为上一次的识别率为零。和学姐分享了一下完整的过程以后,学姐给了以下的几个建议:

  1. 字符分割的方法太简单(哈哈,我是故意的,就是想试试看有没有效果);
  2. 神经网络也太落后,哪有三层就想搞定这种事情;
    基于学姐给的建议以及自己查阅资料以后的结论,我会尝试用更多的算法来对字符进行分割。

分割方法的挑选

分割字符最常用的方法就是:垂直投影法、连通域分析法。其中连通域分析法对于非粘连字符的分割有很好的效果,但是对于粘连字符,就没有任何的效果。
而垂直投影法对于粘连字符可能会有很好的效果。但是,因为我们选用的验证码粘连严重,所以对于垂直投影法的使用,可能会有很大的影响,即,两个字符的连接部位不是像素点最少的位置,很可能造成分割位置的偏移。
但是,不论怎么说,让我们来尝试一下分割效果。

垂直投影法简介:

垂直投影法,简单来说就是将每一列上的符合要求的像素都“沉到”图片底部,并找到最薄弱的位置作为切割的位置。换一种说法就是统计出每一列上的符合要求的像素点的个数,并找到其中的薄弱点进行切割。
垂直投影法有一个重要的假设就是,两个字符的连接位置相对于字符本身位置的像素点的个数较少,也就是可以通过比较相对位置的像素点的个数得到图片的分割位置,如下图所示:
垂直投影法示意图
所以,这一次的字符分割,我们采用垂直投影法进行分割。

算法设计:

1.为了减少不必要的开销,我们首先将图片中的空白部分从验证码中剔除,只保留包含所有的验证码的有效像素点的部分;
2.接着我们通过遍历得到已经分割以后的图片中每一列的有效像素点的个数,并存储在链表中;
3.在相对的四分之一、二分之一、四分之三的相对位置,我们寻找最小的像素点的位置,这一点的目的是为了找到相对位置的最好的分割点,进行直线切割

代码设计:

代码只展示核心部分,完整代码还是老规矩,放入到百度云盘和CSDN上分享出来供大家下载。

def vertical_projection(img):
    rows, cols = img.shape
    maxleft = 100
    maxright = 0
    # 寻找最左边的点
    for j in range(cols):
        for k in range(rows):
            if img[k][j] == 0:
                maxleft = j
                break
        if maxleft != 100:
            break
    # 同样算法,从右边开始,找到最右的点
    for j in range(-cols, 0):
        for k in range(rows):
            if img[k][-j - 1] == 0:
                maxright = -j
                break
        if maxright != 0:
            break
    # 基本想法:
    # 1.取得图片内容的最左的一列,加上最右的一列,将两者中间即为图片中的字符所在的位置
    # 2.将这个范围内的每一列为黑的像素点的数目进行统计,并存放在数组中
    number_of_black = []
    for j in range(maxleft, maxright):
        number = 0
        for k in range(rows):
            if img[k][j] == 0:
                number += 1

        number_of_black.append(number)
    base = int((maxright - maxleft) / 8)
    addition = int((maxright - maxleft) / 4)
    section = []
    for j in range(1, 4):
        min = base + addition * (j - 1)
        for k in range(base + addition * (j - 1), base + addition * j):
            if number_of_black[k] < number_of_black[min]:
                min = k
        section.append((min + maxleft))
    return maxleft, maxright, section
    pass

解释一下为什么要自行设计min函数,而不使用python提供的min函数。主要是因为我们要找到相对的位置,但是相对的位置可能有多个像素点个数相同的点,但是我考虑一下就采用了最小的点中最靠后的点的位置(为什么这样选择,是尝试以后发现这样的效果相对较好,所以这样分割)。这样就得到了分割以后的图片,如下图所示:
在这里插入图片描述
老规矩,完整代码可以从百度云盘上下载:
垂直投影法
提取码:57dj

分析与反思:

可以看到相对于上一次的分割,这一次的分割得到的图片有些可以说是很棒,但是另外一些依旧不尽人意。可以看到有些字符依旧粘连在一起;而更多的字符却是被多分割了很大一部分,造成了缺失。
对于前一种情况,可以清楚地看到是因为连接处的像素点的个数大于字符的像素点的个数,造成了字符分割位置的很大的偏差。
而对于后一种情况,则是因为分割字符的时候采用了直线分割,没有对字符的相对的位置进行分析,来提高分割的效果。换一句话说,字符的分割不应该是一条直线,每一个字符的分割位置应该对应每一行有不同的分割结点对。
对于前一种问题我没有好的办法,或者说很难有好的解决的算法,但是对于后一种情况,我确实知道一种很好的算法,能够得到每一行的切割位置——滴水法。
下一篇,我会用滴水法来和垂直投影法结合得到更好的字符分割的方法。

猜你喜欢

转载自blog.csdn.net/weixin_43666945/article/details/89348576
今日推荐