【論文研讀筆記---九】EAST文本檢測

論文研讀系列彙總:

1.AlexNet論文研讀
2.VGG論文研讀
3.GoogLeNet論文研讀
4.Faster RCNN論文研讀
5.ResNet 論文研讀
6.SENet 論文研讀
7.CTPN 論文研讀
8.CRNN 論文研讀
9.EAST 論文研讀

文本檢測領域,有一篇經典的論文,是曠世科技在2017年提出來的EAST模型,論文的全稱爲《EAST: An Efficient and Accurate Scene Text Detector》,論文的下載地址如下:

論文地址:https://arxiv.org/pdf/1704.03155.pdf
本文將對該方法進行具體介紹,爲解釋詳細並通俗,這篇文章基本涵蓋了網上能找到的大部分熱門east講解文章

EAST模型介紹

1.EAST模型結構

通過下圖我們知道,一個文本檢測有多個階段,就以region proposals系的檢測算法爲例,他們通常包含候選框提取、候選框過濾、bouding box迴歸、候選框合併等階段,EAST的作者認爲,一個文本檢測算法被拆分成多個階段其實並沒有太多好處,實現真正端到端的文本檢測網絡纔是正確之舉。所以EAST的pipeline相當優雅,只分爲FCN生成文本行參數階段和局部感知NMS階段,網絡的簡潔是的檢測的準確性和速度都有了進一步的提高。
在這裏插入圖片描述

2.全文貢獻

  • 提出了一個由兩階段組成的場景文本檢測方法:FCN階段和NMS階段。FCN直接生成文本區域,不包括冗餘和耗時的中間步驟。
  • 該pipeline可靈活生成wordlevel或line level預測,其幾何形狀可爲旋轉框或矩形。
  • 所提出的算法在準確性和速度上明顯優於最先進的方法。

3. 模型原理

EAST的網絡結構總共包含三個部分:feature extractor stem(特徵提取分支), feature-merging branch(特徵合併分支) 以及 output layer(輸出層)。

