YOLOv9独家改进|动态蛇形卷积Dynamic Snake Convolution与RepNCSPELAN4融合

news2024/9/22 11:39:17


专栏介绍:YOLOv9改进系列 | 包含深度学习最新创新,主力高效涨点!!!


一、改进点介绍

        Dynamic Snake Convolution是一种针对细长微弱的局部结构特征与复杂多变的全局形态特征设计的卷积模块。

        RepNCSPELAN4是YOLOv9中的特征提取模块,类似YOLOv5和v8中的C2f与C3模块。


二、RepNCSPELAN4Dynamic模块详解

 2.1 模块简介

       RepNCSPELAN4Dynamic的主要思想:  使用Dynamic Snake Convolution与RepNCSPELAN4中融合。


三、 RepNCSPELAN4Dynamic模块使用教程

3.1 RepNCSPELAN4Dynamic模块的代码

class RepNBottleneck_DySnakeConv(RepNBottleneck):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):  # ch_in, ch_out, shortcut, kernels, groups, expand
        super().__init__(c1, c2, shortcut, g, k, e)
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = RepConvN(c1, c_, k[0], 1)
        self.cv2 = Conv(c_, c2, k[1], s=1, g=g)
        self.add = shortcut and c1 == c2


class RepNCSP_DySnakeConv(RepNCSP):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = DySnakeConv(c1, c_)
        self.cv2 = DySnakeConv(c1, c_)
        self.cv3 = DySnakeConv(2 * c_, c2)  # optional act=FReLU(c2)
        self.m = nn.Sequential(*(RepNBottleneck_DySnakeConv(c_, c_, shortcut, g, e=1.0) for _ in range(n)))

