3D点云学习:SA-SSD④源码注释

[论文阅读]SA-SSD——基于点云的结构感知单阶段三维物体检测 SA-SSD论文阅读 阅读详情

1 SASSD算法神经网络结构

SASSD在神经网络结构上的创新点主要在两点:auxiliary network 以及 PS Warp损失计算方法。这两点在代码实现上也有一定的特点,同时代码结构和论文中的网络结构也存在一定的差异。网路结构
如上图所示,论文中网络主要包括三部分,Backbone network,Auxiliary network和Detection network,但在源码中,Backbone network和Auxiliary network由于其交互性较强,每一步都要互相影响,因此放到了一个部分,Detection network是另一个部分,同时损失计算也都是单独的部分。
以下前向训练图来自于 这篇博客.

2 源码注释

① mmdetection训练网络构建方法

框架作者再知乎的回答.
真个SASSD模型是基于mmdetection框架的,mmdetection框架的特点是在pytorch的基础上进行了模块化封装,从批数据处理,模型搭建,损失计算到参数最优化,都可以很方便的进行开发,用户需要做的就是写好自己的config文件和核心特征提取算法部分。SASSD的构建方法可以学习到一些mmdetection框架的使用方法。
在train.py中的训练网络构建如下:

model = build_detector(
    cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg)
# 分布式并行计算和单机并行计算
if distributed:
    model = MMDistributedDataParallel(model).cuda()
else:
    model = MMDataParallel(model, device_ids=range(cfg.gpus)).cuda()
# 将训练配置传入,其中 build_dataset()在mmdet/datasets/builder.py里实现

# get_dataset,用cfg中的参数信息,通过datasets文件夹,生成包含所有数据的一个数据集
# 再本次实例中,obj_from_dict,更有深层次的理解。根据字典型变量info去指定初始化一个parrent类对象。如果parrent类是一个虚类,
# 它会根据info的变量自动地匹配一个Matched的子类,去指定初始化这个子类的实例。
# 毫无疑问,肯定是生成datasets类子类中的KittiLiDAR类
train_dataset = get_dataset(cfg.data.train)
optimizer = build_optimizer(model, cfg.optimizer)
train_loader = build_dataloader(
    train_dataset,
    cfg.data.imgs_per_gpu,
    cfg.data.workers_per_gpu,
    dist=distributed)
start_epoch = it = 0
last_epoch = -1
lr_scheduler, lr_warmup_scheduler = build_scheduler(
    optimizer, total_iters_each_epoch=len(train_loader), total_epochs=cfg.total_epochs,
    last_epoch=last_epoch, optim_cfg=cfg.optimizer, lr_cfg=cfg.lr_config
)
# -----------------------start training---------------------------
logger.info('**********************Start training**********************')
train_model(
    model,
    optimizer,
    train_loader,
    lr_scheduler=lr_scheduler,
    optim_cfg=cfg.optimizer,
    start_epoch=start_epoch,
    total_epochs=cfg.total_epochs,
    start_iter=it,
    rank=args.local_rank,
    logger=logger,
    ckpt_save_dir=cfg.work_dir,
    lr_warmup_scheduler=lr_warmup_scheduler,
    ckpt_save_interval=cfg.checkpoint_config.interval,
    max_ckpt_save_num=args.max_ckpt_save_num,
    log_interval=cfg.log_config.interval
)

logger.info('**********************End training**********************')

可以看到其中调用了很多的build_XXX,build了很多模块。build_XXX的一般原理就是使用cfg文件中的超参(一般是一个dict)包括名称和参数,以及obj_from_dict函数,生成自己需要的模块。名称就代表调用的类的名称,比如type='SingleStageDetector',这样通过调用build_detector生成的detector就是class SingleStageDetector()。
在train_model中,主要部分和其他模型差不多:

model.train()   # 调整到训练模式(pytorch的自带功能)
optimizer.zero_grad()   # 梯度归零

outputs = batch_processor(model, data_batch)    # 计算损失

outputs['loss'].backward()  # 反向传播
clip_grad_norm_(model.parameters(), **optim_cfg.grad_clip)  # 规范梯度大小
optimizer.step()    # 优化

② SingleStageDetector的前向训练部分

