极验汉字点选识别(二)----基于深度学习的端到端文字识别代码实现

注:该算法原理参考网上相关资源,代码做了部分复用了,并在其基础上做了些许改动,整体训练思路并未作过多改动,应用到了极验点选图片左下方图片的识别处理过程过程中,涉及公司内部数据,本文不会有任何主体代码展示

由于前期对某网站有数据采集需求,在采集过程过程中遇到了极验点选验证码,涉及到汉字识别问题,本文重点介绍一下图片左下方图片的识别过程。

                                

数据处理:

利用已经破解掉的底层极验处理逻辑,一共下载了16w张极验图片,涉及词类包括社会百科,百味食谱等,并按照固定像素位置将下方汉字切割。

按照识别设计需要我们将上述图片集划分为训练集和测试集两部分,在实际应用中我们并没有按照某一固定的“官方”比例划定测试集数量,但是不可太少,且图片测试集和训练集不可有交集,避免影响测试效果,导致测试结果偏高。

            

训练集和测试集内部数据格式:

              

对于上述最原始的数据集,需要将原始数据集转化为lmdb格式(文末会有简单介绍)以方便后续的网络训练。因此我们也需要对该数据集进行lmdb格式转化。下面代码就是用于lmdb格式转化,思路比较简单,就是首先读入图像和对应的文本标签,先使用字典将该组合存储起来(cache),再利用lmdb包的put函数把字典(cache)存储的k,v写成lmdb格式存储好(cache当有了1000个元素就put一次)。(注意:在处理汉字的时候注意编码错误)

#!/usr/bin/env python
# encoding: utf-8
'''
@author: Jonny
@software: pycharm
@file: create_lmdb_dataset.py
@time: 2020/5/7 14:04
@desc:
'''

import lmdb
import cv2
import numpy as np
import os

OUT_PATH = ''
IN_PATH = ''
PREFIX = ''


def checkImageIsValid(imageBin):
    if imageBin is None:
        return False
    try:
        imageBuf = np.fromstring(imageBin, dtype=np.uint8)
        img = cv2.imdecode(imageBuf, cv2.IMREAD_GRAYSCALE)
        imgH, imgW = img.shape[0], img.shape[1]
    except:
        return False
    else:
        if imgH * imgW == 0:
            return False
    return True


def writeCache(env, cache):
    with env.begin(write=True) as txn:
        for k, v in cache.items():
            txn.put(k.encode(), v)


def createDataset(outputPath, imagePathList, labelList, lexiconList=None, checkValid=True):
    """
    Create LMDB dataset for CRNN training.
    ARGS:
        outputPath    : LMDB output path
        imagePathList : list of image path
        labelList     : list of corresponding groundtruth texts
        lexiconList   : (optional) list of lexicon lists
        checkValid    : if true, check the validity of every image
    """
    assert (len(imagePathList) == len(labelList))
    nSamples = len(imagePathList)
    env = lmdb.open(outputPath, map_size=1099511627776)
    cache = {}
    cnt = 1
    for i in range(nSamples):
        imagePath = os.path.join(PREFIX, imagePathList[i]).split()[0].replace('\n', '').replace('\r\n', '')
        print(imagePath)
        label = ''.join(labelList[i])
        with open(imagePath, 'r') as f:
            imageBin = f.read()
        if checkValid:
            if not checkImageIsValid(imageBin):
                print('%s is not a valid image' % imagePath)
                continue
        imageKey = 'image-%09d' % cnt
        labelKey = 'label-%09d' % cnt
        cache[imageKey] = imageBin
        cache[labelKey] = label.encode()
        if lexiconList:
            lexiconKey = 'lexicon-%09d' % cnt
            cache[lexiconKey] = ' '.join(lexiconList[i]).encode()
        if cnt % 1000 == 0:
            writeCache(env, cache)
            cache = {}
            print('Written %d / %d' % (cnt, nSamples))
        cnt += 1
        print(cnt)
    nSamples = cnt - 1
    cache['num-samples'] = str(nSamples).encode()
    writeCache(env, cache)
    print('Created dataset with %d samples' % nSamples)


if __name__ == '__main__':
    outputPath = OUT_PATH
    if not os.path.exists(OUT_PATH):
        os.mkdir(OUT_PATH)
    imgdata = open(IN_PATH,encoding="utf8")
    imagePathList = list(imgdata)
    print(imagePathList)

    labelList = []
    for line in imagePathList:
        word = line.replace("\n","").split()[1]
        labelList.append(word)
        print("活生生:",word)
    # createDataset(outputPath, imagePathList, labelList)

通过上述代码可以将我们的原始的训练集和测试集转换成我们实际训练需要的训练集和测试集的数据样式。另外需要特别说明的时候我们再训练模型市,无论是极验图片上方彩图的汉字还是左下角的小字我们用的其实都不是真正的汉字,而是用的汉字代码至于汉字代码的编码规则自定,最好用数字和子母作为元素,至于是用排列组合或是其他的就看你个人喜好了。

网络设计:

根据CRNN的论文描述,CRNN是由CNN->RNN->CTC三大部分架构而成,分别对应卷积层、循环层和转录层。首先CNN部分用于底层的特征提取,RNN采取了BiLSTM,用于学习关联序列信息并预测标签分布,CTC用于序列对齐,输出预测结果

                                       

为了将特征输入到Recurrent Layers,做如下处理:

  • 首先会将图像缩放到 32×W×3 大小
  • 然后经过CNN后变为 1×(W/4)× 512
  • 接着针对LSTM,设置 T=(W/4) , D=512 ,即可将特征输入LSTM。

以上是理想训练时的操作,但是CRNN论文提到的网络输入是归一化好的100×32大小的灰度图像,即高度统一为32个像素。下面是CRNN的深度神经网络结构图,CNN采取了经典的VGG16,值得注意的是,在VGG16的第3第4个max pooling层CRNN采取的是1×2的矩形池化窗口(w×h),这有别于经典的VGG16的2×2的正方形池化窗口,这个改动是因为文本图像多数都是高较小而宽较长,所以其feature map也是这种高小宽长的矩形形状,如果使用1×2的池化窗口则更适合英文字母识别(比如区分i和l)。VGG16部分还引入了BatchNormalization模块,旨在加速模型收敛。还有值得注意一点,CRNN的输入是灰度图像,即图像深度为1。CNN部分的输出是512x1x16(c×h×w)的特征向量

                                       

【剩余内容暂未整理完。。。】

 

附注:

 

【1】LMDB的全称是Lightning Memory-Mapped Database(快如闪电的内存映射数据库),它的文件结构简单,包含一个数据文件和一个锁文件,LMDB文件可以同时由多个进程打开,具有极高的数据存取速度,访问简单,不需要运行单独的数据库管理进程,只要在访问数据的代码里引用LMDB库,访问时给文件路径即可。如果通过常规方法让系统访问大量小文件的开销很大,而LMDB使用内存映射的方式访问文件,使得文件内寻址的开销非常小,使用指针运算就能实现。同时,数据库单文件还能减少数据集复制/传输过程的开销

 

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值