FoveaBox原理与代码解析
paper:FoveaBox: Beyond Anchor-based Object Detector
code:https://github.com/taokong/FoveaBox
背景
基于anchor的检测模型需要仔细设计anchor,常用方法之一是根据特定数据集的统计结果确定anchor的number、scale、ratio等,但这种针对特定数据集的设计并不总能适用于其它数据集,泛化性较差。另外训练阶段anchor-based的模型通常根据和GT的IoU来定义正负样本,这又引入了额外的计算和超参。
本文的创新点
受到人眼中心凹(fovea)区域的启发:视野中心区域的视觉灵敏度最高,本文提出了一种新的anchor-free的目标检测方法FoveaBox,FoveaBox联合预测对象中心区域可能存在的位置以及每个有效位置处的边界框。在FoveaBox中,每个目标对象通过中心区域的类别得分进行预测,同时预测bounding box,训练阶段不需要使用anchor或是IoU匹配来生成训练目标,训练目标是根据GT box直接生成的。
方法介绍
给定一个GT box \((x_{1},y_{1},x_{2},y_{2})\),首先将其映射到特征金字塔的目标层 \(P_{l}\)

其中 \(s_{l}\) 是下采样步长。定义输出特征图上对应GT box中心区域为正样本区域 \(R^{pos}\)

其中 \(\sigma\) 是收缩系数,文中 \(\sigma = 0.4\)。训练阶段,正样本区域内的每个像素位置都标为对应的目标类别标签,整个特征图上,除了正样本区域其它都是负样本区域。如下图右灰色区域所示

在标签分配中,除了按上述对正负样本区域进行了限制,还对FPN每层负责预测的目标大小即scale进行了限制。对于FPN的输出层 \(P_{3}-P_{7}\),每一层的basic scale \(r_{l}\) 为32至512。\(l\) 层的有效scale区间按下式计算得到

其中 \(\eta \) 是超参,文中 \(\eta =2\)。注意和之前一个目标只会由特征金字塔中的某一层负责预测的方法不同,FoveaBox中一个目标可能会由FPN的多层负责预测。将目标分配给多个相邻的FPN层有两个优点:(1)相邻的特征金字塔层通常具有相似语义表示能力,因此FoveaBox可以同时优化这些相邻层的特征。(2)FPN每一层的训练样本数量增大,使得训练过程更加稳定。
对于一个GT box \(G=(x_{1},y_{1},x_{2},y_{2})\),\(R_{pos}\) 区域中某一点 \((x,y)\) 的回归target即到四条边界的归一化的偏移按下式得到

FoveaBox的结构如下图所示,整体结构和anchor数量为1的RetinaNet是一样的,只不过在样本分配和定义上又区别。