class RepNCSPELAN4DySnakeConv(RepNCSPELAN4):
    # csp-elan
    def __init__(self, c1, c2, c3, c4, c5=1):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__(c1, c2, c3, c4, c5)
        self.cv1 = Conv(c1, c3, k=1, s=1)
        self.cv2 = nn.Sequential(RepNCSP_DySnakeConv(c3 // 2, c4, c5), DySnakeConv(c4, c4, 3))
        self.cv3 = nn.Sequential(RepNCSP_DySnakeConv(c4, c4, c5), DySnakeConv(c4, c4, 3))
        self.cv4 = Conv(c3 + (2 * c4), c2, 1, 1)

class DySnakeConv(nn.Module):
    def __init__(self, inc, ouc, k=3) -> None:
        super().__init__()

        self.conv_0 = Conv(inc, ouc, k)
        self.conv_x = DSConv(inc, ouc, 0, k)
        self.conv_y = DSConv(inc, ouc, 1, k)

    def forward(self, x):
        return torch.cat([self.conv_0(x), self.conv_x(x), self.conv_y(x)], dim=1)


class DSConv(nn.Module):
    def __init__(self, in_ch, out_ch, morph, kernel_size=3, if_offset=True, extend_scope=1):
        """
        The Dynamic Snake Convolution
        :param in_ch: input channel
        :param out_ch: output channel
        :param kernel_size: the size of kernel
        :param extend_scope: the range to expand (default 1 for this method)
        :param morph: the morphology of the convolution kernel is mainly divided into two types
                        along the x-axis (0) and the y-axis (1) (see the paper for details)
        :param if_offset: whether deformation is required, if it is False, it is the standard convolution kernel
        """
        super(DSConv, self).__init__()
        # use the <offset_conv> to learn the deformable offset
        self.offset_conv = nn.Conv2d(in_ch, 2 * kernel_size, 3, padding=1)
        self.bn = nn.BatchNorm2d(2 * kernel_size)
        self.kernel_size = kernel_size

        # two types of the DSConv (along x-axis and y-axis)
        self.dsc_conv_x = nn.Conv2d(
            in_ch,
            out_ch,
            kernel_size=(kernel_size, 1),
            stride=(kernel_size, 1),
            padding=0,
        )
        self.dsc_conv_y = nn.Conv2d(
            in_ch,
            out_ch,
            kernel_size=(1, kernel_size),
            stride=(1, kernel_size),
            padding=0,
        )

        self.gn = nn.GroupNorm(out_ch // 4, out_ch)
        self.act = Conv.default_act

        self.extend_scope = extend_scope
        self.morph = morph
        self.if_offset = if_offset

    def forward(self, f):
        offset = self.offset_conv(f)
        offset = self.bn(offset)
        # We need a range of deformation between -1 and 1 to mimic the snake's swing
        offset = torch.tanh(offset)
        input_shape = f.shape
        dsc = DSC(input_shape, self.kernel_size, self.extend_scope, self.morph)
        deformed_feature = dsc.deform_conv(f, offset, self.if_offset)
        if self.morph == 0:
            x = self.dsc_conv_x(deformed_feature.type(f.dtype))
            x = self.gn(x)
            x = self.act(x)
            return x
        else:
            x = self.dsc_conv_y(deformed_feature.type(f.dtype))
            x = self.gn(x)
            x = self.act(x)
            return x


# Core code, for ease of understanding, we mark the dimensions of input and output next to the code
class DSC(object):
    def __init__(self, input_shape, kernel_size, extend_scope, morph):
        self.num_points = kernel_size
        self.width = input_shape[2]
        self.height = input_shape[3]
        self.morph = morph
        self.extend_scope = extend_scope  # offset (-1 ~ 1) * extend_scope

        # define feature map shape
        """
        B: Batch size  C: Channel  W: Width  H: Height
        """
        self.num_batch = input_shape[0]
        self.num_channels = input_shape[1]

    """
    input: offset [B,2*K,W,H]  K: Kernel size (2*K: 2D image, deformation contains <x_offset> and <y_offset>)
    output_x: [B,1,W,K*H]   coordinate map
    output_y: [B,1,K*W,H]   coordinate map
    """

    def _coordinate_map_3D(self, offset, if_offset):
        device = offset.device
        # offset
        y_offset, x_offset = torch.split(offset, self.num_points, dim=1)

        y_center = torch.arange(0, self.width).repeat([self.height])
        y_center = y_center.reshape(self.height, self.width)
        y_center = y_center.permute(1, 0)
        y_center = y_center.reshape([-1, self.width, self.height])
        y_center = y_center.repeat([self.num_points, 1, 1]).float()
        y_center = y_center.unsqueeze(0)

        x_center = torch.arange(0, self.height).repeat([self.width])
        x_center = x_center.reshape(self.width, self.height)
        x_center = x_center.permute(0, 1)
        x_center = x_center.reshape([-1, self.width, self.height])
        x_center = x_center.repeat([self.num_points, 1, 1]).float()
        x_center = x_center.unsqueeze(0)

        if self.morph == 0:
            """
            Initialize the kernel and flatten the kernel
                y: only need 0
                x: -num_points//2 ~ num_points//2 (Determined by the kernel size)
                !!! The related PPT will be submitted later, and the PPT will contain the whole changes of each step
            """
            y = torch.linspace(0, 0, 1)
            x = torch.linspace(
                -int(self.num_points // 2),
                int(self.num_points // 2),
                int(self.num_points),
            )

            y, x = torch.meshgrid(y, x)
            y_spread = y.reshape(-1, 1)
            x_spread = x.reshape(-1, 1)

            y_grid = y_spread.repeat([1, self.width * self.height])
            y_grid = y_grid.reshape([self.num_points, self.width, self.height])
            y_grid = y_grid.unsqueeze(0)  # [B*K*K, W,H]

            x_grid = x_spread.repeat([1, self.width * self.height])
            x_grid = x_grid.reshape([self.num_points, self.width, self.height])
            x_grid = x_grid.unsqueeze(0)  # [B*K*K, W,H]

            y_new = y_center + y_grid
            x_new = x_center + x_grid

            y_new = y_new.repeat(self.num_batch, 1, 1, 1).to(device)
            x_new = x_new.repeat(self.num_batch, 1, 1, 1).to(device)

            y_offset_new = y_offset.detach().clone()

            if if_offset:
                y_offset = y_offset.permute(1, 0, 2, 3)
                y_offset_new = y_offset_new.permute(1, 0, 2, 3)
                center = int(self.num_points // 2)

                # The center position remains unchanged and the rest of the positions begin to swing
                # This part is quite simple. The main idea is that "offset is an iterative process"
                y_offset_new[center] = 0
                for index in range(1, center):
                    y_offset_new[center + index] = (y_offset_new[center + index - 1] + y_offset[center + index])
                    y_offset_new[center - index] = (y_offset_new[center - index + 1] + y_offset[center - index])
                y_offset_new = y_offset_new.permute(1, 0, 2, 3).to(device)
                y_new = y_new.add(y_offset_new.mul(self.extend_scope))

            y_new = y_new.reshape(
                [self.num_batch, self.num_points, 1, self.width, self.height])
            y_new = y_new.permute(0, 3, 1, 4, 2)
            y_new = y_new.reshape([
                self.num_batch, self.num_points * self.width, 1 * self.height
            ])
            x_new = x_new.reshape(
                [self.num_batch, self.num_points, 1, self.width, self.height])
            x_new = x_new.permute(0, 3, 1, 4, 2)
            x_new = x_new.reshape([
                self.num_batch, self.num_points * self.width, 1 * self.height
            ])
            return y_new, x_new

        else:
            """
            Initialize the kernel and flatten the kernel
                y: -num_points//2 ~ num_points//2 (Determined by the kernel size)
                x: only need 0
            """
            y = torch.linspace(
                -int(self.num_points // 2),
                int(self.num_points // 2),
                int(self.num_points),
            )
            x = torch.linspace(0, 0, 1)

            y, x = torch.meshgrid(y, x)
            y_spread = y.reshape(-1, 1)
            x_spread = x.reshape(-1, 1)

            y_grid = y_spread.repeat([1, self.width * self.height])
            y_grid = y_grid.reshape([self.num_points, self.width, self.height])
            y_grid = y_grid.unsqueeze(0)

            x_grid = x_spread.repeat([1, self.width * self.height])
            x_grid = x_grid.reshape([self.num_points, self.width, self.height])
            x_grid = x_grid.unsqueeze(0)

            y_new = y_center + y_grid
            x_new = x_center + x_grid

            y_new = y_new.repeat(self.num_batch, 1, 1, 1)
            x_new = x_new.repeat(self.num_batch, 1, 1, 1)

            y_new = y_new.to(device)
            x_new = x_new.to(device)
            x_offset_new = x_offset.detach().clone()

            if if_offset:
                x_offset = x_offset.permute(1, 0, 2, 3)
                x_offset_new = x_offset_new.permute(1, 0, 2, 3)
                center = int(self.num_points // 2)
                x_offset_new[center] = 0
                for index in range(1, center):
                    x_offset_new[center + index] = (x_offset_new[center + index - 1] + x_offset[center + index])
                    x_offset_new[center - index] = (x_offset_new[center - index + 1] + x_offset[center - index])
                x_offset_new = x_offset_new.permute(1, 0, 2, 3).to(device)
                x_new = x_new.add(x_offset_new.mul(self.extend_scope))

            y_new = y_new.reshape(
                [self.num_batch, 1, self.num_points, self.width, self.height])
            y_new = y_new.permute(0, 3, 1, 4, 2)
            y_new = y_new.reshape([
                self.num_batch, 1 * self.width, self.num_points * self.height
            ])
            x_new = x_new.reshape(
                [self.num_batch, 1, self.num_points, self.width, self.height])
            x_new = x_new.permute(0, 3, 1, 4, 2)
            x_new = x_new.reshape([
                self.num_batch, 1 * self.width, self.num_points * self.height
            ])
            return y_new, x_new

    """
    input: input feature map [N,C,D,W,H];coordinate map [N,K*D,K*W,K*H] 
    output: [N,1,K*D,K*W,K*H]  deformed feature map
    """

    def _bilinear_interpolate_3D(self, input_feature, y, x):
        device = input_feature.device
        y = y.reshape([-1]).float()
        x = x.reshape([-1]).float()

        zero = torch.zeros([]).int()
        max_y = self.width - 1
        max_x = self.height - 1

        # find 8 grid locations
        y0 = torch.floor(y).int()
        y1 = y0 + 1
        x0 = torch.floor(x).int()
        x1 = x0 + 1

        # clip out coordinates exceeding feature map volume
        y0 = torch.clamp(y0, zero, max_y)
        y1 = torch.clamp(y1, zero, max_y)
        x0 = torch.clamp(x0, zero, max_x)
        x1 = torch.clamp(x1, zero, max_x)

        input_feature_flat = input_feature.flatten()
        input_feature_flat = input_feature_flat.reshape(
            self.num_batch, self.num_channels, self.width, self.height)
        input_feature_flat = input_feature_flat.permute(0, 2, 3, 1)
        input_feature_flat = input_feature_flat.reshape(-1, self.num_channels)
        dimension = self.height * self.width

        base = torch.arange(self.num_batch) * dimension
        base = base.reshape([-1, 1]).float()

        repeat = torch.ones([self.num_points * self.width * self.height
                             ]).unsqueeze(0)
        repeat = repeat.float()

        base = torch.matmul(base, repeat)
        base = base.reshape([-1])

        base = base.to(device)

        base_y0 = base + y0 * self.height
        base_y1 = base + y1 * self.height

        # top rectangle of the neighbourhood volume
        index_a0 = base_y0 - base + x0
        index_c0 = base_y0 - base + x1

        # bottom rectangle of the neighbourhood volume
        index_a1 = base_y1 - base + x0
        index_c1 = base_y1 - base + x1

        # get 8 grid values
        value_a0 = input_feature_flat[index_a0.type(torch.int64)].to(device)
        value_c0 = input_feature_flat[index_c0.type(torch.int64)].to(device)
        value_a1 = input_feature_flat[index_a1.type(torch.int64)].to(device)
        value_c1 = input_feature_flat[index_c1.type(torch.int64)].to(device)

        # find 8 grid locations
        y0 = torch.floor(y).int()
        y1 = y0 + 1
        x0 = torch.floor(x).int()
        x1 = x0 + 1

        # clip out coordinates exceeding feature map volume
        y0 = torch.clamp(y0, zero, max_y + 1)
        y1 = torch.clamp(y1, zero, max_y + 1)
        x0 = torch.clamp(x0, zero, max_x + 1)
        x1 = torch.clamp(x1, zero, max_x + 1)

        x0_float = x0.float()
        x1_float = x1.float()
        y0_float = y0.float()
        y1_float = y1.float()

        vol_a0 = ((y1_float - y) * (x1_float - x)).unsqueeze(-1).to(device)
        vol_c0 = ((y1_float - y) * (x - x0_float)).unsqueeze(-1).to(device)
        vol_a1 = ((y - y0_float) * (x1_float - x)).unsqueeze(-1).to(device)
        vol_c1 = ((y - y0_float) * (x - x0_float)).unsqueeze(-1).to(device)

        outputs = (value_a0 * vol_a0 + value_c0 * vol_c0 + value_a1 * vol_a1 +
                   value_c1 * vol_c1)

        if self.morph == 0:
            outputs = outputs.reshape([
                self.num_batch,
                self.num_points * self.width,
                1 * self.height,
                self.num_channels,
            ])
            outputs = outputs.permute(0, 3, 1, 2)
        else:
            outputs = outputs.reshape([
                self.num_batch,
                1 * self.width,
                self.num_points * self.height,
                self.num_channels,
            ])
            outputs = outputs.permute(0, 3, 1, 2)
        return outputs

    def deform_conv(self, input, offset, if_offset):
        y, x = self._coordinate_map_3D(offset, if_offset)
        deformed_feature = self._bilinear_interpolate_3D(input, y, x)
        return deformed_feature

3.2 在YOlO v9中的添加教程

阅读YOLOv9添加模块教程或使用下文操作

        1. 将YOLOv9工程中models下common.py文件中的最下行(否则可能因类继承报错)增加模块的代码。

         2. 将YOLOv9工程中models下yolo.py文件中的第681行(可能因版本变化而变化)增加以下代码。

            RepNCSPELAN4, SPPELAN, RepNCSPELAN4DySnakeConv}:

3.3 运行配置文件

# YOLOv9
# Powered bu https://blog.csdn.net/StopAndGoyyy

# parameters
nc: 80  # number of classes
#depth_multiple: 0.33  # model depth multiple
depth_multiple: 1  # model depth multiple
#width_multiple: 0.25  # layer channel multiple
width_multiple: 1  # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()

# anchors
anchors: 3

# YOLOv9 backbone
backbone:
  [
   [-1, 1, Silence, []],  
   
   # conv down
   [-1, 1, Conv, [64, 3, 2]],  # 1-P1/2

   # conv down
   [-1, 1, Conv, [128, 3, 2]],  # 2-P2/4

   # elan-1 block
   [-1, 1, RepNCSPELAN4DySnakeConv, [256, 128, 64, 1]],  # 3

   # avg-conv down
   [-1, 1, ADown, [256]],  # 4-P3/8

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 256, 128, 1]],  # 5

   # avg-conv down
   [-1, 1, ADown, [512]],  # 6-P4/16

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 7

   # avg-conv down
   [-1, 1, ADown, [512]],  # 8-P5/32

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 9
  ]

# YOLOv9 head
head:
  [
   # elan-spp block
   [-1, 1, SPPELAN, [512, 256]],  # 10

   # up-concat merge
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 7], 1, Concat, [1]],  # cat backbone P4

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 13

   # up-concat merge
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 5], 1, Concat, [1]],  # cat backbone P3

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [256, 256, 128, 1]],  # 16 (P3/8-small)

   # avg-conv-down merge
   [-1, 1, ADown, [256]],
   [[-1, 13], 1, Concat, [1]],  # cat head P4

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 19 (P4/16-medium)

   # avg-conv-down merge
   [-1, 1, ADown, [512]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 22 (P5/32-large)
   
   
   # multi-level reversible auxiliary branch
   
   # routing
   [5, 1, CBLinear, [[256]]], # 23
   [7, 1, CBLinear, [[256, 512]]], # 24
   [9, 1, CBLinear, [[256, 512, 512]]], # 25
   
   # conv down
   [0, 1, Conv, [64, 3, 2]],  # 26-P1/2

   # conv down
   [-1, 1, Conv, [128, 3, 2]],  # 27-P2/4

   # elan-1 block
   [-1, 1, RepNCSPELAN4, [256, 128, 64, 1]],  # 28

   # avg-conv down fuse
   [-1, 1, ADown, [256]],  # 29-P3/8
   [[23, 24, 25, -1], 1, CBFuse, [[0, 0, 0]]], # 30  

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 256, 128, 1]],  # 31

   # avg-conv down fuse
   [-1, 1, ADown, [512]],  # 32-P4/16
   [[24, 25, -1], 1, CBFuse, [[1, 1]]], # 33 

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 34

   # avg-conv down fuse
   [-1, 1, ADown, [512]],  # 35-P5/32
   [[25, -1], 1, CBFuse, [[2]]], # 36

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 37
   
   
   
   # detection head

   # detect
   [[31, 34, 37, 16, 19, 22], 1, DualDDetect, [nc]],  # DualDDetect(A3, A4, A5, P3, P4, P5)
  ]

