python实现简单验证码识别

2021/6/27 17:23:45

本文主要是介绍python实现简单验证码识别,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

准备工作

首先找到确定这次要识别的验证码   

然后从某网站上下载大量同类型的验证码,人工标记上每个验证码的数值,由于此验证码识别容易就只标记了20个

开始预处理图片

图片是彩色的,我们要先让其变得简单变成灰度图像。

“灰度图像上每个像素的颜色值又称为灰度,指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0。所谓灰度值是指色彩的浓淡程度,灰度直方图是指一幅数字图像中,对应每一个灰度值统计出具有该灰度值的象素数。”

这里就是用的是python的一个PIL的库 安装如下

pip install pillow

使用 from PIL import Image 加载我们下载好的图片 将图片灰度化

img=img.convert("L")

得到了数值范围为0-255的图片由于图片的白色和黄色难以分清 所以要卡一个阈值使得两种颜色可以分开来 多次尝试卡的是220。小于220的变成白色大于220的变成黑色,就完成了图片的二值化。

def ez_map(thresold):
    res=[]
    for i in range(256):
        if i<thresold:
            res.append(1)
        else:
            res.append(0)
    return res 

def pre_hd_ez(path,out_path):
    img=Image.open(path)
    img=img.convert("L")
    #二值
    thresold=230
    table=ez_map(thresold)
    # img=img.convert("1")

    img=img.point(table,'1')
    img.save(out_path)
    return img

接下来要把无关紧要的点处理了,如下图所示可以看下一个像素点周围的颜色,来对其颜色进行修正。如下图中间的黑色很可能白色更适合他。 可以通过一层来判断也可以通过多层来判断,我们管这个步骤叫做降噪。

代码存在硬编码不规范地方日后会进行更正

def get_jz_color(img,x,y,level,layer):
    color_now=img.getpixel((x,y))
    zero=0
    one=0
    all_point=0
    for i in range(0-layer,0+layer+1):
        if x-i<0 or x+i>=img.size[0]:
            continue

        for j in range(0-layer,0+layer+1):
            if y-j<0 or y+j>=img.size[1]:
                continue
            if i==0 and j==0:
                continue
            if img.getpixel((i,j))==0:
                zero+=1
            else:
                one+=1
            all_point+=1
    # return color_now

    # print(color_now)
    # return 0
    #  0 黑
    # return color_now
    if color_now==0:

        if one/all_point>7/8:
            return 1
        else:
            return 0
    if color_now==1:
        if zero/all_point>4/8:
            return 0
        else:
            return 1
    print(color_now)

def pre_jz(img,out_path):

    img_after_table=[]
    for x in range(img.size[0]):
        img_after_table.append([])
        for y in range(img.size[1]):
            num_color=get_jz_color(img,x,y,0,1)
            img_after_table[x].append(num_color)
    draw = ImageDraw.Draw(img)
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            draw.point((i,j),img_after_table[i][j])

    img.save(out_path)
    return img

此时我们得到了黑白分明的图片

分割

由于图片四个数字在图片上是等分的,我们可以直接对图片进行分割得到每个数字一个图片。

def pre_split_img(img,num,name,out_base_path):
    imgs=[]
    wide=img.size[0]
    # print (wide)
    high=img.size[1]
    one=int(wide/4)
    for i in range(num):
        img1=img.crop((i*one,0,(i+1)*one,high))
        imgs.append(img1)
        if out_base_path:
            name1=''.join(name)+str(name[i])
            img1.save(out_base_path+'/'+name[i]+'/'+name1+'.png')
    return imgs

将分割好的图片按照之前标记的结果分成0-9 存到0-9不同的文件夹里面

接下来我们有了各个数字图片的样本。

如何和新来的图片进行匹配?

我们要找到能代替某个数字图片的方法,比如把0这个图片分成6份每一份计算出 黑色像素点/总像素点的值然后 对多有0的图片都如此操作,分别取 分割出来的6份中第一份的平均值,这样的到了能代表0这个图片的6份数值存起来后面用。

def get_block_score(img):
    sum=0
    black=0
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            if img.getpixel((i,j))==0:
                black+=1
            sum+=1

    return black,sum