代码解析
这里以mmdet中的实现为例,代码文件在https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/dense_heads/fovea_head.py,foveabox相对于retinanet的创新点就在于anchor-free以及对应的标签分配部分,这里的核心代码在函数_get_target_single()中,这个函数的作用就是就算FPN输出层中的单层分类和回归的target,完整代码如下
def _get_target_single(self,gt_bboxes_raw,# (2,4), tensor([[52.5, 46.8, 235.7, 274.4], [101.7, 29.6, 221.7, 175.8]], device='cuda:0')gt_labels_raw, # (2), tensor([12, 14], device='cuda:0')featmap_size_list=None,point_list=None):
gt_areas = torch.sqrt((gt_bboxes_raw[:, 2] - gt_bboxes_raw[:, 0]) *(gt_bboxes_raw[:, 3] - gt_bboxes_raw[:, 1])) # torch.Size([2])
label_list = []
bbox_target_list = []
# for each pyramid, find the cls and box target
# self.base_edge_list=[16, 32, 64, 128, 256]
# self.scale_ranges=((1, 64), (32, 128), (64, 256), (128, 512), (256, 2048)), 注意收尾本来分别为16和1024,这里改为了1和2048
# self.strides=[8, 16, 32, 64, 128]
for base_len, (lower_bound, upper_bound), stride, featmap_size, \points in zip(self.base_edge_list, self.scale_ranges,self.strides, featmap_size_list, point_list):# FG cat_id: [0, num_classes -1], BG cat_id: num_classespoints = points.view(*featmap_size, 2) # (1444,2) -> (38,38,2)x, y = points[..., 0], points[..., 1] # (38,38),(38,38)labels = gt_labels_raw.new_zeros(featmap_size) + self.num_classes # (38,38), 值全为self.num_classesbbox_targets = gt_bboxes_raw.new(featmap_size[0], featmap_size[1],4) + 1 # (38,38,4),值全为1# scale assignmenthit_indices = ((gt_areas >= lower_bound) &(gt_areas <= upper_bound)).nonzero().flatten() # torch.Size([1]), tensor([1], device='cuda:0')if len(hit_indices) == 0:label_list.append(labels)bbox_target_list.append(torch.log(bbox_targets))continue_, hit_index_order = torch.sort(-gt_areas[hit_indices])hit_indices = hit_indices[hit_index_order] # 按面积从大到小排列gt_bboxes = gt_bboxes_raw[hit_indices, :] / stridegt_labels = gt_labels_raw[hit_indices]half_w = 0.5 * (gt_bboxes[:, 2] - gt_bboxes[:, 0])half_h = 0.5 * (gt_bboxes[:, 3] - gt_bboxes[:, 1])# valid fovea area: left, right, top, downpos_left = torch.ceil(gt_bboxes[:, 0] + (1 - self.sigma) * half_w - 0.5).long(). \clamp(0, featmap_size[1] - 1)pos_right = torch.floor(gt_bboxes[:, 0] + (1 + self.sigma) * half_w - 0.5).long(). \clamp(0, featmap_size[1] - 1)pos_top = torch.ceil(gt_bboxes[:, 1] + (1 - self.sigma) * half_h - 0.5).long(). \clamp(0, featmap_size[0] - 1)pos_down = torch.floor(gt_bboxes[:, 1] + (1 + self.sigma) * half_h - 0.5).long(). \clamp(0, featmap_size[0] - 1)for px1, py1, px2, py2, label, (gt_x1, gt_y1, gt_x2, gt_y2) in \zip(pos_left, pos_top, pos_right, pos_down, gt_labels,gt_bboxes_raw[hit_indices, :]):labels[py1:py2 + 1, px1:px2 + 1] = labelbbox_targets[py1:py2 + 1, px1:px2 + 1, 0] = \(x[py1:py2 + 1, px1:px2 + 1] - gt_x1) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 1] = \(y[py1:py2 + 1, px1:px2 + 1] - gt_y1) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 2] = \(gt_x2 - x[py1:py2 + 1, px1:px2 + 1]) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 3] = \(gt_y2 - y[py1:py2 + 1, px1:px2 + 1]) / base_lenbbox_targets = bbox_targets.clamp(min=1. / 16, max=16.) # 文中有这个限制吗?label_list.append(labels)bbox_target_list.append(torch.log(bbox_targets))
return label_list, bbox_target_list下面是根据FPN某一层对应的尺度限制取出该层负责预测的GT box的index,即上面的式(3),代码如下
hit_indices = ((gt_areas >= lower_bound) &(gt_areas <= upper_bound)).nonzero().flatten() 下面是按式(2)计算 \(R^{pos}\) 区域的坐标,self.sigma是收缩系数 \(\sigma\),式(3)中是以gt box的中心坐标为基准计算的,而下面的实现是以gt box的左上角坐标为基准计算的。
# valid fovea area: left, right, top, down
pos_left = torch.ceil(gt_bboxes[:, 0] + (1 - self.sigma) * half_w - 0.5).long(). \clamp(0, featmap_size[1] - 1)
pos_right = torch.floor(gt_bboxes[:, 0] + (1 + self.sigma) * half_w - 0.5).long(). \clamp(0, featmap_size[1] - 1)
pos_top = torch.ceil(gt_bboxes[:, 1] + (1 - self.sigma) * half_h - 0.5).long(). \clamp(0, featmap_size[0] - 1)
pos_down = torch.floor(gt_bboxes[:, 1] + (1 + self.sigma) * half_h - 0.5).long(). \clamp(0, featmap_size[0] - 1)下面是按式(4)计算回归target,其中base_len即这一层对应的basic scale \(r_{l}\)。
for px1, py1, px2, py2, label, (gt_x1, gt_y1, gt_x2, gt_y2) in \zip(pos_left, pos_top, pos_right, pos_down, gt_labels,gt_bboxes_raw[hit_indices, :]):labels[py1:py2 + 1, px1:px2 + 1] = labelbbox_targets[py1:py2 + 1, px1:px2 + 1, 0] = \(x[py1:py2 + 1, px1:px2 + 1] - gt_x1) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 1] = \(y[py1:py2 + 1, px1:px2 + 1] - gt_y1) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 2] = \(gt_x2 - x[py1:py2 + 1, px1:px2 + 1]) / base_lenbbox_targets[py1:py2 + 1, px1:px2 + 1, 3] = \(gt_y2 - y[py1:py2 + 1, px1:px2 + 1]) / base_len这里有一些疑问,一是下面这行对回归target进行大小的限制论文中好像没有提到
bbox_targets = bbox_targets.clamp(min=1. / 16, max=16.)二是mmdet中对FPN每一层的basic scale \(r_{l}\) 以及负责预测目标的valid scale range和论文中有些差异,如下