3.4 训练过程


欢迎关注!


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

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

相关文章

【C语言】动态内存管理------常见错误,以及经典笔试题分析,柔性数组【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】动态内存管理------常见错误&#xff0c;以及经典笔试题分析&#xff0c;柔性数组【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 在了解完内存操作中最关键的一节---动…

怎样裁剪视频上下多余的部分?分享3个裁剪的工具!

在数字时代&#xff0c;视频已成为我们生活中不可或缺的一部分。无论是观看电影、制作个人vlog&#xff0c;还是进行专业的视频编辑&#xff0c;我们时常会遇到需要裁剪视频上下多余部分的情况。那么&#xff0c;如何进行视频裁剪呢&#xff1f;本文将为您详细介绍几种常用的视…

day46_Servlet

今日内容 0 复习昨日 1 Servlet基础 1.1 Servlet介绍 1.2 第一个Servlet 1.3 流程分析 1.4 使用细节 1.5 映射细节 1.6 生命周期 2 HttpServlet 2.1 HTTP请求、响应、状态码 2.2 GET和POST的区别 2.3 HttpServlet 0 复习昨日 1 maven创建-java项目结构 2 maven创建-javaweb项目…

16.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-设计数据发送结构实现更复杂的数据发送

上一个内容&#xff1a;15.发送通信数据包至分析工具 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;f691a6a12ab49a711713f8ccdc8dd712c05826e9 代码下载地址&#xff0c;在 titan 目录下&…

