新闻动态
2021年春节放假通知
图片验证码识别包月产品特价来了!
2020年春节放假通知
图片验证码识别-包月版产品说明
2019年国庆放假通知!
对公转账支付和发票开具说明
春季验证码专场活动详情!
3.15钜惠活动详情!
验证码识别接口免费测试步骤
中国6家公司上榜全球AI初创企业100强

PaddlePaddle怎么用验证码识别?

发布时间:2019-01-20 21:27:54

  数据集使用的验证码是方正系统,通过观察大量的验证码发现,该系统的验证码识别只有小写字母和数字,这样分类就少了很多。

验证码识别


  通过上表的说明进行裁剪验证码。


  下载验证码


  编写一个下载验证码的程序DownloadYanZhengMa.py,这里需要传入保存路径和下载的最大数量。


# -*- coding=utf-8 -*-

import re

import uuid

import requests

import os


class DownloadYanZhengMa:

    def __init__(self,save_path,download_max):

        self.download_sum=0

        self.save_path=save_path

        self.download_max=download_max


    def downloadImages(self):

        try:

            pic=requests.get('http://jwsys.ctbu.edu.cn/CheckCode.aspx?', timeout=500)

            pic_name=self.save_path+'/' +str(uuid.uuid1()) + '.png'

            with open(pic_name,'wb') as f:

                f.write(pic.content)

            self.download_sum+=1

            print('已经下载完成'+str(self.download_sum)+'张验证码')

            if self.download_sum>=self.download_max:

                return

            else:

                return self.downloadImages()

        except Exception,e:

            print('当前图片无法下载%s'%e)

            return self.downloadImages()


if __name__== '__main__':

    downloadYanZhenMa = DownloadYanZhengMa(save_path='images/download_yanzhengma', download_max=500)

    downloadYanZhenMa.downloadImages()


  修改验证码的文件名

  上一部分下载将500张图片下载到了images/download_yanzhengma文件夹中, 待下载完成之后,需要做以下几件事:


  1:将每一张验证码命名为其对应的验证码的内容,这是一个庞大的工作。


  2:将命名好的验证码剪切到images/src_yanzhengma/文件夹中


  修改验证码的文件名是一个非常费时的工程,如果快速正确命名,那要发挥你们的想象力了,笔者同时也提供了数据集,这个不用担心. 正确命名是非常重要的, 在一个部分会讲到.


  裁剪验证码


  编写了CropYanZhengMa.py来进行验证码的裁剪,注意以下两点:


  1:验证码的命名一定要对于验证码的内容,这个是最重要也是最费事的。


  2:裁剪的验证码会单独存放在自己对应的文件夹中


# coding=utf-8

import os

import uuid


from PIL import Image



class YanZhenMaUtil():

    def __init__(self):

        pass


    def splitimage(self,src, dstpath):

        # 分割路径,并获得文件名

        name = src.split('/')

        name1 = name[name.__len__() - 1]

        name2 = name1.split('.')[0]

        # 加载四个文字的名字

        l1 = list(name2)

        img = Image.open(src)

        # 按照四张图片的大小裁剪

        box1 = (5, 0, 17, 27)

        box2 = (17, 0, 29, 27)

        box3 = (29, 0, 41, 27)

        box4 = (41, 0, 53, 27)

        # 为每一张图片提供自己的文件夹

        path1 = dstpath + '/%s' % l1[0]

        path2 = dstpath + '/%s' % l1[1]

        path3 = dstpath + '/%s' % l1[2]

        path4 = dstpath + '/%s' % l1[3]

        # 创建对应的文件夹

        if not os.path.exists(path1):

            os.makedirs(path1)

        if not os.path.exists(path2):

            os.makedirs(path2)

        if not os.path.exists(path3):

            os.makedirs(path3)

        if not os.path.exists(path4):

            os.makedirs(path4)

        # 裁剪图片并保存

        img.crop(box1).resize((36, 36), Image.ANTIALIAS).save(path1 + '/%s.png' % uuid.uuid1())

        img.crop(box2).resize((36, 36), Image.ANTIALIAS).save(path2 + '/%s.png' % uuid.uuid1())

        img.crop(box3).resize((36, 36), Image.ANTIALIAS).save(path3 + '/%s.png' % uuid.uuid1())

        img.crop(box4).resize((36, 36), Image.ANTIALIAS).save(path4 + '/%s.png' % uuid.uuid1())



