目标检测yolov3+文字识别CRNN 实现文本检测和识别

2023-05-16

参考链接:
https://github.com/chineseocr/chineseocr
https://zhuanlan.zhihu.com/p/34757009
https://wenku.baidu.com/view/f4ec95e64328915f804d2b160b4e767f5acf80ab.html

基于chineseocr的代码做简单修改,本文主要介绍思路和细节理论。

首先列出chineseocr代码的思路:
1、检测文本行角度: 使用cv2.dnn.readNetFromTensorflow加载一个Angle-model.pb,实现文字方向检测 0、90、180、270度检测;
2、检测文本行区域: 在步骤1摆正文本行的基础上,使用yolov3 检测出text proposals,然后使用CTPN中的文本线构造算法进行文本行合并;
3、OCR识别: 使用CRNN + CTC进行文字识别。

一、文本行角度检测
剪切图像边缘,将图像变成(224,224,3)尺⼨,图像channel中⼼化处理,读取Angle-model(vgg16 : 5层卷积,2层全连接,最后经过softmax预测4个类别),预测⽂字朝向,代码如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jun  3 10:58:35 2020

@author: cong
"""
import os
import cv2
import numpy as np
# 单行文本角度检测
pwd = os.getcwd() # 获取当前路径

AngleModelPb    = os.path.join(pwd,"models","Angle-model.pb")
AngleModelPbtxt = os.path.join(pwd,"models","Angle-model.pbtxt")
angleNet = cv2.dnn.readNetFromTensorflow(AngleModelPb,AngleModelPbtxt)##dnn 文字方向检测

def angle_detect(img,adjust=True):
    """
    文字方向检测
    """
    h,w = img.shape[:2]
    ROTATE = [0,90,180,270]
    if adjust:
       thesh = 0.05
       xmin,ymin,xmax,ymax = int(thesh*w),int(thesh*h),w-int(thesh*w),h-int(thesh*h)
       img = img[ymin:ymax,xmin:xmax]  # 剪切图片边缘
    
    
    inputBlob = cv2.dnn.blobFromImage(img, 
                                      scalefactor=1.0, 
                                      size=(224, 224),
                                      swapRB=True ,
                                      mean=[103.939,116.779,123.68],crop=False);
    angleNet.setInput(inputBlob)
    pred = angleNet.forward()
    index = np.argmax(pred,axis=1)[0]
    return ROTATE[index]

上述代码返回图片文字方向的角度,进入yolov3前只是对图片简单的旋转:

im = Image.fromarray(img).transpose(Image.ROTATE_90)
img = np.array(im)

二、基于yolov3进行本文检测 + 文本线构造算法合并text proposals
进入yolov3,resize图像大小(608,608),进入yolov3进行检测。yolov3的网络结构及其他细节不再赘述,下面只介绍标注的详细过程:

kerasTextModel = os.path.join(pwd,"models","text.h5")  # keras版本--文本行检测模型权重文件
keras_anchors = '8,11, 8,16, 8,23, 8,33, 8,48, 8,97, 8,139, 8,198, 8,283'
class_names = ['none','text']

由上述代码可以看出anchors宽度为8,也就是说标注样本中每个框的宽度为8,高度不定,标签只有两类:[‘none’,‘text’]。更加详细的可以去了解一下CTPN的样本。
yolov3检测的结果就是一系列的带有标签的box,接下来利用CTPN中的文本线构造算法合并text proposals,连接成一个文本检测框:
text
为了说明问题,假设某张图有如上图所示的2个text proposal,即蓝色和红色2组anchor boxes。CTPN采用如下算法构造文本线:
按照水平x坐标排序Anchor;
按照规则依次计算每个Anchor boxi的pair(boxj),组成pair(boxi,boxj);
通过pair(boxi,boxj)建立一个Connect graph,最终获得文本检测框。

下面详细解释,假设每个Anchor index如绿色数字,同时每个Anchor Softmax score如黑色数字:
texti
文本线构造算法通过如下方式建立每个Anchor boxi的 pair(boxi,boxj):
3
4此部分最终返回图像中的每一行的连通文本框boxes。

三、CRNN + CTC进行文字识别
主要解决作者目前的疑惑:
1、CRNN的具体网络结构:输入一个文本行box怎样输出识别结果的???
2、CTC到底是什么??
目标检测输入:608*608
然后检测框进入CRNN中的CNN的输入大小为:(1,1,32,w)
CNN提取特征结束,进入RNN的尺寸为:(w/4,1,512) # T= w/4
RNN的输出结果为:(w/4,1,5530) # T * (N+1) # N:字符个数
先上一个参考链接:
https://zhuanlan.zhihu.com/p/43534801

CRNN的网络结构:CNN+RNN(biLSTM)
crnn
代码中的结构如下:

class CRNN(nn.Module):

    def __init__(self, imgH, nc, nclass, nh, leakyRelu=False,lstmFlag=True,GPU=False,alphabet=None):
        """
        是否加入lstm特征层
        """
        super(CRNN, self).__init__()
        assert imgH % 16 == 0, 'imgH has to be a multiple of 16'

        ks = [3, 3, 3, 3, 3, 3, 2]
        ps = [1, 1, 1, 1, 1, 1, 0]
        ss = [1, 1, 1, 1, 1, 1, 1]
        nm = [64, 128, 256, 256, 512, 512, 512]
        self.lstmFlag = lstmFlag
        self.GPU = GPU
        self.alphabet = alphabet
        cnn = nn.Sequential()

        def convRelu(i, batchNormalization=False):
            nIn = nc if i == 0 else nm[i - 1]
            nOut = nm[i]
            cnn.add_module('conv{0}'.format(i),
                           nn.Conv2d(nIn, nOut, ks[i], ss[i], ps[i]))
            if batchNormalization:
                cnn.add_module('batchnorm{0}'.format(i), nn.BatchNorm2d(nOut))
            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))

        convRelu(0)
        cnn.add_module('pooling{0}'.format(0), nn.MaxPool2d(2, 2))  # 64x16x64
        convRelu(1)
        cnn.add_module('pooling{0}'.format(1), nn.MaxPool2d(2, 2))  # 128x8x32
        convRelu(2, True)
        convRelu(3)
        cnn.add_module('pooling{0}'.format(2),
                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))  # 256x4x16
        convRelu(4, True)
        convRelu(5)
        cnn.add_module('pooling{0}'.format(3),
                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))  # 512x2x16
        convRelu(6, True)  # 512x1x16
        
        self.cnn = cnn
        if self.lstmFlag:
            self.rnn = nn.Sequential(
                BidirectionalLSTM(512, nh, nh),
                BidirectionalLSTM(nh, nh, nclass))
        else:
            self.linear = nn.Linear(nh*2, nclass)
            

    def forward(self, input):
        # conv features
        conv = self.cnn(input)
        b, c, h, w = conv.size()
        
        assert h == 1, "the height of conv must be 1"
        conv = conv.squeeze(2)
        conv = conv.permute(2, 0, 1)  # [w, b, c]
        if self.lstmFlag:
           # rnn features
           output = self.rnn(conv)
           T, b, h = output.size()
           output = output.view(T, b, -1)
           
        else:
             T, b, h = conv.size()
             t_rec = conv.contiguous().view(T * b, h)
             output = self.linear(t_rec)  # [T * b, nOut]
             # view()的作用相当于numpy中的reshape,重新定义矩阵的形状。
             output = output.view(T, b, -1)
       
        return output

网络结构如下:

<bound method CRNN.predict_job of CRNN(
  (cnn): Sequential(
    (conv0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu0): ReLU(inplace)
    (pooling0): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu1): ReLU(inplace)
    (pooling1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (conv2): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (batchnorm2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu2): ReLU(inplace)
    (conv3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu3): ReLU(inplace)
    (pooling2): MaxPool2d(kernel_size=(2, 2), stride=(2, 1), padding=(0, 1), dilation=1, ceil_mode=False)
    (conv4): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (batchnorm4): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu4): ReLU(inplace)
    (conv5): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu5): ReLU(inplace)
    (pooling3): MaxPool2d(kernel_size=(2, 2), stride=(2, 1), padding=(0, 1), dilation=1, ceil_mode=False)
    (conv6): Conv2d(512, 512, kernel_size=(2, 2), stride=(1, 1))
    (batchnorm6): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu6): ReLU(inplace)
  )
  (rnn): Sequential(
    (0): BidirectionalLSTM(
      (rnn): LSTM(512, 256, bidirectional=True)
      (embedding): Linear(in_features=512, out_features=256, bias=True)
    )
    (1): BidirectionalLSTM(
      (rnn): LSTM(256, 256, bidirectional=True)
      (embedding): Linear(in_features=512, out_features=5530, bias=True)
    )
  )
)>

CTC

对于Recurrent Layers,如果使用常见的Softmax cross-entropy loss,则每一列输出都需要对应一个字符元素。那么训练时候每张样本图片都需要标记出每个字符在图片中的位置,再通过CNN感受野对齐到Feature map的每一列获取该列输出对应的Label才能进行训练。

在实际情况中,标记这种对齐样本非常困难(除了标记字符,还要标记每个字符的位置),工作量非常大。另外,由于每张样本的字符数量不同,字体样式不同,字体大小不同,导致每列输出并不一定能与每个字符一一对应。
当然这种问题同样存在于语音识别领域。例如有人说话快,有人说话慢,那么如何进行语音帧对齐,是一直以来困扰语音识别的巨大难题。
所以CTC提出一种对不需要对齐的Loss计算方法,用于训练网络,被广泛应用于文本行识别和语音识别中。

CTC暂时参考https://zhuanlan.zhihu.com/p/43534801
后续再总结!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标检测yolov3+文字识别CRNN 实现文本检测和识别 的相关文章

随机推荐

  • Invoke与begininvoke

    在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate xff0c 至于委托的本质请参考我的另一随笔 xff1a 对 net事件的看法 一 为什么Control类提供了Invoke和BeginInvoke机制
  • 接口成员显示实现

    xff08 interface xff09 用来定义一种程序的协定 实现接口的类或者结构要与接口的定义严格一致 在前面的文章中 xff0c 我们已经对C 接口的概念 xff0c 如何定义接口以及如何对接口进行访问等问题进行了详细的讨论 在这
  • Oracle基础知识整理总结

    1 Oracle跟SQL Server 2005的区别 xff1f 宏观上 xff1a 1 最大的区别在于平台 xff0c oracle可以运行在不同的平台上 xff0c sql server只能运行在windows平台上 xff0c 由于
  • Javascript调用后台方法

    1 javaScript函数中执行C 代码中的函数 xff1a 方法一 xff1a 1 首先建立一个按钮 xff0c 在后台将调用或处理的内容写入button click中 2 在前台写一个js函数 xff0c 内容为document ge
  • Delphi设置某用户对文件(夹)的权限

    以下在代码在D7 43 2003和D7 43 XP中调试通过 unit NTSecurityU interface Uses Windows AclApi AccCtrl Const SECURITY NULL SID AUTHORITY
  • 关于VMWare压缩虚拟机的虚拟磁盘的问题

    随着我们使用虚拟系统的时间越长 xff0c Vmware创建的虚拟磁盘占用空间就越大 xff0c 即使将虚拟系统中的文件删除 xff0c 虚拟磁盘文件占用宿主系统硬盘空间也不会减少 xff0c 这个问题困扰了很多用户 a S 34 N 43
  • GB2312简体中文编码表

    const GB2312 中文编码 CHpb 61 B0 首页码 CHpe 61 F7 尾页码 CHab 61 A1 首地址 CHae 61 FE 尾地址 GB B 61 B0A1 GB E 61 F7FE ChCount 61 chpe
  • 自定义通信协议

    现在大部分的仪器设备都要求能过通过上位机软件来操作 xff0c 这样方便调试 xff0c 利于操作 其中就涉及到通信的过程 在实际制作的几个设备中 xff0c 笔者总结出了通信程序的通用写法 xff0c 包括上位机端和下位机端等 1 xff
  • vc的编译过程

    对VC 43 43 工程编译过程的梳理 VC 43 43 的项目和解决方案文件解读 xff0c 无非就是利用这些信息进行一个软件的编译 xff0c 这些文件里面是存放的项目的配置和工程的组织 xff0c 类似于makefile文件 但是只有
  • 用WSE在Web服务中验证用户身份

    一 Web服务安全与WS Security 毫无疑问 xff0c SOAP和XML Web服务在交互操作和标准上已经完全改变了电子商务领域的格局 然而直到最近 xff0c 在Web服务技术领域仍然存在着一些缺陷 xff0c 那就是处理消息级
  • 安装CUDA wget下载速度慢解决办法(天下无敌)

    因为墙的原因 xff0c 再加上英伟达工作人员脑筋不会急转弯 xff0c 以及wget是个弟中弟 xff0c 下载cuda时可能会很慢 断线 xff0c 翻墙又不方便 但是没关系 xff0c 谈笑间 xff0c 让我们用一分钟成交它 以ub
  • px4下载指定版本的固件、git用法

    https hub fastgit org PX4 PX4 Autopilot git describe tag 查看当前版本号 git tag l 查看所有版本 xff0c 也就是打个tag git checkout v1 9 1 跳转到
  • YOLOX网络结构

    本文为博主DaneAI原创文章 xff0c 遵循 CC 4 0 BY SA 版权协议 xff0c 转载请附上原文出处链接和本声明 原文链接 xff1a https blog csdn net happyday d article detai
  • 阿木实验室P450无人机硬件接线图备忘录

    其中数传是定制的 xff0c 相当于是路由器 43 数传二合一 xff0c 既负责地面站计算机与pixhawk的通讯 xff0c 又负责发射WiFi信号 组建局域网 xff0c 使板载英伟达NX上位机和手机 平板 笔记本电脑等远程终端连接在
  • yolov3损失函数公式及代码位置,绝对良心(更新)

    版本 xff1a darknet yolov3 环境 xff1a ubuntu16 04 本人小白 xff0c 毕设正在做基于yolov3的目标检测系统研究 xff0c 在网上找了一万遍 xff0c 基本没有靠谱的损失函数 xff0c 全是
  • 使用http协议Header中的Authorization传递token

    1 span class token annotation punctuation 64 GetMapping span span class token punctuation span span class token string 3
  • Shell命令之终端打开网页

    一句话用Safari打开百度 span class hljs built in open span span class hljs operator a span span class hljs string 34 Applications
  • 数据结构期末复习五:内部排序

    排序的基本概念和分类 排序 xff1a 将杂乱无章的数据按关键字递增 xff08 或递减 xff09 有序排列 假设Ki 61 Kj xff0c 排序前Ri领先于Rj 稳定排序 xff1a 排序后的序列Ri仍领先于Rj 不稳定排序 xff1
  • 数据同步工具DataX从Mysql同步数据到HDFS实战

    目录 1 查看数据同步模板2 高可用HA的HDFS配置3 MysqlReader针对Mysql类型转换说明4 HdfsWriter支持大部分Hive类型5 Mysql准备数据如下6 新建job mysql2hdfs json7 执行job8
  • 目标检测yolov3+文字识别CRNN 实现文本检测和识别

    参考链接 xff1a https github com chineseocr chineseocr https zhuanlan zhihu com p 34757009 https wenku baidu com view f4ec95e