目标检测6--NanodetPlus算法分析

2023-05-16

文章目录

    • 1.简介
    • 2.辅助训练模块Assign Guidance Module
    • 3.损失函数
    • 4.标签匹配策略
    • 5.后处理介绍
    • 6.特征融合
    • 参考资料


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


1.简介

NanoDet上海人工智能实验室RangiLyu202010月份开源的轻量级检测项目,取得了很好的效果,广受关注。202112月份,作者又更新发布了NanoDetPlus,在coco val上的map提升了7个百分点。

2.辅助训练模块Assign Guidance Module

Nanodet Plus的检测头只使用了2个深度可分离卷积以减少模型的参数,但同时也导致其学习能力有限,对于从零开始学习预测分类和标签
匹配有一定困难。作者使用了同WACV上一篇paperLAD:Improving Object Detection by Label Assignment Distillation一样的做法,通过教师学生模型训练多了一个网络来引导NanodetPlus检测头的训练,同知识蒸馏的思想。

在这里插入图片描述

Nanodet Plus中,辅助训练模块在整个网络中组成中的所占的部分如下图:

https://zhuanlan.zhihu.com/p/449912627
在这里插入图片描述

辅助训练模块和物体的检测头使用共同的backbone提取出特征,并复制一份检测头的PAFPN作为bottleneck,最后接比检测头要大的多的模块作为检测头。其配置为:

    head:
      name: NanoDetPlusHead
      num_classes: 80
      input_channel: 96
      feat_channels: 96
      stacked_convs: 2
      kernel_size: 5
      strides: [8, 16, 32, 64]
      activation: LeakyReLU
      reg_max: 7
      norm_cfg:
        type: BN
    # Auxiliary head, only use in training time.
    aux_head:
      name: SimpleConvHead
      num_classes: 80
      input_channel: 192
      feat_channels: 192
      stacked_convs: 4
      strides: [8, 16, 32, 64]
      activation: LeakyReLU
      reg_max: 7

可以看到检测头headfeat_channels: 96, stacked_convs: 2,辅助训练检测头aux_headfeat_channels: 192, stacked_convs: 4,且检测头中使用的还是深度可分离卷积,因此参数比辅助训练头少很多,因此辅助训练头的学习能力更强。辅助训练分支只在网络的训练过程中起作用,训练时,backbone输出的特征同时送入检测分支和辅助训练分支,因辅助训练分支有更多的参数,故其更容易从初始状态学习判断如何划分正负样本并实现标签匹配。辅助训练分支和检测分支的输出是相同维度的预测框和类别数,因辅助训练分支训练学习的更快更好,因此可以使用辅助训练分支预测框输出结果来做标签匹配,将匹配的结果当成检测分支预测框的匹配结果来计算训练loss

nanodet_plus.py文件foward_train方法中,可以看到前向推理同时计算了aux_headhead,且把aux_head的输出送到了loss函数中。

# in nanodet_plus.py
def forward_train(self, gt_meta):
    img = gt_meta["img"]
    feat = self.backbone(img)
    fpn_feat = self.fpn(feat)
    if self.epoch >= self.detach_epoch:
        aux_fpn_feat = self.aux_fpn([f.detach() for f in feat])
        dual_fpn_feat = (
            torch.cat([f.detach(), aux_f], dim=1)
            for f, aux_f in zip(fpn_feat, aux_fpn_feat)
        )
    else:
        aux_fpn_feat = self.aux_fpn(feat)
        dual_fpn_feat = (
            torch.cat([f, aux_f], dim=1) for f, aux_f in zip(fpn_feat, aux_fpn_feat)
        )
    head_out = self.head(fpn_feat)
    aux_head_out = self.aux_head(dual_fpn_feat)
    loss, loss_states = self.head.loss(head_out, gt_meta, aux_preds=aux_head_out)
    return head_out, loss, loss_states

3.损失函数

Nanodet Plus参考了Generalized Focal Loss中的Distributed Bounding Boxes方法,在特征图尺度上回归检测框距特征grid cell中心距离时,采用离散化的方法,将回归范围分成特征图尺度上的reg_max份,并计算落在0,1,...,reg_max上的概率。因此Nanodet Plus除了检测的分类和box IoU损失外,还加多了一个DistributionFocalLoss。类别评价使用的是QualityFocalLoss,box评价使用的是Generalized Intersection over Union,GIoU

