DBNet论文详解
DBNet:Real-time Scene Text Detection with Differentiable Binarization
论文地址:https://arxiv.org/pdf/1911.08947.pdf
代码地址:https://github.com/MhLiao/DB
背景介绍
DBnet是一种用于文本检测的深度学习模型,在文件检测领域面临的挑战主要有:
-
文本的多样性:颜色、大小、字体、语言、方向、文本长度和文本弯曲等都会影响文本的检测效果
-
复杂的背景干扰:图像失真、模糊、低分辨率、亮度等都会影响检测效果
-
文本分布密集、文本内容重叠
-
文本存在局部一致性,即文本的一部分也可视为独立的文
目前主流的文本检测方法可以分为两大类,一是基于回归的方法,二是基于分割的方法。基于检测的方式类似于目标检测,这种方法存在一个缺点就是没办法精确定位存在一定弧度的文本。基于分割的方法是做1一个像素及的分类,然后利用分类结果生成二值图像,在通过二值图像定位文字区域。
DBNet算法流程
DBNet 中的 DB(Differentiable Binarization )可微分的二值化。在 DBNet 中,分割结果的二值化后处理可以随着模型一起训练,得到一个自适应的阈值。自适应的阈值也可以使得后处理变得简单,提升精度,也提高速度。这也是本文主要的创新点。

如上图所示,以往基于分割的文本检测方式流程是按蓝色的箭头方式走。而本文的DBNet则是按照红色箭头走,为每个图像的每一个输出一个自适应的阈值(threshold map),然后通过可微二值化(DB)操作生存二值图像(binarization map),最后通过轮廓查找的方式确定文本区域。
DBNet 网络结构
DBnet的网络结构并没有太多特殊结构,主要通过backbone提取特征,抽取出4个stage的特征构成特征金字塔,4个stage分别为1/4、1/8、1/16和1/32下采样的特征图。然后将特征金字塔输出的不同大小的特征图调整到1/4降采样特征图大小,在通道方向上拼接起来,输入到head部分生成probabilty map可以理解为前景的置信度,和threshold map自适应阈值图,可以理解为置信度的阈值。如下图所示。在作者的代码中如果backbone使用resnet则在stage2、stage3、stage4中使用了可变形卷积(DCN)来扩大感受野,而在MobileNet作为backbone则没有使用,应该是出于使用场景考虑,使用MobileNet的网络主要是为了嵌入边缘设备,所以可能基于边缘设备的算力和一些推理框架对可变形卷积的支持并不是很好考虑,没有使用DCN。

在通过概率图与阈值图通过DB操作得到近视的二至图,从而确定文字的区域。
可微分二值化
DBNet最大的特点就是最引入了Differentiable Binarization(DB)模块来优化分割预测结果,DB模块将阈值二值化过程变得可微,不仅可以增加错误预测梯度,也可以联合优化各个分支,得到更好的语义概率图,部署推理时如果对速度要求较高也可以将阈值分支去除,通过概率图得到最终的结果。
下面给出可微分二值化的数学公式:
B ^ = 1 e k ( P i , j − T i , j ) \widehat{B}=\frac{1}{e^{k(P_{i,j}-T_{i,j})}} B =ek(Pi,j−Ti,j)1
公式中的P和T为网络输出的概率图和阈值图
通过该方式进行像素值计算可以得到类似二值化的结果,令x=P-T,k=50,标准阈值二值化和可微分二值化的曲线图如下图所示。可以看出可微二值化和标准二值化结果十分接近,而可微二值化曲线是处处可导的,这位神经网络的梯度下降的反向传播提供了条件。

