ctpn详解

ctpn详解一.概述对于复杂场景的文字识别,首先要定位文字的位置,即文字检测。这一直是一个研究热点。文本检测可以看成特殊的目标检测,但它有别于通用目标检测.在通用目标检测中,每个目标都有定义好的边界框,检测出的bbox与当前目标的groundtruth重叠率大于0.5就表示该检测结果正确.文本检测中正确检出需要覆盖整个文本长度,且评判的标准不同于通用目标检测,具体的评判方法参见(ICDAR2017…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

一.概述

对于复杂场景的文字识别,首先要定位文字的位置,即文字检测。这一直是一个研究热点。

文本检测可以看成特殊的目标检测,但它有别于通用目标检测.在通用目标检测中,每个目标都有定义好的边界框,检测出的bbox与当前目标的groundtruth重叠率大于0.5就表示该检测结果正确.文本检测中正确检出需要覆盖整个文本长度,且评判的标准不同于通用目标检测,具体的评判方法参见(ICDAR 2017 RobustReading Competition),所以通用的目标检测方法并不适用文本检测。

CTPN是在ECCV 2016提出的一种文字检测算法。CTPN结合CNN与LSTM深度网络,能有效的检测出复杂场景的横向分布的文字。

二.关键idea

  1. 采用垂直anchor回归机制,检测小尺度的文本候选框
  2. 文本检测的难点在于文本的长度是不固定,可以是很长的文本,也可以是很短的文本.如果采用通用目标检测的方法,将会面临一个问题:**如何生成好的text proposal**.针对上述问题,作者提出了一个vertical anchor的方法,具体的做法是只预测文本的竖直方向上的位置,水平方向的位置不预测。与faster rcnn中的anchor类似,但是不同的是,vertical anchor的宽度都是固定好的了,论文中的大小是16个像素。而高度则从11像素到273像素(每次除以0.7)变化,总共10个anchor.
  3. 采用RNN循环网络将检测的小尺度文本进行连接,得到文本行.
  4. 采用CNN+RNN端到端的训练方式,支持多尺度和多语言,避免后处理                                                                                            ctpn详解

三.ctpn网络结构

假设输入N副Images:

  • 首先VGG提取特征,获得大小为ctpn详解conv5 feature map。
  • 之后在conv5上做3*3的滑动窗口,即每个点都结合周围3*3区域特征获得一个长度为3*3*C的特征向量。输出ctpn详解的feature map,该特征显然只有CNN学习到的空间特征。
  • 再将这个feature map进行Reshape:ctpn详解
  • 然后以Batch=NH且最大时间长度T_{max}=W的数据流输入双向LSTM,学习每一行的序列特征。双向LSTM输出,再经Reshape恢复形状:ctpn详解该特征既包含空间特征,也包含了LSTM学习到的序列特征。
  • 然后经过“FC”卷积层,变为ctpn详解的特征
  • 最后经过类似Faster R-CNN的RPN网络,获得text proposals,如图2-b。

              ctpn详解

 这里解释一下conv5 feature map如何从ctpn详解变为ctpn详解

                ctpn详解

在原版caffe代码中是用im2col提取每个点附近的9点临近点,然后每行都如此处理: 

                                                              ctpn详解

接着每个通道都如此处理:

                                                      ctpn详解 

而im2col是用于卷积加速的操作,即将卷积变为矩阵乘法,从而使用Blas库快速计算。到了tf,没有这种操作,所以一般是用conv2d代替im2col,即强行卷积 ctpn详解

接下来,文章围绕下面三个问题展开:

  1. 为何使用双向LSTM
  2. 如何通过FC层输出产生图2-b中的Text proposals
  3. 如何通过Text proposals确定最终的文本位置,即文本线构造算法

补充说明:

CTPN的具体实现流程包含三个部分:**检测小尺度文本框**,**循环连接文本框**,**文本行边细化**.具体的实现步骤如下:

  1. 使用VGG16作为base net提取特征,得到conv5_3的特征作为feature map,大小是W×H×C
  2. 在上述的feature map上使用大小为3*3的滑动窗进行滑动,每个窗口都能得到一个长度为3×3×C的特征向量,每个滑动窗口中心都会预测k个相对于anchor的偏移
  3. 将上一步得到的特征输入到一个双向的LSTM中,得到长度为W×256的输出,然后接一个512的全连接层,准备输出。
  4. 输出层部分主要有三个输出。2k个vertical coordinate,因为一个anchor用的是中心位置的高(y坐标)和矩形框的高度两个值表示的,所以一个用2k个输出。(注意这里输出的是相对anchor的偏移)。2k个score,因为预测了k个text proposal,所以有2k个分数,text和non-text各有一个分数。k个side-refinement,这部分主要是用来精修文本行的两个端点的,表示的是每个proposal的水平平移量。
  5. 使用一个标准的非极大值抑制算法来滤除多余的text proposal。
  6. 最后使用基于图的文本行构造算法,将得到的一个一个的文本段合并成文本行。

 四.双向LSTM

CNN学习的是感受野内的空间信息,LSTM学习的是序列特征。对于文本序列检测,显然既需要CNN抽象空间特征,也需要序列特征(毕竟文字是连续的)。

CTPN中使用双向LSTM,相比一般单向LSTM有什么优势?双向LSTM实际上就是将2个方向相反的LSTM连起来,如图r。

                       ctpn详解

一般来说,双向LSTM都好于单向LSTM。还是看LSTM介绍文章中的例子:

我的手机坏了,我打算____一部新手机。

Jetbrains全家桶1年46,售后保障稳定

假设使用LSTM对空白部分填词。如果只看横线前面的词,“手机坏了”,那么“我”是打算“修”还是“买”还是“大哭一场”?双向LSTM能看到后面的词是“一部新手机“,那么横线上的词填“买“的概率就大得多了。显然对于文字检测,这种情况也依然适用。

五.全连接层与rpn网络

经过全连接层得到一个512维的feature map。

                     ctpn详解

CTPN通过CNN和BLSTM学到一组“空间 + 序列”特征后,在”FC”卷积层后接入RPN网络。这里的RPN与Faster R-CNN类似,分为两个分支:

  1. 左边分支用于bounding box regression。由于fc feature map每个点配备了10个Anchor,同时只回归中心y坐标与高度2个值,所以rpn_bboxp_red有20个channels
  2. 右边分支用于Softmax分类Anchor

具体RPN网络与Faster R-CNN完全一样,所以不再介绍,只分析不同之处。

 六.竖直Anchor定位文字位置

 由于CTPN针对的是横向排列的文字检测,所以其采用了一组(10个)等宽度的Anchors,用于定位文字位置。Anchor宽高为:

                               ctpn详解

需要注意,由于CTPN采用VGG16模型提取特征,那么conv5 feature map的宽高都是输入Image的宽高的1/16。

同时fc与conv5 width和height都相等。

如图6所示,CTPN为fc feature map每一个点都配备10个上述Anchors。

                                ctpn详解

这样设置Anchors是为了:

  1. 保证在x方向上,Anchor覆盖原图每个点且不相互重叠。
  2. 不同文本在y方向上高度差距很大,所以设置Anchors高度为11-283,用于覆盖不同高度的文本目标。

多说一句,我看还有人不停的问Anchor大小为什么对应原图尺度,而不是conv5/fc特征尺度。这是因为Anchor是目标的候选框,经过后续分类+位置修正获得目标在原图尺度的检测框。那么这就要求Anchor必须是对应原图尺度!除此之外,如果Anchor大小对应conv5/fc尺度,那就要求Bounding box regression把很小的框回归到很大,这已经超出Regression小范围修正框的设计目的。

获得Anchor后,与Faster R-CNN类似,CTPN会做如下处理:

  1. Softmax判断Anchor中是否包含文本,即选出Softmax score大的正Anchor
  2. Bounding box regression修正包含文本的Anchor的中心y坐标高度

注意,与Faster R-CNN不同的是,这里Bounding box regression不修正Anchor中心x坐标和宽度。具体回归方式如下:

                           ctpn详解

(c_y,h)是预测得到的中心坐标和高度值,c_y^ah^a是Anchor的中心y坐标和高度。(c_y^*,h^*)是Ground Truth的中心坐标和高度值。

v=(v_c,v_h)是回归预测的值与anchor的坐标变换,v=(v_c^{*},v_h^{*})是Ground Truth与anchor的坐标变换。

最终需要最小化vv^*的回归误差。可以选用smooth L1损失。

Anchor经过上述Softmax和y方向bounding box regeression处理后,会获得图7所示的一组竖直条状text proposal。后续只需要将这些text proposal用文本线构造算法连接在一起即可获得文本位置。

                 ctpn详解

在论文中,作者也给出了直接使用Faster R-CNN RPN生成普通proposal与CTPN LSTM+竖直Anchor生成text proposal的对比,如图8,明显可以看到CTPN这种方法更适合文字检测。

ctpn详解

 七.文本线构造算法

在上一个步骤中,已经获得了一串或多串text proposal,接下来就要采用文本线构造办法,把这些text proposal连接成一个文本检测框。

                 ctpn详解

为了说明问题,假设某张图有图9所示的2个text proposal,即蓝色和红色2组Anchor,CTPN采用如下算法构造文本线:

  1. 按照水平x坐标排序Anchor
  2. 按照规则依次计算每个Anchor box_ipair(box_j),组成pair(x_i,x_j)
  3. 通过pair(x_i,x_j)建立一个Connect graph,最终获得文本检测框

下面详细解释。假设每个Anchor index如绿色数字,同时每个Anchor Softmax score如黑色数字。

文本线构造算法通过如下方式建立每个Anchor  box_ipair(x_i,x_j)

正向寻找:

  1. 沿水平正方向,寻找和box_i水平距离小于50的候选Anchor
  2. 从候选Anchor中,挑出与box_i竖直方向ctpn详解的Anchor
  3. 挑出符合条件2中Softmax score最大的box_j

再反向寻找:

  1. 沿水平负方向,寻找和box_j水平距离小于50的候选Anchor
  2. 从候选Anchor中,挑出与box_j竖直方向ctpn详解的Anchor
  3. 挑出符合条件2中Softmax score最大的box_k

注:这里的overlap_v是垂直方向的一维IOU。

最后对比score_iscore_k:

  1. 如果ctpn详解,则这是一个最长连接,那么设置ctpn详解
  2. 如果ctpn详解,说明这不是一个最长的连接(即该连接肯定包含在另外一个更长的连接中)。

                   ctpn详解

举例说明,如图10,Anchor已经按照x顺序排列好,并具有图中的Softmax score(这里的score是随便给出的,只用于说明文本线构造算法):

ctpn详解

 然后,这样就建立了一个N \times N的Connect graph(其中N是正Anchor数量)。遍历Graph:

ctpn详解

这样就通过Text proposals确定了文本检测框。

八.文本行的side-refinement

因为这里规定了回归出来的box的宽度是16个像素,所以会导致一些位置上的误差,这时候就是Side-refinement发挥作用的时候 了。定义的式子如下:

                                           ctpn详解

其中带*表示为GroundTruth.。x_{side}表示回归出来的左边界或者右边界,c_x^a表示anchor中心的横坐标,w_a是固定的宽度16像素。所以O的定义相当于是一个缩放的比例,帮助我们去拉伸回归之后的box的结果,从而更好地符合实际文本的位置。对比图如下,红色框是使用了side-refinement的,而黄色框是没有使用side-refinement方法的结果:

                                     ctpn详解

 九.Loss 

 loss的表达式如下:previewctpn详解

CTPN整体包含了3个loss,分类的Ls,边框回归的Lv,边框左右的回归的偏移Lo

Ls为传统的softmax_cross_entropy_loss,其中,i表示所有预测的anchor中的第i个,s_i={0,1},Ns为归一化参数,表示所有的anchor的总和。

Lv使用的smooth_L1_loss,其中,j表示所有IOU>0.5的anchor中的第j个,Nv为归一化参数,表示所有的anchor和groudtruth的IOU>0.5的anchor数总和。λ1为多任务的平衡参数,λ1=1.0。
Lo也是使用的smooth_L1_loss,其中,k表示边界anchor中的第k个,即预测和groundtruth相距32个像素的边界anchor的集合。Nv为归一化参数,表示所有边界anchor数总和。λ1为多任务的平衡参数,λ1=2.0。

十.代码分析

 测试和训练的输入图片大小:

测试:

测试的时候和faster的机制一样,也是短边大于600,长边小于1200,这样的按比例缩放的机制。

def resize_im(im, scale, max_scale=None):
    f=float(scale)/min(im.shape[0], im.shape[1])
    if max_scale!=None and f*max(im.shape[0], im.shape[1])>max_scale:
        f=float(max_scale)/max(im.shape[0], im.shape[1])
    return cv2.resize(im, None,None, fx=f, fy=f,interpolation=cv2.INTER_LINEAR), f

训练:

训练的时候,为了可以走batch,所以是对一个batch内的图片,取最大的宽,高,其余小于该宽高的图片的其他位置补0,这样进行操作的。

def im_list_to_blob(ims):
    """Convert a list of images into a network input.
    Assumes images are already prepared (means subtracted, BGR order, ...).
    """
    max_shape = np.array([im.shape for im in ims]).max(axis=0)
    num_images = len(ims)
    blob = np.zeros((num_images, max_shape[0], max_shape[1], 3),
                    dtype=np.float32)
    for i in range(num_images):
        im = ims[i]
        blob[i, 0:im.shape[0], 0:im.shape[1], :] = im
 
    return blob

Anchor合并机制:

ctpn需要将每一个anchor回归的框进行合并,从而生成最终的文本框。

合并的主要原则,

(1)水平的最大连接距离为MAX_HORIZONTAL_GAP=50个像素

(2)垂直方向的一维IOU是否满足大于MIN_V_OVERLAPS=0.7比例

(3)两个相邻的anchor的高度是否满足小于MIN_SIZE_SIM=0.7比例

然后基于上述的原则,首先从左往右扫描一遍,将满足条件的anchor合并,然后从右往左扫描一遍,将满足条件的anchor合并。

其中,从左往右扫描的代码,

    def get_successions(self, index):
        box = self.text_proposals[index]
        results = []
        for left in range(int(box[0]) + 1, min(int(box[0]) + TextLineCfg.MAX_HORIZONTAL_GAP + 1, self.im_size[1])):
            adj_box_indices = self.boxes_table[left]
            for adj_box_index in adj_box_indices:
                if self.meet_v_iou(adj_box_index, index):
                    results.append(adj_box_index)
            if len(results) != 0:
                return results
        return results

从右往左扫描的代码,

   def get_precursors(self, index):
        box = self.text_proposals[index]
        results = []
        for left in range(int(box[0]) - 1, max(int(box[0] - TextLineCfg.MAX_HORIZONTAL_GAP), 0) - 1, -1):
            adj_box_indices = self.boxes_table[left]
            for adj_box_index in adj_box_indices:
                if self.meet_v_iou(adj_box_index, index):
                    results.append(adj_box_index)
            if len(results) != 0:
                return results
        return results

一维高度IOU的代码,和高度差距小于0.7的代码,

def meet_v_iou(self, index1, index2):
        def overlaps_v(index1, index2):
            h1 = self.heights[index1]
            h2 = self.heights[index2]
            y0 = max(self.text_proposals[index2][1], self.text_proposals[index1][1])
            y1 = min(self.text_proposals[index2][3], self.text_proposals[index1][3])
            return max(0, y1 - y0 + 1) / min(h1, h2)
 
        def size_similarity(index1, index2):
            h1 = self.heights[index1]
            h2 = self.heights[index2]
            return min(h1, h2) / max(h1, h2)
 
        return overlaps_v(index1, index2) >= TextLineCfg.MIN_V_OVERLAPS and \
               size_similarity(index1, index2) >= TextLineCfg.MIN_SIZE_SIM

最后附上一段tensorflow版本的ctpn网络结构代码,非常清楚明了。

def model(image):
    image = mean_image_subtraction(image) # vgg网络数据预处理,图像三个通道减去均值
    with slim.arg_scope(vgg.vgg_arg_scope()):
        conv5_3 = vgg.vgg_16(image)

    rpn_conv = slim.conv2d(conv5_3, 512, 3) # 对于vgg16的conv5_3进行3*3的滑动卷积操作

    lstm_output = Bilstm(rpn_conv, 512, 128, 512, scope_name='BiLSTM') # 得到双向lstm的结果

    bbox_pred = lstm_fc(lstm_output, 512, 10 * 4, scope_name="bbox_pred") # 10*4的bbox的预测值
    cls_pred = lstm_fc(lstm_output, 512, 10 * 2, scope_name="cls_pred") # 10*2的正负anchor分类score
    # transpose: (1, H, W, A x d) -> (1, H, WxA, d)
    cls_pred_shape = tf.shape(cls_pred)
    cls_pred_reshape = tf.reshape(cls_pred, [cls_pred_shape[0], cls_pred_shape[1], -1, 2])

    cls_pred_reshape_shape = tf.shape(cls_pred_reshape)
    cls_prob = tf.reshape(tf.nn.softmax(tf.reshape(cls_pred_reshape, [-1, cls_pred_reshape_shape[3]])),
                          [-1, cls_pred_reshape_shape[1], cls_pred_reshape_shape[2], cls_pred_reshape_shape[3]],
                          name="cls_prob")

    return bbox_pred, cls_pred, cls_prob

十一.总结

优点:

CTPN对于检测的边框在上下左右4个点上都比较准确,这点比EAST要好。

缺点:

(1)CTPN只可以检测水平方向的文本,竖直方向的话就会出现一个字一个字断开的想象。倾斜角度的话需要修改后处理anchor的连接方式,但是应该会引入新的问题。

(2)CTPN由于涉及到anchor合并的问题,何时合并,何时断开,这是一个问题。程序使用的是水平50个像素内合并,垂直IOU>0.7合并。或许由于BLSTM的引入,导致断开这个环节变差。所以对于双栏,三栏的这种文本,ctpn会都当做一个框处理,有时也会分开处理,总之不像EAST效果好。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/200778.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号