# in nanodet_plus_head.py
class NanoDetPlusHead:
    ...
    def _get_loss_from_assign(self, cls_preds, reg_preds, decoded_bboxes, assign):
        device = cls_preds.device
        labels, label_scores, bbox_targets, dist_targets, num_pos = assign
        num_total_samples = max(
            reduce_mean(torch.tensor(sum(num_pos)).to(device)).item(), 1.0
        )

        labels = torch.cat(labels, dim=0)
        label_scores = torch.cat(label_scores, dim=0)
        bbox_targets = torch.cat(bbox_targets, dim=0)
        cls_preds = cls_preds.reshape(-1, self.num_classes)
        reg_preds = reg_preds.reshape(-1, 4 * (self.reg_max + 1))
        decoded_bboxes = decoded_bboxes.reshape(-1, 4)
        loss_qfl = self.loss_qfl(
            cls_preds, (labels, label_scores), avg_factor=num_total_samples
        )

        pos_inds = torch.nonzero(
            (labels >= 0) & (labels < self.num_classes), as_tuple=False
        ).squeeze(1)

        if len(pos_inds) > 0:
            weight_targets = cls_preds[pos_inds].detach().sigmoid().max(dim=1)[0]
            bbox_avg_factor = max(reduce_mean(weight_targets.sum()).item(), 1.0)

            loss_bbox = self.loss_bbox(
                decoded_bboxes[pos_inds],
                bbox_targets[pos_inds],
                weight=weight_targets,
                avg_factor=bbox_avg_factor,
            )

            dist_targets = torch.cat(dist_targets, dim=0)
            loss_dfl = self.loss_dfl(
                reg_preds[pos_inds].reshape(-1, self.reg_max + 1),
                dist_targets[pos_inds].reshape(-1),
                weight=weight_targets[:, None].expand(-1, 4).reshape(-1),
                avg_factor=4.0 * bbox_avg_factor,
            )
        else:
            loss_bbox = reg_preds.sum() * 0
            loss_dfl = reg_preds.sum() * 0

        loss = loss_qfl + loss_bbox + loss_dfl
        loss_states = dict(loss_qfl=loss_qfl, loss_bbox=loss_bbox, loss_dfl=loss_dfl)
        return loss, loss_states

    def loss(self, preds, gt_meta, aux_preds=None):
        """Compute losses.
        Args:
            preds (Tensor): Prediction output.
            gt_meta (dict): Ground truth information.
            aux_preds (tuple[Tensor], optional): Auxiliary head prediction output.

        Returns:
            loss (Tensor): Loss tensor.
            loss_states (dict): State dict of each loss.
        """
        gt_bboxes = gt_meta["gt_bboxes"]
        gt_labels = gt_meta["gt_labels"]
        device = preds.device
        batch_size = preds.shape[0]
        input_height, input_width = gt_meta["img"].shape[2:]
        featmap_sizes = [
            (math.ceil(input_height / stride), math.ceil(input_width) / stride)
            for stride in self.strides
        ]
        # get grid cells of one image
        mlvl_center_priors = [
            self.get_single_level_center_priors(
                batch_size,
                featmap_sizes[i],
                stride,
                dtype=torch.float32,
                device=device,
            )
            for i, stride in enumerate(self.strides)
        ]
        center_priors = torch.cat(mlvl_center_priors, dim=1)

        cls_preds, reg_preds = preds.split(
            [self.num_classes, 4 * (self.reg_max + 1)], dim=-1
        )
        dis_preds = self.distribution_project(reg_preds) * center_priors[..., 2, None]
        decoded_bboxes = distance2bbox(center_priors[..., :2], dis_preds)

        if aux_preds is not None:
            # use auxiliary head to assign
            aux_cls_preds, aux_reg_preds = aux_preds.split(
                [self.num_classes, 4 * (self.reg_max + 1)], dim=-1
            )
            aux_dis_preds = (
                self.distribution_project(aux_reg_preds) * center_priors[..., 2, None]
            )
            aux_decoded_bboxes = distance2bbox(center_priors[..., :2], aux_dis_preds)
            batch_assign_res = multi_apply(
                self.target_assign_single_img,
                aux_cls_preds.detach(),
                center_priors,
                aux_decoded_bboxes.detach(),
                gt_bboxes,
                gt_labels,
            )
        else:
            # use self prediction to assign
            batch_assign_res = multi_apply(
                self.target_assign_single_img,
                cls_preds.detach(),
                center_priors,
                decoded_bboxes.detach(),
                gt_bboxes,
                gt_labels,
            )

        loss, loss_states = self._get_loss_from_assign(
            cls_preds, reg_preds, decoded_bboxes, batch_assign_res
        )

        if aux_preds is not None:
            aux_loss, aux_loss_states = self._get_loss_from_assign(
                aux_cls_preds, aux_reg_preds, aux_decoded_bboxes, batch_assign_res
            )
            loss = loss + aux_loss
            for k, v in aux_loss_states.items():
                loss_states["aux_" + k] = v
        return loss, loss_states