def forward_train(self, img, img_meta, **kwargs):
    # [N,]
    batch_size = len(img_meta) # B

    ret = self.merge_second_batch(kwargs)
    #  Neck的粗糙结构如下所示:
    #  输入点云 => Backbone Network => reshape 操作 => BEV Network => (x, conv6)
    #                    ||
    #                    || Tensor2Point (体素变点云)
    #                    ||
    #                 辅助网络层 => MLP层 => point_misc
    #
    # 稀疏卷积 和 Reshape 和 Tensor2Point 的细节我在上一篇博客已经讨论了。
    #
    # 输入分析:
    # vx 可以理解为 pointclpoud_range 内的点云,包含 xyz 和雷达强度项,是 (N,4).
    # ret['coordinates'] 是 pointclpoud_range 内的点云体素化的结果,点对应的体素坐标
    # batch_size 是批处理的大小
    # 吐槽: ret['coordinates'] 才是真体素,如果我的理解有误,请大家多多指正
    #
    # 输出分析 :
    # x, conv6 都是 BEV特征图,但是两个不同,两者中间还有一个卷积层
    # point_misc = (points_mean, point_cls, point_reg) 它是个元组
    # points_mean 是 bxyz 类型数据,xyz 是点云位置,b 是体素化后 z 轴分量, 它是(N,4)张量,为什么会有 b 这个分量,我也不太清楚,但是代码是这样写的
    # point_cls 是点云分类结果,它是(N,1)张量,用于前景分割(可不是3d目标分类呀)
    # point_reg 是点云回归结果,回归每一个3d类的中心位置,它是(N,3)张量
    #
    # 因为 SA-SSD 采用的是一个粗糙体素化处理方式,所以 vx  和 points_mean 的长度都是 N
    vx = self.backbone(ret['voxels'], ret['num_points'])
    (x, conv6), point_misc = self.neck(vx, ret['coordinates'], batch_size)
    # 这里的x经过了三维卷积,大小变为了原来1/8,又经过reshape,减少了D维度,又经过二维卷积,最后(2, 256, 176, 200)
    # point_misc包括了(points_mean, point_cls, point_reg),原始点云数据,语义分割结果,中心点结果

    losses = dict()

    aux_loss = self.neck.aux_loss(*point_misc, gt_bboxes=ret['gt_bboxes'])
    losses.update(aux_loss)

    # RPN forward and loss
    if self.with_rpn:
        rpn_outs = self.rpn_head(x) #这里代表三个输出box_preds, cls_preds, dir_cls_preds[B, 200, 176, 14],[B, 200, 176, 2],[B, 200, 176, 4]
        rpn_loss_inputs = rpn_outs + (ret['gt_bboxes'], ret['gt_labels'], ret['anchors'], ret['anchors_mask'], self.train_cfg.rpn)
        rpn_losses = self.rpn_head.loss(*rpn_loss_inputs)
        losses.update(rpn_losses)
        guided_anchors = self.rpn_head.get_guided_anchors(*rpn_outs, ret['anchors'], ret['anchors_mask'], ret['gt_bboxes'], thr=0.1)
    else:
        raise NotImplementedError

    # bbox head forward and loss
    if self.extra_head:
        bbox_score = self.extra_head(conv6, guided_anchors)
        refine_loss_inputs = (bbox_score, ret['gt_bboxes'], ret['gt_labels'], guided_anchors, self.train_cfg.extra)
        refine_losses = self.extra_head.loss(*refine_loss_inputs)
        losses.update(refine_losses)

    return losses

这里大部分注释参考了这篇博客.
在这里插入图片描述
图片来源这篇博客.
在上图中Backbone几乎没有任何作用,输入什么就输出什么;Neck部分包含了论文给的图中的Backbone network和Auxiliary network两部分;rpn_head是论文中的Detection部分;rpn_head.get_guided_anchors是对提取出来的bbox进行筛选;extra_head对应的就是PSWarp部分。下面一块一块的学习。

③ SpMiddleFHD.forward

SpMiddleFHD也就是Neck部分,也就是论文中的Backbone network和Auxiliary network两部分。注释如下

