DBNet文本检测网络 (FPN、batch normalization、Transpose conv)

news2024/9/27 5:45:09

DB Net文本检测网络概述

DBNet论文地址:https://arxiv.org/pdf/1911.08947.pdf
DBNet是一种基于分割的文本检测网络,使用分割网络提供自适应的thresh用于二值化。

在这里插入图片描述
原始二值化方法和DBNet中的动态阈值

传统的基于分割的检测方法,对于分割后的特征层,使用直接二值化,生成检测结果。
在这里插入图片描述
直接二值化的方法不可微分,不能参与到网络模型的训练中。
DB Net增加了threshold map,动态生成每一个像素点对应的阈值,得到二值化结果。

在训练阶段,probability mapthreshold mapapproximate binary map都参与计算,
其中probability mapapproximate binary map受相同的变量监督


DB Net网络结构

在这里插入图片描述

特征提取网络为FPN(Feature Pyramid Network)提取到不同尺度的特征,将深层的网络和浅层的网络特征层融合。
在这里插入图片描述
将原始图片经过卷积操作下采样,分别得到宽、高变为原来1/2,1/4,1/8,1/16,1/32大小的特征层;
将P1特征层经过两倍上采样,与P2特征层对应像素值相加,得到P6;
将P6特征层经过两倍上采样,与P3特征层对应像素值相加,得到P7;
将P7特征层经过两倍上采样,与P4特征层对应像素值相加,得到P8;

P8经过3×3卷积;
P7经过3×3卷积,再经过一次2倍上采样;
P6经过3×3卷积,再经过一次4倍上采样;
P5(P1)经过3×3卷积,再经过一次8倍上采样;
以上四个特征层宽高为原始图片的1/4,将四个特征层concat,完成FPN特征提取。

concat 后的特征层为原始图片宽高的1/4。
pred操作包含一个3×3卷积操作和两次stride为2的反卷积操作进行上采样。
在这里插入图片描述
分别生成probability mapthreshold map
其中probability map为分割结果,每个像素点属于哪一个类别;
threshold map动态设置每一个像素点的阈值。
接着通过可微分二值化操作,得出二值化的分割结果,得到approximate map
最后经过box formation操作得到最终的文本检测结果。


DB可微分二值化

二值化过程了微分,可以使二值化过程与分割网络一同在训练过程中优化。
可微分二值化的方法:
在这里插入图片描述
probability map 中的像素值与threshold map中的对应像素值做差,
如果值>0,B值=1;判断为文本区域
如果值<0,B值=0;判断为非文本区域

在这里插入图片描述

左侧为probability mapground truth标注,将原本文字区域(虚线部分)shrink 后标注为正样本,白色区域像素值为1,黑色区域像素值为0

右侧为threshold mapground truth标注,对于文本检测,文本与非文本的边界区域重点处理。通过threshold map来决定每一个像素点对应的阈值。
虚线为原文本区域的边界,分别向内shrink 和向外dilate距离为d,圆环区域对应的每一个像素点的阈值是动态变化的。

可微分二值化参数k取值为50,
(1)对于文本块内部,计算Pij-Tij>0,B=1
在这里插入图片描述
(2)对于文本块边缘,计算Pij-Tij<0,B=0
在这里插入图片描述
(3)对于非文本块区域,计算Pij-Tij<0,B=0
在这里插入图片描述


threshold map中的每一个值如何计算
在这里插入图片描述

红色虚线为文本框区域,圆环区域为文本框分别向内shrink、向外dilate,
(1)区域内的每一个像素点,距离文本框的最短距离
(2)计算所有点得到,圆环中心区域像素值d(i,j)为0,圆环边缘区域值d(i,j)较大
(3)1-d(i,j),使得中间区域接近1,边缘区域接近0
(4)再将取值范围归一化到指定区域

计算损失Loss

在这里插入图片描述
Ls表示probability map对应的损失
Lb表示approximate binary map对应的损失
Lt表示threshold map对应的损失
α、β分别取值1.0和10

网络模型部分代码

DBNet代码来自
(1)backbone网络使用ResNet18
对于resnet18网络每一种卷积层的个数如图所示[2, 2, 2, 2]
在这里插入图片描述