DistributionFocalLoss的定义见工程gfocal_loss.py文件,通过box2distance转换得到的label是浮点数,而网络回归的标签是离散形的,故在计算DistributionFocalLoss时会计算取左边离散值和右边离散值当作target,并按距离给左右两个点来分配损失权重。

class DistributionFocalLoss
    ...
    def forward(
        self, pred, target, weight=None, avg_factor=None, reduction_override=None
    ):
        """Forward function.

        Args:
            pred (torch.Tensor): Predicted general distribution of bounding
                boxes (before softmax) with shape (N, n+1), n is the max value
                of the integral set `{0, ..., n}` in paper.
            target (torch.Tensor): Target distance label for bounding boxes
                with shape (N,).
            weight (torch.Tensor, optional): The weight of loss for each
                prediction. Defaults to None.
            avg_factor (int, optional): Average factor that is used to average
                the loss. Defaults to None.
            reduction_override (str, optional): The reduction method used to
                override the original reduction method of the loss.
                Defaults to None.
        """
        assert reduction_override in (None, "none", "mean", "sum")
        reduction = reduction_override if reduction_override else self.reduction
        loss_cls = self.loss_weight * distribution_focal_loss(
            pred, target, weight, reduction=reduction, avg_factor=avg_factor
        )
        return loss_cls

def distribution_focal_loss(pred, label):
    r"""Distribution Focal Loss (DFL) is from `Generalized Focal Loss: Learning
    Qualified and Distributed Bounding Boxes for Dense Object Detection
    <https://arxiv.org/abs/2006.04388>`_.

    Args:
        pred (torch.Tensor): Predicted general distribution of bounding boxes
            (before softmax) with shape (N, n+1), n is the max value of the
            integral set `{0, ..., n}` in paper.
        label (torch.Tensor): Target distance label for bounding boxes with
            shape (N,).

    Returns:
        torch.Tensor: Loss tensor with shape (N,).
    """
    dis_left = label.long()
    dis_right = dis_left + 1
    weight_left = dis_right.float() - label
    weight_right = label - dis_left.float()
    loss = (
        F.cross_entropy(pred, dis_left, reduction="none") * weight_left
        + F.cross_entropy(pred, dis_right, reduction="none") * weight_right
    )
    return loss

4.标签匹配策略

NanodetPlus使用了DynamicSoftLabelAssigner,DSLA,参考YoloX中的SimOTA算法来做标签匹配,SimOTA是一种动态标签匹配算法,基于dynamic k来实现,先计算cost matrix,再将其当作任务分配问题,关于YoloX中的
SimOTA算法可以参考这里。

5.后处理介绍

虽然NanoDetPlus作者将模型最终的输出concat为了一个输出,从下图可以看到NanoDetPlus有四个输出头,对应的stride分别为[8, 16, 32, 64]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5mRVvqm-1666880487603)(/images/Detection/10NanodetPlus/1.png)]

上图中四个输出头特征图的shape为[1, 33, 80, 80]/[1, 33, 40, 40]/[1, 33, 20, 20]/[1, 33, 10, 10],shape分别对应的含义是[batch_size, num_class+4*(reg_max+1), feature_map_height, feature_map_width]batch_size,num_class,feature_map_{height,width}都好理解,reg_max却是新引入的一个超参数,值得介绍一下。

NanodetPlus是类FCOSAnchorFree算法,直接预测的是检测框距离中心点的距离(left,top,right,bottom)

