Oriented rcnn

article/2025/11/10 16:50:12

oriented rcnn代码解析

文章目录

  • rpn_head.forward_train
  • roi_head.forward_train

class OrientedRCNN(RotatedTwoStageDetector)
类似rotated faster rcnn它们都继承两阶段检测器类。
所以训练的整体框架都如下:请添加图片描述

rpn_head.forward_train

代码主体👇

outs = self(x)
if gt_labels is None:loss_inputs = outs + (gt_bboxes, img_metas)
else:loss_inputs = outs + (gt_bboxes, gt_labels, img_metas)
losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore)
if proposal_cfg is None:return losses
else:proposal_list = self.get_bboxes(*outs, img_metas=img_metas, cfg=proposal_cfg)return losses, proposal_list
rpn_head.forward_train
forward
loss
get_bboxes

  • forward部分:

这一部分可以看到和faster rcnn是一致的,只不过faster初始化的时候
rpn_cls=nn.Conv2d(256,anchorcls_out_channels,1)
rpn_reg=nn.Conv2d(256,anchor
4,1)
而oriented rcnn中
rpn_reg=nn.Conv2d(256,6,1)
看过oriented_rcnn的论文的朋友知道,oriented rcnn预测的不是角度,而是offset,用于调整框,所以这里改成了6而不是4
对于oriented rcnn number_anhcors=3而不是faster中的5
还有一个值得注意的细节是这里的rpn_cls是预测出前景的概率而不是类别概率,所以实际上对于一个特征图的一个特征点,它是一个标量而不是向量。
请添加图片描述


  • loss部分(以下为get_anchors和get_targets部分)

为了计算loss,参考上一个faster的文章:
下面三个函数共同组成了rpn_head.loss

self.get_anchors
self.get_targets
self.loss_single

其逻辑也很清晰:
get_anchors利用anchor_generator,xyxy格式
get_targets根据anchor来匹配对应的gt(cxcywha)(所以gt到这就利用好了,下面的loss只需要根据这一步的返回来计算,不再需要输入gt)
loss_single用于计算具体的loss。

get_anchors并不过多作介绍,这里介绍以下get_targets虽然继承的是同faster的rpn_head的部分,但是复写了_get_targets_single部分所以有必要说一下。
由于二者之间最大的区别就在于self.bbox_coder.encode这一部分,但是这里看到gt_hbboxes已经通过obb2xyxy转化成了没有角度信息的部分,用于assign和sample。

所以在
pos_bbox_targets = self.bbox_coder.encode( sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes)需要修改pos_gt_bboxes的值,因为这里默认是gt_hbboxes中的信息。所以下面的代码中if判断语句中修改了pos_gt_bboxes的值。
这里anchors 和 gt_hbboxes都是xyxy形式。
请添加图片描述
这里展示了da,db的定义方
gx,gy是外接矩形的中心点
gw,gh是外接矩形的宽高
ga是最下面点的x坐标
gb是最右边点的y坐标
请添加图片描述
请添加图片描述
这一部分encoder的输出,作为bbox_targets的pos_id的部分得以保存。并且bbox_targets的neg_id的部分为0
return (labels, label_weights, bbox_targets, bbox_weights, pos_inds, neg_inds, sampling_result)这是get_targets_single的输出
labels(261888,)
bbox_targets(261888.6)
248个负例框,8个正例框


  • loss_single(当然本质上也属于loss部分)

loss_cls=dict( type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
loss_bbox=dict( type='SmoothL1Loss', beta=0.1111111111111111, loss_weight=1.0)),
讲到这里反而没什么好说的了,就如同配置一样。
下面的cls_socres,bbox_preds是forward得到的
all_anchor_list是AnchorGenerater生成的
下面的5个参数都是sampler中输出的

losses_cls, losses_bbox = multi_apply(self.loss_single,cls_scores,bbox_preds,all_anchor_list,labels_list,label_weights_list,bbox_targets_list,bbox_weights_list,num_total_samples=num_total_samples)

  • get_bboxes

首先,把anchors,cls_scores变成单张图片对应的部分计算
比如cls_scores变为cls_scores_list : list 5 0=tensor(3,256,256), 1=(3,128,128) …
cls_score为前面forward得到的

