赞
踩
主要用于管状结构分割的卷积,分割例如血管、道路等细长连续的结构。 蛇形卷积能够进行精确分割,主要的挑战源于细长微弱的局部结构特征与复杂多变的全局形态特征。所以模型在学习特征的过程中,改变卷积核的形状,从而关注管状结构的核心结构特点。由于管状结构所占比例较小,模型不可避免地失去对相应结构的感知,卷积核完全游离在目标以外。因此我们希望根据管状结构的特点来设计特定的网络结构,从而指导模型关注关键特征。
本文关注到管状结构细长连续的拓扑特征,并利用这一信息在神经网络以下三个阶段同时增强感知:特征提取、特征融合和损失约束。分别设计了动态蛇形卷积,多视角特征融合策略与连续性拓扑约束损失。我们同时给出了基于 2D 和 3D 的方法设计,通过实验证明了本文所提出的 DSCNet 在管状结构分割任务上提供了更好的精度和连续性。
论文地址:https://arxiv.org/abs/2307.08388
以下代码包括
DySnakeConv--单个的蛇形卷积 DSConv--蛇形卷积用到的偏置卷积 DSC--偏置卷积用到的模块
- class DySnakeConv(nn.Module):
- def __init__(self, inc, ouc, k=3, act=True) -> None:
- super().__init__()
-
- self.conv_0 = Conv(inc, ouc, k, act=act)
- self.conv_x = DSConv(inc, ouc, 0, k)
- self.conv_y = DSConv(inc, ouc, 1, k)
- self.conv_1x1 = Conv(ouc * 3, ouc, 1, act=act)
-
- def forward(self, x):
- return self.conv_1x1(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
DySnakeConv是卷积的一种,将完整代码放在卷积的路径中,我的路径是ultralytics/nn/modules/conv.py。
在Python代码中,__all__
是一个特殊的变量,用于指定一个模块中哪些对象(函数、类、变量等)应该被导出(即可以通过from 模块名 import *
导入),以及哪些对象应该被视为模块的公共接口。
- __all__ = [
- 'Conv', 'LightConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv', 'ChannelAttention',
- 'SpatialAttention', 'CBAM', 'Concat', 'RepConv',
- 'DCNv2','DiverseBranchBlock','DySnakeConv']
在路径ultralytics/nn/modules/__init__.py中加入DySnakeConv
- from .conv import (CBAM, ChannelAttention, Concat, Conv, ConvTranspose, DWConv, DWConvTranspose2d, Focus, GhostConv,
- LightConv, RepConv, SpatialAttention,
- DCNv2,DySnakeConv,
- )
在下方all中也加入'DySnakeConv'。
- __all__ = [
- 'Conv', 'LightConv', 'RepConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv','DySnakeConv',
- 'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat', 'TransformerLayer', 'TransformerBlock', 'MLPBlock',
- 'LayerNorm2d', 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost',
- 'GhostBottleneck', 'Bottleneck', 'BottleneckCSP', 'Proto', 'Detect', 'Segment', 'Pose', 'Classify',
- 'TransformerEncoderLayer', 'RepC3', 'RTDETRDecoder', 'AIFI', 'DeformableTransformerDecoder',
- 'DeformableTransformerDecoderLayer', 'MSDeformAttn', 'MLP','deattn',
- 'DCNv2','ODConv_3rd',
- 'C2f_DCN','C2f_DSConv','C2f_Biformer','DiverseBranchBlock','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','C2f_ODConv']
路径在ultralytics/nn/tasks.py,添加导入的名称DySnakeConv。
- from ultralytics.nn.modules import (AIFI, C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x,
- Classify, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Focus,
- GhostBottleneck, GhostConv, HGBlock, HGStem, Pose, RepC3, RepConv, RTDETRDecoder,
- DCNv2,
- DySnakeConv,
- Segment,CBAM,C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB, C2f_EMA,EVCBlock,
- ODConv_3rd)
在def parse_model(d, ch, verbose=True):中,找到并添加DySnakeConv。
- if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus,DCNv2,DySnakeConv,
- BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x, RepC3,BasicRFB_a,BasicRFB,
- C2f_DCN,C2f_DSConv,C2f_DBB,EVCBlock,BoT3, C2f_EMA,ODConv_3rd,C2f_Biformer):
由于DySnakeConv并不改变图像大小,没有步长设置为2的过程。所以选择添加一层,通道数设置和输入一样,卷积核大小就为3。YOLOv8的ymal中没有不改变图像大小的conv,所以是无法替换的。
- [-1, 1, DySnakeConv, [512, 3]]
- head:
- - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 40*40*512 w*r
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4 11 40*40*512 *(w+wr)
- - [-1, 3, C2f, [512]] # 12 # 40*40*512 *w
-
- - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 80*80*256 *w
- - [[-1, 4], 1, Concat, [1]] # cat backbone P3 80*80*512 *w
- - [-1, 3, C2f, [256]] # 15 (P3/8-small) 80*80*256 *w --Detect
-
- - [-1, 1, Conv, [256, 3, 2]] # 40*40*256 *w
- - [-1, 1, DySnakeConv, [256, 3]]
- - [[-1, 12], 1, Concat, [1]] # cat head P4 # 40*40*512 *w
- - [-1, 3, C2f, [512]] # 18 (P4/16-medium) # 40*40*512 *w --Detect
-
- - [-1, 1, Conv, [512, 3, 2]] # 20*20*512 *w
- - [[-1, 9], 1, Concat, [1]] # cat head P5 # 20*20*512 *(w+wr)
- - [-1, 3, C2f, [1024]] # 21 (P5/32-large) # 20*20*512 *w --Detect
-
- - [[15, 18, 22], 1, Detect, [nc]] # Detect(P3=80*80*256 *w, P4=40*40*512 *w, P5=20*20*512)
将DySnakeConv融入Bottleneck形成Bottleneck_DySnakeConv,在作为C2F的模型形成C2F_DySnakeConv。
和其他卷积一样,作为模型的子模块导入。
from .conv import Conv, DWConv, GhostConv, LightConv, RepConv,DCNv2,DySnakeConv
- class Bottleneck_DySnakeConv(nn.Module):
- # Standard bottleneck with DCN
- def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5): # ch_in, ch_out, shortcut-残差连接, groups, kernels, expand
- super().__init__()
- c_ = int(c2 * e) # hidden channels
- if k[0] == 3:
- self.cv1 = DySnakeConv(c1, c_, k[0], 1)
- else:
- self.cv1 = Conv(c1, c_, k[0], 1) #self.cv2 = DySnakeConv(c_, c2, 3)
- if k[1] == 3:
- self.cv2 = DySnakeConv(c_, c2, k[1])
- else:
- self.cv2 = Conv(c_, c2, k[1], 1, g=g)
- self.add = shortcut and c1 == c2 #如果残差连接以及通道数等
-
- def forward(self, x):
- return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
- class C2f_DySnakeConv(nn.Module):
- # CSP Bottleneck with 2 convolutions #两个卷积的梯度流
- def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
- super().__init__()
- self.c = int(c2 * e) # hidden channels
- self.cv1 = Conv(c1, 2 * self.c, 1, 1)
- self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
- self.m = nn.ModuleList(Bottleneck_DySnakeConv(self.c, self.c, shortcut, g, k=(3, 3), e=1.0) for _ in range(n)) #Bottleneck
-
- def forward(self, x):
- y = list(self.cv1(x).split((self.c, self.c), 1)) #先进行卷积 在进行切分
- y.extend(m(y[-1]) for m in self.m)
- return self.cv2(torch.cat(y, 1))
all路径在同一个脚本中
- __all__ = [
- 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost', 'GhostBottleneck',
- 'Bottleneck', 'BottleneckCSP', 'Proto', 'RepC3',
- 'C2f_DCN','C2f_DSConv','C2f_Biformer','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','ODConv_3rd','C2f_DySnakeConv']
C2f_DySnakeConv是一个模块,在block中
- from .block import (C1, C2, C3, C3TR, DFL, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x, GhostBottleneck,
- HGBlock, HGStem, Proto, RepC3,
- C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB,Bottleneck_DBB,EVCBlock, C2f_EMA,ODConv_3rd,C2f_DySnakeConv)#自己加的
在下面all中也添加
- __all__ = [
- 'Conv', 'LightConv', 'RepConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv','DySnakeConv',
- 'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat', 'TransformerLayer', 'TransformerBlock', 'MLPBlock',
- 'LayerNorm2d', 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost',
- 'GhostBottleneck', 'Bottleneck', 'BottleneckCSP', 'Proto', 'Detect', 'Segment', 'Pose', 'Classify',
- 'TransformerEncoderLayer', 'RepC3', 'RTDETRDecoder', 'AIFI', 'DeformableTransformerDecoder',
- 'DeformableTransformerDecoderLayer', 'MSDeformAttn', 'MLP','deattn',
- 'DCNv2','ODConv_3rd',
- 'C2f_DCN','C2f_DSConv','C2f_Biformer','DiverseBranchBlock','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','C2f_ODConv','C2f_DySnakeConv']
路径在ultralytics/nn/tasks.py。
从moulde中导入C2f_DySnakeConv
- from ultralytics.nn.modules import (AIFI, C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x,
- Classify, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Focus,
- GhostBottleneck, GhostConv, HGBlock, HGStem, Pose, RepC3, RepConv, RTDETRDecoder,
- DCNv2,
- DySnakeConv,C2f_DySnakeConv,
- Segment,CBAM,C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB, C2f_EMA,EVCBlock,
- ODConv_3rd)
在解析参数-def parse_model(d, ch, verbose=True):中添加
- if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus,DCNv2,DySnakeConv,
- BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x, RepC3,BasicRFB_a,BasicRFB,
- C2f_DCN,C2f_DSConv,C2f_DBB,EVCBlock,BoT3, C2f_EMA,ODConv_3rd,C2f_Biformer,C2f_DySnakeConv):
C2f_DySnakeConv多次使用,下面哪个也添加
- if m in (BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, C3x, RepC3,
- C2f_DCN,C2f_DSConv,C2f_DBB,BoT3,C2f_EMA,C2f_Biformer,C2f_DySnakeConv):#增加block
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。