场景文字检测——CTPN模型介绍及代码实现

一、CTPN文字检测的概述

CTPN:Detecting Text in Natural Image with Connectionist Text Proposal Network,(即使用连接的文本建议框网络进行自然图像的文本检测),是在ECCV 2016提出的一种文字检测算法。CTPN是结合CNN与LSTM的深度网络,能有效的检测出复杂场景的横向分布的文字。CTPN是从Faster RCNN改进而来,因此也是基于anchor的目标检测网络,网络架构和Faster RCNN相似,但是加入了LSTM层,其支持任意尺寸的图像输入,并能够直接在卷积层中定位文本行。即由特征提取网络VGG+连接的文本候选框选取CTPN构成。
Alt

二、CTPN模型架构

Alt
CTPN的主干特征提取网络采用了VGG16的卷积部分,通过卷积不断进行下采样,下采样的步长为16,即得到VGG的conv5的特征图;后面部分便是改进的RPN网络CTPN网络,即连接的文本建议框网络;首先在conv5特征图上做3x3的滑动窗口,来产生学习到的空间特征;由于文本具有较强的序列特征,将特征进行Reshape操作后传入双向的LSTM网络,得到上下文的编码信息;然后再进行“FC”卷积层,也就是进行特征通道数的调整;最后是传入到CTPN的预测网络(三个分支),第一个分支的输出通道数为2xk vertical coordinates,k为anchor的数量,2分别表示预测框中心y轴和高度h的偏移量;第二个分支的输出通道数2xk scores,k为anchor的数量,2表示anchor是前景还是背景;第三个分支的输出通道数1xk side-refinement,k为anchor的数量,1表示水平方向上左边或者右边预测框中心点x轴的偏移量。

1、主干网络VGG16

VGG16是2014年ImageNet上提出的非常优秀的分类网络,其主要特征就是采样小的卷积核3x3不断进行特征提取以及最大池化进行下采样,通道数的变化采用了network in network的思想,其具体结构如下:

Alt
图片输入:原始VGG分类网络图片的输入为224x224,论文中将输入图片的最短边Reshape到600,因此下面以600x800来描述图片shape的变化。
conv1:经过两次的3x3卷积,输出通道数为64,shape为[64,600,800],再经过一次pool_size为2的最大池化,shape变为[64,300,400]。
conv2:两次3x3卷积,输出的通道数为128,shape为(128,300,400),再经过一次pool_size为2最大池化,输出shape为(128,150,200)。
conv3:三次3x3卷积,输出的通道数为256,shape为(256,150,200),再经过一次pool_size为2最大池化,输出shape为(256,75,100)。
conv4:三次3x3卷积,输出的通道数为512,shape为(512,75,100),再经过一次pool_size为2最大池化,输出shape为(512,37,50)。
conv5:三次3x3卷积,输出的通道数为512,shape为(512,37,50)。

VGG16网络的详细信息可以参考这篇博客https://blog.youkuaiyun.com/weixin_44791964/article/details/102585038

后面我们将得到的conv5(batch,512,37,50)的特征层称之为有效特征层,这与Faster RCNN的中的feature map是一样的。之后的CTPN网络便是作用在该有效特征图上。

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models

class CTPN_Model(nn.Module):
    def __init__(self):
        super().__init__()
        base_model = models.vgg16(pretrained=False)
        layers = list(base_model.features)[:-1]
        self.base_layers = nn.Sequential(*layers)  # block5_conv3 output

2、CTPN连接的文本建议框网络

类似于区域建议框网络(RPN)[25],CTPN本质上是一个全卷积网络,允许任意大小的输入图像。

(1)原论文中,CTPN网络首先在conv5上做3x3的滑动窗口,即每个点都结合3x3区域特征获得一个长度为3x3x512的特征向量。输出(batch,9x512,37,50)的特征图,使网络学习到空间的特征。
这里解释一下conv5 feature map如何从(batch,512,37,50)变成(batch,9x512,37,50):
conv
在原版caffe代码中是用im2col提取每个点附近的9点临近点,然后每行都如此处理:
37x50 -> 9x37x50
接着每个通道都如此处理:
512x37x50 -> 9*512x37x50
而im2col是用于卷积加速的操作,即将卷积变为矩阵乘法,从而使用Blas库快速计算。

但是这里我们并没有遵循原始论文的做法,而是采用了一个3x3x512的卷积操作来替代上述的空间特征提取。特征层shape由(batch,512,37,50)–>(batch,512,37,50)

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models

class CTPN_Model(nn.Module):
    def __init__(self):
        super().__init__()
        base_model = models.vgg16(pretrained=False)
        layers = list(base_model.features)[:-1]
        self.base_layers = nn.Sequential(*layers)  # block5_conv3 output
		#使用一个3x3卷积来替代上述的空间特征提取
         self.rpn = basic_conv(512, 512, 3, 1, 1, bn=False)

