OCR文本识别模型CRNN

news2024/11/25 20:18:07

CRNN网络结构

论文地址:https://arxiv.org/pdf/1507.05717
参考:https://blog.csdn.net/xiaosongshine/article/details/112198145

git:https://github.com/shuyeah2356/crnn.pytorch
CRNN文本识别实现端到端的不定长文本识别。
CRNN网络把包含三部分:卷积层(CNN)、循环层(RNN)和转录层(CTC loss)
在这里插入图片描述

1、卷积层::通过深层卷积操作对输入图像做特征提取,得到特征图;
2、循环层: 循环层使用双向LSTM(BLSTM)对特征序列进行预测,对序列中的每一个特征向量进行学习,并输出预测标签(真实值)的分布;
3、转录层:转录层使用CTC loss ,把循环层获取的一系列标签分布转换成最终的标签序列。

对于输入图片:
输入图像为灰度图(单通道);
高度为32,经过卷积处理后高度变为1;
输入图片宽度为100,输入图片大小为(100,32,1)
CNN输出尺寸为(512, 1, 40),卷积操作输出512个特征图,每一个特征图高度为1,宽度为26。

在代码中有判断图片的高度能够被16整除。

assert imgH % 16 == 0

1、CNN

卷积层用来提取文图像的特征,堆叠使用卷积层和最大池化层,特别的,最后两个最大池化层在宽度和高度上的步长是不相等的池化的窗口尺寸是(w,h):(1,2),因为待识别的文本图片多数是高较小而宽较长,使用1×2的池化窗口尽量不丢失在宽度方向的信息。
卷积操作的具体实现代码:

# 输入图片大小为(160,32,1)
assert imgH % 16 == 0, 'imgH has to be a multiple of 16 图片高度必须为16的倍数'
		# 一共有7次卷积操作
        ks = [3, 3, 3, 3, 3, 3, 2]  # 卷积层卷积尺寸3表示3x3,2表示2x2
        ps = [1, 1, 1, 1, 1, 1, 0]  # padding大小
        ss = [1, 1, 1, 1, 1, 1, 1]  # stride大小
        nm = [64, 128, 256, 256, 512, 512, 512]  # 卷积核个数,卷积操作输出特征层的通道数

        cnn = nn.Sequential()
        def convRelu(i, batchNormalization=False):  # 创建卷积层
            nIn = nc if i == 0 else nm[i - 1]  # 确定输入channel维度,如果是第一层网络,输入通道数为图片通道数,输入特征层的通道数为上一个特征层的输出通道数
            nOut = nm[i]  # 确定输出channel维度
            cnn.add_module('conv{0}'.format(i),
                           nn.Conv2d(nIn, nOut, ks[i], ss[i], ps[i]))  # 添加卷积层
            # BN层
            if batchNormalization:
                cnn.add_module('batchnorm{0}'.format(i), nn.BatchNorm2d(nOut))
            # Relu激活层
            if leakyRelu:
                cnn.add_module('relu{0}'.format(i),
                               nn.LeakyReLU(0.2, inplace=True))
            else:
                cnn.add_module('relu{0}'.format(i), nn.ReLU(True))
        # 卷积核大小为3×3,s=1,p=1,输出通道数为64,特征层大小为100×32×64
        convRelu(0)
        # 经过2×2Maxpooling,宽高减半,特征层大小变为50×16×64
        cnn.add_module('pooling{0}'.format(0), nn.MaxPool2d(2, 2))
        # 卷积核大小为3×3,s=1,p=1,输出通道数为128,特征层大小为50×16×128
        convRelu(1)
        # 经过2×2Maxpooling,宽高减半,特征层大小变为25×8×128
        cnn.add_module('pooling{0}'.format(1), nn.MaxPool2d(2, 2))
        # 卷积核大小为3×3,s=1,p=1,输出通道数为256,特征层大小为25×8×256,卷积后面接BatchNormalization
        convRelu(2, True)
        # 卷积核大小为3×3,s=1,p=1,输出通道数为256,特征层大小为25×8×256
        convRelu(3)
        # 经过MaxPooling,卷积核大小为2×2,在h上stride=2,p=0,s=2,h=(8+0-2)//2+1=4,w上的stride=1,p=1,s=1,w=(25+2-2)//1+1=26通道数不变,26×4×256
        cnn.add_module('pooling{0}'.format(2),
                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))    # 参数 (h, w)
        # 卷积核大小为3×3,s=1,p=1,输出通道数为512,特征层大小为50×16×512,卷积后面接BatchNormalization
        convRelu(4, True)
        # 卷积核大小为3×3,s=1,p=1,输出通道数为512,特征层大小为26×4×512
        convRelu(5)
        # 经过MaxPooling,卷积核大小为2×2,在h上stride=2,p=0,s=2,h=(4+0-2)//2+1=2,w上的stride=1,p=1,s=1,w=(26+2-2)//1+1=27通道数不变,27×2×512
        cnn.add_module('pooling{0}'.format(3),
                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))
        # 卷积核大小为2×2,s=1,p=0,输出通道数为512,特征层大小为26×1×512
        convRelu(6, True)