https://foobarweb.net/2022/09/22/7FCOSNet/
在这里插入图片描述

中心点就是通过meshgrid(range(feature_width), range(feature_height))*stride得到的从特征图映射到输入图像尺度中的点,而(left,top,right,bottom)的预测作者使用的是Generalized Focal Loss(GFL)中提出的离散化回归的方法。

Generalized Focal Loss(GFL)是南开大学的李翔在2020年6月发表的论文中提出的。该方法是离散化检测框回归的范围,选取range(0, reg_max+1)上的离散值作为回归目标,reg_max是最大回归范围。

如上选reg_max=7,则可以理解为在特征图上将检测框上下左右边距离中心的距离设置为[0,1,...,7]这8种离散值,网络输出预测的分别是上下左右边落在[0,1,..,7]上的概率,因此输入的大小为4*(reg_max+1),为求边距中心的距离,需求落在[0,1,..7]上各点的期望,然后再利用stride将检测框映射到输入图尺寸上即可。当reg_max=7,stride=8时,对应检测框的最大尺寸为(7x8+7x8)x(7x8+7x8)=112x112,因此检测框范围可以覆盖(0-112)。关于这一部分详细的介绍可以参考源码nanodet/model/head/gfl_head.py

class Integral(nn.Module):
    """A fixed layer for calculating integral result from distribution.
    This layer calculates the target location by :math: `sum{P(y_i) * y_i}`,
    P(y_i) denotes the softmax vector that represents the discrete distribution
    y_i denotes the discrete set, usually {0, 1, 2, ..., reg_max}
    Args:
        reg_max (int): The maximal value of the discrete set. Default: 16. You
            may want to reset it according to your new dataset or related
            settings.
    """

    def __init__(self, reg_max=16):
        super(Integral, self).__init__()
        self.reg_max = reg_max
        self.register_buffer(
            "project", torch.linspace(0, self.reg_max, self.reg_max + 1)
        )

    def forward(self, x):
        """Forward feature from the regression head to get integral result of
        bounding box location.
        Args:
            x (Tensor): Features of the regression head, shape (N, 4*(n+1)),
                n is self.reg_max.
        Returns:
            x (Tensor): Integral result of box locations, i.e., distance
                offsets from the box center in four directions, shape (N, 4).
        """
        shape = x.size()
        x = F.softmax(x.reshape(*shape[:-1], 4, self.reg_max + 1), dim=-1)
        x = F.linear(x, self.project.type_as(x)).reshape(*shape[:-1], 4)
        return x

除了Integral处理外,其余的就是常规的后处理操作了,distance2Box然后做multiclass_nms。还有一点就是作者计算分类的评分时使用的sigmoid函数,一个detection box有可能分配多个标签,直观上NanodetPlus应该对不同类别的物体遮挡有相对好的检测效果。具体可以参考nanodet/model/module/nms.pymulticlass_nms函数的下面部分代码:

def multiclass_nms(...args):
    ...
    num_classes = multi_scores.size(1) - 1
    # exclude background category
    if multi_bboxes.shape[1] > 4:
        bboxes = multi_bboxes.view(multi_scores.size(0), -1, 4)
    else:
        bboxes = multi_bboxes[:, None].expand(multi_scores.size(0), num_classes, 4)
    scores = multi_scores[:, :-1]

    # filter out boxes with low scores
    valid_mask = scores > score_thr

    # We use masked_select for ONNX exporting purpose,
    # which is equivalent to bboxes = bboxes[valid_mask]
    # we have to use this ugly code
    bboxes = torch.masked_select(
        bboxes, torch.stack((valid_mask, valid_mask, valid_mask, valid_mask), -1)
    ).view(-1, 4)
    if score_factors is not None:
        scores = scores * score_factors[:, None]
    scores = torch.masked_select(scores, valid_mask)
    labels = valid_mask.nonzero(as_tuple=False)[:, 1]

6.特征融合

bottleneck部分除了FPNPANNanodetPlus中还引入了GhostPAN更好的做特征融合,GhostNet是华为诺亚实验室在CVPR2020上提出的模型,GhostNet作者指出在一个训练好的神经网络中,通常会包含丰富甚至冗余的特征图,

在这里插入图片描述