在這裏插入圖片描述

  • 在特徵提取分支部分,主要由四層卷積層組成,可以是一些預訓練好的卷積層,作者採用的是VGG16中pooling-2到pooling-5每一層得到的feature map。記每一層卷積層卷積後得到feature map爲fi,如圖1所示,從上到下。

  • 在特徵合併分支部分,其實作者借鑑了U-net的思想,只是U-net採用的是反捲積的操作,而這裏採用的是反池化的操作,具體的計算大致如下,對於一個fi,首先經過一層反池化操作,得到與上一層卷積feature map同樣大小的特徵,然後將其與fi+1進行拼接,拼接後再依次進入一層和的卷積層,以減少拼接後通道數的增加,得到對應的,在特徵合併分支的最後一層,是一層的卷積層,卷積後得到的feature map最終直接進入輸出層。具體的計算公式如下:
    在這裏插入圖片描述
    其中,gi被稱爲合併基,hi是合併後得到feature map。之所以要引入特徵合併分支,是因爲在場景文字識別中,文字的大小非常極端,較大的文字需要神經網絡高層的特徵信息,而比較小的文字則需要神經網絡淺層的特徵信息,因此,只有將網絡不同層次的特徵進行融合才能滿足這樣的需求。

        #代碼實現該部分
         for i in range(4):
                print('Shape of f_{} {}'.format(i, f[i].shape))
            g = [None, None, None, None]
            h = [None, None, None, None]
            num_outputs = [None, 128, 64, 32]
            for i in range(4):
                if i == 0:
                    h[i] = f[i]  # 計算h
                else:
                    c1_1 = slim.conv2d(tf.concat([g[i-1], f[i]], axis=-1), num_outputs[i], 1)
                    h[i] = slim.conv2d(c1_1, num_outputs[i], 3)
                if i <= 2:
                    g[i] = unpool(h[i]) # 計算g
                else:
                    g[i] = slim.conv2d(h[i], num_outputs[i], 3)
                print('Shape of h_{} {}, g_{} {}'.format(i, h[i].shape, i, g[i].shape))
  • 在輸出層部分,主要有兩部分,一部分是用單個通道的卷積得到score map(分數圖),記爲Fs,另一部分是多個通道的卷積得到geometry map(幾何形狀圖),記爲Fg,在這一部分,幾何形狀可以是RBOX(旋轉盒子)或者QUAD(四邊形)。(即可以檢測水平文本的數據集(如IC2013)標註也可以是幾何圖形(IC2015)標註)
    對於RBOX,主要有5個通道,其中四個通道表示每一個像素點與文本線上、右、下、左邊界距離記爲R,另一個通道表示該四邊形的旋轉角度。
    對於QUAD,則採用四邊形四個頂點的座標表示,每個點的座標爲,因此,總共有8個通道。關於RBOX和QUAD的表示可以見表1:
    在這裏插入圖片描述
    在這裏插入圖片描述
    RBOX的參數解釋如圖所示,從像素位置到矩形的頂部,右側,底部,左側邊界的4個距離和通道角度

  • (a) 中黃色的爲人工標註的框,綠色爲對黃色框進行0.3倍邊長的縮放後的框,這樣做可以進一步去除人工標註的誤差,拿到更準確的label信息。

  • (b) 爲根據(a)中綠色框生成的label信息

  • (c )中先生成一個(b)中白色區域的最小外接矩,然後算每一個(b)中白色的點到粉色最小外接矩的上下左右邊的距離,即生成(d),然後生成粉色的矩形和水平方向的夾角,即生成角度信息(e),e中所有灰色部分的角度信息一樣,都是同樣的角度。

      # 計算score_map
            F_score = slim.conv2d(g[3], 1, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None)
            # 4 channel of axis aligned bbox and 1 channel rotation angle
            # 計算RBOX的geometry_map
            geo_map = slim.conv2d(g[3], 4, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None) * FLAGS.text_scale
            # angle is between [-45, 45] #計算angle
            angle_map = (slim.conv2d(g[3], 1, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None) - 0.5) * np.pi/2
            F_geometry = tf.concat([geo_map, angle_map], axis=-1)

3.1模型結構全代碼

#獲得文本框的預測分數和幾何圖形
def model(images, weight_decay=1e-5, is_training=True):
    '''
    define the model, we use slim's implemention of resnet
    '''
    images = mean_image_subtraction(images)
 
    with slim.arg_scope(resnet_v1.resnet_arg_scope(weight_decay=weight_decay)):
        logits, end_points = resnet_v1.resnet_v1_50(images, is_training=is_training, scope='resnet_v1_50')
 
    with tf.variable_scope('feature_fusion', values=[end_points.values]):
        batch_norm_params = {
        'decay': 0.997,
        'epsilon': 1e-5,
        'scale': True,
        'is_training': is_training
        }
        with slim.arg_scope([slim.conv2d],
                            activation_fn=tf.nn.relu,
                            normalizer_fn=slim.batch_norm,
                            normalizer_params=batch_norm_params,
                            weights_regularizer=slim.l2_regularizer(weight_decay)):
            #提取四個級別的特徵圖(記爲fi)
            f = [end_points['pool5'], end_points['pool4'],
                 end_points['pool3'], end_points['pool2']]
            for i in range(4):
                print('Shape of f_{} {}'.format(i, f[i].shape))
 
            #基礎特徵圖
            g = [None, None, None, None]
 
            #合併後的特徵圖
            h = [None, None, None, None]
 
            #合併階段每層的卷積核數量
            num_outputs = [None, 128, 64, 32]
 
            #自下而上合併特徵圖
            for i in range(4):
                if i == 0:
                    h[i] = f[i]
                else:
                    #先合併特徵圖,再進行1*1卷積 3*3卷積
                    c1_1 = slim.conv2d(tf.concat([g[i-1], f[i]], axis=-1), num_outputs[i], 1)
                    h[i] = slim.conv2d(c1_1, num_outputs[i], 3)
 
                if i <= 2:
                    #上池化特徵圖
                    g[i] = unpool(h[i])
                else:
                    g[i] = slim.conv2d(h[i], num_outputs[i], 3)
                print('Shape of h_{} {}, g_{} {}'.format(i, h[i].shape, i, g[i].shape))
 
            #輸出層
            # here we use a slightly different way for regression part,
            # we first use a sigmoid to limit the regression range, and also
            # this is do with the angle map
            #獲得預測分數的特徵圖,與原圖尺寸一致,每一個值代表此處是否有文字的可能性
            F_score = slim.conv2d(g[3], 1, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None)
 
            # 4 channel of axis aligned bbox and 1 channel rotation angle
            #獲得旋轉框像素偏移的特徵圖,有四個通道,分別代表每個像素點到文本矩形框上,右,底,左邊界的距離
            geo_map = slim.conv2d(g[3], 4, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None) * FLAGS.text_scale
            #獲得旋轉框角度特徵圖 angle is between [-45, 45]
            angle_map = (slim.conv2d(g[3], 1, 1, activation_fn=tf.nn.sigmoid, normalizer_fn=None) - 0.5) * np.pi/2
            #按深度連接旋轉框特徵圖和角度特徵圖
            F_geometry = tf.concat([geo_map, angle_map], axis=-1)
 
    return F_score, F_geometry