京东商品优惠券API获取商品到手价

item_get_app-获得JD商品详情原数据 公共参数 请求地址: jd/item_get_app 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,i…

Git 指令深入浅出【2】—— 分支管理

Git 指令深入浅出【2】—— 分支管理 分支管理1. 常用分支管理指令2. 合并分支合并冲突合并模式 3. 实战演习 分支管理 1. 常用分支管理指令 # 查看本地分支 git branch# 查看远程分支 git branch -r# 查看全部分支 git branch -aHEAD 指向的才是当前的工作分支 # 查看当前分…

源码的角度分析Vue2数据双向绑定原理

什么是双向绑定 我们先从单向绑定切入&#xff0c;其实单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xff0c;View就会自动更新。那么双向绑定就可以从此联想到&#xff0c;即在单向绑定的基础上&#xff0c;用户更新…

win中删除不掉的文件,火绒粉碎删除亲测有效

看网上的 win R 然后终端输入什么删除的&#xff0c;照做了都没有删掉 有火绒的可以试试&#xff1a; 拖进去就删掉了 很好使

开源项目_代码生成项目介绍

1 CodeGeeX 系列 1.1 CodeGeeX 项目地址&#xff1a;https://github.com/THUDM/CodeGeeX 7.6k Star主要由 Python 编写深度学习框架是 Mindspore代码约 2.5W 行有 Dockerfile&#xff0c;可在本地搭建环境模型大小为 150 亿参数相对早期的代码生成模型&#xff0c;开放全部代…

