关于PointHeadBox类的理解

forward函数

 def forward(self, batch_dict):"""Args:batch_dict:batch_size:point_features: (N1 + N2 + N3 + ..., C) or (B, N, C)point_features_before_fusion: (N1 + N2 + N3 + ..., C)point_coords: (N1 + N2 + N3 + ..., 4) [bs_idx, x, y, z]point_labels (optional): (N1 + N2 + N3 + ...)gt_boxes (optional): (B, M, 8)Returns:batch_dict:point_cls_scores: (N1 + N2 + N3 + ..., 1)point_part_offset: (N1 + N2 + N3 + ..., 3)"""if self.model_cfg.get('USE_POINT_FEATURES_BEFORE_FUSION', False):point_features = batch_dict['point_features_before_fusion']else:point_features = batch_dict['point_features']#通过全连接层128-->256-->256-->3生成类别信息point_cls_preds = self.cls_layers(point_features)  # (total_points, num_class)#通过全连接层128-->256-->256-->8生成回归框信息point_box_preds = self.box_layers(point_features)  # (total_points, box_code_size)#在预测的3个类别中求出最大可能的类别作为标签信息,并经过sigmod函数point_cls_preds_max, _ = point_cls_preds.max(dim=-1)batch_dict['point_cls_scores'] = torch.sigmoid(point_cls_preds_max)ret_dict = {'point_cls_preds': point_cls_preds,'point_box_preds': point_box_preds}if self.training:#主要是生成每个点对应的真实的标签信息#以及真实框G相对于预测G_hat的框的参数偏移,每个点对应是1*8维向量targets_dict = self.assign_targets(batch_dict)ret_dict['point_cls_labels'] = targets_dict['point_cls_labels']ret_dict['point_box_labels'] = targets_dict['point_box_labels']if not self.training or self.predict_boxes_when_training:#求出每个点对应的预测的标签信息#以及P相对于预测的框G_hat的参数偏移,每个点对应是1*8维向量point_cls_preds, point_box_preds = self.generate_predicted_boxes(points=batch_dict['point_coords'][:, 1:4],point_cls_preds=point_cls_preds, point_box_preds=point_box_preds)batch_dict['batch_cls_preds'] = point_cls_predsbatch_dict['batch_box_preds'] = point_box_predsbatch_dict['batch_index'] = batch_dict['point_coords'][:, 0]batch_dict['cls_preds_normalized'] = Falseself.forward_ret_dict = ret_dictreturn batch_dict

注意:对于每一个point,point_box_preds是1×8维向量,8维分别表示[xt, yt, zt, dxt, dyt, dzt, cost, sint],[xt, yt, zt]为中心点偏移量,[dxt, dyt, dzt]为长宽高偏移量,[cost, sint]为角度偏移量。

在这里插入图片描述

forward函数得到了每个前景点对应的真实标签值以及标注框信息;(self.assign_targets--------->self.assign_stack_targets-----> self.box_coder.encode_torch调用了PointResidualCoder类中的encode_torch函数)

得到了从G_hat到G的1*8维参数


每个前景点对应的预测标签值以及预测框信息;(self.generate_predicted_boxes--------->self.box_coder.decode_torch调用了PointResidualCoder类中的decode_torch函数)

得到了从P到G_hat的1*8维参数

得到这两组参数后用于后续计算损失时计算的box损失,采用的是L1回归损失

 point_loss_box_src = F.smooth_l1_loss(point_box_preds[None, ...], point_box_labels[None, ...], weights=reg_weights[None, ...])

边框回归(Bounding Box Regression)详解

PointResidualCoder