4.標籤生成

Geometry Map生成(圖c~e)
對於其文本區域以QUAD格式標註的數據集(例如ICDAR 2015),我們首先生成一個旋轉矩形,以最小面積覆蓋該區域。然後,對於每個有正分數的像素,我們計算它與文本框4個邊界的距離,並將它們放到RBOX ground truth的4個通道中。對於QUAD ground truth,8通道幾何圖形每個像素的正分數值是它與四邊形4個頂點的座標偏移

5.局部感知NMS算法

當預測結束後,需要對文本線進行構造,爲了提高構造的速度,作者提出了一種局部感知NMS算法。在假設來自附近像素的幾何圖形傾向於高度相關的情況下,提出逐行合併幾何圖形,並且在合併同一行中的幾何圖形時,我們將迭代合併當前遇到的幾何圖形與最後一個合併圖形。這種改進的技術在場景中以O(n)運行。合併的四邊形座標是通過兩個給定四邊形的得分進行加權平均的。是平均而非選擇,充當了投票機制,採取這種方法可以將所有框的座標信息都加以利用,而不像常規NMS一樣直接棄掉得分低的框,也許會丟失信息。
在這裏插入圖片描述
當合並完成後,會將合併後的幾何形狀作爲一個整體繼續合併下去,直到不滿足合併條件,將此時合併後的幾何形狀作爲一個文本線保存到當中,重複該過程,直到所有的幾何形狀都遍歷一遍爲止
代碼實現如下:

def weighted_merge(g, p):
    g[:8] = (g[8] * g[:8] + p[8] * p[:8])/(g[8] + p[8])
    g[8] = (g[8] + p[8])
    return g

6.損失函數設計

損失函數分爲兩種,QUAD和RBOX,在真正實現上,選擇的是RBOX,實驗評估放在ICDAR2015和ICDAR2017上。QUAR也可以被使用。下面介紹RBOX的損失函數設計。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
角度誤差爲:
在這裏插入圖片描述
迴歸有2個loss,分別迴歸邊框的上下左右距離L_AABB,和邊框與水平方向的夾角L_theta。
其中,

L_AABB=-log(area_intersect/area_union),

L_theta = 1 - tf.cos(theta_pred - theta_gt)

其中,

d1_pred, d2_pred, d3_pred, d4_pred分別爲距離上,右,下,左,邊框的預測的距離。