def forward(self, voxel_features, coors, batch_size, is_test=False):
    # voxel_features是(b_s x N) x 4 体素
    # coors已经经过了pad和cat,是一个batch的coors
    points_mean = torch.zeros_like(voxel_features)
    points_mean[:, 0] = coors[:, 0]
    points_mean[:, 1:] = voxel_features[:, :3]
    # points_mean代表点云的真实距离值(包含了每个点属于哪个batch)!!!!!!!!!!!!!!!!!!!!!!!
    coors = coors.int()
    x = spconv.SparseConvTensor(voxel_features, coors, self.sparse_shape, batch_size)   # !!!!!!!!!真正的变成了体素
    # 对voxel_features按照coors进行索引,coors在之前的处理中加入例如batch这个位置,变成了四维
    # 输出是一个【batch_size,channels, sparse_shape】的数据(2, 4, 40, 1600, 1408)
    # 就是让数据按照coors里的坐标进行了排列,成为了标准的体素空间
    x, point_misc = self.backbone(x, points_mean, is_test)
    # x是backbone的输出,体素维度缩小8倍后的64维特征,point_misc包括几部分(mean cls reg)是auxiliary的输出,即预测出来的Seg和Center
    x = x.dense()
    N, C, D, H, W = x.shape     #(2, 64, 5, 200, 176)
    x = x.view(N, C * D, H, W)  #(2, 320, 200 ,176)
    # 这里的D, H, W全部缩小了8倍,D=5
    # reshape D是立起来的那个维度,直接把D维度上的特征都视为BEV图上一个点的不同特征,拼接,变成二维的特征
    # fcn就是BEVnet,包含几个二维卷积
    x = self.fcn(x)     # (2, 256, 200, 176)
    if is_test:
        return x
    return x, point_misc
    # x是detector的输出(2, 256, 200, 176),point_misc是auxiliary的输出

spconv中包含了点云体素化的方法,就是spconv.SparseConvTensor,注意函数的输入coors中要包含每个点所属的batch中的第几帧数据,也就是pad操作。
其中还包括了三维稀疏特征到2D平面特征的转化,就是reshape造作,将第三个维度和channel维度合并。

④ SSDRotateHead.forward

这个head是得到最终预测的3D bbox和class,由于两者是分开得到的,中间经历了一个不同的卷积层,因此可能存在一定的位置误差。

def forward(self, x):
    # x从SpMiddleFHD中来,是[B(2), C(256), H(200), W(176)]的张量
    box_preds = self.conv_box(x)    # 输出 [B, 14, 200, 176] 的张量
    cls_preds = self.conv_cls(x)    # 输出 [B, 2, 200, 176] 的张量
    # [N, C, y(H), x(W)]
    # 对张量做转置,contiguous 是让置换后的张量内存分布连续的操作
    box_preds = box_preds.permute(0, 2, 3, 1).contiguous()  # [B, 200, 176, 14]
    cls_preds = cls_preds.permute(0, 2, 3, 1).contiguous()  # [B, 200, 176, 2]

    if self._use_direction_classifier:
        dir_cls_preds = self.conv_dir_cls(x)                # 输出 [B, 4, 200, 176] 的张量
        # 为什么是 4 呢?
        # 是因为 conv_dir_cls 的通道数定义为 num_anchor_per_loc * 2 = 2*2
        # 输出 [B, 200, 176, 4] 的张量,因为每个loc有两个角度的anchor,都包括前向后向两个状态
        dir_cls_preds = dir_cls_preds.permute(0, 2, 3, 1).contiguous() # [B, 200, 176, 4]

    return box_preds, cls_preds, dir_cls_preds  # [B, 200, 176, 14],[B, 200, 176, 2],[B, 200, 176, 4]

⑤ PSWarpHead.get_guided_anchors