class PointResidualCoder(object):def __init__(self, code_size=8, use_mean_size=True, **kwargs):super().__init__()self.code_size = code_sizeself.use_mean_size = use_mean_sizeif self.use_mean_size:self.mean_size = torch.from_numpy(np.array(kwargs['mean_size'])).cuda().float()assert self.mean_size.min() > 0def encode_torch(self, gt_boxes, points, gt_classes=None):"""Args:gt_boxes: (N, 7 + C) [x, y, z, dx, dy, dz, heading, ...]points: (N, 3) [x, y, z]gt_classes: (N) [1, num_classes]Returns:box_coding: (N, 8 + C)"""gt_boxes[:, 3:6] = torch.clamp_min(gt_boxes[:, 3:6], min=1e-5)xg, yg, zg, dxg, dyg, dzg, rg, *cgs = torch.split(gt_boxes, 1, dim=-1)xa, ya, za = torch.split(points, 1, dim=-1)if self.use_mean_size:assert gt_classes.max() <= self.mean_size.shape[0]point_anchor_size = self.mean_size[gt_classes - 1]dxa, dya, dza = torch.split(point_anchor_size, 1, dim=-1)diagonal = torch.sqrt(dxa ** 2 + dya ** 2)xt = (xg - xa) / diagonalyt = (yg - ya) / diagonalzt = (zg - za) / dzadxt = torch.log(dxg / dxa)dyt = torch.log(dyg / dya)dzt = torch.log(dzg / dza)else:xt = (xg - xa)yt = (yg - ya)zt = (zg - za)dxt = torch.log(dxg)dyt = torch.log(dyg)dzt = torch.log(dzg)cts = [g for g in cgs]return torch.cat([xt, yt, zt, dxt, dyt, dzt, torch.cos(rg), torch.sin(rg), *cts], dim=-1)def decode_torch(self, box_encodings, points, pred_classes=None):"""Args:box_encodings: (N, 8 + C) [x, y, z, dx, dy, dz, cos, sin, ...]points: [x, y, z]pred_classes: (N) [1, num_classes]Returns:"""xt, yt, zt, dxt, dyt, dzt, cost, sint, *cts = torch.split(box_encodings, 1, dim=-1)xa, ya, za = torch.split(points, 1, dim=-1)if self.use_mean_size:assert pred_classes.max() <= self.mean_size.shape[0]point_anchor_size = self.mean_size[pred_classes - 1]dxa, dya, dza = torch.split(point_anchor_size, 1, dim=-1)diagonal = torch.sqrt(dxa ** 2 + dya ** 2)xg = xt * diagonal + xayg = yt * diagonal + yazg = zt * dza + zadxg = torch.exp(dxt) * dxadyg = torch.exp(dyt) * dyadzg = torch.exp(dzt) * dzaelse:xg = xt + xayg = yt + yazg = zt + zadxg, dyg, dzg = torch.split(torch.exp(box_encodings[..., 3:6]), 1, dim=-1)rg = torch.atan2(sint, cost)cgs = [t for t in cts]return torch.cat([xg, yg, zg, dxg, dyg, dzg, rg, *cgs], dim=-1)

decode_torch:如何通过point_box_preds的8维向量得到proposal的7维坐标?将每一个point原始xyz坐标加上坐标偏移量[xt, yt, zt]即可得到proposal中心点坐标,利用作者预设的point_anchor_size乘上长宽高偏移量[dxt, dyt, dzt]得到proposal长宽高,利用atan2函数计算角度heading。

在这里插入图片描述
论文出处
3D Object Detection for Autonomous Driving: A Review and New Outlooks

个人的理解是觉得这样可以同时优化生成的anchor大小并且可以调节中心坐标的偏移。

assign_targets

 def assign_targets(self, input_dict):"""Args:input_dict:point_features: (N1 + N2 + N3 + ..., C)batch_size:point_coords: (N1 + N2 + N3 + ..., 4) [bs_idx, x, y, z]gt_boxes (optional): (B, M, 8)Returns:point_cls_labels: (N1 + N2 + N3 + ...), long type, 0:background, -1:ignoredpoint_part_labels: (N1 + N2 + N3 + ..., 3)"""point_coords = input_dict['point_coords']gt_boxes = input_dict['gt_boxes']assert gt_boxes.shape.__len__() == 3, 'gt_boxes.shape=%s' % str(gt_boxes.shape)assert point_coords.shape.__len__() in [2], 'points.shape=%s' % str(point_coords.shape)batch_size = gt_boxes.shape[0]extend_gt_boxes = box_utils.enlarge_box3d(gt_boxes.view(-1, gt_boxes.shape[-1]), extra_width=self.model_cfg.TARGET_CONFIG.GT_EXTRA_WIDTH).view(batch_size, -1, gt_boxes.shape[-1])targets_dict = self.assign_stack_targets(points=point_coords, gt_boxes=gt_boxes, extend_gt_boxes=extend_gt_boxes,set_ignore_flag=True, use_ball_constraint=False,ret_part_labels=False, ret_box_labels=True)return targets_dict

extend_gt_boxes 主要是将groud truth boxex在长、宽、高方向上扩展

在这里插入图片描述

在这里插入图片描述

assign_stack_targets