if __name__ == '__main__':

    # 原图片路径

    root_path = './images/src_yanzhengma/'

    # 裁剪后图片的路径

    dstpath = './images/dst_yanzhengma/'

    # 获取所以图片

    imgs = os.listdir(root_path)

    yanZhenMaUtil = YanZhenMaUtil()

    # 开始裁剪

    for src in imgs:

        src = root_path + src

yanZhenMaUtil.splitimage(src=src, dstpath=dstpath)

  生成图像列表


  编写一个生成CreateDataList.py的程序,然后我们要把刚才的验证码图片生成一个图像列表。


  这里就用到了一个部分裁剪后的数据集,通过传入../images/dst_yanzhengma这个路径,会把之前裁剪好的所有图像都生成它的相对路径,给之后的训练程序使用.


# coding=utf-8

import os

import json


class CreateDataList:

    def __init__(self):

        pass


    def createDataList(self, data_root_path):

        # # 把生产的数据列表都放在自己的总类别文件夹中

        data_list_path = ''

        # 所有类别的信息

        class_detail = []

        # 获取所有类别

        class_dirs = os.listdir(data_root_path)

        # 类别标签

        class_label = 0

        # 获取总类别的名称

        father_paths = data_root_path.split('/')

        while True:

            if father_paths[father_paths.__len__() - 1] == '':

                del father_paths[father_paths.__len__() - 1]

            else:

                break

        father_path = father_paths[father_paths.__len__() - 1]


        all_class_images = 0

        # 读取每个类别

        for class_dir in class_dirs:

            # 每个类别的信息

            class_detail_list = {}

            test_sum = 0

            trainer_sum = 0

            # 把生产的数据列表都放在自己的总类别文件夹中

            data_list_path = "../data/%s/" % father_path

            # 统计每个类别有多少张图片

            class_sum = 0

            # 获取类别路径

            path = data_root_path + "/" + class_dir

            # 获取所有图片

            img_paths = os.listdir(path)

            for img_path in img_paths:

                # 每张图片的路径

                name_path = path + '/' + img_path

                # 如果不存在这个文件夹,就创建

                isexist = os.path.exists(data_list_path)

                if not isexist:

                    os.makedirs(data_list_path)

                # 每10张图片取一个做测试数据

                if class_sum % 10 == 0:

                    test_sum += 1

                    with open(data_list_path + "test.list", 'a') as f:

                        f.write(name_path + "\t%d" % class_label + "\n")

                else:

                    trainer_sum += 1

                    with open(data_list_path + "trainer.list", 'a') as f:

                        f.write(name_path + "\t%d" % class_label + "\n")

                class_sum += 1

                all_class_images += 1

            # 说明的json文件的class_detail数据

            class_detail_list['class_name'] = class_dir

            class_detail_list['class_label'] = class_label

            class_detail_list['class_test_images'] = test_sum

            class_detail_list['class_trainer_images'] = trainer_sum

            class_detail.append(class_detail_list)

            class_label += 1

        # 获取类别数量

        all_class_sum = class_dirs.__len__()

        # 说明的json文件信息

        readjson = {}

        readjson['all_class_name'] = father_path

        readjson['all_class_sum'] = all_class_sum

        readjson['all_class_images'] = all_class_images

        readjson['class_detail'] = class_detail

        jsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))

        with open(data_list_path + "readme.json",'w') as f:

            f.write(jsons)



if __name__ == '__main__':

    createDataList = CreateDataList()

createDataList.createDataList('../images/dst_yanzhengma')

读取数据


  因为是使用自定义数据集,所以同样使用到reader.py,但是这次有点不一样,这次使用的单通道的灰度图,所以我们的参数要变一变,把is_color的参数变成False,因为默认的是True。


paddle.v2.image.simple_transform(im,resize_size,crop_size,is_train,is_color = True,mean = None )


参数:


    im(ndarray) - HWC布局的输入图像。

    resize_size(int) - 调整大小的图像的较短边缘长度。

    crop_size(int) - 裁剪尺寸。

    is_train(bool) - 是否训练。

    is_color(bool) - 图像是否是彩色的。

    mean(numpy array | list) - 平均值,可以是每个通道的元素平均值或平均值。

MyReader代码


为了做一下区分,我把命名改成了MyReade.py,在旧版本的该程序是有bug的,如果读者想使用这个程序,想要更新本地PaddlePaddle的版本,旧版本的bug是没有对灰度的图像处理,所以在做这个灰度的验证码时会报错。