PSWarpHead.forward的输入还有一个guided_anchors,要把这部分的意义搞懂,才能知道PSWarpHead到底是计算的那些bbox的分类。上源码。

    # anchors_mask 是 (1408*1600*2,1) 的 bool 型向量
    # anchors 是 (1600*1408*2,7) 的张量
    # box_preds, cls_preds, dir_cls_preds 是 [B, 200, 176, 14],[B, 200, 176, 2],[B, 200, 176, 4]
    # 每个变量的 C 值都不一样,分别是 7, num_class, 2
    # N 是 batch size
    def get_guided_anchors(self, box_preds, cls_preds, dir_cls_preds, anchors, anchors_mask, gt_bboxes, thr=.1):
        batch_size = box_preds.shape[0]

        # batch_box_preds 是 [N(2), H x W x 2(1600 x 1408 x 2),7] 的张量
        batch_box_preds = box_preds.view(batch_size, -1, self._box_code_size)
        #  batch_anchors_mask 是 [N(2), H x W x 2(1600*1408*2)] 的张量
        batch_anchors_mask = anchors_mask.view(batch_size, -1)
        # [N(2), H x W x 2(1600*1408*2)]
        batch_cls_preds = cls_preds.view(batch_size, -1)

        batch_box_preds = second_box_decode(batch_box_preds, anchors)

        if self._use_direction_classifier:
            # [N(2), H x W x 2(1600*1408*2), 2]
            batch_dir_preds = dir_cls_preds.view(batch_size, -1, 2)

        new_boxes = []
        if gt_bboxes is None:
            gt_bboxes = [None] * batch_size

        # 一个batch一个batch的处理
        # 首先,把跟网络初次预测的 3d框 跟 Anchor_mask 下的 Anchor比较
        #      把重叠度高的 Anchor 保留下来(mask中为0的框直接去掉,因为为0代表着框里没有点);
        # 其次,这些 Anchor 对应的网络预测的 3d框 所对应的cls_preds 用 sigmoid 处理一遍,
        #      把高于阈值 thr 的 Anchor 框保留下来
        # 再者,如果是训练阶段,给每一个框一个 3d框 的label真值
        for box_preds, cls_preds, dir_preds, a_mask, gt_boxes in zip(
                batch_box_preds, batch_cls_preds, batch_dir_preds, batch_anchors_mask, gt_bboxes
        ):
            # 根据mask(代表这个anchor里边有没有点云点存在),拿出有效box_preds,cls_preds,dir_preds
            box_preds = box_preds[a_mask]
            cls_preds = cls_preds[a_mask]
            dir_preds = dir_preds[a_mask]

            if self._use_direction_classifier:
                # [N(2), H x W x 2(1600*1408*2), 2],这里拿出[H x W x 2(1600*1408*2), 2](一个batch),拿出最后一个维度的最大值来,代表这个框预测出来的最有可能的分类结果(朝向分类结果)
                dir_labels = torch.max(dir_preds, dim=-1)[1] # [H x W x 2(1600*1408*2)]

            if self._use_sigmoid_cls:
                # 进行一下sigmoid,0-1之间,代表某个框是某个分类的可能性大小,每个框对每个分类都有可能性
                total_scores = torch.sigmoid(cls_preds)
            else:
                total_scores = F.softmax(cls_preds, dim=-1)[..., 1:]

            # 去掉维度大小为1的维度
            top_scores = torch.squeeze(total_scores, -1)

            # 把预测可能性高于阈值 thr 的 Anchor 框保留下来
            selected = top_scores > thr

            box_preds = box_preds[selected]

            if self._use_direction_classifier:
                # 根据dir和box_preds,得到框的真实角度
                dir_labels = dir_labels[selected]
                opp_labels = (box_preds[..., -1] > 0) ^ dir_labels.byte()
                box_preds[opp_labels, -1] += np.pi

            # add ground-truth
            if gt_boxes is not None:
                box_preds = torch.cat([gt_boxes, box_preds],0)
            # 保存每一个合格的 Anchor
            new_boxes.append(box_preds)
        return new_boxes    # 注意这里面放的是点云坐标系下的真实距离坐标

最终剩下的box_preds中,经过了层层的筛选,大概意思就是本来是每个体素点上都有2个框,好几万个,经过这么一筛选,得到了有可能是目标种类物体的框,就几十个。这些甚至几乎可以放到图中进行可视化了(还差一步NMS)。

⑤ PSWarpHead.forward

PSWarpHead的前向计算部分就是得到了每个框的得分(可能性)。