对应的网络结构:
在这里插入图片描述

这里卷积操作后的特征图大小为26×1×512

2、RNN

对于卷积操作输出的结果经过处理之后才能输入到RNN中。
卷积操作输出的特征图高度一定为1,代码中也做约束

assert h == 1

将h,w维度合并,合并后的维度变为输入到RNN中的时间不长(time_step),每一个序列的长度为原始特征层的通道数512
输入到LSTM中的特征图大小是多少(面试被问到的问题):
每次输入到LSTM中的特征,时间不长的数量为原始特征的h×w(26),每次输入一个序列,序列长度为原始特征层的通道数512

def forward(self, input):
        # conv features
        conv = self.cnn(input)
        b, c, h, w = conv.size()# batch_size,512,1,26
        assert h == 1, "the height of conv must be 1"
        # 将宽高维度合并,特征层大小为(batch_size, 512, 26)
        conv = conv.squeeze(2)
        # 维度顺序调整(26, batch_size, 512),w作为时间步长(作为LSTM中的一个时间不长time_step)
        conv = conv.permute(2, 0, 1)  

        # rnn features
        output = self.rnn(conv)
        # print(output.size())

        return output

序列是按照列从左到右生成的,每一列包含512为特征,输入到LSTM中的第i个特征是特征图第i列像素的连接。
在这里插入图片描述
对于卷积操作、Maxpooling层和BatchNormalization卷积操作具有平移不变性。每一个从左到右的序列对应原始图像中的一个矩形区域,且顺序是对应的。特征序列中的每一个向量对一个原图中的一个感受野。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/03b310b41f4a46e2b4d56423f7cf7c95.png =600x500在这里插入图片描述

将特征序列按照time_step输入到RNN中,RNN隐藏层的神经元数量为256,使用多层的双向LSTM。
输入时间不长的数量为26,经过RNN得到26个特征向量,输出特征向量的类别分类结果。
每一个特征向量对应的是原图中的一个小的矩形区域。RNN来判断这个矩形区域属于哪个字符。
根据输入的特征向量得到所有字符的softmax概率分布,这是一个长度为待识别字符类别总数量的向量,RNN的输出向量作为转录层CTC的输入。
LSTM实现的代码:

class BidirectionalLSTM(nn.Module):

    def __init__(self, nIn, nHidden, nOut):
        super(BidirectionalLSTM, self).__init__()

        self.rnn = nn.LSTM(nIn, nHidden, bidirectional=True)     # nIn:输入神经元个数
        # *2因为使用双向LSTM,将双向的隐藏层单元拼接在一起,两层个256单元的双向LSTM
        self.embedding = nn.Linear(nHidden * 2, nOut)
    def forward(self, input):
        # 经过RNN输出feature map特征结果
        recurrent, _ = self.rnn(input)
        T, b, h = recurrent.size()  # T:时间步长,b:batch size,h:hiden unit
        t_rec = recurrent.view(T * b, h)
		# 第一次LSTM得到特征层[26×256,256],view成[26, 256, 256]
		# 第二次LSTM得到特征层[26×256,num_class],view成[26, 256, num_class]
        output = self.embedding(t_rec)  # [T * b, nOut]
        output = output.view(T, b, -1)

        return output
# nh为隐藏层神经节点数,nclass为所有识别字符的类别总数
self.rnn = nn.Sequential(
	BidirectionalLSTM(512, nh, nh), # 输入的时间步长为512
	BidirectionalLSTM(nh, nh, nclass))

第一次LSTM得到特征层[26×256,256],view成[26, 256, 256]
第二次LSTM得到特征层[26×256,num_class],view成[26, 256, num_class]

3、转录层CTC(Connectionist Temporal Classification)