# -*- coding=utf-8

from multiprocessing import cpu_count

import paddle.v2 as paddle


class MyReader:

    def __init__(self,imageSize):

        self.imageSize = imageSize


    def train_mapper(self,sample):

        '''

        map image path to type needed by model input layer for the training set

        '''

        img, label = sample

        img = paddle.image.load_image(img)

        img = paddle.image.simple_transform(img, 70, self.imageSize, True)

        return img.flatten().astype('float32'), label


    def test_mapper(self,sample):

        '''

        map image path to type needed by model input layer for the test set

        '''

        img, label = sample

        img = paddle.image.load_image(img)

        img = paddle.image.simple_transform(img, 70, self.imageSize, False)

        return img.flatten().astype('float32'), label


    def train_reader(self,train_list, buffered_size=1024):

        def reader():

            with open(train_list, 'r') as f:

                lines = [line.strip() for line in f]

                for line in lines:

                    img_path, lab = line.strip().split('\t')

                    yield img_path, int(lab)


        return paddle.reader.xmap_readers(self.train_mapper, reader,

                                          cpu_count(), buffered_size)


    def test_reader(self,test_list, buffered_size=1024):

        def reader():

            with open(test_list, 'r') as f:

                lines = [line.strip() for line in f]

                for line in lines:

                    img_path, lab = line.strip().split('\t')

                    yield img_path, int(lab)


        return paddle.reader.xmap_readers(self.test_mapper, reader,

cpu_count(), buffered_size)

使用PaddlePaddle开始训练


首先需要定义一个神经网络vgg.py


# coding=utf-8

import paddle.v2 as paddle


# ***********************定义VGG卷积神经网络模型***************************************

def vgg_bn_drop(datadim):

    # 获取输入数据大小

    img = paddle.layer.data(name="images",

                            type=paddle.data_type.dense_vector(datadim))


    def conv_block(ipt, num_filter, groups, dropouts, num_channels=None):

        return paddle.networks.img_conv_group(

            input=ipt,

            num_channels=num_channels,

            pool_size=2,

            pool_stride=2,

            conv_num_filter=[num_filter] * groups,

            conv_filter_size=3,

            conv_act=paddle.activation.Relu(),

            conv_with_batchnorm=True,

            conv_batchnorm_drop_rate=dropouts,

            pool_type=paddle.pooling.Max())

#如下64指的是滤波器的个数,2指的这个模块有两个卷积层,[0.3,0]指的是两个卷积层后的dropout比率的大小,3指的3个通道。

    conv1 = conv_block(img, 64, 2, [0.3, 0], 3)

    conv2 = conv_block(conv1, 128, 2, [0.4, 0])

    conv3 = conv_block(conv2, 256, 3, [0.4, 0.4, 0])

    conv4 = conv_block(conv3, 512, 3, [0.4, 0.4, 0])

    conv5 = conv_block(conv4, 512, 3, [0.4, 0.4, 0])


    drop = paddle.layer.dropout(input=conv5, dropout_rate=0.5)

    fc1 = paddle.layer.fc(input=drop, size=512, act=paddle.activation.Linear())

    bn = paddle.layer.batch_norm(input=fc1,

                                 act=paddle.activation.Relu(),

                                 layer_attr=paddle.attr.Extra(drop_rate=0.5))

    fc2 = paddle.layer.fc(input=bn, size=512, act=paddle.activation.Linear())


    # 通过神经网络模型再使用Softmax获得分类器(全连接)

    out = paddle.layer.fc(input=fc2,

                          size=10,

                          act=paddle.activation.Softmax())

    return out

训练网络模型


# coding:utf-8

import sys

import os

import paddle.v2 as paddle

from MyReader import MyReader

from vgg import vgg_bn_drop

from cnn import convolutional_neural_network



