fasterrcnn tensorflow代码详解_从代码实现的角度理解CTPN

57448d66aef3df086f080126e539d802.png

论文:https://arxiv.org/pdf/1609.03605.pdf

Tensorflow实现:eragonruan/text-detection-ctpn

一、网络结构

  1. 首先,使用VGG16作为base net提取特征,将conv5_3作为输出,大小是B×H×W×C;
  2. 在conv5_3上做3x3滑窗(这里的滑窗在tensorflow中的代码实现就是卷积操作),经过滑窗后得到的feature map大小仍为B×H×W×C,但此时的每个像素点融合了3*3*C的信息。(论文作者使用caffe中的im2col实现的滑窗操作,将B×C×H×W大小的feature map转换为B×9C×H×W,本文只讨论tensorflow版本的代码实现);
  3. B×H×W×C大小的feature map经过BLSTM得到[B*H,W,256]大小的feature map;
  4. [B*H,W,256]大小的feature map接一个卷积层(FC代表卷积),输出[B*H,W,512];
  5. [B*H,W,512]分成三个预测支路(实际上就是使用矩阵乘法强行将输出转换为想要的形状);
    [B,H,W,K*2],2K个vertical coordinate,对某个像素点的某个anchor预测

    [B,H,W,K*2],2K个score,记为s=[text,non-text];
    [B,H,W,K],K个side-refinement,对某个像素点的某个anchor预测

    K代表每个像素点设置的Anchor的个数,文中K=10;这里的
    及 s 是网络预测输出,下文提到的
    是真值标签;
  6. 根据上面的输出结果,可以得到密集的text proposal,使用NMS算法抑制掉多余的box,抑制后的效果如图一中的(b);
  7. 使用文本线构造方法将上述得到的一个一个的文本段合并成文本行。

be5fb377ee2eb8ac77f2c2069c01c5f4.png
图一 CTPN网络结构

通过上述网络,在步骤5可以得到网络预测输出。先通过训练使得网络预测值准确率足够高,训练完成后直接使用预测值进行nms及文本线构造(后处理)得到最终的文本检测结果。接下来,我将从如何训练网络及如何进行后处理两部分进行说明。

二、如何训练网络?

  • 数据预处理
  • 网络:
    前向网络:网络inference
    后向网络:如何计算loss 及最小化loss(优化器的选择)
  • 训练:加载预训练好的权重+保存权重及运行日志(代码见main/train.py,可以参考这里)

补充说明:CTPN中的Anchor

Anchor概念源于Faster R-CNN(要想彻底理解CTPN,建议首先掌握Faster R-CNN中的RPN),简单来说,Anchor就是在feature map上每个像素点中心设置K个针对原图尺寸的框。

CTPN在conv5_3 feature map的每个像素点中心设置K=10个针对原图尺寸的Anchor。这10个Anchor的宽度固定为16,高度设置为[11,16,23,33,48,68,97,139,198,283](依次除以0.7),如图二所示(仅画四种高度进行示意)。

为什么这样设置Anchor?

(1)文本长度变化剧烈,直接预测十分困难。相较于宽度,文本高度变化相对小一些。因此作者固定宽度,先在竖直方向进行预测,再通过文本线构造方法得到文本行。
(2)CTPN采用VGG16模型提取特征,conv5_3 feature map的大小是原图的1/16。因为设置的Anchor针对原图尺寸,所以将宽度固定为16,这样可以确保无重叠的覆盖原图水平方向每个像素点。
(3)Anchor高度的设置原则是尽可能涵盖样本集的文本高度。

43c3e3600f7c45b6602e7a73134272e7.png
图二 某个像素点的Anchor示意图

1.数据预处理(代码见util/prepare/split_label.py)

(1)处理图像尺寸

保持图片比例不变且短边

600,长边
1200;再将图像的w,h reseize为16的整数倍;
img = cv.imread(img_path)#读取图像
img_size = img.shape#获取图像的[h,w,c]
im_size_min = np.min(img_size[0:2])#获取h,w中较小的
im_size_max = np.max(img_size[0:2])#获取h,w中较大的

