YOLO26改进 - C3k2 C3k2融Decoder Block解码器块 ,去模糊和提升图像清晰度 CVPR 2025

0 阅读6分钟

前言

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

文章目录: YOLO26改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

专栏链接: YOLO26改进专栏

@[TOC]

介绍

image-20250806222621652

摘要

在夜间或昏暗环境下拍摄照片时,由于光线暗淡且常使用长曝光,照片通常会存在噪点、亮度低和模糊等问题。尽管在这些情况下,去模糊和低光图像增强(LLIE)这两项任务存在关联,但大多数图像恢复方法都是将它们分开处理的。 在本文中,我们提出了一种高效且稳健的神经网络,用于多任务低光图像恢复。不同于当前流行的基于Transformer的模型,我们设计了新的注意力机制,以增强高效卷积神经网络(CNNs)的感受野。与以往的方法相比,我们的方法在参数和乘加运算(MAC)方面降低了计算成本。我们的模型DarkIR在常用的LOLBlur、LOLv2和Real-LOLBlur数据集上取得了新的最先进结果,并且能够很好地适配真实世界中的夜间和昏暗环境图像。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

在DarkIR模型中,DBlock(解码器块)是负责图像去模糊、提升细节锐度并将低分辨率增强图像上采样至原尺寸的核心组件,它聚焦于空间域的特征处理,整体遵循Metaformer架构并针对去模糊任务做了定制化设计:

  1. 核心定位与输入输出

    • 定位:DBlock部署在模型的解码器阶段,其核心任务是基于编码器输出的低分辨率光照增强图像x^8\hat{x}_{↓8},完成三大目标:将低分辨率图像上采样至原尺寸、消除图像模糊(包括手持抖动、长曝光等导致的模糊)、提升图像边缘与细节的锐度,且无需再处理光照问题(光照已由编码器解决)。
    • 输入:编码器传递的光照增强深层特征,以及低分辨率图像x^8\hat{x}_{↓8}的深度表征;
    • 输出:空间维度恢复至原图大小、无模糊且细节清晰的高质量图像。
  2. 整体架构与计算流程 DBlock遵循Metaformer的基础结构,由**扩张空间注意力模块(Di-SpAM)门控前馈网络(GatedFFN)**两大核心组件构成,整体计算流程为残差连接形式: [z_1 = \text{Di-SpAM}(\text{LayerNorm}(z)) + z] [z_2 = \text{GatedFFN}(\text{LayerNorm}(z_1)) + z_1] 其中zz为输入特征,z2z_2为DBlock最终输出特征,LayerNorm用于特征归一化,残差连接则保证了特征信息的高效传递,避免梯度消失。

  3. 核心组件1:扩张空间注意力模块(Di-SpAM) 这是DBlock的核心创新模块,灵感来源于大核注意力(LKA),但通过多尺度扩张卷积实现了更高效的大感受野特征提取,具体设计如下:

    • 多尺度扩张卷积分支:采用3个不同扩张系数(dilation factor)的深度卷积(DW)分支,分别为dilation=1、dilation=4、dilation=9,可同时捕捉图像中近程、中程和远程的空间依赖关系,覆盖更大的特征区域,从而精准识别并消除不同范围的模糊。
    • 特征融合与优化:先将3个分支的特征进行逐元素融合,再通过**简化通道注意力(SCA)**对融合后的特征做通道维度的权重分配,强化有用的边缘、细节特征,抑制冗余信息。
    • 门控MLP:最后接入带简单门控机制的MLP(而非传统激活函数),进一步筛选和增强空间特征,保证去模糊后的特征更精准。
    • 优势:相比传统LKA,Di-SpAM在实现更大有效感受野的同时,参数数量和计算量(MACs)更低,且去模糊效果更优(PSNR可达27.00dB,高于LKA的26.45dB)。
  4. 核心组件2:门控前馈网络(GatedFFN) 该模块用于对Di-SpAM输出的特征做精细化的通道维度变换,其核心特点是引入门控机制

    • 先通过1×1卷积对特征做通道扩张与压缩,再利用门控单元对特征进行筛选,只保留对去模糊和锐化有帮助的特征信息,避免无效特征的干扰,提升特征表达的针对性。
  5. 与编码器的协同及上采样逻辑

    • DBlock会接收编码器传递的光照增强特征,并通过简单的逐元素加法完成编码器-解码器的特征融合(论文消融实验证明该方式比CurveNLU、1DLUT等复杂模块的融合效果更优);

    • 在解码器整体流程中,DBlock输出的特征会通过**像素重排(P-Shuffle)**和1×1卷积完成上采样,逐步将特征分辨率恢复至原图大小,最终输出清晰的高分辨率图像。

核心代码

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',
                )
    
 

结果

image-20260124105820427