























本文介绍了用于多任务低光图像恢复的DarkIR模型中的DBlock,并将其集成到YOLO26。DBlock部署在模型解码器阶段,负责图像去模糊、提升细节锐度及上采样。它遵循Metaformer架构,由扩张空间注意力模块(Di - SpAM)和门控前馈网络(GatedFFN)构成。Di - SpAM通过多尺度扩张卷积高效提取大感受野特征,GatedFFN引入门控机制筛选特征。DBlock与编码器协同,通过简单加法融合特征并上采样。我们将DBlock集成到YOLO26,经注册和配置yaml文件后进行实验,展现出良好效果。
文章目录: YOLO26改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总
专栏链接: YOLO26改进专栏

在夜间或昏暗环境下拍摄照片时,由于光线暗淡且常使用长曝光,照片通常会存在噪点、亮度低和模糊等问题。尽管在这些情况下,去模糊和低光图像增强(LLIE)这两项任务存在关联,但大多数图像恢复方法都是将它们分开处理的。 在本文中,我们提出了一种高效且稳健的神经网络,用于多任务低光图像恢复。不同于当前流行的基于Transformer的模型,我们设计了新的注意力机制,以增强高效卷积神经网络(CNNs)的感受野。与以往的方法相比,我们的方法在参数和乘加运算(MAC)方面降低了计算成本。我们的模型DarkIR在常用的LOLBlur、LOLv2和Real-LOLBlur数据集上取得了新的最先进结果,并且能够很好地适配真实世界中的夜间和昏暗环境图像。
论文地址:论文地址
代码地址:代码地址
在DarkIR模型中,DBlock(解码器块)是负责图像去模糊、提升细节锐度并将低分辨率增强图像上采样至原尺寸的核心组件,它聚焦于空间域的特征处理,整体遵循Metaformer架构并针对去模糊任务做了定制化设计:
核心定位与输入输出
整体架构与计算流程 DBlock遵循Metaformer的基础结构,由扩张空间注意力模块(Di-SpAM)和门控前馈网络(GatedFFN)两大核心组件构成,整体计算流程为残差连接形式: [z_1 = \text{Di-SpAM}(\text{LayerNorm}(z)) + z] [z_2 = \text{GatedFFN}(\text{LayerNorm}(z_1)) + z_1] 其中$z$为输入特征,$z_2$为DBlock最终输出特征,LayerNorm用于特征归一化,残差连接则保证了特征信息的高效传递,避免梯度消失。
核心组件1:扩张空间注意力模块(Di-SpAM) 这是DBlock的核心创新模块,灵感来源于大核注意力(LKA),但通过多尺度扩张卷积实现了更高效的大感受野特征提取,具体设计如下:
核心组件2:门控前馈网络(GatedFFN) 该模块用于对Di-SpAM输出的特征做精细化的通道维度变换,其核心特点是引入门控机制:
与编码器的协同及上采样逻辑
class DBlock(nn.Module):
'''
Change this block using Branch
'''
def __init__(self, c, DW_Expand=2, FFN_Expand=2, dilations = [1], extra_depth_wise = False):
super().__init__()
#we define the 2 branches
self.dw_channel = DW_Expand * c
self.conv1 = nn.Conv2d(in_channels=c, out_channels=self.dw_channel, kernel_size=1, padding=0, stride=1, groups=1, bias=True, dilation = 1)
self.extra_conv = nn.Conv2d(self.dw_channel, self.dw_channel, kernel_size=3, padding=1, stride=1, groups=c, bias=True, dilation=1) if extra_depth_wise else nn.Identity() #optional extra dw
self.branches = nn.ModuleList()
for dilation in dilations:
self.branches.append(Branch(self.dw_channel, DW_Expand = 1, dilation = dilation))
assert len(dilations) == len(self.branches)
self.dw_channel = DW_Expand * c
self.sca = nn.Sequential(
nn.AdaptiveAvgPool2d(1),
nn.Conv2d(in_channels=self.dw_channel // 2, out_channels=self.dw_channel // 2, kernel_size=1, padding=0, stride=1,
groups=1, bias=True, dilation = 1),
)
self.sg1 = SimpleGate()
self.sg2 = SimpleGate()
self.conv3 = nn.Conv2d(in_channels=self.dw_channel // 2, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True, dilation = 1)
ffn_channel = FFN_Expand * c
self.conv4 = nn.Conv2d(in_channels=c, out_channels=ffn_channel, kernel_size=1, padding=0, stride=1, groups=1, bias=True)
self.conv5 = nn.Conv2d(in_channels=ffn_channel // 2, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True)
self.norm1 = LayerNorm2d(c)
self.norm2 = LayerNorm2d(c)
self.gamma = nn.Parameter(torch.zeros((1, c, 1, 1)), requires_grad=True)
self.beta = nn.Parameter(torch.zeros((1, c, 1, 1)), requires_grad=True)
def forward(self, inp, adapter = None):
y = inp
x = self.norm1(inp)
# x = self.conv1(self.extra_conv(x))
x = self.extra_conv(self.conv1(x))
z = 0
for branch in self.branches:
z += branch(x)
z = self.sg1(z)
x = self.sca(z) * z
x = self.conv3(x)
y = inp + self.beta * x
#second step
x = self.conv4(self.norm2(y)) # size [B, 2*C, H, W]
x = self.sg2(x) # size [B, C, H, W]
x = self.conv5(x) # size [B, C, H, W]
x = y + x * self.gamma
return x
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
# 修改为自己的配置文件地址
model = YOLO('./ultralytics/cfg/models/26/yolo26-C3k2_DBlock.yaml')
# 修改为自己的数据集地址
model.train(data='./ultralytics/cfg/datasets/coco8.yaml',
cache=False,
imgsz=640,
epochs=10,
single_cls=False, # 是否是单类别检测
batch=8,
close_mosaic=10,
workers=0,
optimizer='MuSGD',
# optimizer='SGD',
amp=False,
project='runs/train',
name='yolo26-C3k2_DBlock',
)

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。