即插即用系列 | TMM 2026 | FDFAM:频域特征聚合!跨模态互补与频域解耦,多模态融合更高效! | 代码分享
0. 前言本文介绍了LFEMLocal Feature Enhancement Module局部特征增强模块其通过并行融合标准卷积、空洞卷积、可变形卷积与深度可分离卷积四种异构卷积架构并结合通道洗牌与残差强化策略首次在目标检测领域实现对多尺度局部特征的精细化协同建模有效破解了传统单一卷积难以同时捕捉边缘细节与大范围上下文特征的难题。将其作为即插即用模块轻松助力CNN、YOLO、Transformer等深度学习模型增强多尺度局部感知能力、提升跨通道信息交互效率让模型在面对航拍小目标、红外热成像、工业缺陷检测及密集遮挡场景时依然能够保持精准的边界定位与稳定的检测精度。专栏链接即插即用系列专栏链接可点击跳转免费订阅目录0. 前言1. LFEM模块简介2. LFEM模块原理与创新点 LFEM模块基本原理 LFEM模块创新点3. 适用范围与模块效果适用范围⚡模块效果4. LFEM模块代码实现1. LFEM模块简介本文提出一种局部特征增强模块Local Feature Enhancement Module, LFEM用于可见光-红外目标检测任务中的多模态局部特征强化。LFEM首先采用1×1卷积层结合批归一化与SiLU激活函数来提升非线性表达能力而不改变特征图尺寸。随后模块并行使用标准3×3卷积、空洞卷积、可变形卷积和深度可分离卷积四种不同感受野和采样方式的卷积层以捕捉不同区域和尺度的局部特征信息。通过这些异构卷积层的协同建模LFEM能够从多个维度挖掘图像中的边缘、纹理及结构细节。之后采用拼接操作融合多分支提取的特征并通过通道洗牌Channel Shuffle操作重新排列特征通道增强跨通道信息交互与模型表达能力。最后通过1×1卷积进行特征降维并引入残差连接将初始特征与增强特征相加实现局部特征的精细化增强。大量实验证明集成LFEM的可见光-红外目标检测框架在FLIR、LLVIP和M³FD等多个公开数据集上取得了优异的检测性能。原始论文https://arxiv.org/pdf/2511.10046原始代码https://github.com/WenCongWu/FreDFT2. LFEM模块原理与创新点 LFEM模块基本原理LFEM局部特征增强模块的核心设计思想是通过多分支异构卷积架构实现对局部特征的多尺度、多形态感知与增强。传统卷积模块通常采用单一类型和尺度的卷积核进行特征提取这限制了模型对复杂场景中不同形状、不同尺度目标的适应能力。LFEM通过并行引入多种具有不同采样策略和感受野的卷积层使模型能够同时捕捉目标边缘、纹理、结构等多层次的局部信息从而为后续的检测任务提供更丰富且判别性更强的特征表示。1多分支异构卷积并行处理LFEM首先对输入特征图进行1×1卷积的通道变换与非线性激活随后将特征同时馈入四个并行的卷积分支标准3×3卷积用于常规局部特征提取空洞卷积通过增大感受野捕捉更大范围的上下文信息可变形卷积根据目标形状自适应调整采样位置增强对形变目标的感知能力深度可分离卷积则以极低的计算代价提取基础局部特征。这四个分支从不同角度对局部信息进行建模形成互补的特征表达。2多分支特征融合与通道重组四个卷积分支输出的特征图通过拼接操作在通道维度上进行融合得到包含多种局部信息的复合特征。随后LFEM引入通道洗牌操作通过将不同分支的通道进行交织重排促进跨分支、跨通道的信息交互。这一机制能够有效打破分组卷积带来的通道间隔离使各分支学习到的互补特征充分混合提升模型的整体表达能力。3残差增强与特征保持在完成多分支特征提取与通道重组后LFEM通过1×1卷积将特征通道数恢复至原始维度并采用残差连接将增强后的特征与输入特征进行逐元素相加。这种设计既保证了梯度流动的顺畅性又使模块能够专注于学习输入与输出之间的残差增量避免深层网络中特征退化的风险同时保留了原始特征的完整性。 LFEM模块创新点创新维度具体内容多分支异构卷积架构首次在同一模块中并行集成标准卷积、空洞卷积、可变形卷积与深度可分离卷积实现多尺度、多形态局部特征的协同建模通道重组增强机制引入通道洗牌操作通过跨分支通道交织增强特征通道间的信息流动有效提升多分支融合特征的表达能力轻量化残差设计采用深度可分离卷积控制计算开销结合残差连接实现梯度顺畅传播与特征完整性保持即插即用模块化模块化设计使其可无缝嵌入主流目标检测框架的C3k2等特征提取模块中具有良好的通用性与迁移性3. 适用范围与模块效果适用范围LFEM模块适用于通用视觉领域特别是需要增强局部特征感知能力、处理多尺度目标或应对复杂场景干扰的视觉任务。具体适用场景包括但不限于可见光-红外多模态目标检测LFEM专门针对RGB与IR模态的差异化特征进行设计通过异构卷积同时建模纹理细节与热结构信息有效缓解跨模态特征失衡问题航拍与遥感图像分析航拍图像中目标尺度变化大、背景复杂LFEM的多分支感受野设计能够同时捕捉大范围地理特征与细小目标纹理适用于道路提取、车辆检测、建筑物识别等任务工业缺陷质检工业零件表面的裂纹、划痕等缺陷尺度小且形态多样LFEM的可变形卷积能够自适应学习不规则缺陷的几何特征提升检测精度与鲁棒性边缘端实时部署LFEM采用深度可分离卷积控制计算开销模块轻量化程度高可在计算资源受限的边缘设备上实现实时推理满足工业产线与移动端应用的性能需求小目标与密集遮挡场景通过多分支与通道重组机制LFEM增强了模型对弱小目标的特征响应能力有效降低密集遮挡场景下的漏检率与误检率⚡模块效果模块效果性能和融合效果SOTA。消融实验第二行和第三行对比使用LFEM后涨点说明LFEM的有效性。总结LFEM模块的消融实验表明单独引入LFEM可使mAP50从81.7%提升至82.8%提升1.1个百分点而当LFEM与CGMM、FDFAM三者协同工作时模型达到最佳检测性能83.5% mAP50充分验证了LFEM在多模态局部特征增强中的核心作用与不可替代性。4. LFEM模块代码实现以下为LFEM模块的官方pytorch实现代码 LFEM Module - Local Feature Enhancement Module Local Feature Enhancement Module for multi-scale local feature modeling with multi-branch convolutions including standard convolution, dilated convolution, deformable convolution, and depthwise separable convolution, with channel shuffle. 单模态自增强模式用于单特征输入通过多分支多感受野卷积增强局部特征表达 双模态模式分别处理RGB和IR特征实现各模态的局部特征增强 Input modes: Single mode: [B, C, H, W] - [B, C, H, W] Dual mode: [rgb, ir] - [rgb_enhanced, ir_enhanced] import torch import torch.nn as nn import torch.nn.functional as F def autopad(k, pNone, d1): 自动计算padding保证卷积输出尺寸与输入一致 if d 1: k d * (k - 1) 1 if isinstance(k, int) else [d * (x - 1) 1 for x in k] if p is None: p k // 2 if isinstance(k, int) else [x // 2 for x in k] return p class Conv(nn.Module): 标准CBS卷积块Conv BatchNorm SiLU default_act nn.SiLU() def __init__(self, c1, c2, k1, s1, pNone, g1, d1, actTrue): super().__init__() self.conv nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groupsg, dilationd, biasFalse) self.bn nn.BatchNorm2d(c2) self.act self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity() def forward(self, x): return self.act(self.bn(self.conv(x))) def channel_shuffle(x, groups): 通道混洗操作实现跨分支的特征信息交互打破通道冗余 核心功能打乱通道顺序让不同分支的特征充分交互 batch_size, num_channels, height, width x.size() channels_per_group num_channels // groups # 通道分组-转置-展平实现混洗 x x.view(batch_size, groups, channels_per_group, height, width) x torch.transpose(x, 1, 2).contiguous() x x.view(batch_size, -1, height, width) return x class DeformConv2d(nn.Module): 带调制的可变形卷积V2Deformable ConvNets v2 核心功能自适应学习卷积核的采样偏移精准捕捉形变目标、不规则边缘的局部特征 def __init__(self, inc, outc, kernel_size3, padding1, stride1, biasNone, modulationFalse): super(DeformConv2d, self).__init__() self.kernel_size kernel_size self.padding padding self.stride stride self.zero_padding nn.ZeroPad2d(padding) # 主卷积层对偏移后的特征做卷积 self.conv nn.Conv2d(inc, outc, kernel_sizekernel_size, stridekernel_size, biasbias) # 偏移量预测卷积输出每个采样点的x/y偏移 self.p_conv nn.Conv2d(inc, 2 * kernel_size * kernel_size, kernel_size3, padding1, stridestride) nn.init.constant_(self.p_conv.weight, 0) self.p_conv.register_full_backward_hook(self._set_lr) # 调制门控V2核心学习每个采样点的权重 self.modulation modulation if modulation: self.m_conv nn.Conv2d(inc, kernel_size * kernel_size, kernel_size3, padding1, stridestride) nn.init.constant_(self.m_conv.weight, 0) self.m_conv.register_full_backward_hook(self._set_lr) staticmethod def _set_lr(module, grad_input, grad_output): 偏移量学习率缩放保证训练稳定性 if grad_input is not None: grad_input (grad_input[i] * 0.1 if grad_input[i] is not None else None for i in range(len(grad_input))) if grad_output is not None: grad_output (grad_output[i] * 0.1 if grad_output[i] is not None else None for i in range(len(grad_output))) def forward(self, x): # 预测采样偏移量 offset self.p_conv(x) # 预测调制权重V2 if self.modulation: m torch.sigmoid(self.m_conv(x)) dtype offset.data.type() ks self.kernel_size N offset.size(1) // 2 if self.padding: x self.zero_padding(x) # 计算最终采样坐标 p self._get_p(offset, dtype) p p.contiguous().permute(0, 2, 3, 1) # 双线性插值的四个邻域坐标 q_lt p.detach().floor() q_rb q_lt 1 q_lt torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2) - 1), torch.clamp(q_lt[..., N:], 0, x.size(3) - 1)], dim-1).long() q_rb torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2) - 1), torch.clamp(q_rb[..., N:], 0, x.size(3) - 1)], dim-1).long() q_lb torch.cat([q_lt[..., :N], q_rb[..., N:]], dim-1) q_rt torch.cat([q_rb[..., :N], q_lt[..., N:]], dim-1) # 裁剪坐标避免越界 p torch.cat([torch.clamp(p[..., :N], 0, x.size(2) - 1), torch.clamp(p[..., N:], 0, x.size(3) - 1)], dim-1) # 双线性插值核计算 g_lt (1 (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 (q_lt[..., N:].type_as(p) - p[..., N:])) g_rb (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:])) g_lb (1 (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:])) g_rt (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 (q_rt[..., N:].type_as(p) - p[..., N:])) # 获取四个邻域的特征值 x_q_lt self._get_x_q(x, q_lt, N) x_q_rb self._get_x_q(x, q_rb, N) x_q_lb self._get_x_q(x, q_lb, N) x_q_rt self._get_x_q(x, q_rt, N) # 双线性插值聚合特征 x_offset g_lt.unsqueeze(dim1) * x_q_lt \ g_rb.unsqueeze(dim1) * x_q_rb \ g_lb.unsqueeze(dim1) * x_q_lb \ g_rt.unsqueeze(dim1) * x_q_rt # 调制权重加权V2核心 if self.modulation: m m.contiguous().permute(0, 2, 3, 1) m m.unsqueeze(dim1) m torch.cat([m for _ in range(x_offset.size(1))], dim1) x_offset * m # 特征维度重整适配主卷积 x_offset self._reshape_x_offset(x_offset, ks) out self.conv(x_offset) return out def _get_p_n(self, N, dtype): 生成卷积核的基准采样坐标 p_n_x, p_n_y torch.meshgrid( torch.arange(-(self.kernel_size - 1) // 2, (self.kernel_size - 1) // 2 1), torch.arange(-(self.kernel_size - 1) // 2, (self.kernel_size - 1) // 2 1), indexingij ) p_n torch.cat([torch.flatten(p_n_x), torch.flatten(p_n_y)], 0) p_n p_n.view(1, 2 * N, 1, 1).type(dtype) return p_n def _get_p_0(self, h, w, N, dtype): 生成每个像素的基准中心坐标 p_0_x, p_0_y torch.meshgrid( torch.arange(1, h * self.stride 1, self.stride), torch.arange(1, w * self.stride 1, self.stride), indexingij ) p_0_x torch.flatten(p_0_x).view(1, 1, h, w).repeat(1, N, 1, 1) p_0_y torch.flatten(p_0_y).view(1, 1, h, w).repeat(1, N, 1, 1) p_0 torch.cat([p_0_x, p_0_y], 1).type(dtype) return p_0 def _get_p(self, offset, dtype): 生成最终采样坐标 基准坐标 核偏移 学习到的偏移量 N, h, w offset.size(1) // 2, offset.size(2), offset.size(3) p_n self._get_p_n(N, dtype) p_0 self._get_p_0(h, w, N, dtype) p p_0 p_n offset return p def _get_x_q(self, x, q, N): 根据采样坐标获取对应特征 b, h, w, _ q.size() padded_w x.size(3) c x.size(1) x x.contiguous().view(b, c, -1) index q[..., :N] * padded_w q[..., N:] index index.contiguous().unsqueeze(dim1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1) x_offset x.gather(dim-1, indexindex).contiguous().view(b, c, h, w, N) return x_offset staticmethod def _reshape_x_offset(x_offset, ks): 重整偏移特征适配卷积输入格式 b, c, h, w, N x_offset.size() x_offset torch.cat([x_offset[..., s:s ks].contiguous().view(b, c, h, w * ks) for s in range(0, N, ks)], dim-1) x_offset x_offset.contiguous().view(b, c, h * ks, w * ks) return x_offset class LFEMModule(nn.Module): LFEM核心模块局部特征增强 通过多分支卷积提取多尺度、形变、细节特征实现局部特征的全面增强 def __init__(self, channels: int): super().__init__() self.channels channels # 输入1×1卷积调整通道、降低计算量 self.CBSk1 Conv(channels, channels, 1, 1) # 分支1标准3×3卷积捕捉常规局部特征 self.CBSk3 Conv(channels, channels, 3, 1) # 分支2空洞卷积D-Conv扩大感受野捕捉大尺度/上下文特征 self.dconv nn.Sequential( nn.Conv2d(channels, channels, kernel_size3, stride1, padding3, dilation3, biasFalse), nn.BatchNorm2d(channels), nn.SiLU() ) # 分支3可变形卷积Df-Conv捕捉形变目标、不规则边缘特征 self.dfconv DeformConv2d(channels, channels, kernel_size3, padding1, biasFalse, modulationTrue) self.bn nn.BatchNorm2d(channels) self.silu nn.SiLU() # 分支4深度可分离卷积Dw-Conv轻量化捕捉细节特征 self.dwconv nn.Sequential( nn.Conv2d(channels, channels, kernel_size3, stride1, padding1, groupschannels, biasFalse), nn.BatchNorm2d(channels), nn.SiLU() ) # 特征融合1×1卷积将多分支特征融合为原始通道数 self.CBS4C Conv(4 * channels, channels, 1, 1) def forward(self, x: torch.Tensor) - torch.Tensor: 单模态特征增强 # 步骤11×1卷积预处理 fea0 self.CBSk1(x) # 步骤2四分支并行特征提取 fea1 self.CBSk3(fea0) # 标准3×3卷积分支 fea2 self.dconv(fea0) # 空洞卷积分支 fea3 self.silu(self.bn(self.dfconv(fea0))) # 可变形卷积分支 fea4 self.dwconv(fea0) # 深度可分离卷积分支 # 步骤3多分支特征拼接 fea_cat torch.cat([fea1, fea2, fea3, fea4], dim1) # 步骤4通道混洗实现跨分支特征交互 fea_cat channel_shuffle(fea_cat, 32) # 步骤51×1卷积融合多分支特征 new_fea self.CBS4C(fea_cat) # 步骤6残差连接 return new_fea x class LFEM(nn.Module): LFEM (Local Feature Enhancement Module) 的封装类 兼容YOLO的解析器 单输入模式自融合增强输出尺寸与输入相同 双输入模式分别处理两个模态输出两个增强后的特征 Args: c1: 输入通道数第一个输入特征 c2: 第二个输入通道数可选如果不提供则使用c1 *args: 可变位置参数兼容YOLO解析器 **kwargs: 可变关键字参数兼容YOLO解析器 Input modes: 单输入模式: x [B, C, H, W] - output [B, C, H, W] 双输入模式: x [B, C, H, W], y [B, C, H, W] - (rgb_out, ir_out) def __init__(self, c1, c2None, *args, **kwargs): super().__init__() # 处理通道参数 if c2 is None: c2 c1 self.in_channels c1 self.lfem_core LFEMModule(c1) # 可选的特征对齐层当两个输入通道不一致时使用 self.align_rgb nn.Conv2d(c1, c1, 1) if c1 ! c2 else None self.align_ir nn.Conv2d(c2, c1, 1) if c1 ! c2 else None # 双模态模式下的第二个特征处理分支通道不一致时或者需要独立分支时使用 # 注意当c1 ! c2时需要为第二个模态创建独立的LFEM核心 self.use_dual_core False if kwargs.get(dual_core, False) or c1 ! c2: self.use_dual_core True self.lfem_core_ir LFEMModule(c2 if c1 ! c2 else c1) def _single_modal_forward(self, x: torch.Tensor) - torch.Tensor: 单模态前向传播 return self.lfem_core(x) def _dual_modal_forward(self, x: torch.Tensor, y: torch.Tensor) - tuple: 双模态前向传播 # 处理通道对齐 if self.align_rgb is not None: x self.align_rgb(x) y self.align_ir(y) # 特征增强 if self.use_dual_core: # 使用独立的LFEM核心分别处理两个模态 out_x self.lfem_core(x) out_y self.lfem_core_ir(y) else: # 共享同一个LFEM核心通道数相同时可共享 out_x self.lfem_core(x) out_y self.lfem_core(y) return out_x, out_y def forward(self, x, yNone): 前向传播 Args: x: 单模态模式 - 主特征 [B, C, H, W] 双模态模式 - RGB特征 [B, C, H, W] y: 双模态模式 - IR特征 [B, C, H, W] (可选) Returns: Single mode: 增强后的特征 [B, C, H, W] Dual mode: (rgb_enhanced, ir_enhanced) if y is not None: return self._dual_modal_forward(x, y) else: return self._single_modal_forward(x) class LFEMBlock(nn.Module): LFEM块用于单特征输入场景自增强 保持输入输出尺寸一致 适用于YOLO backbone中的即插即用模块 def __init__(self, c1, c2None, *args, **kwargs): super().__init__() if c2 is None: c2 c1 self.lfem LFEM(c1, c2, *args, **kwargs) def forward(self, x): return self.lfem(x) class LFEMDualBlock(nn.Module): LFEM双模态块用于RGBIR双特征输入场景 适用于多模态目标检测任务 def __init__(self, c1, c2None, *args, **kwargs): super().__init__() if c2 is None: c2 c1 self.lfem LFEM(c1, c2, *args, **kwargs) def forward(self, rgb, ir): Args: rgb: RGB特征 [B, C, H, W] ir: 红外特征 [B, C, H, W] Returns: (rgb_enhanced, ir_enhanced) return self.lfem(rgb, ir) # 测试代码 if __name__ __main__: device torch.device(cuda if torch.cuda.is_available() else cpu) print(f使用设备: {device}) print( * 60) print(测试LFEM模块 - 单输入模式自增强) print( * 60) # 测试单输入模式 model_single LFEM(64).to(device) print(LFEM单模块初始化成功) x_single torch.randn(1, 64, 32, 32).to(device) print(f输入特征: {x_single.shape}) y_single model_single(x_single) print(f输出特征: {y_single.shape}) print(f尺寸是否保持不变: {y_single.shape x_single.shape}) print() print( * 60) print(测试LFEM模块 - 双输入模式分别增强) print( * 60) # 测试双输入模式 model_dual LFEM(64, 64).to(device) rgb torch.randn(1, 64, 32, 32).to(device) ir torch.randn(1, 64, 32, 32).to(device) print(fRGB输入特征: {rgb.shape}) print(fIR输入特征: {ir.shape}) rgb_out, ir_out model_dual(rgb, ir) print(fRGB输出特征: {rgb_out.shape}) print(fIR输出特征: {ir_out.shape}) print(f输出尺寸保持不变: {rgb_out.shape rgb.shape}) print() print( * 60) print(测试LFEMBlock - 即插即用模块) print( * 60) # 测试即插即用模块 block LFEMBlock(64).to(device) x_block torch.randn(1, 64, 32, 32).to(device) y_block block(x_block) print(fLFEMBlock输入: {x_block.shape}) print(fLFEMBlock输出: {y_block.shape}) print() print( * 60) print(测试LFEMDualBlock - 双模态即插即用模块) print( * 60) # 测试双模态即插即用模块 dual_block LFEMDualBlock(64).to(device) rgb_test torch.randn(1, 64, 32, 32).to(device) ir_test torch.randn(1, 64, 32, 32).to(device) rgb_out, ir_out dual_block(rgb_test, ir_test) print(fLFEMDualBlock RGB输入: {rgb_test.shape}, 输出: {rgb_out.shape}) print(fLFEMDualBlock IR输入: {ir_test.shape}, 输出: {ir_out.shape}) print() # 参数统计 print( * 60) print(参数统计) print( * 60) print(f单模态参数量: {sum(p.numel() for p in model_single.parameters()):,}) print(f双模态参数量: {sum(p.numel() for p in model_dual.parameters()):,}) print(fLFEMBlock参数量: {sum(p.numel() for p in block.parameters()):,})结合自己的思路可将其即插即用至任何模型做结构创新设计该模块博主已成功嵌入至YOLO26模型中可订阅博主YOLO系列算法改进或YOLO26自研改进专栏YOLO系列算法改进专栏链接、YOLO26自研改进系列专栏