def forward(self, x, guided_anchors, is_test=False):
    x = self.convs(x)# 张量尺寸为 [B, 28, 200, 176],称之为 confidence map
    bbox_scores = list()
    # i代表第几个batch
    for i, ga in enumerate(guided_anchors):
        if len(ga) == 0:
            # 如果这个批次没有 guided anchor,就输出一个零值张量,尺寸跟 x 一样
            bbox_scores.append(torch.empty(0).type_as(x))
            continue
        # 得到guided_anchor的x和y区域
        # xs, ys 分别是矩形区域内的栅格采样点在 x 和 y 轴坐标,是 [4*7, N] 张量
        # N代表这个batch的guided_anchor里共有这么多的框
        (xs, ys) = self.gen_grid_fn(ga[:, [0, 1, 3, 4, 6]])
        # (xs, ys)[28, N]是指,在每个框里面取出来28个点的坐标(BEV)

        # 提取这个batch下的特征图,im 是 [C(28), 200, 176] 的张量
        im = x[i]
        # 在每个 anchor 内的栅格采样点中插值出特征图中的特征向量,是 [C(28), 200, 176]
        out = bilinear_interpolate_torch_gridsample(im, xs, ys) # 28 x 1 x N x 1
        # 以第一个维度做平均,score 是一个标量,返回每个 anchor 中所有采样点对应特征之平均值,28个特征求平均
        # 这样理解:在进行双线性插值时,两个输入分别是[28, 1, 200, 176]和[28, N, 1, 2](详情见bilinear_interpolate_torch_gridsample)
        #          第一个输入就可以看作200 x 176个特征点,每个特征点在周围采样28点中的第一点的特征1 x 1 x200 x 176,特征channel是1
        #          每个点都要采样28个点,一共就是[28, 1, 200, 176],特征channel是1
        #          在进行双线性插值时,先归一化,最终我要得到的是N x 1的维度,28个采样点视为28种插值方法依次对中心点进行插值,就得到28个N x 1的特征,就是28 x 1 x N x 1,特征channel是1
        #          再对这28种插值特征求平均,得到的就是需要的N x 1的特征,维度[N, 1]
        score = torch.mean(out, 0).view(-1)
        # [B, N] 再loss中,bbox_scores与labels求损失,其实bbox_scores这里就是指插值得到的这个框的分类
        bbox_scores.append(score)

    # B x 200 x 176
    # 如果是模型学习阶段,就只输出 bbox_scores
    if is_test:
        return bbox_scores, guided_anchors
    else:
        return torch.cat(bbox_scores, 0)

PSWarp的方法就是在每个guided_anchors中均匀采样28个点,用28个点插值出每个guided_anchor的得分,28 x N,再平均一下就是每个guided_anchors的最终分类得分。用来插值的特征是conv6,是SpMiddleFHD,也就是Backbone network部分的一个输出。

这里其实只贴出了一小部分的源码,还有大量的调用函数需要阅读才能完全弄懂这个模型,还有三个loss的计算方法没写,有时间再写。整个mmdetection框架包含的内容更多,一个一个看的话太慢,建议还是跟着算法走,了解常用的方法。
SASSD的了解应该到此为止了,中间的启发有很多,尤其是辅助网络,可以适用于很多的模型,PSWarp部分有一定的作用,但感觉作用不是很大。再者就是spconv库的灵活运用,非常适合3D稀疏卷积。
之前注释过源码的SequeezeSeg,Rangenet++,Salsanet等算法是针对语义分割任务的,而且均是基于深度图方法,voxel_base的方法与深度图方法相比较,能更大程度保存点云数据的3D信息,似乎更适用于点云处理,但也不是太过绝对的,当然还有point_based的方法保存的信息更多,处理起来也更难。
最后还是非常感谢这位作者.

sparse conv稀疏卷积 """Args:"""self.features = features # 储存密集的featureself.indices = indices # 储存每个feature对应的voxel坐标系下的坐标self.spatial_shape = spatial_shape #存储voxel的最大边界self.batch_size = batch_size # 储存batch sizeself.indice_dict = {} # 储存坐标之间的对应关系...参数。 阅读详情

相关推荐

Spconv 库简单介绍

spconv(Sparse Convolution)是一个专为处理稀疏张量而设计的深度学习库,**特别适用于需要高效稀疏卷积操作的场景。**以下是对spconv库的介绍,包括其主要功能、优势、适用场景以及一些常用的模块和用法。

AI_eNyu的博客 2063

spconv中的SparseTensor中的常用数据结构说明