#保持宽高比不变,且短边不大于600,长边不大于1200
im_scale = float(600) / float(im_size_min)
if np.round(im_scale * im_size_max) > 1200:
    im_scale = float(1200) / float(im_size_max)
new_h = int(img_size[0] * im_scale)
new_w = int(img_size[1] * im_scale)

#使得图像的w,h都是16的整数倍
new_h = new_h if new_h // 16 == 0 else (new_h // 16 + 1) * 16
new_w = new_w if new_w // 16 == 0 else (new_w // 16 + 1) * 16

(2)处理ground truth

一般数据库给的都是整个文本行或者单词级别的标注,如图三所示。我们需要对人为设置的Anchor进行Bounding box regression,回归的范围是有限的,因此需要将ground truth处理成一系列固定宽度的box,如图四所示。

5bff0cadaabf0f93f780343567205f52.png
图三 原始标注

7436642f91af97afee7e3bee8c181f13.png
图四 处理后的ground truth

处理ground truth的大致流程如下:

  • 得到在resize后的图像上的gt尺寸
  • 使gt坐标为顺时针,即:左上角=[x1,y1],右上角=[x2,y2],右下角=[x3,y3],左下角=[x4,y4]
  • 将原始标注划分为宽度16的四边形(首尾四边形宽度不一定为16)
  • 取四边形坐标中的x_min, y_min, x_max, y_max,构成划分后宽度为16的矩形坐标

2.网络

2.1 前向网络(代码见nets/model_train.py)

conv5_3 = vgg.vgg_16(image)#VGG16作为base net提取特征,将conv5_3作为输出
rpn_conv = slim.conv2d(conv5_3, 512, 3)#在conv5_3上做3x3滑窗

#B×H×W×C大小的feature map经过BLSTM得到[B*H,W,256]大小的feature map
lstm_output = Bilstm(rpn_conv, 512, 128, 512, scope_name='BiLSTM')

#本代码做了调整:1.[B*H,W,256]大小的feature map没有接卷积层(FC代表卷积) 2.[B*H,W,256]大小的feature map直接预测的四个回归量
bbox_pred = lstm_fc(lstm_output, 512, 10 * 4, scope_name="bbox_pred")#网络预测回归输出
cls_pred = lstm_fc(lstm_output, 512, 10 * 2, scope_name="cls_pred")#网络预测分类输出

B×H×W×C大小的feature map如何通过BLSTM得到[B*H,W,256]大小的feature map?

BLSTM在tensorflow中可以通过如下方式实现:

(outputs, output_states) = tf.nn.bidirectional_dynamic_rnn(
cell_fw, # 前向RNN
cell_bw, # 后向RNN
inputs, # 输入向量
sequence_length=None,# 输入序列的实际长度(可选,默认为输入序列的最大长度)
initial_state_fw=None,  # 前向的初始化状态(可选)
initial_state_bw=None,  # 后向的初始化状态(可选)
dtype=None, # 初始化和输出的数据类型(可选)
parallel_iterations=None,
swap_memory=False, 
time_major=False,
# time_major=true, 参数inputs及outputs的形状必须为 `[max_time, batch_size, depth]`. 
# time_major=false, 参数inputs及outputs的形状必须为`[batch_size, max_time, depth]`. 
scope=None
)

其中的参数inputs就是大小为B×H×W×C的feature map。由于inputs要求的形状为[batch_size, max_time, depth],那么两者如何对应呢?

对一张输入图片而言,BLSTM提取的只是文本水平方向的关联性,行与行之间相互独立,batch之间自然也相互独立,因此BH可以当做输入BLSTM的batch_size;将feature map的每一行作为一个时间序列 max_time输入BLSTM,如图一所示,BLSTM中时间序列 max_time对应W;每个时间序列的维度depth对应feature map通道数C(注意论文作者使用caffe中的im2col实现的滑窗操作,每个时间序列的维度depth对应feature map通道数为3*3*C);因此[batch_size, max_time, depth]=[B*H,W,C]。

