YOLOv11改进 – C3k2融合 _ C3k2融合EBlock(Encoder Block):低光增强编码器块,利用傅里叶信息增强低光图像

# 前言

本文介绍了用于多任务低光图像恢复的DarkIR模型中的EBlock在YOLOv11中的结合应用。EBlock由空间注意力模块(SpAM)和频域前馈网络(Fre - MLP)组成,通过处理图像的傅里叶域信息提升亮度,具有高效、针对性强和轻量等优势。我们将EBlock集成到YOLOv11的C3k2模块中,构建了C3k2_EBlock。该模块可增强模型在低光环境下的特征提取能力。实验证明,YOLOv11 - C3k2_EBlock在相关任务中表现良好。

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

专栏链接: YOLOv11改进专栏

介绍

摘要

在夜间或昏暗环境下进行照片拍摄时,因光线条件不佳且常采用长曝光方式,照片往往会出现噪点、亮度不足以及模糊等问题。尽管在此类情形下,去模糊与低光图像增强(LLIE)这两项任务存在一定关联,但多数图像恢复方法仍将二者分开处理。

本文提出了一种高效且稳健的神经网络,用于多任务低光图像恢复。与当前流行的基于Transformer的模型不同,本研究设计了新型注意力机制,以增强高效卷积神经网络(CNNs)的感受野。相较于以往方法,该方法在参数和乘加运算(MAC)方面降低了计算成本。所提出的模型DarkIR在常用的LOLBlur、LOLv2和Real - LOLBlur数据集上取得了新的最优结果,且能较好地适用于真实世界中的夜间及昏暗环境图像。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

在文章所提出的DarkIR模型中,EBlock(Encoder Block,编码器块)作为实现低光图像增强的核心组件,其主要功能是借助傅里叶域信息改善图像的光照条件,同时为后续的解码器提供经光照增强后的特征。该组件的设计遵循Metaformer和NAFBlock的结构,具体情况如下:

EBlock的核心功能

EBlock专为低光增强任务而设计,通过处理图像的傅里叶域信息来提升图像亮度,同时提取有效的空间特征。其核心目标如下:

  1. 增强图像在傅里叶域的幅度信息(该信息与光照条件高度相关);
  2. 生成低分辨率的光照校正图像,为解码器提供基础;
  3. 在保证增强效果的同时,降低计算成本。

EBlock的结构组成

EBlock由两个关键模块构成,二者协同完成低光增强任务。

1. 空间注意力模块(SpAM)

  • 作用:提取图像的空间特征,为后续的频域增强提供辅助。
  • 结构
    • 基于NAFBlock的反向残差块(Inverted Residual Block),在减少参数数量的同时保留重要特征;
    • 采用简化的通道注意力(SCA),通过对通道维度进行权重分配,突出关键区域(如高光或暗光区域);
    • 运用门控机制(而非传统激活函数),更灵活地控制特征流动,避免因过度激活而导致的信息丢失。
  • 功能:聚焦于图像中对光照敏感的空间区域,为频域处理提供具有针对性的空间信息。

2. 频域前馈网络(Fre - MLP)

  • 作用:直接在傅里叶域进行操作,增强与光照相关的幅度信息。
  • 原理
    • 图像的光照条件主要与傅里叶域的幅度相关(相位信息更多地影响图像结构),因此仅需增强幅度即可改善图像亮度;
    • 通过快速傅里叶变换(FFT)将图像从空间域转换至频域,分离出幅度和相位;
    • 对幅度进行增强处理(如调整对比度、抑制噪声),再通过逆快速傅里叶变换(IFFT)将其转换回空间域。
  • 优势:相较于空间域操作(如通道MLP),频域处理能够更高效地对光照进行全局优化,且对低分辨率图像同样有效。

EBlock的工作流程

  1. 输入处理:接收低光图像的特征图(该特征图来自前一层或原始输入);
  2. 空间特征提取:通过SpAM提取空间注意力特征,突出光照敏感区域;
  3. 频域增强:将特征图转换至傅里叶域,通过Fre - MLP增强幅度信息,再转换回空间域;
  4. 下采样与特征传递:通过步长卷积(Strided Convolution)对特征图进行下采样(分辨率减半),以减少深层网络的计算量;
  5. 生成低分辨率图像:将编码后的深层特征通过卷积层进行线性组合,生成分辨率为原始图像1/8的低光增强图像($\hat{x}_{↓8}$),用于模型正则化(确保光照增强效果)。

EBlock的设计优势

  1. 高效性:通过下采样减少深层网络的运算量,同时频域操作避免了空间域的冗余计算;
  2. 针对性:专注于傅里叶域的幅度增强,直接解决低光图像的核心问题——光照不足;
  3. 轻量性:与Transformer模型相比,EBlock基于CNN结构,参数数量更少,适合在移动端进行部署。

核心代码

class EBlock(nn.Module):
    '''
    Change this block using Branch
    '''

    def __init__(self, c, DW_Expand=2, dilations = [1], extra_depth_wise = False):
        super().__init__()
        #we define the 2 branches
        self.dw_channel = DW_Expand * c 
        self.extra_conv = nn.Conv2d(c, c, kernel_size=3, padding=1, stride=1, groups=c, bias=True, dilation=1) if extra_depth_wise else nn.Identity() #optional extra dw
        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.branches = nn.ModuleList()
        for dilation in dilations:
            self.branches.append(Branch(c, DW_Expand, 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.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)
        # second step

        self.norm1 = LayerNorm2d(c)
        self.norm2 = LayerNorm2d(c)
        self.freq = FreMLP(nc = c, expand=2)
        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)

#        self.adapter = Adapter(c, ffn_channel=None)

#        self.use_adapters = False

#    def set_use_adapters(self, use_adapters):
#        self.use_adapters = use_adapters

    def forward(self, inp):
        y = inp
        x = self.norm1(inp)
        x = self.conv1(self.extra_conv(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_step2 = self.norm2(y) # size [B, 2*C, H, W]
        x_freq = self.freq(x_step2) # size [B, C, H, W]
        x = y * x_freq 
        x = y + x * self.gamma

#        if self.use_adapters:
#            return self.adapter(x)
#        else:
        return x 

实验

脚本

import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO

if __name__ == '__main__':
#     修改为自己的配置文件地址
    model = YOLO('/root/ultralytics-main/ultralytics/cfg/models/11/yolov11-C3k2_EBlock.yaml')
#     修改为自己的数据集地址
    model.train(data='/root/ultralytics-main/ultralytics/cfg/datasets/coco8.yaml',
                cache=False,
                imgsz=640,
                epochs=10,
                single_cls=False,  # 是否是单类别检测
                batch=8,
                close_mosaic=10,
                workers=0,
                optimizer='SGD',
                amp=True,
                project='runs/train',
                name='C3k2_EBlock',
                )

结果

THE END