其中base_edge_list就是每一层的 \(r_{l}\),如果按照文中计算方式,实际的valid scale range应该如下

如果以设定的scale_ranges为准,则实际的 \(r_{l}\) 应该是[32, 64, 128, 256, 512],并且第一个值由16改为了1,最后一个值由1024改为2048。
实验结果
Comparision with SOTA
下面是FoveaBox和当时的一些SOTA方法的对比,可以看出FoveaBox取得了最优的精度,而且好于当时刚刚提出的其它anchor-free方法比如CornerNet和ExtremeNet。

相关文章:
FoveaBox原理与代码解析
paper:FoveaBox: Beyond Anchor-based Object Detectorcode:https://github.com/taokong/FoveaBox背景基于anchor的检测模型需要仔细设计anchor,常用方法之一是根据特定数据集的统计结果确定anchor的number、scale、ratio等,但这种…...
Linux内核启动(1,0.11版本)启动BIOS与加载内核
从电源到启动BIOS 从我们按下启动电源到BIOS,按下电源–>主板会向电源组发出信号–> 接受到信号后,当主板收到电源正常启动信号后,主板会启动CPU(CPU重置所有寄存器数据,并且初始化数据),比如32位系统ÿ…...
python制作贪吃蛇小游戏,畅玩无限制
前言 大家早好、午好、晚好吖 ❤ ~ 现在这年头,无论玩个什么游戏都有健康机制, 这让我们愉悦玩游戏得步伐变得承重起来, 于是无聊之下我写了个贪吃蛇小游戏,来玩个快乐 代码展示 导入模块 import random import sys import …...
MySQL-InnoDB数据页结构浅析
在MySQL-InnoDB行格式浅析中,们简单提了一下 页 的概念,它是 InnoDB 管理存储空间的基本单位,一个页的大小一般是 16KB 。 InnoDB 为了不同的目的而设计了许多种不同类型的 页: 存放表空间头部信息的页存放 Insert Buffer信息的…...
Java、JSP职工人事管理系统设计与实现
技术:Java、JSP等摘要:现在随着我们这个社会的计算机技术的快速发展,计算机在企业管理中得到普遍的应用,现在我们利用计算机在实现企业职工的管理越来越重要。当今社会是快速发展的信息社会,自动化信息的作用也变得越来…...
数据结构与算法这么难,为什么我们还要学习?
文章目录前言1. 数据结构与算法是什么?2. 为什么数据结构与算法很难?3. 如何系统学习数据结构与算法?🍑 复杂度🍑 线性表🍑 树形结构🍑 图🍑 排序🍑 字符串🍑…...
剑指 Offer 52. 两个链表的第一个公共节点
摘要 剑指 Offer 52. 两个链表的第一个公共节点 一、双指针解法 使用双指针的方法,可以将空间复杂度降至 O(1)。只有当链表 headA headB都不为空时,两个链表才可能相交。因此首先判断链表 headA和 headB是否为空,如果其中至少有一个链表为…...
可以写进简历的软件测试电商项目,不进来get一下?
前言 说实话,在找项目的过程中,我下载过(甚至付费下载过)N多个项目、联系过很多项目的作者,但是绝大部分项目,在我看来,并不适合你拿来练习,它们或多或少都存在着“问题”ÿ…...
蓝桥杯-算法-印章问题
这个题真的顶啊!思路:n种图案,m张印章,每一个图案的概率是1/n,这个概率以后用P表示首先我们定义dp[i][j]是买了i张印章(对应于上面的m),凑齐j种图案的概率(对应于上面的n…...
戴尔游匣G16电脑U盘安装系统操作教程分享
戴尔游匣G16电脑U盘安装系统操作教程分享。有用户在使用戴尔游匣G16电脑的时候遇到了系统问题,比如电脑蓝屏、自动关机重启、驱动不兼容等问题。遇到这些问题如果无法进行彻底解决,我们可以通过U盘重新安装系统的方法来解决,因为这些问题一般…...
2023数学建模美赛赛题思路分析 2023美赛 美国大学生数学建模数模
将在本帖更新2023美国大学生数学建模数模美赛各个赛题思路,大家可以点赞收藏! 一、参赛报名 组队参赛(每队人数3人,专业不限)。 二、赛题思路及资料 会在本帖更新思路分析,Q群可领取模型代码/赛题思路资料…...
vue3与vue2的对比
Vue 3.0 和 Vue 2.0 是 Vue 前端框架的两个主要版本,它们有着不同的更新和优化: Vue 3.0 主要更新内容: 采用 TypeScript 作为开发语言,提高了代码的类型安全性。 速度更快,内存使用更少,支持大规模数据处…...
史上最全软件测试工程师常见的面试题总结(百度、oppo、中软国际、华为)备战金三银四
1、面试:神州数码1.介绍你下你项目中一个自动化实现的流程2.你觉得做自动化的意义在哪里 >需要对之前已经实现的功能进行回归测试、保证当前版本更新的内容不能影响到之前已经实现好的功能3.你们做自动化产生了什么结果 >测试报告、报错截图和报错日志、测试报…...
“深度学习”学习日记。卷积神经网络--用CNN的实现MINIST识别任务
2023.2.11 通过已经实现的卷积层和池化层,搭建CNN去实现MNIST数据集的识别任务; 一,简单CNN的网络构成: 代码需要在有网络的情况下运行,因为会下载MINIST数据集,运行后会生成params.pkl保留训练权重&…...
JavaWeb--JDBC练习
JDBC练习5.1 需求5.2 案例实现5.2.1 环境准备5.2.2 查询所有5.2.3 添加数据5.2.4 修改数据5.2.5 删除数据5.1 需求 完成商品品牌数据的增删改查操作 查询:查询所有数据添加:添加品牌修改:根据id修改删除:根据id删除 5.2 案例实…...
【LeetCode】2335. 装满杯子需要的最短总时长
2335. 装满杯子需要的最短总时长 题目描述 现有一台饮水机,可以制备冷水、温水和热水。每秒钟,可以装满 2 杯 不同 类型的水或者 1 杯任意类型的水。 给你一个下标从 0 开始、长度为 3 的整数数组 amount ,其中 amount[0]、amount[1] 和 a…...
Android 12.0 通过驱动实现禁用usb鼠标和usb键盘功能
1.1概述 在12.0的系统产品定制化开发中,在进行定制中有关于usb键盘和usb鼠标的需求中,产品要求禁止usb口挂载usb鼠标和usb键盘,所以需要要求在usb挂载类型的时候 判断如果是usb鼠标和usb键盘就不让挂载,这就需要从驱动方面入手来解决这个问题,接下来看下驱动的某些挂载usb…...
C++入门——内存管理
C入门——内存管理 C/C内存分布 分类是为了更好的管理 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] {1, 2, 3, 4};char char2[] "abcd";char* pChar3 "abcd";int* ptr1 (…...
MySQL-InnoDB行格式浅析
简介 我们知道读写磁盘的速度非常慢,和内存读写差了几个数量级,所以当我们想从表中获取某些记录时, InnoDB 存储引擎需要一条一条的把记录从磁盘上读出来么? 不,那样会慢死,InnoDB 采取的方式是:…...
AXI 总线协议学习笔记(4)
引言 前面两篇博文从简单介绍的角度说明了 AXI协议规范。 AXI 总线协议学习笔记(2) AXI 总线协议学习笔记(3) 从本篇开始,详细翻译并学习AXI协议的官方发布规范。 文档中的时序图说明: AXI指࿱…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