#此函数传入的都是对应点的真实预测值和真实标注框def assign_stack_targets(self, points, gt_boxes, extend_gt_boxes=None,ret_box_labels=False, ret_part_labels=False,set_ignore_flag=True, use_ball_constraint=False, central_radius=2.0):"""Args:points: (N1 + N2 + N3 + ..., 4) [bs_idx, x, y, z]gt_boxes: (B, M, 8)extend_gt_boxes: [B, M, 8]ret_box_labels:ret_part_labels:set_ignore_flag:use_ball_constraint:central_radius:Returns:point_cls_labels: (N1 + N2 + N3 + ...), long type, 0:background, -1:ignoredpoint_box_labels: (N1 + N2 + N3 + ..., code_size)"""assert len(points.shape) == 2 and points.shape[1] == 4, 'points.shape=%s' % str(points.shape)assert len(gt_boxes.shape) == 3 and gt_boxes.shape[2] == 8, 'gt_boxes.shape=%s' % str(gt_boxes.shape)assert extend_gt_boxes is None or len(extend_gt_boxes.shape) == 3 and extend_gt_boxes.shape[2] == 8, \'extend_gt_boxes.shape=%s' % str(extend_gt_boxes.shape)assert set_ignore_flag != use_ball_constraint, 'Choose one only!'#将数据分批次处理batch_size = gt_boxes.shape[0]bs_idx = points[:, 0]point_cls_labels = points.new_zeros(points.shape[0]).long()point_box_labels = gt_boxes.new_zeros((points.shape[0], 8)) if ret_box_labels else Nonepoint_part_labels = gt_boxes.new_zeros((points.shape[0], 3)) if ret_part_labels else None#将数据分批次处理for k in range(batch_size):bs_mask = (bs_idx == k)#这里以*_single应该是中间缓存变量,作为每一批次处理的变量存储数据#points_single取出对应批次的点云的坐标信息points_single = points[bs_mask][:, 1:4]point_cls_labels_single = point_cls_labels.new_zeros(bs_mask.sum())#将每一个点云数据分配到真实标注框上box_idxs_of_pts = roiaware_pool3d_utils.points_in_boxes_gpu(         points_single.unsqueeze(dim=0), gt_boxes[k:k + 1, :, 0:7].contiguous()).long().squeeze(dim=0)#box_idxs_of_pts是每个点对应分配的标注框索引值,没有匹配的赋值为-1box_fg_flag = (box_idxs_of_pts >= 0) #根据之前扩展的3D框计算被忽略的点if set_ignore_flag:#将每一个点云数据分配到扩展后的标注框上extend_box_idxs_of_pts = roiaware_pool3d_utils.points_in_boxes_gpu(points_single.unsqueeze(dim=0), extend_gt_boxes[k:k+1, :, 0:7].contiguous()).long().squeeze(dim=0)fg_flag = box_fg_flag#异或运算,未扩展前没有包括,扩展后包含到的框,即被忽略的框ignore_flag = fg_flag ^ (extend_box_idxs_of_pts >= 0)point_cls_labels_single[ignore_flag] = -1elif use_ball_constraint:box_centers = gt_boxes[k][box_idxs_of_pts][:, 0:3].clone()box_centers[:, 2] += gt_boxes[k][box_idxs_of_pts][:, 5] / 2ball_flag = ((box_centers - points_single).norm(dim=1) < central_radius)fg_flag = box_fg_flag & ball_flagelse:raise NotImplementedError#记录前景点信息,可以理解为论文中所说的前景点分割gt_box_of_fg_points = gt_boxes[k][box_idxs_of_pts[fg_flag]]#最后一维代表的是标注框对应的类别信息,对应前景点的类别信息point_cls_labels_single[fg_flag] = 1 if self.num_class == 1 else gt_box_of_fg_points[:, -1].long()#记录一次批处理流程中所有点的类别信息point_cls_labels[bs_mask] = point_cls_labels_singleif ret_box_labels and gt_box_of_fg_points.shape[0] > 0:point_box_labels_single = point_box_labels.new_zeros((bs_mask.sum(), 8))#记录每一个前景点从G_hat到G的参数偏移,每个前景点最后输出是1*8维向量fg_point_box_labels = self.box_coder.encode_torch(gt_boxes=gt_box_of_fg_points[:, :-1], points=points_single[fg_flag],gt_classes=gt_box_of_fg_points[:, -1].long())point_box_labels_single[fg_flag] = fg_point_box_labelspoint_box_labels[bs_mask] = point_box_labels_singleif ret_part_labels:point_part_labels_single = point_part_labels.new_zeros((bs_mask.sum(), 3))transformed_points = points_single[fg_flag] - gt_box_of_fg_points[:, 0:3]transformed_points = common_utils.rotate_points_along_z(transformed_points.view(-1, 1, 3), -gt_box_of_fg_points[:, 6]).view(-1, 3)offset = torch.tensor([0.5, 0.5, 0.5]).view(1, 3).type_as(transformed_points)point_part_labels_single[fg_flag] = (transformed_points / gt_box_of_fg_points[:, 3:6]) + offsetpoint_part_labels[bs_mask] = point_part_labels_singletargets_dict = {'point_cls_labels': point_cls_labels,'point_box_labels': point_box_labels,'point_part_labels': point_part_labels}return targets_dict

