Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【FCOS】2D目标检测算法

【FCOS】2D目标检测算法

作者头像
Srlua
发布于 2024-11-28 00:58:52
发布于 2024-11-28 00:58:52
15800
代码可运行
举报
文章被收录于专栏:CSDN社区搬运CSDN社区搬运
运行总次数:0
代码可运行

概述

FCOS提出了一个全卷积的单阶段目标检测器,以逐像素预测的方式解决目标检测,类似于语义分割。FCOS通过消除预先定义的锚框集合,完全避免了训练过程中与锚框相关的复杂计算,例如与锚框相关的所有超参数,而这些参数通常对最终的检测性能非常敏感。

模型架构

如图是FCOS的网络架构,其中C3、C4、C5表示骨干网络的特征图,P3到P7是用于最终预测的特征级别,H×WH×W是特征图的高度和宽度。

网络输出 对应于训练目标,FCOS网络的最后一层预测分类标签得80D向量p(此时是COCO数据集,总共有90类)和4D边界框坐标向量t=(l,t,r,b)。FCOS网络不是训练一个多类分类器,而是训练C个二元分类器。和Focal Loss相同,FCOS网络分别在主干网络的特征图之后添加四个卷积层进行分类和回归分支。此外,由于回归目标总是正的,FCOS网络使用exp(x)将任何实数映射到回归分支顶部的(0,+∞)。值得注意的是FCOS的网络输出变量比流行的基于锚的检测器少九倍,因为流行的基于锚框的检测器每个位置由9个锚框。

损失函数 FCOS网络将训练损失定义如下

L({px,y},{tx,y})=1Npos∑x,yLcls(px,y,cx,y∗)+λNpos∑x,y1{cx,y∗>0}Lreg(tx,y,tx,y∗),L({px,y​},{tx,y​})​=Npos​1​x,y∑​Lcls​(px,y​,cx,y∗​)+Npos​λ​x,y∑​1{cx,y∗​>0}​Lreg​(tx,y​,tx,y∗​),​

其中LclsLcls​是Focal loss,LregLreg​是IoU损失,NposNpos​表示正样本的个数,λλ用来平衡LregLreg​的权重。求和是在特征图FiFi​上的所有位置上计算1{ci∗>0}1{ci∗​>0}​是指标函数,如果ci∗>0ci∗​>0则为1,否则为0

核心逻辑

返回特征图上每个像素点对应的bbox

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def prepare_targets(self, points, targets):
        object_sizes_of_interest = [ 
            [-1, 64], # 不同的FPN层每层需要规定检测目标的大小
            [64, 128],
            [128, 256],
            [256, 512],
            [512, INF],
        ]
        # 扩展感兴趣区域的事情
        expanded_object_sizes_of_interest = []
        # points指代的是坐标,针对每一个坐标都应该有一个bbox
        for l, points_per_level in enumerate(points):
            object_sizes_of_interest_per_level = \
                points_per_level.new_tensor(object_sizes_of_interest[l])
            expanded_object_sizes_of_interest.append(
                object_sizes_of_interest_per_level[None].expand(len(points_per_level), -1)
            )
        # [22400,2]
        expanded_object_sizes_of_interest = torch.cat(expanded_object_sizes_of_interest, dim=0)
        # [num_points_per_level] 获取不同特征图上具有的点的个数
        num_points_per_level = [len(points_per_level) for points_per_level in points]
        self.num_points_per_level = num_points_per_level
        # 确定5FPN结构上有多少个点,此时的个数和上述的Iou一致,对于每个点都需要规定一个回归大小的数值
        points_all_level = torch.cat(points, dim=0)
        # 为每个特征图上的点预定义label的bbox的情况
        labels, reg_targets = self.compute_targets_for_locations(
            points_all_level, targets, expanded_object_sizes_of_interest
        )
        # 针对于batch_size个图片,经过backbone,每个图片具有n个点
        for i in range(len(labels)):
            # 按照FPN结构的网格生成相应地结构
            labels[i] = torch.split(labels[i], num_points_per_level, dim=0)
            reg_targets[i] = torch.split(reg_targets[i], num_points_per_level, dim=0)

        labels_level_first = []
        reg_targets_level_first = []
        # points表示不同的特征图
        for level in range(len(points)):
            # 是将不同的batch_size,但是一个层上面的所有点进行拼接
            labels_level_first.append(
                torch.cat([labels_per_im[level] for labels_per_im in labels], dim=0)
            )

            reg_targets_per_level = torch.cat([
                reg_targets_per_im[level]
                for reg_targets_per_im in reg_targets
            ], dim=0)

            if self.norm_reg_targets:
                reg_targets_per_level = reg_targets_per_level / self.fpn_strides[level]
            reg_targets_level_first.append(reg_targets_per_level)
        # 返回一个batch_size中不同特征层上所有的点的拼接,也就是返回五层特征图结构
        return labels_level_first, reg_targets_level_first