DBNet提升效果的原因可以通过反向梯度传播解释。在BCELoss中正样本损失和负样本损失计算如下:
{
l
+
=
−
l
o
g
1
1
+
e
k
x
l
−
=
−
l
o
g
(
1
−
1
1
+
e
−
k
x
)
\begin{cases} l_+=-log\frac{1}{1+e^{kx}}\\ l_-=-log(1 - \frac{1}{1+e^{-kx}}) \end{cases}
{l+=−log1+ekx1l−=−log(1−1+e−kx1)
loss对于x的偏导数计算如下:
{
∂
l
+
∂
x
=
−
k
f
(
x
)
e
−
k
x
∂
l
−
∂
x
=
k
f
(
x
)
\begin{cases} \frac{\partial l_+}{\partial x}=-kf(x)e^{-kx}\\ \frac{\partial l_-}{\partial x}=kf(x) \end{cases}
{∂x∂l+=−kf(x)e−kx∂x∂l−=kf(x)
通过偏导数我们可以注意到:
- 错误预测的梯度通过增强因子k kk被加强,更利于网络的优化学习,使预测结果更清晰;
- 图(b)为 l + l_+ l+的导数曲线,如果发生误报(正样本被预测为负样本,即x<0), 图(b)中小于0的部分导数值非常大,说明损失也非常大,则更能清晰的进行梯度回传;
- 图©为 l − l_- l−的导数曲线,如果发生误报(负样本被预测为正样本,即x>0), 梯度也比较大,损失也很大。
标签制作
DB算法在进行模型训练的时,需要根据标注框生成两幅图像:概率图和阈值图。生成过程如下图所示:

上图中红线表示文本的标注框,可以用集合G表示文本标注框的轮廓点,如下公式所示
G
=
{
S
k
}
k
=
1
n
G={\{S_k\}}_{k=1}^n
G={Sk}k=1n
n表示外轮廓定点的个数。
在图像中对标注外轮廓外扩和内缩相同的距离得到polygon图中的绿色轮廓和蓝色轮廓,具体外扩的距离可以通过下列公式计算得出
D
=
A
(
1
−
r
2
)
L
D=\frac{A(1-r^2)}{L}
D=LA(1−r2)
式中,A表示标注区域的面积,L表示标注区域的周长,r是缩放比,论文中取0.4。
根据计算出的distance,对标注框进行外扩和内缩操作,用Vatti算法实现,参考链接https://github.com/fonttools/pyclipper和中文文档https://www.cnblogs.com/zhigu/p/11943118.html,在python中调用pyclipper库的接口操作即可,简单示例如下。
import cv2
import pyclipper
import numpy as np
from shapely.geometry import Polygon
def draw_img(subject, canvas, color=(255,0,0)):
"""作图函数"""
for i in range(len(subject)):
j = (i+1)%len(subject)
cv2.line(canvas, subject[i], subject[j], color)
# 论文默认shrink值
r=0.4
# 假定标注框
subject = ((100, 100), (250, 100), (250, 200), (100, 200))
# 创建Polygon对象
polygon = Polygon(subject)
# 计算偏置distance
distance = polygon.area*(1-np.power(r, 2))/polygon.length
print(distance)
# 25.2
# 创建PyclipperOffset对象
padding = pyclipper.PyclipperOffset()
# 向ClipperOffset对象添加一个路径用来准备偏置
# padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
# adding.AddPath(subject, pyclipper.JT_SQUARE, pyclipper.ET_CLOSEDPOLYGON)
padding.AddPath(subject, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON)
# polygon外扩
polygon_expand = padding.Execute(distance)[0]
polygon_expand = [tuple(l) for l in polygon_expand]
print(polygon_expand)
# [(75, 75), (275, 75), (275, 225), (75, 225)]
# polygon内缩
polygon_shrink = padding.Execute(-distance)[0]
polygon_shrink = [tuple(l) for l in polygon_shrink]
print(polygon_shrink)
# [(125, 125), (225, 125), (225, 175), (125, 175)]
# 作图
canvas = np.zeros((300,350,3), dtype=np.uint8)
# 原轮廓用红色线条展示
draw_img(subject, canvas, color=(0,0,255))
# 外扩轮廓用绿色线条展示
draw_img(polygon_expand, canvas, color=(0,255,0))
# 内缩轮廓用蓝色线条展示
draw_img(polygon_shrink, canvas, color=(255,0,0))
cv2.imshow("Canvas", canvas)
cv2.waitKey(0)
整体效果图如下,红色框为标注框,绿色框外扩后的效果,蓝色框为内缩后的效果。

得到外扩和内缩图的边界后就可以进行概率图标签和阈值图标签的制作
概率图标签制作
概率图标签制作相对容易,只需要把标注框内缩后覆盖的区域填充为1,其余区域概率值为0即可。这里给出一个带角度的矩形文字区域的试例图片的概率图。

阈值图制作
阈值图标签制作相对复杂,需要计算外扩区域(绿色轮廓)与内缩区域(蓝色轮廓)包围的范围内每个点到标注框外轮廓(红色轮廓)的距离,在上诉python代码生成的简单式例中标注框看作四条线段,计算出每个位置点到这四条线段的距离,取最小值为最终距离。
计算出每个位置点的值后,进行归一化。区域内所有的值除以前面计算出来的distance,限定取值在[0, 1]之间。得到相对距离比例的数值,用1减去距离归一化的值,即获得canvas图。靠近标注框的位置,阈值接近1,如下图所示。

最后获取通过下列下面代码将阈值图缩放到thresh_min和thresh_max之间,论文中这两个值为0.3和0.7。
threshold_map=canvas * (thresh_max - thresh_min) + thresh_min
这里给出上述一个带角度的矩形文字区域的试例图片的对应阈值图。

损失计算
模型训练过程中输出三个图:概率图、阈值图和二值化图。从而在损失函数计算时,也要结合这3个图与它们对应的真实标签构建3部分损失函数。总的损失函数公式定义如下:
L
=
L
s
+
α
L
b
+
β
L
t
L=L_s+\alpha L_b + \beta L_t
L=Ls+αLb+βLt
其中L为总的损失,
L
s
L_s
Ls为概率图损失,
L
b
L_b
Lb为二值化图损失,
L
t
L_t
Lt为阈值图损失。ɑ 和β 为权重系数,论文中分别设置为1和10。
对于
L
s
L_s
Ls和
L
b
L_b
Lb的损失计算都使用BCELoss, 为了解决正负样本不均衡的问题,在损失计算过程中使用错题集策略,同时正负样本比例设为1:3。
L
t
L_t
Lt计算方式为扩展多边形
G
d
G_d
Gd内预测结果和阈值图标签的L1距离之和。
L
t
=
∑
i
∈
R
d
∣
y
i
∗
−
x
i
∗
∣
L_t=\sum_{i\in R_d}|y^*_i-x^*_i|
Lt=i∈Rd∑∣yi∗−xi∗∣
式中,
R
d
R_d
Rd表示外扩轮廓内的像素索引,
y
i
∗
y^*_i
yi∗表示阈值图标签,
x
i
∗
x^*_i
xi∗表示模型输出的阈值图。
参考
[1] Real-time Scene Text Detection with Differentiable Binarization
3379

被折叠的 条评论
为什么被折叠?