【PCL】 (十六)点云距离图可视化

&#xff08;十六&#xff09;点云距离图可视化 以下代码实现点云及其对应距离图的可视化。 数据样例&#xff1a;sphere100.pcd range_image_visualization.cpp #include <iostream>#include <pcl/range_image/range_image.h> #include <pcl/io/pcd_io.h&g…

CHI协议学习

原始文档&#xff1a;https://developer.arm.com/documentation/102407/0100/?langen CHI 总线拓扑结构 CHI总线拓扑是实现自定义的&#xff0c;可以是RING/MESH/CROSSBAR的类型&#xff1b; RING 一般适用于中等规模芯片MESH 一般适用于大规模芯片CROSSBAR 一般适用于小规模…

30天JS挑战(第十五天)------本地存储菜谱

第十五天挑战(本地存储菜谱) 地址&#xff1a;https://javascript30.com/ 所有内容均上传至gitee&#xff0c;答案不唯一&#xff0c;仅代表本人思路 中文详解&#xff1a;https://github.com/soyaine/JavaScript30 该详解是Soyaine及其团队整理编撰的&#xff0c;是对源代…

11.互信息-机器学习模型性能的常用的评估指标

互信息&#xff08;Mutual Information&#xff09;是机器学习中常用的一种评估指标&#xff0c;特别是在无监督学习和聚类分析中。它用于衡量两个随机变量之间的相关性或相似性。 定义 给定两个随机变量X和Y&#xff0c;它们的互信息I(X;Y)定义如下&#xff1a; 其中&…