例如,如果稀疏张量的形状为 (B, C, D, H, W),则 indices 属性的形状为 (N, 5),其中每一行包含 5 个整数,表示非零元素在 (B, C, D, H, W) 这 5 个轴上的位置。每一行表示一个非零元素的特征向量,例如,如果稀疏张量的形状为 (B, C, D, H, W),并且每个非零元素需要一个 4 维的特征向量,例如表示物体类别和得分,则 features 属性的形状为 (N, 4)。对象中是一个表示稀疏张量空间维度大小的元组,通常用于描述图像数据的大小。

Jayn的专栏 3445

【OpenPCDet】稀疏卷积SPConv-v1.2代码解读(1)

以下左图展示了一个2D卷积,使用一个3x3的卷积核,在单通道图像上进行卷积,其中Padding为1,得到输出。右图为一个单通道的3D卷积,与2D卷积不同之处在于,输入图像多了一个 depth 维度,卷积核也多了一个depth维度,之前2D卷积上3x3的卷积核现在变成了3x3x3。这里的3D不是通道导致的,而是深度(多层切片,多帧视频),因此,虽然输入和卷积核和输出都是3D的,但都可以是单通道的。

3703

SASSD

Structure Aware Single-stage 3D Object Detection from Point Cloud 文章:SA-SSD 代码:SA-SSD 这是来自香港理工大学和达摩院的一篇文章,一作是为在读的博士生,何晨航。通讯作者是张磊,香港理工大学的博导,目前在达摩院。华先胜达摩院的的人,也是为大牛。 Abstract 提出问题: 目前的单级探测器通过完全卷积方式逐步缩小三维点云是有效的。但是缩小特征不可避免的损失空间信息和无法充分利用3D点云的结构信息,降低了位置精度。 文章工作:

xinxiang7 1999

【信息科学与工程学】【安全领域】第二百零五篇 漏洞治理 01

形式:RabbitMQ管理插件SSRF CVE-2026-57211(CVSSv4 8.3,Windows多插件可诱发SMB NTLM泄露)、管理API资源无限CWE-770 CVSSv4 5.3、管理UI存储XSS CVE-2026-57214/其他;用法:GET /cgi-bin/.%2e/%2e%2e/etc/passwd→读密文/源码→RCE。定量:CVSS 9.0;例:Spectre CVE-2017-5753、Meltdown CVE-2017-5754(CPU越界/未授权内核读);

weixin_49199313的博客 105

【三维目标检测】SASSD(一)

SASSD是用于点云三维目标检测模型算法,发表在CVPR 2020《Structure Aware Single-stage 3D Object Detection from Point Cloud》,论文地址为“https://www4.comp.polyu.edu.hk/~cslzhang/paper/SA-SSD.pdf”。SASSD与基于Anchor的目标检测模型的结构基本保持一致,其核心特点在于采用了一个语义分割网络来辅助候选框特征提取,使产生的候选框质量更高。辅助网络这一点与PointRCNN和

叶子的博客 3492

3D点云学习:SA-SSD③源码注释

1 参考博客 在注释源码时,主要参考了另一个作者的几篇博客,帮助非常大。 参考博客: 简析SA-SSD在预处理训练评估的框架. 作者博客空间: 小白科研笔记. 由于代码量和注释量都比较大,因此截取几个第一次接触或者有启发的代码。 2 ...

themasterbi的博客 2910

点云数据处理与可视化

总结起来,本文介绍了如何使用SASSD库进行点云数据的处理和可视化。通过加载点云数据并使用SASSD提供的功能,我们可以对点云进行语义分割、特征提取和目标检测等操作,为点云数据的分析和应用提供了强大的工具。接下来,我们将使用SASSD库进行点云数据的语义分割。SASSD提供了许多预训练的模型,可以直接用于点云数据的分割任务。SASSD是一个强大的工具,它提供了丰富的功能和算法,用于点云数据的语义分割和描述。上述代码首先加载了一个预训练的语义分割模型,并使用该模型对点云数据进行分割。函数将点云可视化出来。

YadxFix的博客 191

点云3d检测SA-SSD

点云3d目标检测方法SA-SSD,引入点级监督学习辅助网络,学习结构信息,借助前景分割任务提高对象边界定位精度,中心估计任务帮助对象尺度和形状的确定,实现更精准的目标定位。同时,改进PSRoIAlign,提出PSWarp,减少后处理NMS预测框和置信度不一致问题。......