outputs为(output_fw, output_bw),包含前向cell输出和后向cell输出组成的二元组。通常使用tf.concat(outputs,-1)将其拼接。拼接后outputs的形状为[B*H,W,2 * hidden_unit_num]。代码中LSTM有128个隐层单元数,故BLSTM有256个隐层单元数,故outputs=[B*H,W,256]。

2.2 后向网络(下面给出原文的损失函数,tensorflow的代码直接回归

,使用的损失函数跟Faster RCNN一样)

损失函数

0c93e9911090cfdc4350431559b5cd89.png

loss由三部分组成:
分类损失:

使用交叉熵,其中
代表网络预测输出,
代表anchor的真值标签;

anchor竖直方向的回归损失:
使用smooth L1 loss,其中
代表网络预测输出,
代表真值标签;

anchor水平偏移量回归损失:
使用smooth L1 loss,其中
代表网络预测输出 ,
代表真值标签;

=1,
=2用于平衡各任务的损失,
表示对应任务的样本数量。

如何计算

及Anchor的分类标签?

(1)Anchor(下标带a的)与ground truth间的中心坐标平移量

与宽高比值
计算公式如下:

(2)分类的真值标签通过计算Anchor与ground truth的IoU来确定。IoU是指Anchor与ground truth面积的交并比。

  • Anchor与ground truth IoU大于0.7的anchor定义为正样本(
    );
  • Anchor与ground truth IoU小于0.3的anchor定义为负样本(
    );
  • 按照上面的做法,会出现一个问题,可能有些真值框找不到心仪的Anchor,那这些训练数据就没法利用了。因此我们用一个折中的办法来保证每个真值框至少有一个Anchor与之对应:与ground truth IoU最大的那个anchor也定义为正样本,这个时候不考虑IoU大小有没有大于0.7。

为什么不直接回归

根据上述介绍的数据预处理及anchor设置原则,我们可以得到图五(这里只画了一种高度的anchor示意)。

由图五可以看出:

  • 中间的ground truth的真值
    均为常数,故只需要预测
    。对某个像素点的k=10个anchor来说,每个anchor都要预测
    ,因此网络的预测输出为[B,H,W,K*2];
  • 对于两侧的ground truth,
    不再是常数,因此文中单独对两侧的anchor加了水平偏移量
    的回归(不是很清楚为什么不把
    一起回归了),因此网络的预测输出为[B,H,W,K]

d4d5e251f085e1a0b4ad19256dbd08bc.png
图五 anchor与ground truth示意图

三、后处理

1.nms:参考NMS原理,使用nms后可以得到图六所示的text proposals。

13f7cb44c696d9597c6aa45f6651c23f.png
图六 使用nms后得到的text proposals

2.文本线构造

获得了图六所示的一串或多串text proposal,接下来就要采用文本线构造办法,把这些text proposal连接成文本行。
文本行是由一系列大于

的text proposals的
邻居对构成的,如果text proposals
是text proposals
的邻居对,需要满足如下条件:
  • 是距离
    水平距离最近的text proposals;
  • 的水平距离小于
    个像素值;
  • 的竖直方向的重合率大于