#  计算特征值
def get_features_vaule_by_img(img):
    wide=img.size[0]
    one_wide = int(wide/2)
    high=img.size[1]
    one_high=int(high/3)
    score_lsit=[]
    for i in range(3):
        for j in range(2):
            img_one=img.crop((j*one_wide,i*one_high,(j+1)*one_wide,(i+1)*one_high))
            black,sum=get_block_score(img_one)

            score_lsit.append(black*1.0/sum)
    return score_lsit

def get_features_vaule(dir_path):
    arry_size=6
    score_lsit_res=[]
    for i in range(arry_size):
        score_lsit_res.append(0)
    sum_file=0
    for root, dirs, files in os.walk(dir_path):
        for f in files:
            if not '.png' in f:
                continue
            img=Image.open(os.path.join(root, f))
            sum_file+=1
            score_lsit=get_features_vaule_by_img(img)
            for i in range(arry_size):
                score_lsit_res[i]=score_lsit[i]+score_lsit_res[i]
    # 求平均值
    for i in range(arry_size):
        score_lsit_res[i]=str(score_lsit_res[i]/sum_file)


    value='\n'.join(score_lsit_res)
    fs=open(dir_path+'feture.txt','w')
    fs.write(value)
    fs.close()
            

识别图片

此时我们拿来一个新的验证码

让这个新的验证码经过 灰度化,二值化,分割,在每个数字分割6份计算黑点/总点的占比,将计算好的 6个值与我们之前给0-9计算的这个值分别进行比较 找出和0-9最相似的数字 这个数字就是我们想要的结果

完整代码如下 获取二维码的网站已经特殊处理

import requests,os
from PIL import Image
from PIL import ImageDraw
import math
def download_image():
    url='xxx'
    for i in range(0,20):
        import time
        time.sleep(1)
        res=requests.get(url).content

        f=open('D:\project/ocr/image/'+str(i)+'test.png','wb')
        f.write(res)
        f.close()
# download_image()
def makefile():
    for i in range(0,10):
        os.makedirs('D:\project/ocr/yangben/'+str(i))


def ez_map(thresold):
    res=[]
    for i in range(256):
        if i<thresold:
            res.append(1)
        else:
            res.append(0)
    return res 

def pre_hd_ez(path,out_path):
    img=Image.open(path)
    img=img.convert("L")
    #二值
    thresold=230
    table=ez_map(thresold)
    # img=img.convert("1")

    img=img.point(table,'1')
    img.save(out_path)
    return img

def get_jz_color(img,x,y,level,layer):
    color_now=img.getpixel((x,y))
    zero=0
    one=0
    all_point=0
    for i in range(0-layer,0+layer+1):
        if x-i<0 or x+i>=img.size[0]:
            continue

        for j in range(0-layer,0+layer+1):
            if y-j<0 or y+j>=img.size[1]:
                continue
            if i==0 and j==0:
                continue
            if img.getpixel((i,j))==0:
                zero+=1
            else:
                one+=1
            all_point+=1
    # return color_now

    # print(color_now)
    # return 0
    #  0 黑
    # return color_now
    if color_now==0:

        if one/all_point>7/8:
            return 1
        else:
            return 0
    if color_now==1:
        if zero/all_point>4/8:
            return 0
        else:
            return 1
    print(color_now)

def pre_jz(img,out_path):

    img_after_table=[]
    for x in range(img.size[0]):
        img_after_table.append([])
        for y in range(img.size[1]):
            num_color=get_jz_color(img,x,y,0,1)
            img_after_table[x].append(num_color)
    draw = ImageDraw.Draw(img)
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            draw.point((i,j),img_after_table[i][j])

    img.save(out_path)
    return img

def pre_split_img(img,num,name,out_base_path):
    imgs=[]
    wide=img.size[0]
    # print (wide)
    high=img.size[1]
    one=int(wide/4)
    for i in range(num):
        img1=img.crop((i*one,0,(i+1)*one,high))
        imgs.append(img1)
        if out_base_path:
            name1=''.join(name)+str(name[i])
            img1.save(out_base_path+'/'+name[i]+'/'+name1+'.png')
    return imgs
# img=pre_hd_ez('D:\project/ocr/image/0/6809.png','D:\project/ocr/image/0/res.png')
# pre_jz(img,'D:\project/ocr/image/0/res1.png')
# makefile()