class PaddleUtil:

    # ***********************初始化操作***************************************

    def __init__(self):

        # 初始化paddpaddle,只是用CPU,把GPU关闭

        paddle.init(use_gpu=False, trainer_count=2)


    # **********************获取参数***************************************

    def get_parameters(self, parameters_path=None, cost=None):

        if not parameters_path:

            # 使用cost创建parameters

            if not cost:

                raise NameError('请输入cost参数')

            else:

                # 根据损失函数创建参数

                parameters = paddle.parameters.create(cost)

                print "cost"

                return parameters

        else:

            # 使用之前训练好的参数

            try:

                # 使用训练好的参数

                with open(parameters_path, 'r') as f:

                    parameters = paddle.parameters.Parameters.from_tar(f)

                print "使用parameters"

                return parameters

            except Exception as e:

                raise NameError("你的参数文件错误,具体问题是:%s" % e)


    # ***********************获取训练器***************************************

    # datadim 数据大小

    def get_trainer(self, datadim, type_size, parameters_path):

        # 获得图片对于的信息标签

        label = paddle.layer.data(name="label",

                                  type=paddle.data_type.integer_value(type_size))


        # 获取全连接层,也就是分类器

        out = vgg_bn_drop(datadim=datadim, type_size=type_size)

        # out = convolutional_neural_network(datadim=datadim, type_size=type_size)


        # 获得损失函数

        cost = paddle.layer.classification_cost(input=out, label=label)


        # 获得参数

        if not parameters_path:

            parameters = self.get_parameters(cost=cost)

        else:

            parameters = self.get_parameters(parameters_path=parameters_path)


        '''

        定义优化方法

        learning_rate 迭代的速度

        momentum 跟前面动量优化的比例

        regularzation 正则化,防止过拟合

        '''

        # ********************如果使用VGG网络模型就用这个优化方法******************

        optimizer = paddle.optimizer.Momentum(

            momentum=0.9,

            regularization=paddle.optimizer.L2Regularization(rate=0.0005 * 128),

            learning_rate=0.0001 / 128,

            learning_rate_decay_a=0.1,

            learning_rate_decay_b=128000 * 35,

            learning_rate_schedule="discexp", )


        # ********************如果使用LeNet-5网络模型就用这个优化方法******************

        # optimizer = paddle.optimizer.Momentum(learning_rate=0.00001 / 128.0,

        #                                       momentum=0.9,

        #                                       regularization=paddle.optimizer.L2Regularization(rate=0.005 * 128))


        '''

        创建训练器

        cost 分类器

        parameters 训练参数,可以通过创建,也可以使用之前训练好的参数

        update_equation 优化方法

        '''

        trainer = paddle.trainer.SGD(cost=cost,

                                     parameters=parameters,

                                     update_equation=optimizer)

        return trainer


    # ***********************开始训练***************************************

    def start_trainer(self, trainer, num_passes, save_parameters_name, trainer_reader, test_reader):

        # 获得数据

        reader = paddle.batch(reader=paddle.reader.shuffle(reader=trainer_reader,

                                                           buf_size=50000),

                              batch_size=128)

        # 保证保存模型的目录是存在的

        father_path = save_parameters_name[:save_parameters_name.rfind("/")]

        if not os.path.exists(father_path):

            os.makedirs(father_path)


        # 指定每条数据和padd.layer.data的对应关系

        feeding = {"image": 0, "label": 1}


        # 定义训练事件

        def event_handler(event):

            if isinstance(event, paddle.event.EndIteration):

                if event.batch_id % 100 == 0:

                    print "\nPass %d, Batch %d, Cost %f, %s" % (

                        event.pass_id, event.batch_id, event.cost, event.metrics)

                else:

                    sys.stdout.write('.')

                    sys.stdout.flush()


            # 每一轮训练完成之后

            if isinstance(event, paddle.event.EndPass):

                # 保存训练好的参数

                with open(save_parameters_name, 'w') as f:

                    trainer.save_parameter_to_tar(f)


                # 测试准确率

                result = trainer.test(reader=paddle.batch(reader=test_reader,

                                                          batch_size=128),

                                      feeding=feeding)

                print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)


        '''

        开始训练

        reader 训练数据

        num_passes 训练的轮数

        event_handler 训练的事件,比如在训练的时候要做一些什么事情

        feeding 说明每条数据和padd.layer.data的对应关系

        '''

        trainer.train(reader=reader,

                      num_passes=num_passes,

                      event_handler=event_handler,

                      feeding=feeding)



if __name__ == '__main__':

    # 类别总数

    type_size = 33

    # 图片大小

    imageSize = 32

    # 总的分类名称

    all_class_name = 'dst_yanzhengma'

    # 保存的model路径

    parameters_path = "../model/model.tar"

    # 数据的大小

    datadim = imageSize * imageSize

    paddleUtil = PaddleUtil()


    # *******************************开始训练**************************************

    myReader = MyReader(imageSize=imageSize)

    # parameters_path设置为None就使用普通生成参数,

    trainer = paddleUtil.get_trainer(datadim=datadim, type_size=type_size, parameters_path=None)

    trainer_reader = myReader.train_reader(train_list="../data/%s/trainer.list" % all_class_name)

    test_reader = myReader.test_reader(test_list="../data/%s/test.list" % all_class_name)


    paddleUtil.start_trainer(trainer=trainer, num_passes=500, save_parameters_name=parameters_path,

trainer_reader=trainer_reader, test_reader=test_reader)