weixin_34961608的博客 1万+

【三维目标检测】SASSD(二)

yolo目标检测数据采用矩形框进行标注,其标注格式为[cls_id xp yp wp hp],cls_id表示目标所属的类别序号。xp、yp表示目标中心点相对坐标,其中xp等于目标的绝对横坐标除以图像宽度,yp等于目标的绝对纵坐标除以图像高度。wp和hp表示目标的相对宽度和高度,其中wp等于目标的绝对宽度除以图像宽度,hp等于目标的绝对高度除以图像高度。每张图片的标注结果以txt文本文件存储,每一行[cls_id xp yp wp hp]表示一个目标。

叶子的博客 3446

海康工业相机SDK开发实战:从环境搭建到实时图像采集(附避坑指南)

本文详细介绍了海康工业相机SDK在Windows平台上的开发实战流程,涵盖从环境搭建、运行时库路径配置到实时图像采集的全过程。重点解析了MVS SDK部署、设备连接、参数设置及图像处理等核心环节,并提供了实用的避坑指南和错误排查方法,帮助开发者构建稳定的机器视觉系统。

weixin_29248611的博客 846

3D点云学习:SA-SSD①

0.序言 硕士阶段开始接触基于深度学习的自动驾驶课题,主要研究方向是基于激光雷达的环境感知。目前环境感知的结果包括:语义分割,目标识别和目标跟踪。在这里记录一下自己看过的论文,用的时候可以方便的找到。 1.论文简介 源码链接: https://github.com/skyhehe123/SA-SSD. 论文链接: https://www4.comp.polyu.edu.hk/~cslzhang/paper/SA-SSD.pdf. SA-SSD这是一篇来自 港理工和阿里达摩院的合作文章。投出了一种基于sing

themasterbi的博客 1303

SAP SD销售订单抬头自定义字段增强:BADI_SLS_HEAD_SCR_CUS实战指南

SAP ERP系统中的销售订单管理是企业核心业务流程,经常需要扩展标准功能来满足个性化业务需求。在SAP SD模块中,销售订单抬头屏幕的增强是常见需求。通过BADI(Business Add-In)技术,可以在不修改标准代码的前提下,实现自定义字段的添加与数据处理。本文详细介绍了如何使用BADI_SLS_HEAD_SCR_CUS实现销售订单抬头自定义字段,包括函数组子屏幕设计、数据存储表构建以及BADI方法的具体实现。这种增强方式适用于VA01/VA02/VA03等事务,保证了系统升级的兼容性和维护的便捷性

weixin_29758911的博客 349

3d稀疏卷积——spconv源码剖析(二)