** 其中将不同大小的特征图规定回归特定大小的目标,对于仍有多个的情况时选取最小的一个进行回归。**

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 为每个位置生成一个target bbox的情况
    def compute_targets_for_locations(self, locations, targets, object_sizes_of_interest):
        labels = []
        reg_targets = []
        xs, ys = locations[:, 0], locations[:, 1] # 分别获得每个x,y的取值

        for im_i in range(len(targets)):
            targets_per_im = targets[im_i] # 获取每张图片上的真实bbox有多少个
            # 此时确定的是真值框的模式
            assert targets_per_im.mode == "xyxy"
            bboxes = targets_per_im.bbox # 获得bbox的坐标形式
            labels_per_im = targets_per_im.get_field("labels") # 获取标签
            area = targets_per_im.area() # 获取面积,这里应该都是COCO数据集自带的
            # 预测的是ltrb,来进行计算,此时取值为正
            l = xs[:, None] - bboxes[:, 0][None]
            t = ys[:, None] - bboxes[:, 1][None]
            r = bboxes[:, 2][None] - xs[:, None]
            b = bboxes[:, 3][None] - ys[:, None]
            # [20267,17,4] 17表示此时有多少个bbox,针对于20267个像素,每个像素上可能有17个bbox
            reg_targets_per_im = torch.stack([l, t, r, b], dim=2)

            if self.center_sampling_radius > 0:
                is_in_boxes = self.get_sample_region(
                    bboxes,
                    self.fpn_strides,
                    self.num_points_per_level,
                    xs, ys,
                    radius=self.center_sampling_radius
                )
            else:
                # ltrb本身每个取值都应该是正的,min选取它们的最小值,dim=2表明是在坐标的维度上进行选择
                is_in_boxes = reg_targets_per_im.min(dim=2)[0] > 0
            # 获取四个坐标中最大的一个,其中要保证最大的一个坐标处于确定的坐标范围内
            max_reg_targets_per_im = reg_targets_per_im.max(dim=2)[0]
            # limit the regression range for each location
            is_cared_in_the_level = \
                (max_reg_targets_per_im >= object_sizes_of_interest[:, [0]]) & \
                (max_reg_targets_per_im <= object_sizes_of_interest[:, [1]])
            # 针对于每一个坐标都规定需要回归的面积有area个
            locations_to_gt_area = area[None].repeat(len(locations), 1)
            # 对于不符合要求的将其定义为INF,也就是背景区域
            locations_to_gt_area[is_in_boxes == 0] = INF
            locations_to_gt_area[is_cared_in_the_level == 0] = INF
            # if there are still more than one objects for a location,
            # we choose the one with minimal area
            locations_to_min_area, locations_to_gt_inds = locations_to_gt_area.min(dim=1)
            # 如果还有多个bbox的时候,选取bbox数值最小的一个来进行回归
            reg_targets_per_im = reg_targets_per_im[range(len(locations)), locations_to_gt_inds]
            labels_per_im = labels_per_im[locations_to_gt_inds] # 选择数值最小的标签
            labels_per_im[locations_to_min_area == INF] = 0 # 对不存在的区域设置为背景区域

            labels.append(labels_per_im)
            reg_targets.append(reg_targets_per_im)
        # 此时返回的是所有位置上需要回归的bbox和cls
        
        return labels, reg_targets

演示效果

运行fcos_demo.py

部署方式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
conda create --name FCOS
conda activate FCOS

# this installs the right pip and dependencies for the fresh python
conda install ipython

# FCOS and coco api dependencies
pip install ninja yacs cython matplotlib tqdm

# follow PyTorch installation in https://pytorch.org/get-started/locally/
# we give the instructions for CUDA 10.2
conda install pytorch torchvision cudatoolkit=10.2 -c pytorch

export INSTALL_DIR=$PWD

# install pycocotools. Please make sure you have installed cython.
cd $INSTALL_DIR
git clone https://github.com/cocodataset/cocoapi.git
cd cocoapi/PythonAPI
python setup.py build_ext install

# install PyTorch Detection
cd $INSTALL_DIR
git clone https://github.com/tianzhi0549/FCOS.git
cd FCOS

# the following will install the lib with
# symbolic links, so that you can modify
# the files if you want and won't need to
# re-build it
python setup.py build develop --no-deps


unset INSTALL_DIR

参考文献

github地址 论文地址

​​​

希望对你有帮助!加油!

若您认为本文内容有益,请不吝赐予赞同并订阅,以便持续接收有价值的信息。衷心感谢您的关注和支持!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验