d1 -> top, d2->right, d3->bottom, d4->left分別爲距離上,右,下,左,邊框的label的距離。

theta_pred爲預測的角度。

theta_gt爲label的角度。

6.1損失函數代碼解讀

#獲得預測得分損失
def dice_coefficient(y_true_cls, y_pred_cls,
                     training_mask):
    '''
    dice loss
    :param y_true_cls:
    :param y_pred_cls:
    :param training_mask:
    :return:
    '''
    eps = 1e-5
    intersection = tf.reduce_sum(y_true_cls * y_pred_cls * training_mask)
    union = tf.reduce_sum(y_true_cls * training_mask) + tf.reduce_sum(y_pred_cls * training_mask) + eps
    loss = 1. - (2 * intersection / union)
    tf.summary.scalar('classification_dice_loss', loss)
    return loss
 
#獲得總損失
def loss(y_true_cls, y_pred_cls,
         y_true_geo, y_pred_geo,
         training_mask):
    '''
    define the loss used for training, contraning two part,
    the first part we use dice loss instead of weighted logloss,
    the second part is the iou loss defined in the paper
    :param y_true_cls: ground truth of text
    :param y_pred_cls: prediction os text
    :param y_true_geo: ground truth of geometry
    :param y_pred_geo: prediction of geometry
    :param training_mask: mask used in training, to ignore some text annotated by ###
    :return:
    '''
    classification_loss = dice_coefficient(y_true_cls, y_pred_cls, training_mask)
    # scale classification loss to match the iou loss part
    classification_loss *= 0.01
 
    # d1 -> top, d2->right, d3->bottom, d4->left
    d1_gt, d2_gt, d3_gt, d4_gt, theta_gt = tf.split(value=y_true_geo, num_or_size_splits=5, axis=3)
    d1_pred, d2_pred, d3_pred, d4_pred, theta_pred = tf.split(value=y_pred_geo, num_or_size_splits=5, axis=3)
    #計算真實旋轉框、預測旋轉框面積
    area_gt = (d1_gt + d3_gt) * (d2_gt + d4_gt)
    area_pred = (d1_pred + d3_pred) * (d2_pred + d4_pred)
    #計算相交部分的高度和寬度  面積
    w_union = tf.minimum(d2_gt, d2_pred) + tf.minimum(d4_gt, d4_pred)
    h_union = tf.minimum(d1_gt, d1_pred) + tf.minimum(d3_gt, d3_pred)
    area_intersect = w_union * h_union
    #獲得並集面積
    area_union = area_gt + area_pred - area_intersect
    #計算旋轉框IOU損失、角度  加1爲了防止交集爲0
    L_AABB = -tf.log((area_intersect + 1.0)/(area_union + 1.0))
    L_theta = 1 - tf.cos(theta_pred - theta_gt)
    tf.summary.scalar('geometry_AABB', tf.reduce_mean(L_AABB * y_true_cls * training_mask))
    tf.summary.scalar('geometry_theta', tf.reduce_mean(L_theta * y_true_cls * training_mask))
    L_g = L_AABB + 20 * L_theta
 
    return tf.reduce_mean(L_g * y_true_cls * training_mask) + classification_loss

7.參考

全論文翻譯:https://blog.csdn.net/zhangwl27/article/details/86542160

參考鏈接(推薦)****
https://blog.csdn.net/qq_14845119/article/details/78986449
擁有advancedeast改進思路:https://blog.csdn.net/linchuhai/article/details/84677249

參考鏈接:https://blog.csdn.net/attitude_yu/article/details/80724187
參考鏈接:https://blog.csdn.net/weixin_38708130/article/details/83789457
參考鏈接:https://blog.csdn.net/attitude_yu/article/details/80724187
參考鏈接:https://blog.csdn.net/zhangwei15hh/article/details/79899300
參考鏈接:https://blog.csdn.net/sdlypyzq/article/details/78425128

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章