转录层将RNN对每个特征向量做的预测转换成标签序列的过程。对于不定长序列的对齐问题。
RNN进行序列分类时,可能会出现一个字被识别多次,需要去除冗余机制。
处理的方法(引入blank机制)这一过程称为解码过程:

  1. 在重复的字符之间增加一个空格‘-’,
  2. 删除连续重复的字符,
  3. 再去掉路径中左右的‘-’字符
    编码过程是由神经网络来实现的。
    文本标签可以有多个不同的字符组合路径得到。

CTC loss如何计算:
在训练阶段根据这些概率分布向量和对应的文本标签计算损失函数。
根据能得到对应标签的所有路径的分数之和类计算损失函数。
每条路径的概率为每一个时间步中对应字符的分数的乘积。CTC损失函数定义为概率的负最大似然函数,为了计算方便对函数取对数。

p(l|y)= ∑ π : B ( π ) p ( π ∣ y ) \sum\limits_{π:B(π)}p(π|y) πB(π)p(πy)

预测过程如何实现
先使用标准的CNN网络提取文本特征;
利用BLSTM将特征向量进行融合,已提取字符序列的上下文特征,得到每列特征的概率分布;
最后通过CTC进行预测得到文本序列。

在训练阶段CRNN将特征图像统一缩放到w×32,而在测试阶段对于输入的图片拉伸会导致识别率降低。CRNN保持输入图像尺寸比例,但是图像的高度h必须统一为32,卷积特征图的尺寸动态决定了LSTM的时序长度(时间步长)。


感谢:
https://blog.csdn.net/xiaosongshine/article/details/112198145
https://github.com/meijieru/crnn.pytorch
https://www.bilibili.com/video/BV1Wy4y1473z?p=2&vd_source=91cfed371d5491e2973d221d250b54ae

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1651209.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推荐5个免费的国内平替版GPT

提起AI,大家第一个想到的就是GPT。 虽然它确实很厉害,但奈何于我们水土不服,使用门槛有些高。 不过随着GPT的爆火,现在AI智能工具已经遍布到各行各业了,随着时间的推移,国内的AI工具也已经“百花盛放”了…

简单了解泛型

基本数据类型和对应的包装类 在Java中, 基本数据类型不是继承自Object, 为了在泛型代码中可以支持基本类型, Java给每个基本类型都对应了一个包装类型. 简单来说就是让基本数据类型也能面向对象.基本数据类型可以使用很多方法, 这就必须让它变成类. 基本数据类型对定的包装类…

Istio 流量管理(请求路由、流量转移、请求重试、流量镜像、故障注入、熔断等)介绍及使用