经典框架解读 | 论文+代码 | 3D Detection | OpenPCDet | PointRCNN

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

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

相关文章

【计算机组成 课程笔记】7.1 存储层次结构概况

课程链接&#xff1a; 计算机组成_北京大学_中国大学MOOC(慕课) 7 - 1 - 701-存储层次结构概况&#xff08;15-14--&#xff09;_哔哩哔哩_bilibili 这是我们已经非常熟悉的冯诺依曼计算机结构&#xff0c; 其中和存储功能相关的部件有&#xff1a;存储器和外部记录介质肯定具有…

80%测试员被骗,关于jmeter 的一个弥天大谎!

jmeter是目前大家都喜欢用的一款性能测试工具&#xff0c;因为它小巧、简单易上手&#xff0c;所以很多人都愿意用它来做接口测试或者性能测试&#xff0c;因此&#xff0c;在目前企业中&#xff0c;使用各个jmeter的版本都有&#xff0c;其中以jmeter3.x、4.x的应该居多。 但是…

Waves 14混音特效插件合集mac/win

Waves14是一款音频处理软件&#xff0c;主要用于音频编辑、混音和母带处理。该软件提供了各种插件&#xff0c;包括EQ、压缩、混响、延迟、失真等&#xff0c;以及一些专业的音频处理工具&#xff0c;如L2限幅器、Linear Phase EQ和多频道扬声器管理。 Mac软件下载&#xff1a;…

凉鞋的 Unity 笔记 106. 第二轮循环场景视图Sprite Renderer

106. 第二轮循环&场景视图&Sprite Renderer 从这一篇开始&#xff0c;我们开始进行第二轮循环。 这次我们至少能够在游戏运行窗口看到一些东西。 首先还是在场景层次窗口进行编辑&#xff0c;先创建一个 Sprite&#xff0c;操作如下: 创建后&#xff0c;会在 Scene …

Nginx与Spring Boot的错误模拟实践:探索502和504错误的原因

文章目录 前言502和504区别---都是Nginx返回的access.log和error.log介绍SpringBoot结合Nginx实战502 and 504准备工作Nginx配置host配置SpringBoot 502模拟access.logerror.log 504模拟access.logerror.log 500模拟access.logerror.log 总结 前言 刚工作那会&#xff0c;最常…

JMeter接口自动化测试(数据驱动)

之前我们的用例数据都是配置在HTTP请求中&#xff0c;每次需要增加&#xff0c;修改用例都需要打开JMeter重新编辑&#xff0c;当用例越来越多的时候&#xff0c;用例维护起来就越来越麻烦&#xff0c;有没有好的方法来解决这种情况呢&#xff1f;我们可以将用例的数据存放在cs…

MyBatis过时了吗?

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 免责声明~ 任何文章不要过度深思&#xff01; 万事万物都经不起审视&#xff0c;因为世上没有同样的成长环境&#xff0c;也没有同样的认知水平&#xff0c;更「没有适用于所有人的解决方案…

3263页学习资料,一本在手,python不愁!

Python3.11已经发布&#xff0c;新的版本速度提升2倍&#xff0c;以弥补与其他编程语言在速度上的缺陷。可以预见Python语言在未来的应用范围会越来越广。 python学习方向建议&#xff1a; 如果你是本科及以下学历&#xff0c;建议你学习以下两个方向 爬虫。简单的爬虫库&am…

手机图片合成gif怎么操作?用这个网站试试

制作gif动图的工具越来越多&#xff0c;但是很多时候使用电脑并不方便&#xff0c;想要在手机上制作gif动图的时候应该怎么办呢&#xff1f;很简单&#xff0c;给大家分享一款无需下载手机浏览器就能操作的gif制作&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网&a…