def get_block_score(img):
    sum=0
    black=0
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            if img.getpixel((i,j))==0:
                black+=1
            sum+=1

    return black,sum

#  计算特征值
def get_features_vaule_by_img(img):
    wide=img.size[0]
    one_wide = int(wide/2)
    high=img.size[1]
    one_high=int(high/3)
    score_lsit=[]
    for i in range(3):
        for j in range(2):
            img_one=img.crop((j*one_wide,i*one_high,(j+1)*one_wide,(i+1)*one_high))
            black,sum=get_block_score(img_one)

            score_lsit.append(black*1.0/sum)
    return score_lsit

def get_features_vaule(dir_path):
    arry_size=6
    score_lsit_res=[]
    for i in range(arry_size):
        score_lsit_res.append(0)
    sum_file=0
    for root, dirs, files in os.walk(dir_path):
        for f in files:
            if not '.png' in f:
                continue
            img=Image.open(os.path.join(root, f))
            sum_file+=1
            score_lsit=get_features_vaule_by_img(img)
            for i in range(arry_size):
                score_lsit_res[i]=score_lsit[i]+score_lsit_res[i]
    # 求平均值
    for i in range(arry_size):
        score_lsit_res[i]=str(score_lsit_res[i]/sum_file)


    value='\n'.join(score_lsit_res)
    fs=open(dir_path+'feture.txt','w')
    fs.write(value)
    fs.close()
            

def pre_img_pipeline():
    for root, dirs, files in os.walk('D:\project/ocr/image'):
        # 遍历文件
        for f in files:
            img=pre_hd_ez(os.path.join(root, f),'D:\project/ocr/image1/'+f)
            img=pre_jz(img,'D:\project/ocr/image2/'+f)
            pre_split_img(img,4,f.replace('.png',''),'D:\project/ocr/yangben')

    for i in range(10):
        get_features_vaule('D:\project/ocr/yangben/'+str(i)+'/')
# pre_img_pipeline()

# 获取0-9的特征值
def get_base_features_vaules(path):
    res={}
    for i in range(10):
        fs=open(path+str(i)+'/feture.txt','r')
        value=fs.read()
        values=value.split('\n')
        values=[float(feature_value) for feature_value in values]
        res[i]=values
        fs.close()
    return res



# 拉去待识别数据
def download_pending_image():
    url='xxx'
    for i in range(1):
        import time
        time.sleep(1)
        res=requests.get(url).content
        path='D:\project/ocr/pending/'+str(i)+'test.png'
        f=open(path,'wb')
        f.write(res)
        f.close()
        img=Image.open(path)

        return path,img
# 关联性计算
# 求向量点积
def add_vectors(a,b):
    res = 0
    for i in range(len(a)):
        res += float(a[i])*float(b[i])
    return res

# 求向量的模
def module_vectors(a):

    return math.sqrt(sum([float(x)**2 for x in a]))

# 求向量夹角余弦值
def get_cos(a,b):
    add_a, add_b = module_vectors(a),module_vectors(b)
    if add_a!=0 and add_b!=0:
        return add_vectors(a,b)/(add_a*add_b)
    return 0

# 
 # 计算一个数字的值
def sb_vaule(pending_score_list,base_features_map):
    res=[]

    max_score=-10.0
    max_key=None
    for key,values in base_features_map.items():
        idx=0
        scores=0


        scores=get_cos(pending_score_list,values)

        if scores>max_score:
            max_score=scores
            max_key=key
        idx+=1
    return max_key



def run():
    #取基础信息 
    base_features_map=get_base_features_vaules('D:\project/ocr/yangben/')
    path,img=download_pending_image()

    img=pre_hd_ez(path,'D:\project/ocr/pending/deal/first_step.png')
    img=pre_jz(img,'D:\project/ocr/pending/deal/second_step.png')
    imgs=pre_split_img(img,4,'','')
    res=''
    for img_one in imgs:
        pending_score_list=get_features_vaule_by_img(img_one)
        # 计算一个数字的值
        res_one=sb_vaule(pending_score_list,base_features_map)
        res+=str(res_one)
    print(res)
    

run()



这篇关于python实现简单验证码识别的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程