接下来开始进行预测


编写infer.py做验证码预测,这次预测要做的事情比较多.因为传进来的是一个完整的验证码,所以首先要对验证码进行裁剪.

然后把裁剪后的数据传该PaddlePaddle进行预测.预测出来的是一个label值,所以还有通过label找到对应的字符


# coding:utf-8

import json


import numpy as np

import paddle.v2 as paddle

from PIL import Image


from vgg import vgg_bn_drop



# **********************获取参数***************************************

def get_parameters(parameters_path):

    with open(parameters_path, 'r') as f:

        parameters = paddle.parameters.Parameters.from_tar(f)

    return parameters



# *****************获取你要预测的参数********************************

def get_TestData(path, imageSize):

    test_data = []

    img = Image.open(path)

    # 切割图片并保存

    box1 = (5, 0, 17, 27)

    box2 = (17, 0, 29, 27)

    box3 = (29, 0, 41, 27)

    box4 = (41, 0, 53, 27)

    temp = '../images/temp'

    img.crop(box1).resize((32, 32), Image.ANTIALIAS).save(temp + '/1.png')

    img.crop(box2).resize((32, 32), Image.ANTIALIAS).save(temp + '/2.png')

    img.crop(box3).resize((32, 32), Image.ANTIALIAS).save(temp + '/3.png')

    img.crop(box4).resize((32, 32), Image.ANTIALIAS).save(temp + '/4.png')

    # 把图像加载到预测数据中

    test_data.append((paddle.image.load_and_transform(temp + '/1.png', 38, imageSize, False, is_color=False)

                      .flatten().astype('float32'),))

    test_data.append((paddle.image.load_and_transform(temp + '/2.png', 38, imageSize, False, is_color=False)

                      .flatten().astype('float32'),))

    test_data.append((paddle.image.load_and_transform(temp + '/3.png', 38, imageSize, False, is_color=False)

                      .flatten().astype('float32'),))

    test_data.append((paddle.image.load_and_transform(temp + '/4.png', 38, imageSize, False, is_color=False)

                      .flatten().astype('float32'),))

    return test_data



# *****************把预测的label对应的真实字符找到********************************

def lab_to_result(lab, json_str):

    myjson = json.loads(json_str)

    class_details = myjson['class_detail']

    for class_detail in class_details:

        if class_detail['class_label'] == lab:

            return class_detail['class_name']



# ***********************使用训练好的参数进行预测***************************************

def to_prediction(test_data, parameters, out, all_class_name):

    with open('../data/%s/readme.json' % all_class_name) as f:

        txt = f.read()

    # 获得预测结果

    probs = paddle.infer(output_layer=out,

                         parameters=parameters,

                         input=test_data)

    # 处理预测结果

    lab = np.argsort(-probs)

    # 返回概率最大的值和其对应的概率值

    result = ''

    for i in range(0, lab.__len__()):

        print '第%d张预测结果为:%d,可信度为:%f' % (i + 1, lab[i][0], probs[i][(lab[i][0])])

        result = result + lab_to_result(lab[i][0], txt)

    return str(result)



if __name__ == '__main__':

    paddle.init(use_gpu=False, trainer_count=2)

    # 类别总数

    type_size = 33

    # 图片大小

    imageSize = 32

    # 总的分类名称

    all_class_name = 'dst_yanzhengma'

    # 保存的model路径

    parameters_path = "../model/model.tar"

    # 数据的大小

    datadim = imageSize * imageSize


    # *******************************开始预测**************************************

    out = vgg_bn_drop(datadim=datadim, type_size=type_size)

    parameters = get_parameters(parameters_path=parameters_path)

    # 添加数据

    test_data = get_TestData("../images/src_yanzhengma/0a13.png", imageSize=imageSize)

    result = to_prediction(test_data=test_data,

                           parameters=parameters,

                           out=out,

                           all_class_name=all_class_name)

print '预测结果为:%s' % result

上一篇:危险!人工智能轻松破解验证码?
下一篇:重庆尖叫网络科技有限公司简介

周一至周五 9:00-18:00

尖叫网络

尖叫数据