其实部分特征图完全可以通过一次线性变换 Φ i \Phi_{i} Φi来实现,因此卷积层输出的通道就部分来自于卷积,部分通过对卷积结果线性变换得到,concat后得到最终的输出。

在这里插入图片描述


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


参考资料

  • 1.大白话 Generalized Focal Loss
  • 2.超简单辅助模块加速训练收敛,精度大幅提升!移动端实时的NanoDet升级版NanoDet-Plus来了!
  • 3.YOLO之外的另一选择,手机端97FPS的Anchor-Free目标检测模型NanoDet现已开源~
  • 4.CVPR 2020:华为GhostNet,超越谷歌MobileNet,已开源
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标检测6--NanodetPlus算法分析 的相关文章

  • Gitlab全量迁移

    背景 公司研发使用的Gitlab由于服务器下架需要迁移到新的Gitlab服务器上 Gitlab官方推荐了先备份然后再恢复的方法 个人觉得这个方案不是很友好 xff0c 故采用官方的另外一种方法 xff0c 百度后发现这个方法还没有人记录 x
  • Swift使用极光推送JPush的Demo

    JPushDemo github项目地址 需要导入JPush框架 xff0c 可以使用cocoapod导入 xff0c 也可以手动导入 环境配置 配置环境可以参考极光推送的官方文档 xff1a iOS 证书设置指南 iOS SDK 集成指南
  • 拨测API接口+监控方案

    简介 在业务运维场景中 xff0c 需要对核心的API接口进行拨测 而各个接口需要传递的参数或者接口之间的依赖是比较复杂的 xff0c 通常接口之间都是通过链式请求来完成一个业务场景 常见的就是先登录 xff0c 拿到token以后 xff
  • KVM常用命令集合

    背景 曙光的一台物理服务器6240通过KVM进行虚拟化 xff0c 创建多台虚机 记录一下整个过程中常用的命令 物理服务器信息如下 操作系统 Kylin Linux Advanced Server release V10 span clas
  • 通过cfssl自签证书https证书

    背景 公司内部自建Web服务 xff0c 通过自签CA xff0c 然后签发https证书 工具地址 GitHub cloudflare cfssl CFSSL Cloudflare 39 s PKI and TLS toolkit 使用步
  • vSphere Web Client 6.5 如何上传ISO文件

    vSphere Web Client 6 5 如何上传ISO文件 xff1f 1 xff0c 先开启SSH功能 WEB登陆管理端 xff0c 选中一台主机 xff0c 配置 xff0d 安全配置文件 xff0d 服务编辑 xff0d SSH
  • linux中sed在指定字符前后添加内容

    假设文档内容如下 xff1a 1 2 3 4 5 root 64 localhost cat tmp input txt null 000011112222 test 要求 xff1a 在1111之前添加AAA 方法如下 xff1a sed
  • 解决 - Prometheus 监控Kubelet Metrics 报错"server returned HTTP status 403 Forbidden"

    简介 使用prometheus监控kubelet的时候 xff0c 报如下403的错误 xff1a 或者报401的错误 该问题的原因是webhook的授权地址使用127 0 0 1 所以其它IP发起的请求都会被拒绝 将该地址改为0 0 0
  • 为什么每次进入命令都要重新source /etc/profile 才能生效?

    span style color 999988 编辑JDK8 span span style color 0086b3 export span JAVA HOME 61 span style color dd1144 34 usr java
  • MySQL配置文件my.ini的一般设置

    mysqld 设置3306端口 port 61 3306 设置mysql的安装目录 basedir 61 D Software Package mysql 8 0 12 winx64 mysql 8 0 12 winx64 设置mysql数
  • Linux shell脚本编程时bad substitution解决办法

    首先 xff0c 我们要理解bad substitution的字面意思 xff0c 它的字面意思是 替换错误 的意思 这种错误的原因呢 xff0c 通常是我们编写脚本时 和 xff08 xff09 错误使用导致的 比如应该用 xff08 x
  • Vue中的watch 和computed 属性

    之前写过一篇关于computed计算属性的文章 xff0c 详见这里 computed 内的function只执行一次 xff0c 仅当function内涉及到Vue实例绑定的data的值的改变 xff0c function才会从新执行 x
  • Swift使用XMPPFramework做IM即时通信的Demo

    上一篇文章处理了文本中表情的替换 xff0c 现在来完成消息的发送功能吧 xff08 貌似前后并没有逻辑关系哈 xff09 首先为了测试 xff0c 我们需要下载spark工具 xff0c 它可以连接openfire搭建的后台来完成即时通信
  • P1591 阶乘数码

    题目描述 求n 中某个数码出现的次数 输入格式 第一行为 t t 10 xff0c 表示数据组数 接下来 t 行 xff0c 每行一个正整数n n 1000 和数码 a 输出格式 对于每组数据 xff0c 输出一个整数 xff0c 表示 n
  • nginx缓存命中率统计(转)

    转自 xff1a http www libertyvps com thread 275 1 1 html nginx提供了 upstream cache status这个变量来显示缓存的状态 xff0c 我们可以在配置中添加一个http头来
  • windows远程桌面连接到Linux服务器(ubuntu系统)、解决xrdp登录界面port问题、解决password failed

    一 xff1a 一般在windows系统安装ssh客户端远程连接Linux服务器 xff0c 可以很方便地传输文件 xff08 注意 xff1a 文件路径不能有小括号 xff0c 空格之类的 xff0c 不然会出现erro xff09 但如
  • linux之文件系统命令

    第一章 linux之帮助命令 第二章 linux命令行快捷键 第三章 linux之防火墙 第四章 linux之服务开机自启 第五章 linux之关机与重启 第六章 linux之环境变量 第七章 linux之目录操作命令 第八章 linux之
  • 解决linux底下cmake编译使用C++ 11标准库自带的thread报错问题

    本人在编写linux底下socket编程测试服务端时候 xff0c 发现使用std thread函数时候 xff0c cmake编译通过 xff0c make编译失败 xff0c CMakeLists txt如下 xff1a cmake m
  • unity UGUI 解决ScrollView加载大量Item导致卡顿的问题

    目录 1 引言2 问题分析3 代码部分4 使用举例4 1 场景搭建4 2 测试4 3 效果展示 5 Demo下载6 结束语 1 引言 我们在平常的开发中常常碰到列表类的数据处理 xff01 典型的像玩家列表这种可能数量非常庞大 xff0c
  • 使用 Amazon EC2 启动 Windows 虚拟机

    本教程将教授您如何使用 Amazon Elastic Compute Cloud EC2 来启动 配置和连接至 Windows 虚拟机 Amazon EC2 是用于在云中创建和运行虚拟机 xff08 我们将这些虚拟机称为 实例 xff09