命名空间(namespace)

定义 在C中&#xff0c;命名空间&#xff08;Namespace&#xff09;是一个特性&#xff0c;用于封装代码并避免名称冲突。命名空间可以看作是一个容器&#xff0c;其中可以包含类、函数、变量、常量、其他命名空间等。通过使用命名空间&#xff0c;我们可以更好地组织代码&…

linux gdb 调试工具

1.写程序 首先&#xff0c;我们先写出一个 .c 或者.cpp程序 如 然后 gcc -g hello.c -o hello 或者 g -g hello.cpp -o hello &#xff08;-g&#xff09;要加 2. gdb调试 用 gdb &#xff08;可执行程序&#xff0c;如hello&#xff09; 进入之后&#xff0c;有…

redis实战笔记汇总

文章目录 1 NoSQL入门概述1.1 能干嘛&#xff1f;1.2 传统RDBMS VS NOSQL1.3 NoSQL数据库的四大分类1.4 分布式数据库CAP原理 BASE原则1.5 分布式集群简介1.6 淘宝商品信息的存储方案 2 Redis入门概述2.1 是什么&#xff1f;2.2 能干嘛&#xff1f;2.3 怎么玩&#xff1f;核心…

《幻兽帕鲁》游戏对服务器性能的具体要求是什么?