请添加图片描述
这里两个图片并在一个batch,但是每一个get_bboxes需要在单张图片上进行,所以无意外的,需要复写get_bboxes_single
然后送入get_bboxes_single
proposals = self._get_bboxes_single(cls_score_list, bbox_pred_list, mlvl_anchors, img_shape, scale_factor, cfg, rescale)

那么get_bboxes到底在做什么事情呢:同faster rcnn 就是把

  • 层至多保留2000个,五个层加起来一共8000多个,按照得分排序
  • decode
  • nms一下(只剩不到一半了)
  • 保留前cfg.max_per_img个

这里看到nms也是有点意思,并不是直接对旋转框nms而是把它们转为hbb再nms之后按照idx保留,绝了。

 if proposals.numel() > 0:hproposals = obb2xyxy(proposals, self.version)_, keep = batched_nms(hproposals, scores, ids, cfg.nms)dets = torch.cat([proposals, scores[:, None]], dim=1)dets = dets[keep]

roi_head.forward_train

roi_head.forward_train
assigner&sampler
_bbox_forward_train
_bbox_forward_train
_bbox_forward
bbox_head.get_targets
loss
rbbox2roi

  • assigner&sampler

在rpn中,这一部分没有做过多介绍,这里可以补充介绍一下。
通过一些代价损失函数和一些匹配策略,给框进行正负例匹配
常见的代价函数为MaxIoUAssigner,设置阈值0.7,0.3,中间的不做匹配。
roi_head.forward_train这个代码虽然是复写的,但与rotated faster rcnn不同的地方就在于assigner sampler
而faster这一部分只是简单的计算了hbb之间的iou

此刻我们需要注意,在rpn阶段,assigner和sampler可是实实在在的写在了get_targets里面,这里却写在了外面,
但是他们的逻辑是一样的,只不过一阶段时,bbox是生成出来的,而这里是前面rpn中get_bboxes筛出来。
回忆rpn中,先rpn_forward,再AnchorGenerator,就可以assigner&sampler+get_targets,然后计算loss。

这里顺序变了一下:
因为在rpn中,anchor是根据特征图的点逐点生成的,但是这里的box是筛选出来的,后面需要特地作roi_extractor,再forward得到scores bbox_preds

所以这里就先作了一次assigner&sampler,其实放到后面做好像也可以,不过这里还有一个小作用就是把2000个框匹配一次之后筛到512个。后面的get_targets也就不用在

assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,match_low_quality=False,iou_calculator=dict(type='RBboxOverlaps2D'),ignore_iof_thr=-1),
sampler=dict(type='RRandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),

  • _bbox_forward_train
# _bbox_forward_train
rois = rbbox2roi([res.bboxes for res in sampling_results])
bbox_results = self._bbox_forward(x, rois)bbox_targets = self.bbox_head.get_targets(sampling_results, gt_bboxes,gt_labels, self.train_cfg)
loss_bbox = self.bbox_head.loss(bbox_results['cls_score'],bbox_results['bbox_pred'], rois,*bbox_targets)bbox_results.update(loss_bbox=loss_bbox)
return bbox_results

这里rbbox2roi就是给图片加上了序号从1024,5 变成了1024,6(序号加在第一个维度)
于是进入了最重要的函数_bbox_forward(x,rois)


  • _bbox_forward(x,rois)
bbox_feats = self.bbox_roi_extractor(x[:self.bbox_roi_extractor.num_inputs], rois)
if self.with_shared_head:bbox_feats = self.shared_head(bbox_feats)
cls_score, bbox_pred = self.bbox_head(bbox_feats)bbox_results = dict(cls_score=cls_score, bbox_pred=bbox_pred, bbox_feats=bbox_feats)
return bbox_results

roi_extractor这里是RotatedSingleRoIExtractor而不是rotated faster rcnn中的SingleRoIExtractor
(毫无疑问,这个函数对于p2rb来说起到至关重要的作用)

经过roi_extractor, 这里的bbox_feats ={Tensor:(1024,256,7,7)}
cls_score, bbox_pred = self.bbox_head(bbox_feats)
请添加图片描述
这里可以看到,最重要的东西往往最简单。是这样的。


  • self.bbox_head.get_targets
    请添加图片描述
    可以看到,由于前面已经作了sampler,这里gt的信息都不需要再利用了。
    至于get_targets_single,由于不需要assigner和sampler,只需要做一个啥呢??来得到那一套返回👇
    return labels, label_weights, bbox_targets, bbox_weights

答案揭晓,就是encoder操作,获取bbox_targets,至于label_weights,bbox_weights,通过自定义+切片赋值
但是对于labels这一部分看似需要gt,实则当然需要,只不过sampling 中同样已经提取过一次信息。只需要从sampling中读取即可。

好了说到这里又水了一个函数,(不过前面大家看着理解就发现,已经很清晰了)


  • loss

分析完这么多,觉得这反而是最不需要介绍的部分了。突然发现,很多时候,代码本身的核心思想所需要的部分,并不多,反而是搭建框架必须的细节很多。
其实forward经常就是几个卷积层 就写完了。


20220720加油,多看看代码,明显感觉自己学到了很多,也知道怎么去操作了,加油!!!毛毛


http://chatgpt.dhexx.cn/article/EdFVEVZF.shtml

相关文章

srcnn fsrcnn espcn rdn超分网络的结构

1.Srcnn Code: 数据集制作方法:以x2为例 训练数据:一张原始图作为高分辨率图像(h, w),先下采样到(h/2, w/2),然后再cubic上采样到(h, w)得到低分辨率图像&a…

RDD

RDD <1> 概述一. 什么是RDD二. spark 编程模型1. DataSource2. SparkContext3. Diver&#xff08;1&#xff09;SparkConf&#xff08;2&#xff09;SparkEnv&#xff08;3&#xff09;DAGScheduler&#xff08;4&#xff09;TaskScheduler&#xff08;5&#xff09;Sche…

RDNet

RDNet&#xff1a;Density Map Regression Guided Detection Network for RGB-D Crowd Counting and Localization IntroductionMethodExperiments Introduction Motivation&#xff1a;Regression-based方法有局限性&#xff0c;希望还是使用detection-based可以估计出每个人…

【超分辨率】(RDN)Residual Dense Network for Image Super-Resolution论文翻译

机翻&#xff0c;我尽量调整了公式和图片的排版 发现机翻根本看不了&#xff0c;自己人工翻译了下&#xff0c;其中摘要、网络部分&#xff08;第三节&#xff09;为人工翻译。 自己翻译过程中难免会出错&#xff0c;希望各位海涵&#xff0c;同时也欢迎各位提出翻译过程中的错…

LIIF超分辨率之RDN(残差密集网络)

1. 背景 用PaddlePaddle复现论文LIIF&#xff0c;LIIF中使用的Encoder是RDN&#xff0c;本文介绍一下RDN。 RDN论文&#xff1a;https://arxiv.org/abs/1802.08797 Torch代码&#xff1a; https://github.com/yinboc/liif/blob/main/models/rdn.py 2. RDN的组成 RDN网络结…

图像超分辨之RDN(Residual Dense Network)

论文&#xff1a; Residual Dense Network for Image Super-Resolution Github&#xff1a; https://github.com/yulunzhang/RDN https://github.com/hengchuan/RDN-TensorFlow 整体结构&#xff1a; RDN&#xff08;Residual Dense Network&#xff09;主要包含4个模块。…

超分辨率-RDN

一、简介 RDN——Residual Dense Network—— 残差深度网络 RDN是基于深度学习的超分方法之一&#xff0c;发表于CVPR 2018 二、结构 RDN网络结构分为4个部分&#xff1a; 1、SFENet(Shallow Feature Extraction Net, 浅层特征提取网络) 2、RDBs( Residual Dense Blocks, 残…

【图像超分辨】RDN

RDN 网络结构实现细节讨论&#xff08;与其他网络的区别&#xff09;实验设置实验结果参考博客 RDN——Residual Dense Network—— 残差深度网络。RDN是基于深度学习的超分方法之一&#xff0c;发表于CVPR 2018。 网络结构 RDN网络结构分为4个部分&#xff1a; SFENet(Shall…

【图像复原】RDN论文详解(Residual Dense Network for Image Restoration)

这是CVPR2018的一篇文章&#xff0c;提出了针对图像复原任务的CNN模型RDN(residual dense network)。 RDN主要是提出了网络结构RDB(residual dense blocks)&#xff0c;它本质上就是残差网络结构与密集网络结构的结合。 1.残差网络&#xff08;resnet&#xff09;与密集网络&am…

超分之RDN

这篇文章提出了一种结合ResNet结构和DenseNet结构的深度超分网络——Residual Dense Network(后文简称RDN)。RDN基于Residual Dense Block(后文简称RDB)块以及全局残差连接来提取全局特征&#xff0c;而RDB块基于Dense结构和局部残差连接进一步提取局部特征。通过这种结构&…

Java线程中的用户态和内核态

内核态用户态是什么? 操作系统对程序的执行权限进行分级,分别为用户态和内核态。用户态相比内核态有较低的执行权限&#xff0c;很多操作是不被操作系统允许的&#xff0c;简单来说就是用户态只能访问内存,防止程序错误影响到其他程序&#xff0c;而内核态则是可以操作系统的…

「操作系统」什么是用户态和内核态?为什么要区分

「操作系统」什么是用户态和内核态&#xff1f;为什么要区分 参考&鸣谢 从根上理解用户态与内核态 程序员阿星 并发编程&#xff08;二十六&#xff09;内核态和用户态 Lovely小猫 操作系统之内核态与用户态 fimm 文章目录 「操作系统」什么是用户态和内核态&#xff1f;为…

用户态与内核态之间切换详解

用户空间和内核空间 用户程序有用户态和内核态两种状态。用户态就是执行在用户空间中&#xff0c;不能直接执行系统调用。必须先切换到内核态&#xff0c;也就是系统调用的相关数据信息必须存储在内核空间中&#xff0c;然后执行系统调用。 操作硬盘等资源属于敏感操作&#…

用户态和内核态 中断处理机制

操作系统 一、操作系统基础知识 1.1 用户态和内核态 1.1.1 定义&#xff1a;什么是用户态和内核态&#xff1f; Kernel 运行在超级权限模式&#xff08;Supervisor Mode&#xff09;下&#xff0c;所以拥有很高的权限。按照权限管理的原则&#xff0c;多数应用程序应该运行…

用户态和内核态:用户态线程和内核态线程有什么区别?

转载 文章来源于 拉钩教育 重学操作系统 林䭽 用户态和内核态&#xff1a;用户态线程和内核态线程有什么区别&#xff1f; 什么是用户态和内核态 Kernel 运行在超级权限模式&#xff08;Supervisor Mode&#xff09;下&#xff0c;所以拥有很高的权限。按照权限管理的原则&a…

什么是用户态和内核态

什么是用户态和内核态 从图上我们可以看出来通过系统调用将Linux整个体系分为用户态和内核态&#xff08;或者说内核空间和用户空间&#xff09;。 那内核态到底是什么呢&#xff1f;其实从本质上说就是我们所说的内核&#xff0c;它是一种特殊的软件程序&#xff0c;特殊在哪儿…

用户态和内核态之间的切换

用户态和内核态之间的切换 切换方式 从用户态到内核态切换可以通过三种方式&#xff0c;或者说会导致从用户态切换到内核态的操作&#xff1a; 系统调用&#xff0c;这个上面已经讲解过了&#xff0c;在我公众号之前的文章也有讲解过。其实系统调用本身就是中断&#xff0c;…

OS用户态和内核态

1、linux进程有4GB地址空间&#xff0c;如图所示&#xff1a; 3G-4G大部分是共享的&#xff0c;是内核态的地址空间。这里存放整个内核的代码和所有的内核模块以及内核所维护的数据。 2、特权级的概念&#xff1a; 对于任何操作系统来说&#xff0c;创建一个进程是核心功能。…

用户态和内核态区别

操作系统用户态和内核态之间的切换过程 1. 用户态和内核态的概念区别 究竟什么是用户态&#xff0c;什么是内核态&#xff0c;这两个基本概念以前一直理解得不是很清楚&#xff0c;根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重点和着眼的角度放在了实现的功能…

操作系统用户态和内核态之间的切换过程

操作系统用户态和内核态之间的切换过程 1. 用户态和内核态的概念区别 究竟什么是用户态&#xff0c;什么是内核态&#xff0c;这两个基本概念以前一直理解得不是很清楚&#xff0c;根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重点和着眼的角度放在了实现的功能和…