一、Istio 流量管理 Istio是一个开源的服务网格,它为分布式微服务架构提供了网络层的抽象。它使得服务之间的通信变得更为可靠、安全,并且提供了细粒度的流量管理、监控和策略实施功能。Istio通过在服务之间插入一个透明的代理(Envoy&#x…

Unreal游戏GPU参数详解,游戏性能优化再升级

UWA GOT Online For Unreal GPU模式近期全新发布,方便开发者从渲染和带宽的角度进行GPU分析。同时,此次更新中UWA也增加了丰富的GPU参数,涵盖了GPU SoC和GPU Counter模块。这些新增的参数不仅能够帮助Unreal开发者从宏观层面监控GPU的压力状况…

QT-TCP通信

网上的资料太过于书面化,所以看起来有的让人云里雾里,看不懂C-tcpsockt和S-tcpsocket的关系 所以我稍微画了一下草图帮助大家理解两个套接字之间的关系。字迹有的飘逸勉强看看 下面是代码 服务端: MainWindow::MainWindow(QWidget *parent) …

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

作者:天彤 Atlassian 在 2020 年官方发布公告,从 2021 年起停止 Jira Server 产品的销售,并且在 2024 年彻底停止 Server 端产品的服务支持,这对于国内使用 Jira 产品的企业和研发团队造成了不小的影响。而此时国内很多 DevOps 产…

本地主机访问服务器的Redis -- 配置 ssh 端口转发

前言 在进行Java开发时,高度的依赖 Windows 上的开发软件 idea ,那么我们想访问位于服务器上的 redis 怎么办呢?在平时我们想访问位于服务器上的程序,只需要开放它的端口即可,比如我们创建的网站,比如 tomc…

【拆位法 决策包容性 位运算】2871. 将数组分割成最多数目的子数组

本文涉及知识点 拆位法 贪心 位运算 决策包容性 位运算、状态压缩、子集状态压缩汇总 LeetCode2871. 将数组分割成最多数目的子数组 给你一个只包含 非负 整数的数组 nums 。 我们定义满足 l < r 的子数组 nums[l…r] 的分数为 nums[l] AND nums[l 1] AND … AND nums[r…

使用图网络和视频嵌入预测物理场

文章目录 一、说明二、为什么要预测&#xff1f;三、流体动力学模拟的可视化四、DeepMind神经网络建模五、图形编码六、图形处理器七、图形解码器八、具有不同弹簧常数的轨迹可视化九、预测的物理编码和推出轨迹 一、说明 这是一篇国外流体力学专家在可视化流体物理属性的设计…

如何使用vue脚手架创建项目

前言 使用vue搭建项目的时候&#xff0c;我们可以通过对应的cmd命令去打开脚手架&#xff0c;然后自己配置对应的功能插件 说明&#xff1a; 要使用Vue脚手架创建项目&#xff0c;你需要先确保你已经安装了Node.js和npm&#xff08;Node.js的包管理器&#xff09;。然后&#…

Github 2024-05-07 开源项目日报 Tp10

根据Github Trendings的统计,今日(2024-05-07统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目4Jupyter Notebook项目2Python项目1Batchfile项目1非开发语言项目1Java项目1HTML项目1C#项目1从零开始构建你喜爱的技术 创建周期…

性能测试基础介绍

1.性能测试的意义 1.1.现状 互联网行业发展快&#xff0c;用户量大大增加&#xff1b; 业务和系统架构越来越复杂&#xff0c;数据越来越多&#xff0c;用户不仅仅满足于功能的实现&#xff0c;在某些场景下&#xff0c;更 在意系统性能。 1.2.什么是性能测试 通过一定的手段&…

Google准备好了吗?OpenAI发布ChatGPT驱动搜索引擎|TodayAI

在科技界波澜壮阔的发展中&#xff0c;OpenAI正式宣布其最新突破——一个全新的基于ChatGPT技术的搜索引擎&#xff0c;旨在直接挑战谷歌在搜索领域的统治地位。这一创新将可能彻底改变用户上网搜索的方式。 据悉&#xff0c;这款AI驱动的搜索引擎利用了ChatGPT的强大功能&…

快速搭建linux虚拟机环境

1、虚拟机资源 VMwareWorkstation&#xff1a;Download VMware Workstation Pro virtualbox&#xff1a;Oracle VM VirtualBox 2、虚拟机系统资源 链接&#xff1a;系统资源链接 提取码&#xff1a;0gat 说明&#xff1a;此处的系统资源是采用VMwareWorkstation 虚拟机进…

【postgreessql 】查询数据库表占用物理空间

查询单个表的磁盘使用量&#xff1a; SELECTrelname,pg_size_pretty ( pg_total_relation_size ( relid ) ) AS total_size FROMpg_catalog.pg_statio_user_tables; 查询所有表的总磁盘使用量&#xff1a; SELECTpg_size_pretty ( SUM ( pg_total_relation_size ( relid ) )…

【机器学习与实现】线性回归示例——波士顿房价分析

目录 一、创建Pandas对象并查看数据的基本情况二、使用皮尔逊相关系数分析特征之间的相关性三、可视化不同特征与因变量MEDV&#xff08;房价中值&#xff09;间的相关性四、划分训练集和测试集并进行回归分析 一、创建Pandas对象并查看数据的基本情况 boston.csv数据集下载&a…

《起风了》观后感

我想宫崎骏的电影是很多人心目中美好的回忆&#xff0c;每当听到有他的新电影要上映&#xff0c;总是迫不及待想去捧场&#xff0c;一刷二刷三刷却还是依然看得津津有味&#xff0c;这就是宫崎骏电影独特的魅力。《起风了》跟他的其他电影有很明显的不同&#xff0c;他的大部分…

Hive Partitioned Tables 分区表

Hive Partitioned Tables 分区表 1.分区表概念 Hive分区表&#xff08;Partitioned Tables&#xff09;是一种用于管理大量数据的机制&#xff0c;它可以将数据分散到不同的目录或分区中&#xff0c;以提高查询性能、优化数据存储和管理。 这种表结构可以根据某个列的值进行分…

【记录】Python3| 将 PDF 转换成 HTML/XML(✅⭐PyMuPDF+tqdm)

本文将会被汇总至 【记录】Python3&#xff5c;2024年 PDF 转 XML 或 HTML 的第三方库的使用方式、测评过程以及对比结果&#xff08;汇总&#xff09;&#xff0c;更多其他工具请访问该文章查看。 文章目录 PyMuPDF 使用体验与评估1 安装指南2 测试代码3 测试结果3.1 转 HTML …

【2024最新华为OD-C卷试题汇总】字符串分割(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; 文章目录 前…