def resnet18(pretrained=True, **kwargs):
    """Constructs a ResNet-18 model.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    # 对应每一种卷积层在网络中的数量
    model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
    if pretrained:
        assert kwargs['in_channels'] == 3, 'in_channels must be 3 whem pretrained is True'
        print('load from imagenet')
        model.load_state_dict(model_zoo.load_url(model_urls['resnet18']), strict=False)
    return model

BasicBlock
在这里插入图片描述

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None):
        super(BasicBlock, self).__init__()
        self.with_dcn = dcn is not None   # deformable convolution是否使用
        self.conv1 = conv3x3(inplanes, planes, stride)    # 3×3卷积操作,kernel_size=3,stride=1,padding=1
        self.bn1 = BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.with_modulated_dcn = False
        if not self.with_dcn:
            self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, padding=1, bias=False)
        else:
            from torchvision.ops import DeformConv2d
            deformable_groups = dcn.get('deformable_groups', 1)
            offset_channels = 18   # 需要有18个偏移量的参数
            self.conv2_offset = nn.Conv2d(planes, deformable_groups * offset_channels, kernel_size=3, padding=1)
            self.conv2 = DeformConv2d(planes, planes, kernel_size=3, padding=1, bias=False)
        self.bn2 = BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        # out = self.conv2(out)
        if not self.with_dcn:
            out = self.conv2(out)
        else:
            offset = self.conv2_offset(out)
            out = self.conv2(out, offset)
        out = self.bn2(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

BottleNeck

class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None):
        super(Bottleneck, self).__init__()
        self.with_dcn = dcn is not None
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = BatchNorm2d(planes)
        self.with_modulated_dcn = False
        if not self.with_dcn:
            self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        else:
            deformable_groups = dcn.get('deformable_groups', 1)
            from torchvision.ops import DeformConv2d
            offset_channels = 18
            self.conv2_offset = nn.Conv2d(planes, deformable_groups * offset_channels, stride=stride, kernel_size=3, padding=1)
            self.conv2 = DeformConv2d(planes, planes, kernel_size=3, padding=1, stride=stride, bias=False)
        self.bn2 = BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
        self.bn3 = BatchNorm2d(planes * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride
        self.dcn = dcn
        self.with_dcn = dcn is not None

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        # out = self.conv2(out)
        if not self.with_dcn:
            out = self.conv2(out)
        else:
            offset = self.conv2_offset(out)
            out = self.conv2(out, offset)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

搭建ResNet网络结构

class ResNet(nn.Module):
    def __init__(self, block, layers, in_channels=3, dcn=None):
        self.dcn = dcn
        self.inplanes = 64
        super(ResNet, self).__init__()
        self.out_channels = []
        self.conv1 = nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dcn=dcn)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dcn=dcn)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dcn=dcn)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
        if self.dcn is not None:
            for m in self.modules():
                if isinstance(m, Bottleneck) or isinstance(m, BasicBlock):
                    if hasattr(m, 'conv2_offset'):
                        constant_init(m.conv2_offset, 0)

    def _make_layer(self, block, planes, blocks, stride=1, dcn=None):
        downsample = None
        # 使用一个conv block 接若干个identity block
        if stride != 1 or self.inplanes != planes * block.expansion:
            # 当输出的特征层通道数与输入特征层通道数不相等,需要使用1×1卷积来调整特征层维度
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample, dcn=dcn))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes, dcn=dcn))
        self.out_channels.append(planes * block.expansion)
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x2 = self.layer1(x)
        x3 = self.layer2(x2)
        x4 = self.layer3(x3)
        x5 = self.layer4(x4)

        return x2, x3, x4, x5

deformable conv

使用deformable 卷积的作用:增大感受野
其中deformable convolution卷积再pytorch通过调用DeformConv2d实现
在这里插入图片描述
3×3卷积每一个kernel对应的9个值,每个值由两个坐标确定。
在这里插入图片描述
图片来自deformable 论文
每个坐标的偏移量都会有一个offset偏移量。在basicblock代码中,如果使用deformable convolution,18个偏移量参数有网络训练获得。

deformable_groups = dcn.get('deformable_groups', 1)
            offset_channels = 18   # 需要有18个偏移量的参数
            self.conv2_offset = nn.Conv2d(planes, deformable_groups * offset_channels, kernel_size=3, padding=1)
            self.conv2 = DeformConv2d(planes, planes, kernel_size=3, padding=1, bias=False)
        self.bn2 = BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

Batch Normalization

在网络中添加Batch Normalization 来实现加速网络训练。
将一个batch(批量)特征层调整到满足均值为0方差为1
在这里插入图片描述
图片来自https://blog.csdn.net/qq_54185421/article/details/125429533

  • 对于一个mini-batch数据,计算统计量μB;
  • 对于一个mini-batch数据,计算统计量σ²B;
  • 做归一化处理;
  • 通过训练过程迭代γ和β

Transpose Conv

  • 转置卷积不是卷积的逆运算
  • 转置卷积也是卷积

转置卷积的作用:上采样(upsampling)
转置卷积可以将特征层的大小还原回卷积操作之前特征层的大小,但得到特征层的数值与原特征层的数值不同。
将卷积操作转化为矩阵相乘。
通过TransposeConv通过卷积实现上采样
在这里插入图片描述
在这里插入图片描述
感谢:
https://www.bilibili.com/video/BV1xf4y1p7Gf/?p=4&spm_id_from=pageDriver&vd_source=91cfed371d5491e2973d221d250b54ae

https://blog.csdn.net/qq_54185421/article/details/125429533

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1323889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++第一讲之初入C++

注&#xff1a;本文是对于学完C语言再学C同学的讲解&#xff0c;主要补充C与C语言不同之处&#xff0c;如果你没学过C语言&#xff0c;不建议观看本文。 一.C简介 我们都知道C语言是过程性语言&#xff08;强调的是实现过程&#xff09;&#xff0c;即对计算机语言要处理的两…

【持续更新】汇总了一份前端领域必看面试题

文章目录 1. 写在前面2. 前端面试汇总2.0.1. 如何提⾼webpack的打包速度2.0.2. 数组去重2.0.3. 前端有几种缓存方式&#xff1f;2.0.4. nextTick描述一下&#xff1f;2.0.5. Webpack层面的优化&#xff1f;2.0.6. 代码层面的优化&#xff1f;2.0.7. Web 技术的优化&#xff1f;…

ESP32 - Thonny+MicroPython+ESP32 继电器的使用

ESP32 - ThonnyMicroPythonESP32 继电器的使用 认真理解&#xff0c;能看懂 继电器默认为断开 from machine import Pin p13 Pin(13, Pin.OUT) p13.value(1) # 吸合 #p13.value(0) # 断开

专攻代码型闪存芯片赛道,芯天下授权世强硬创代理全线产品

近年来受下游应用需求增长的驱动&#xff0c;代码型闪存芯片市场空间持续扩张&#xff0c;在后疫情之下NOR Flash及SLC NAND Flash市场规模整体仍保持逐步增长的趋势。 为了迎合市场需求&#xff0c;世强先进&#xff08;深圳&#xff09;科技股份有限公司&#xff08;下称“世…

【离散数学】——期末刷题题库(树其二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

vue2的小练习——实现自定义指令v-nodata的商品列表

实现&#xff1a;一个商品列表&#xff0c;每个商品包含商品名称、商品价格&#xff0c;我们可以对每个商品进行删除操作&#xff0c;要求如下。 实现一个自定义指令v-nodata&#xff0c;指令的功能是当商品列表中没有商品数据时会显示一个div&#xff0c;div中有“暂无数据”…

node.js mongoose middleware

目录 官方文档 简介 定义模型 注册中间件 创建doc实例&#xff0c;并进行增删改查 方法名和注册的中间件名相匹配 执行结果 分析 错误处理中间件 手动抛出错误 注意点 官方文档 Mongoose v8.0.3: Middleware 简介 在mongoose中&#xff0c;中间件是一种允许在执…

智能停车场系统施工布线方案

一、停车场系统所用的控制主板的信号线是不可以与交流电源线平行铺设的&#xff0c;所以应该尽量避免穿在同一PVC管内。 二、停车场系统在布线的时候需要避免导线产生接头。如果必须有接头&#xff0c;那么接头也应该使用压线或焊接并作防水处理。 三、停车场布线所有外部设备…

玩转大数据19:数据治理与元数据管理策略

随着大数据时代的到来&#xff0c;数据已经成为企业的重要资产。然而&#xff0c;如何有效地管理和利用这些数据&#xff0c;成为了一个亟待解决的问题。数据治理和元数据管理是解决这个问题的关键。 1.数据治理的概念和重要性 数据治理是指对数据进行全面、系统、规范的管理…

netty线程调度定制

1、netty的线程调度问题 在netty的TCP调度中&#xff0c;线程的调度封装在NioEventLoopGroup中&#xff0c;线程执行则封装在NioEventLoop中。 线程调度规则封装在MultithreadEventExecutorGroup的next方法中&#xff0c;这个方法又封装了EventExecutorChooserFactory&#xf…

低代码核心能力详解:简化应用开发的新思路

低代码平台作为一种快速地应用开发解决方法&#xff0c;为中小企业实现数字化转型提供了机会。但是&#xff0c;对于一些刚开始触碰低代码平台的企业来说&#xff0c;了解其核心能力是很重要的。本文将详细分析低代码平台的核心能力&#xff0c;并在挑选低代码平台以前为中小企…

Jmeter接口程序项目实战教程

1.什么是jmeter&#xff1f; JMeter是100%完全由Java语言编写的&#xff0c;免费的开源软件&#xff0c;是非常优秀的性能测试和接口测试工具&#xff0c;支持主流协议的测试 2.jmeter能做什么&#xff1f; JMeter是100%完全由Java语言编写的软件性能测试的GUI的测试工具&am…

六、W5100S/W5500+RP2040之MicroPython开发<UDP示例>

文章目录 1. 前言2. 相关网络信息2.1 简介2.2 UDP通讯过程2.3 优点2.4 应用 3. WIZnet以太网芯片4. UDP通信示例讲解以及使用4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 烧录验证 5. 注意事项6. 相关链接 1. 前言 在这个智能硬件和物联网时代&#xff0c;MicroPyt…

drf知识--01

前后端开发模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 前后端混合开发: bbs 项目--renderajax 1、全栈开发--前端html后端都是一个人写 2、前端人员&#xff1a;写空页面&#xff0c;没有模板语法&#xff0c;只要html&#xff0c;c…

【Spring】14 ApplicationEventPublisherAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动3.5 工作流程图 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一是 Bean 生命周期中的回调接口。本文将专注介绍一个与事件发布相关的接口 Applicatio…

Opencv实验合集——实验四:图片融合

1.概念 图像融合是将两个或多个图像结合在一起&#xff0c;创建一个新的图像的过程。这个过程的目标通常是通过合并图像的信息来获得比单个图像更全面、更有信息量的结果。图像融合可以在许多领域中应用&#xff0c;包括计算机视觉、遥感、医学图像处理等。 融合的方法有很多…

同义词替换器降低论文重复率的最新技术动态

大家好&#xff0c;今天来聊聊同义词替换器降低论文重复率的最新技术动态&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;同义词替换器降低论文重复率的最…

Jmeter的接口测试详细步骤并实现业务闭环

一、首先是了解Jmeter接口测试用到的组件 1、测试计划&#xff1a;Jmeter的起点和容器2、线程组&#xff1a;代表一定的虚拟用户3、取样器&#xff1a;发送请求的最小单元4、逻辑控制器&#xff1a;控制组件的执行顺序5、前置处理器&#xff1a;在请求之前的操作6、后置处理器…

SOME/IP SubscriberEventGroup

1 SOME/IP SubscriberEventGroup SubscriberEventGroup是SOME/IP中的一种服务发现和注册的消息类型,它用于让服务使用者订阅服务提供者的事件组。 事件组是一种将服务的方法和字段分组的方式,它可以让服务使用者只接收感兴趣的数据,而不是所有的数据。 SubscriberEventGrou…