朋友圈怎么定点发朋友圈?

微信朋友圈是我们日常生活中常用的社交媒体之一。但有时我们忙碌而可能会忘记发布朋友圈&#xff0c;或是因时间不合适而无法发布。那么&#xff0c;有没有一种方法可以在规定的时间内自动发布朋友圈呢&#xff1f; 当然有啦&#xff01; 定时发朋友圈可以帮助我们在特定时间点…

使用ebpf 监控linux内核中的nat转换

1.简介 Linux NAT&#xff08;Network Address Translation&#xff09;转换是一种网络技术&#xff0c;用于将一个或多个私有网络内的IP地址转换为一个公共的IP地址&#xff0c;以便与互联网通信。 在k8s业务场景中&#xff0c;业务组件之间的关系十分复杂. 由于 Kubernete…

tortoiseSVN树冲突解决方案

方案一&#xff1a; 手动导出 trunk 上的文件(夹)&#xff0c;把本地目录文件(夹)删了并替换成 trunk上的&#xff0c;再点击测试合并方案二&#xff1a; 如果执行了方案一还是冲突&#xff0c;确认本地和trunk文件一致后&#xff0c;可以跳过冲突的revision

【数据结构】初探时间与空间复杂度:算法评估与优化的基础

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;数据结构 &#x1f525;该文章主要了解算法的时间复杂度与空间复杂度等相关知识。 目录&#xff1a; &#x1f30f; 时间复杂度&#x1f52d…

目标检测算法改进系列之Backbone替换为FocalNet

FocalNet 近些年&#xff0c;Transformers在自然语言处理、图像分类、目标检测和图像分割上均取得了较大的成功&#xff0c;归根结底是自注意力&#xff08;SA &#xff1a;self-attention&#xff09;起到了关键性的作用&#xff0c;因此能够支持输入信息的全局交互。但是由于…

计算机网络——计算机网络的性能指标(上)-速率、带宽、吞吐量、时延

目录 速率 比特 速率 例1 带宽 带宽在模拟信号系统中的意义 带宽在计算机网络中的意义 吞吐量 时延 发送时延 传播时延 处理时延 例2 例3 速率 了解速率之前&#xff0c;先详细了解一下比特&#xff1a; 比特 计算机中数据量的单位&#xff0c;也是信息论中信…

剑指offer——JZ26 树的子结构 解题思路与具体代码【C++】

一、题目描述与要求 树的子结构_牛客题霸_牛客网 (nowcoder.com) 题目描述 输入两棵二叉树A&#xff0c;B&#xff0c;判断B是不是A的子结构。&#xff08;我们约定空树不是任意一个树的子结构&#xff09; 假如给定A为{8,8,7,9,2,#,#,#,#,4,7}&#xff0c;B为{8,9,2}&…

cad图纸如何防止盗图(一个的制造设计型企业如何保护设计图纸文件)

在现代企业中&#xff0c;设计图纸是公司的重要知识产权&#xff0c;关系到公司的核心竞争力。然而&#xff0c;随着技术的发展&#xff0c;员工获取和传播设计图纸的途径越来越多样化&#xff0c;如何有效地防止员工复制设计图纸成为了企业管理的一大挑战。本文将从技术、管理…

计算机竞赛 题目:基于python的验证码识别 - 机器视觉 验证码识别

文章目录 0 前言1 项目简介2 验证码识别步骤2.1 灰度处理&二值化2.2 去除边框2.3 图像降噪2.4 字符切割2.5 识别 3 基于tensorflow的验证码识别3.1 数据集3.2 基于tf的神经网络训练代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于pyt…

NEFU数字图像处理(1)绪论

一、简介 1.1什么是数字图像 图像是三维场景在二维平面上的影像。根据其存储方式和表现形式&#xff0c;可以将图像分为模拟图像和数字图像两大类 图像处理方法&#xff1a;光学方法、电子学方法 模拟图像&#xff1a;连续的图像数字图像&#xff1a;通过对时间上和数值上连续…

阿里云ECS和轻量服务器有什么区别?

阿里云服务器ECS和轻量应用服务器有什么区别&#xff1f;轻量和ECS优缺点对比&#xff0c;云服务器ECS是明星级云产品&#xff0c;适合企业专业级的使用场景&#xff0c;轻量应用服务器是在ECS的基础上推出的轻量级云服务器&#xff0c;适合个人开发者单机应用访问量不高的网站…