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指࿱…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...

Element-Plus:popconfirm与tooltip一起使用不生效?
你们好,我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip,产品要求是两个需要结合一起使用,也就是鼠标悬浮上去有提示文字,并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...