随机推荐

  • linux ss 命令用法说明

    ss 是 Socket Statistics 的缩写 ss 命令可以用来获取 socket 统计信息 xff0c 它显示的内容和 netstat 类似 但 ss 的优势在于它能够显示更多更详细的有关 TCP 和连接状态的信息 xff0c 而
  • 谷歌浏览器 跨域遇到的坑 cors 错误(亲测可行)

    浏览器版本 xff1a 一 现象 xff1a 解决方案 xff1a 方案一 xff1a xff08 已论证 xff09 步骤1 xff1a 谷歌浏览器 打开 chrome flags block insecure private netwo
  • 安装Visual Studio 2015时出现安装包丢失或损坏

    1 现象描述 在线安装vs时 xff0c 在线下载一直为0 xff0c 提示网络异常 xff0c 检查网络 xff1b 实际网络是能联网的 离线安装ISO xff0c 安装1分钟左右 提示安装包损坏或丢失 xff0c 选择从inter下载或
  • 数据库实验-数据查询练习

    用SQL语句完成以下查询 1 查询所在系为 CS 的学生学号和姓名 xff1b select sno sname from student where sdept 61 39 CS 39 2 查询选修了3号课程的学生学号 xff1b sel
  • web前端播放视频基础(多种格式,mp4, ogg, flv)(普通项目和vue项目)

    前端播放视频 满足一般播放条件 话不多说 先上代码 以下包含我能做出来可以做出来播放的视频格式 测试ok 可能还有ogg和wmv的格式可以播放 但我没测试通过 所以不展示 span class token keyword if span s
  • javascript中正则匹配多个条件, 常用正则匹配, 正则详解

    javascript中正则匹配多个条件 常用正则匹配 正则表达式常用方法实现其他的常用正则匹配详解 注意 本篇文章是根据在下日常编码过程中逐渐丰富的 越往后看 收获越丰富 收藏起来以后随时回顾 准备工作 1 汉字和unicode码的在线转换
  • antd react ProTable 基本使用

    antd react pro系列 ProTable 基本使用 一 安装二 常用字段 antd 全称 Ant Design 是目前来说运用最广泛的 react 的 ui 框架 下文就用略写 antd 代替了 pro系列不做过多解释 毕竟ui框
  • canvas插件 fabric.js 使用

    fabric js使用 fabric js 是 常用的 canvas 插件1 在项目中使用2 特殊用法 基本设置 画板数据的导入导出 遮罩 Pattern 引用官网案例 多个对象合并 并设置为 fabric 背景 适用于变色和更多场景 把
  • 前端 百度地图 javascript api 在线地图, 离线地图

    前端 百度地图 javascript api 在线地图 离线地图 在线地图登录百度api获取key注册为个人开发者后 创建应用 离线地图 瓦片地图 下载离线瓦片地图二 下载api js 一些常用api难找见的api 在线地图 在线地图 百度
  • 基于 jquery imagesloaded masonry 实现的瀑布流图片展示

    基于 jquery imagesloaded masonry 实现的瀑布流图片展示 基于 jquery imagesloaded masonry 实现的瀑布流图片展示简述 xff0c 版本 xff0c 及插件案例htmlcssjs 基于 j
  • 微信小程序开发双重for循环, wx: if else 语法

    微信小程序开发 双重for循环 多重for循环 重点在于 wx for item 61 34 problemItem 34 和 wx for index 61 34 idx 34 确定循环的 item 和index 理论上可以实现无上线条循
  • react hocks ggeditor 脑图开发(双向细目表), 实用技巧

    ggeditor脑图开发 目录 配置说明api介绍 xff0c 引入插件 xff0c 获取api xff08 withPropsAPI xff09 xff0c 获取node api xff0c 常用api汇总 xff0c 绑定事件功能实现
  • TX2开发板CMake Error at CMakeLists.txt:11 (message): FATAL: In-source builds are not allowed.

    因为之前已经cmake过 把之前cmake生成的build目录中的文件 xff0c 以及opencv目录下生成的CmakeCache txt删除就可以解决了
  • 玩客云刷ARMBIAN当服务器过程记录

    玩客云的可玩性 1 可以刷成电视 43 游戏盒子的双系统 也可以刷成单独的电视盒子和游戏盒子 不过因为内存有限放不了多少游戏 还是建议用外置SD卡存储游戏比较合适 2 刷成Armbian linux系统 xff08 可以实现docker 可
  • Android9.0从系统层面修改android_id

    原因 按照android官方说明 xff0c android id会随着恢复出厂设置或重新烧录系统 xff0c 或切换用户登陆都会重新随机生成新的anroid id 因此对于一些依赖anroid id做唯一识别标识之一的软件来说就有问题了
  • Linux系统下基于Docker安装Yapi,并且迁移Yapi数据

    本文主要讲四个部分 xff1a 1 什么是Yapi 2 Centos7 下基于docker安装Yapi 3 Yapi数据迁移 4 利用Nginx反向代理 什么是Yapi YApi 是高效 易用 功能强大的 api 管理平台 xff0c 旨在
  • C语言多文件编程基本格式

    1 背景 xff1a 用一个丢骰子的简单案例熟悉了C语言多文件编程该咋写 2 格式 xff08 1 xff09 主函数文件main c 文件头part1 所有要使用的函数 include include include 文件头part2 放
  • STM32汇编程序及点灯实验

    目录 一 Keil下编写汇编程序1 新建工程2 配置环境3 测试代码 二 HEX文件格式说明1 简介2 格式说明3 举例说明 上述工程生成的 hex文件 三 基于STM32 汇编每间隔1秒钟闪烁一次LED的程序 一 Keil下编写汇编程序
  • c++单向链表逆序

    include lt iostream gt using namespace std struct node int data node next node createlist int n 创建一个链表 xff0c n为存储的数据个数 n
  • 目标检测6--NanodetPlus算法分析

    文章目录 1 简介2 辅助训练模块Assign Guidance Module3 损失函数4 标签匹配策略5 后处理介绍6 特征融合参考资料 欢迎访问个人网络日志 x1f339 x1f339 知行空间 x1f339 x1f339 1 简介