《幻兽帕鲁》游戏对服务器性能的具体要求是什么&#xff1f; CPU&#xff1a;官方最低要求为i5-3570K&#xff0c;但在多人游玩时可能会有明显卡顿。此外&#xff0c;还有建议选择4核或更高性能的处理器&#xff0c;以确保游戏运行流畅。 内存&#xff1a;对于不同人数的联机&…

LL-34/DO-213AC/MiniMELF/NSMC/DO-213AB封装

最近在找几个特殊的二极管封装&#xff0c;能查到资料太少了&#xff0c;如同大海捞针&#xff0c;好不容易找到了一些资料&#xff0c;把相关信息总结一下. 1、LL-34/DO-213AC/MiniMELF/SOD80这三个封装尺寸很接近 LL-34以c5345992为例 MiniMELF以c131658为例 2、NSMC这个封装…

盘点3个正规靠谱的赚钱软件,作为副业,空闲时间发小财

随着移动互联网的蓬勃发展&#xff0c;手机成为了我们生活中不可或缺的一部分&#xff0c;更是赚钱的新工具。然而&#xff0c;面对琳琅满目的赚钱软件&#xff0c;如何挑选出那些既靠谱又正规的平台呢&#xff1f;接下来&#xff0c;我将为大家揭秘几款备受推崇的赚钱软件。 1…

20240304-1-操作系统

操作系统 知识体系 Questions 1.进程和线程的区别 进程是系统进行资源分配和调度的基本单位&#xff1b;线程是CPU调度和分派的基本单位。 每个进程都有独立的代码和数据空间&#xff08;程序上下文&#xff09;&#xff0c;程序之间的切换会有较大的开销&#xff1b;线程可…