其中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OCR技术是一种能够将图像中的文本内容转化为可编辑文本的技术,其中ctpn和crnn是OCR技术中的两个重要组成部分。 ctpn(Connectionist Text Proposal Network)是一种基于深度学习的文本检测算法,其主要任务是检测图像中的文本行和单个字符,并将其转换为一组矩形边界框。这些边界框可以用于后续的文本识别操作。 crnn(Convolutional Recurrent Neural Network)是一种基于深度学习的文本识别算法,其主要任务是根据文本检测阶段生成的文本行或单个字符图像,识别其中的文本内容。crnn算法通常由卷积神经网络(CNN)和循环神经网络(RNN)两个部分组成,其中CNN用于提取图像特征,RNN用于对特征序列进行建模。 以下是一个基于ctpn和crnn的OCR代码实现示例(Python): ```python import cv2 import numpy as np import tensorflow as tf # 加载ctpn模型 ctpn_model = cv2.dnn.readNet('ctpn.pb') # 加载crnn模型 crnn_model = tf.keras.models.load_model('crnn.h5') # 定义字符集 charset = '0123456789abcdefghijklmnopqrstuvwxyz' # 定义字符到索引的映射表 char_to_index = {char: index for index, char in enumerate(charset)} # 定义CTPN参数 ctpn_params = { 'model': 'ctpn', 'scale': 600, 'max_scale': 1200, 'text_proposals': 2000, 'min_size': 16, 'line_min_score': 0.9, 'text_proposal_min_score': 0.7, 'text_proposal_nms_threshold': 0.3, 'min_num_proposals': 2, 'max_num_proposals': 10 } # 定义CRNN参数 crnn_params = { 'model': 'crnn', 'img_w': 100, 'img_h': 32, 'num_classes': len(charset), 'rnn_units': 128, 'rnn_dropout': 0.25, 'rnn_recurrent_dropout': 0.25, 'rnn_activation': 'relu', 'rnn_type': 'lstm', 'rnn_direction': 'bidirectional', 'rnn_merge_mode': 'concat', 'cnn_filters': 32, 'cnn_kernel_size': (3, 3), 'cnn_activation': 'relu', 'cnn_pool_size': (2, 2) } # 定义文本检测函数 def detect_text(image): # 将图像转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 缩放图像 scale = ctpn_params['scale'] max_scale = ctpn_params['max_scale'] if np.max(gray) > 1: gray = gray / 255 rows, cols = gray.shape if rows > max_scale: scale = max_scale / rows gray = cv2.resize(gray, (0, 0), fx=scale, fy=scale) rows, cols = gray.shape elif rows < scale: scale = scale / rows gray = cv2.resize(gray, (0, 0), fx=scale, fy=scale) rows, cols = gray.shape # 文本检测 ctpn_model.setInput(cv2.dnn.blobFromImage(gray)) output = ctpn_model.forward() boxes = [] for i in range(output.shape[2]): score = output[0, 0, i, 2] if score > ctpn_params['text_proposal_min_score']: x1 = int(output[0, 0, i, 3] * cols / scale) y1 = int(output[0, 0, i, 4] * rows / scale) x2 = int(output[0, 0, i, 5] * cols / scale) y2 = int(output[0, 0, i, 6] * rows / scale) boxes.append([x1, y1, x2, y2]) # 合并重叠的文本框 boxes = cv2.dnn.NMSBoxes(boxes, output[:, :, :, 2], ctpn_params['text_proposal_min_score'], ctpn_params['text_proposal_nms_threshold']) # 提取文本行图像 lines = [] for i in boxes: i = i[0] x1, y1, x2, y2 = boxes[i] line = gray[y1:y2, x1:x2] lines.append(line) return lines # 定义文本识别函数 def recognize_text(image): # 缩放图像 img_w, img_h = crnn_params['img_w'], crnn_params['img_h'] image = cv2.resize(image, (img_w, img_h)) # 归一化图像 if np.max(image) > 1: image = image / 255 # 转换图像格式 image = image.transpose([1, 0, 2]) image = np.expand_dims(image, axis=0) # 预测文本 y_pred = crnn_model.predict(image) y_pred = np.argmax(y_pred, axis=2)[0] # 将预测结果转换为文本 text = '' for i in y_pred: if i != len(charset) - 1 and (not (len(text) > 0 and text[-1] == charset[i])): text += charset[i] return text # 读取图像 image = cv2.imread('test.png') # 检测文本行 lines = detect_text(image) # 识别文本 texts = [] for line in lines: text = recognize_text(line) texts.append(text) # 输出识别结果 print(texts) ``` 上述代码实现了一个基于ctpn和crnn的OCR系统,其中ctpn用于检测文本行,crnn用于识别文本内容。在使用代码时,需要将ctpn和crnn的模型文件替换为自己训练的模型文件,并根据实际情况调整参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值