(2)CNN学习的是感受野内的空间信息,LSTM学习的是序列特征。对于文本序列检测,显然既需要CNN抽象空间特征,也需要序列特征(毕竟文字是连续的)。使用双向的LSTM,使得它能够在两个方向上对递归上下文进行编码,以便连接感受野能够覆盖整个图像宽度。
下图显示了带有LSTM和不带有LSTM的CTPN网络的预测结果,上半部分:没有LSTM,下半部分:有LSTM,可以看到包含了上下文信息更能进行文本的精确定位。
在这里插入图片描述
在进入LSTM之前要进行特征层shape的变化,(batch,512,37,50)–>(batch,37,50,512)–>(batchx37,50,512),最大时间步长Time_step=50,学习每一行的序列特征。

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models

class CTPN_Model(nn.Module):
    def __init__(self):
        super().__init__()
        base_model = models.vgg16(pretrained=False)
        layers = list(base_model.features)[:-1]
        self.base_layers = nn.Sequential(*layers)  # block5_conv3 output
		# 使用一个3x3卷积来替代上述的空间特征提取
         self.rpn = basic_conv(512, 512, 3, 1, 1, bn=False)
         # 使用双向的LSTM对上下文信息进行编码
         self.brnn = nn.GRU(512,128, bidirectional=True, batch_first=True)

(3)经过双向LSTM后,特征层shape为(batchx37,50,256),再经过reshape操作,(batchx37,50,256)–>(batch,37,50,256)–>(batch,256,37,50)。“FC”卷积层使用1x1的卷积进行通道数的调整,256->521。

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models

class CTPN_Model(nn.Module):
    def __init__(self):
        super().__init__()
        base_model = models.vgg16(pretrained=False)
        layers = list(base_model.features)[:-1]
        self.base_layers = nn.Sequential(*layers)  # block5_conv3 output
		# 使用一个3x3卷积来替代上述的空间特征提取
         self.rpn = basic_conv(512, 512, 3, 1, 1, bn=False)
         # 使用双向的LSTM对上下文信息进行编码
         self.brnn = nn.GRU(512,128, bidirectional=True, batch_first=True)
         self.lstm_fc = basic_conv(256, 512, 1, 1, relu=True, bn=False)

(4)CTPN的Head层,从上述得到的特征层(batch,256,37,50),获得网络最终的预测结果。如何理解这三个分支所获得的预测结果? 不考虑batch维度,上述特征层的shape为(256,37,50),也就是可以理解为该特征层将我们输入的图片划分成了37x50的区域,每个区域都存在一个特征点,如果物体的中心点落在这个区域内的话,那么就由这个特征点来负责物体的预测。
预测网路的三个分支:
第一个分支:通过1x1x2k的卷积操作,最后输出shape为(batch,2k,37,50),
即2xk vertical coordinates,其中2代表的是相对预测框中心点坐标y和预测框的高度h的偏移量,如何理解这个相对预测框的偏移量?我们在原始图像中预先设定的很多的先验框,先验框就会根据上述预测出来的y和h的偏移量进行调整,将先验框调整称为预测框应该有的样子。k表示的是每一个特征点处设计的anchor的数量,为10。每一个区域内每一个特征点上有10个锚框来负责该区域物体的预测。

第二个分支:同样通过1x1x2k的卷积操作,最后输出shape为(batch,2k,37,50),2xk代表的是2k scores 置信度得分,其中2就是代表的就是前景或背景,即每一个特征点处10个anchors属于前景或者背景的概率值。如果该特征点属于前景,我们才会对先验框进行上述的y和h的偏移,对先验框进行下述x的偏移。
第三个分支:通过1x1xk的卷积操作,最后输出shape为(batch,k,37,50),k side refinement代表的是左右两侧水平方向上每个锚框中心点x坐标的偏移量,用于提高定位的准确性。

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models

class CTPN_Model(nn.Module):
    def __init__(self):
        super().__init__()
        base_model = models.vgg16(pretrained=False)
        layers = list(base_model.features)[:-1]
        self.base_layers = nn.Sequential(*layers)  # block5_conv3 output
		# 使用一个3x3卷积来替代上述的空间特征提取
         self.rpn = basic_conv(512, 512, 3, 1, 1, bn=False)
         # 使用双向的LSTM对上下文信息进行编码
         self.brnn = nn.GRU(512,128, bidirectional=True, batch_first=True)
         self.lstm_fc = basic_conv(256, 512, 1, 1, relu=True, bn=False)
         self.rpn_class = basic_conv(512, 10 * 2, 1, 1, relu=False, bn=False)
        self.rpn_regress = basic_conv(512, 10 * 2, 1, 1, relu=False, bn=False)
        self.rpn_refiment = basic_conv(512, 10, 1, 1, relu=False, bn=False)

    def forward(self, x):
        x = self.base_layers(x)
        # rpn
        x = self.rpn(x)    #[b, c, h, w]

        x1 = x.permute(0,2,3,1).contiguous()  # channels last   [b, h, w, c]
        b = x1.size()  # b, h, w, c
        x1 = x1.view(b[0]*b[1], b[2], b[3])

        x2, _ = self.brnn(x1)

        xsz = x.size()
        x3 = x2.view(xsz[0], xsz[2], xsz[3], 256)  # torch.Size([4, 20, 20, 256])

        x3 = x3.permute(0,3,1,2).contiguous()  # channels first [b, c, h, w]
        x3 = self.lstm_fc(x3)
        x = x3

        cls = self.rpn_class(x)
        regr = self.rpn_regress(x)
        refi = self.rpn_refiment(x)

        cls = cls.permute(0
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值