验证码去除干扰线

在邦购登陆时,选择了人工检验验证码,这次用机器检测试试。
先说基本逻辑:载入图像,转灰度,二值化,连通域检测,去除连通域小的,根据各连通域的范围切割图像。

先下载图片。

def getcaptcha():
    for i in range(1,10):
        with open('test1-{}.png'.format(i),'wb') as f:
            url ='https://passport.banggo.com/CASServer//custom/loginCode.do'
            res = requests.get(url,stream =True)
            f.write(res.content)

得到的验证码长这样:


可以看到验证码有干扰线,好在干扰线比较细,而且验证码之间没有粘连。看看别家怎么弄得,在python验证码识别
写到了一些验证码识别的方法,但是几乎都是针对像素级别,不过提供了一个思路叫连通域,就是一个笔画里含有的像素较多,删除那些小的联通域就能去除干扰线了。

我看到的大多是自己写的像素级别的函数,既然这是降噪领域的主要思想,就会有现成的轮子啊,直到我发现了scikit-image,有自己的文档,这篇文章进行了少量总结api总结,搬运如下

验证码去除干扰线_第1张图片
api

其中有专门针对连通域的api,
from skimage import measure
labels = measure.label(二值图像,connectivity=None)
通过label_att = measure.regionprops(labels)得到各连通域的属性如下。label_att是一个list,有几个连通域就有几项。


验证码去除干扰线_第2张图片
属性

更好的是skimage还提供了剔除较小连通域的函数,
from skimage import morphology
img1 = morphology.remove_small_objects(ar, min_size=要删除的连通域大小阈值, connectivity=1,in_place=False)
怎么知道连通域的大小呢,就是label属性里的area,第二个bbox用来切割图像简直不要太方便。
二者联合起来就是现成的去掉干扰线+切割图像的方法。

from skimage import io,filters,measure,morphology
import warnings
from matplotlib import pyplot as plt
def skim():
    img = io.imread('1.png',as_gray=True)  #变成灰度图
    thresh = filters.threshold_otsu(img) #自动确定二值化的阈值
    bwimg =(img<=thresh) #留下小于阈值的部分,及黑的部分
    b = morphology.remove_small_objects(bwimg, 40) #去掉小于40的连通域,可以先全局看看连通域的大小和位置后决定去掉的阈值
    labels = measure.label(b)  
    label_att = measure.regionprops(labels)
    arr =[]
    for la in label_att:
        (x,y,w,h) =la.bbox
        #print((x,y,w,h))
        bei = round((h-y)/15)
        if bei >1: #宽度超过30像素的,说明有粘连,从中切开
            for i in range(bei):
                arr.append((x, y+round((h-y)/2)*i, w, y+round((h-y)/2)*(i+1)))
        elif (y>10) and (h<100) :
            arr.append((x, y, w, h))

    b =b*img
    b[np.where(b != 0)] = 1
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        fig = plt.figure()
        for id, (x, y, w, h) in enumerate(arr):
            roi = b[x:w,y:h]
            thr = roi.copy()
            # io.imsave('seg1-{}.jpg',thr)
            ax = fig.add_subplot(1,4,id+1)
            ax.imshow(thr)
    plt.show()

反思:
1.这个验证码相对规整,可以发现他家的验证码多是验证码本身是有色彩的,而干扰线一直是黑色的,可以直接将小于15的像素变成255,效果也不错,但是出现的问题是会把验证码本身连通域打断,需要再次填充。

def ty():
    # for i in range(1,10):
    img = io.imread('{}.png'.format(4))
    img[np.where(img>235)] =255
    img[np.where(img < 15)] = 255
    imgray = color.rgb2gray(img)
    thresh = filters.threshold_otsu(imgray)
    # bwimg =(imgray <= thresh)
    bwimg = morphology.closing(imgray <= thresh, morphology.square(3))
    b = morphology.remove_small_objects(bwimg, 20)
    labels = measure.label(b)
    label_att = measure.regionprops(labels)
    arr =[]

    for la in label_att:
        (x, y, w, h) = la.bbox
        bei = round((h - y) / 15)
        if bei > 1:
            for i in range(bei):
                arr.append((x, y + round((h - y) / 2) * i, w, y + round((h - y) / 2) * (i + 1)))
        elif (y > 10) and (h < 100) :
            arr.append((x, y, w, h))
            if (len(arr)>1) and (h-y)<12 and (arr[-2][3]-arr[-2][1] <12): #干扰线去狠了导致断开验证码断开了要拼接一下
                arr[-1] = ((x, arr[-2][1], w, h))
                del arr[-2]

    b = b * imgray
    b[np.where(b != 0)] = 1
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        fig = plt.figure()
        for id, (x, y, w, h) in enumerate(arr):
            roi = b[x:w, y:h]
            thr = roi.copy()
            # io.imsave('new{}-{}.png'.format(1,id+1), thr)
            ax = fig.add_subplot(1, len(arr), id + 1)
            ax.imshow(thr)
        plt.show()

2.验证码相对规整,而且放置位置也相对固定,可以直接用经验值切割,但是有些比较胖就比较麻烦了。

3.在使用skimage时,确是遇到二值图直接转rgb会变成的黑图或者很灰的图,像这样。

b = b * imgray
b[np.where(b != 0)] = 1
就是把图调亮,因为float类型,[0,1]0是黑色,1是白色,这样存的图就是黑白的了。
另在float转rgb的时候会引发warnings说,向下保存会引起精度下降,用with就好了,官方文档推荐。

4.验证码需观察其特点对症下药比较快。

你可能感兴趣的:(验证码去除干扰线)