本文基于OpenPCDet框架中算法,对spconv库中稀疏卷积源码进行剖析:首先看OpenPCDet下的​。package`目录下有__init__.py 文件,在导入spconv时__init__.py中的可执行代码会被执行在导入完spconv后,可以直接使用等子模块__init__.py是Python中package的标识,定义了包的属性和方法__在__init__.py中定义了3d稀疏卷积的核心数据结构,但是本身并不是一个,只是对稀疏Tensor的一个抽象。

xiaohu的博客 4544

Excel炒股党必备:手把手教你用Power Query免费获取并刷新股票历史数据

本文详细介绍了如何利用Excel中的Power Query工具免费获取并自动刷新股票历史数据,特别适合Excel炒股党。从环境配置、数据源选择到实战操作,手把手教你建立智能数据管道,实现高效投资分析。通过参数化股票代码和定时刷新功能,大幅提升数据更新效率。

weixin_26767391的博客 654

基于Unidbg的小红书加密协议动态分析与算法还原实战

在移动应用安全领域,加密协议与签名机制是保障数据传输安全、实现客户端身份验证的核心技术。其原理通常涉及哈希算法、对称/非对称加密以及自定义的混淆逻辑,旨在防止请求被篡改或重放。理解这些协议对于安全研究、风控策略制定及合规的数据交互分析具有重要价值。在实际的工程实践中,面对高度混淆和加固的Native代码(SO库),传统的静态分析往往难以深入。此时,沙盒模拟执行技术提供了一种动态、可控的分析方案。通过搭建一个虚拟的移动应用运行环境,研究人员可以在无需真机、规避反调试机制的情况下,完整地追踪和观察加密算法的执行

weixin_29315091的博客 326

【OpenPCDet】稀疏卷积SPConv-v1.2代码解读(2)

在上一篇文章里分别展示了spconv源码中的Python和c++/cuda目录,这里再来看一下spconv编译安装完后的目录结构。当我们 在Second代码中import spconv就是在导入安装好的spconv package。既然是package,那就必然有文件,别且在导入spconv时__init__.py中的可执行代码会被执行。

3412

拆开一个烧坏的IGBT模块,手把手教你识别过压、过流、过温的“案发现场”

本文详细解析了IGBT模块失效分析的实战技巧,通过观察铝层熔化纹路、焊锡溢出形态等物理痕迹,手把手教你识别过压、过流、过温的典型失效模式。文章提供了从基础工具包到现场快速诊断的完整流程,帮助工程师精准定位IGBT模块失效的根本原因。

weixin_30655569的博客 426

【论文阅读】【三维目标检测】Structure Aware Single-stage 3D Object Detection from Point Cloud

文章目录SASSDmotivationFrameworkAuxiliary NetworkPS WarpExperiment思考Point和Voxel的结合之作PSwarp将one stage和two stage的方法联系起来 香港理工大学和阿里巴巴达摩院出品,之前听说阿里的团队检测KITTI排第一,可能就是这篇文章吧。 如果对2D Object Detection有一定了解,其实本文读起来还是比...

麒麒哈尔的博客 4825

微信小程序审核避坑指南:巧用 wx.getAccountInfoSync 实现审核与线上环境无感切换

本文详细介绍了如何利用`wx.getAccountInfoSync`API实现微信小程序审核与线上环境的无感切换。通过环境识别技术和动态渲染架构设计,开发者可以优雅解决审核资质门槛问题,同时保持生产环境的完整用户体验。文章包含组件化实现、全局状态管理等实用方案,帮助开发者提升审核通过率。

weixin_30527423的博客 260

华为OD机试双机位C卷-开心消消乐(Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【开心消消乐】,提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为od机试 - 开心消消乐。

qq_45776114的博客 1512

复现SASSD论文时出现的问题及解决方式

Q1: 在复现SASSD论文是,出现ModuleNotFoundError: No module named 'pycocotools’错误 A1: 试了一下把下方百度网盘链接的压缩文件复制到anaconda-envs-项目所在环境-Lib-site-packages然后路径下命令行里 就可以(记得先修改文件权限),不行的话就去Pycharm里环境刷新一下看有没有 pip install pycocotools-2.0.2.tar.gz 链接:https://pan.baidu.com/s/1tuegW7

Fzc_Ztt的博客 1428

Arcgis加载在线地图(内涵各种在线地图)

这三个在线地图是最常用的在线地图,其他几个类型的在线地图请自行查看。,加载出在线图层说明连接成功,点击。1、首先打开Arcgis,找到。

爱吃雪糕的小布丁的博客 1万+

3-D HANet:一种用于目标检测的柔性三维 HeatMap 辅助网络

论文笔记:《3-D HANet: A Flexible 3-D Heatmap Auxiliary Network for Object Detection》

学习记录 1429

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

pointpillars代码阅读-----架构篇

Brief  目前已经理论知识丰富,但是实践很少,因此决定在这两周内,做以下两件事: 阅读pointpillars代码,侧重点在理解怎么在nuscence上进行多目标检测的。并写笔记。 完成以下网络的搭建:(也即是是voxel的检测) 1 pointpillars代码 1.1 简单介绍 这里是 pytorch的版本 这个版本已经升级到了V2.0,也就是加入了nuscence的检测任务,同时S...

有所为,有所成长 9802

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

pointpillars代码阅读--网络结构篇

Brief 细节阅读 1. voxel_generator 代码 voxel_generator = voxel_builder.build(model_cfg.voxel_generator) 用处 用于每一个voxel_generater的生成器,内容如下,也就是对应的分割pc为voxel的参数。 拥有函数 def generate(self, points, ...

有所为,有所成长 5425
上一篇: 3D点云学习:SA-SSD③源码注释
下一篇: leetcode闯关:①--⑤
themasterbi
博客等级 码龄9年 42粉丝 8原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值