YOLO11改进 - C3k2融合 | RFGM残差傅里叶引导模块:用频域先验修复极暗图像全局照明与结构细节 | TGRS 2026
2026/8/23 22:25:47 网站建设 项目流程

前言

本文介绍了面向极暗图像细节恢复的残差傅里叶引导模块 RFGM,用于缓解低照度场景中结构丢失、边缘模糊和噪声干扰导致的特征退化问题。该方法在频域中分别建模幅值与相位信息,通过阶段间残差传递、通道相关性筛选和幅值先验引导,稳健恢复全局照明与轮廓结构,并结合空间与频率的互补处理进一步细化纹理细节。我们将 RFGM 成功集成进 YOLO11 的 C3k2 模块,替代原有部分卷积结构,实现更强的暗光特征增强与目标表征能力。

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

专栏链接: YOLO11改进专栏

介绍

在极暗图像中恢复细粒度细节仍然非常困难,因为这类图像通常存在严重的结构信息丢失和噪声污染。现有增强方法往往难以保留复杂纹理和清晰边缘,从而限制了它们在文本检测、边缘检测等下游任务中的效果。为解决这些问题,论文提出了一种以暗光图像细节恢复为核心的高效双阶段方法。第一阶段引入 Residual Fourier-Guided Module(RFGM),在频域中有效恢复全局照明。RFGM 通过残差连接捕获阶段间和通道间依赖,为高保真频域处理提供稳健先验,同时缓解不可靠先验带来的误差累积风险。第二阶段使用互补的 Mamba 模块进行纹理结构细化:Patch Mamba 作用于通道拼接的非下采样图像块,细致建模像素级相关性,在不损失分辨率的情况下增强细粒度细节;Grad Mamba 显式关注高梯度区域,缓解状态空间模型中的状态衰减问题,并优先重建锐利边缘和边界。大量基准数据集和下游应用实验表明,该方法在保持效率的同时显著提升了暗光图像的细节恢复能力,并且这些模块轻量、可插拔,能够以较小计算开销集成到已有傅里叶框架中。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

1. 解决的关键问题

RFGM 主要解决极暗图像恢复中“频域先验不稳”和“细节随阶段处理逐步损失”的问题。论文指出,极暗图像并不只是亮度低,它还伴随结构退化、边缘模糊、纹理缺失和噪声破坏。傅里叶域方法通常把幅值谱与相位谱分开处理:幅值更接近图像亮度、能量和全局照明分布,相位则保留轮廓、边缘和空间结构。但已有方法往往按卷积块顺序处理幅值和相位,前一阶段中有用的亮度或结构信息可能无法被后续阶段充分利用,导致信息衰减。另一方面,不同通道中包含的结构轮廓和亮度响应并不一致,如果孤立处理通道,也会错过通道之间可互补的先验。RFGM 因此用“阶段间关联 + 通道间关联 + 残差融合”来筛选更可靠的频域信息,既利用上一阶段的有效幅值先验,又避免错误先验直接覆盖当前特征。

2. 整体架构

论文整体是一个双阶段暗光图像恢复框架。输入极暗图像先经过一个3×3卷积得到浅层特征,然后进入第一阶段的频域全局建模。第一阶段由六个相同的 RFGM 组成,重点恢复全局照明和整体结构。每个 RFGM 都会把输入特征映射到傅里叶域,拆分为幅值分量和相位分量,并分别处理。幅值分支负责选择和融合上一阶段中最有价值的亮度先验,相位分支负责把前一阶段的结构信息作为补偿注入当前阶段。第一阶段完成后,第二阶段转向空间域细节精修,由 Patch Mamba 和 Grad Mamba 共同恢复纹理、边缘和高梯度区域。也就是说,RFGM 不是孤立的亮度增强块,而是整套方法的频域基础模块:它先把全局照明和结构底座恢复得更稳,再交给后续 Mamba 分支做细粒度纹理和边界重建。

3. 技术原理

在第i个 RFGM 中,来自上一阶段的特征F_{i-1}先经过 FFT 转换到频域,得到幅值A_{i-1}和相位P_{i-1}。二者分别经过卷积和 ReLU 得到当前阶段的幅值A_i与相位P_i。幅值分支会把A_{i-1}A_i展平成R^{HW×C},计算通道相似性矩阵M ∈ R^{C×C},然后从中选择 Top-1 相关通道,作为上一阶段最可靠的亮度先验。该先验经过1×1卷积和 Sigmoid 得到幅值引导权重P_a,再与当前幅值相乘,并通过残差形式融合:Ã_i = A_i × P_a + A_i。这种设计的关键在于,先验只作为引导而不是替代当前表示,因此能降低错误先验累积的风险。

相位分支则把上一阶段相位P_{i-1}与当前相位P_i在通道维度拼接,通过卷积进行自适应融合,并以残差形式得到增强后的相位P̃_i。这里的相位信息承担结构补偿作用,有助于恢复轮廓、边界和空间布局。最后,增强后的幅值Ã_i与相位P̃_i通过 iFFT 回到空间特征,作为下一阶段输入。实验上,去掉 RFGM 后模型性能下降;把 RFGM 插入 FourLLIE、DMFourLLIE 等傅里叶低光增强框架中也能带来增益,说明它确实是一个轻量、可插拔、面向频域先验稳定化的核心模块。

核心代码

classRFGM(nn.Module):def__init__(self,nc,n=1):super(RFGM,self).__init__()self.conv0=nn.Sequential(nn.Conv2d(3,nc,1,1,0),ProcessBlock(nc),)self.conv1=ProcessBlock(nc)self.conv2=ProcessBlock(nc)self.conv3=ProcessBlock(nc)self.conv4=nn.Sequential(ProcessBlock(nc*2),nn.Conv2d(nc*2,nc,1,1,0),)self.conv5=nn.Sequential(ProcessBlock(nc*2),nn.Conv2d(nc*2,nc,1,1,0),)self.convout=nn.Sequential(ProcessBlock(nc*2),nn.Conv2d(nc*2,3,1,1,0),)defforward(self,x):x=self.conv0(x)x1=self.conv1(x)x2=self.conv2(x1)x3=self.conv3(x2)x4=self.conv4(torch.cat((x2,x3),dim=1))x5=self.conv5(torch.cat((x1,x4),dim=1))xout=self.convout(torch.cat((x,x5),dim=1))returnxout

YOLO11引入代码

在根目录下的ultralytics/nn/目录,新建一个C3k2目录,然后新建一个以C3k2_RFGM为文件名的py文件, 把代码拷贝进去。

importtorchimporttorch.nnasnnimporttorch.nn.functionalasFfromultralytics.nn.modules.convimportRepConv,Convfromultralytics.nn.modules.blockimportC3k,C3k2classSpaBlock(nn.Module):def__init__(self,nc):super(SpaBlock,self).__init__()self.block=nn.Sequential(nn.Conv2d(nc,nc,3,1,1),nn.LeakyReLU(0.1,inplace=True),nn.Conv2d(nc,nc,3,1,1),nn.LeakyReLU(0.1,inplace=True))defforward(self,x):returnx+self.block(x)classProcessBlock(nn.Module):def__init__(self,in_nc,spatial=True):super(ProcessBlock,self).__init__()self.spatial=spatial self.spatial_process=SpaBlock(in_nc)ifspatialelsenn.Identity()self.frequency_process=LightTopKFreBlock(nc=in_nc,top_k=in_nc)self.cat=nn.Conv2d(2*in_nc,in_nc,1,1,0)ifspatialelsenn.Conv2d(in_nc,in_nc,1,1,0)defforward(self,x):xori=x x_out_four=self.frequency_process(x)x_spatial=self.spatial_process(x)xcat=torch.cat([x_spatial,x_out_four],1)x_out=self.cat(xcat)ifself.spatialelseself.cat(x_out_four)returnx_out+xoriclassLightTopKFreBlock(nn.Module):def__init__(self,nc,top_k):super(LightTopKFreBlock,self).__init__()self.nc=nc self.top_k=top_k self.conv0=nn.Conv2d(nc,nc,1,1,0)self.process1_mag=nn.Sequential(nn.Conv2d(nc,nc,1,1,0),nn.LeakyReLU(0.1,inplace=True),nn.Conv2d(nc,nc,1,1,0))self.process1_pha=nn.Sequential(nn.Conv2d(nc,nc,1,1,0),nn.LeakyReLU(0.1,inplace=True),nn.Conv2d(nc,nc,1,1,0))self.process2_pha=nn.Sequential(nn.Conv2d(nc*2,nc,1,1,0),nn.LeakyReLU(0.1,inplace=True),nn.Conv2d(nc,nc,1,1,0))self.magGuideFusion=MagGuidedFusion(channels=nc)self.conv_out=nn.Conv2d(nc*2,nc,1,1,0)defforward(self,x):B,C,H,W=x.shape x_conv0=self.conv0(x)x_freq=torch.fft.rfft2(x_conv0,norm='backward')mag0=torch.abs(x_freq)pha0=torch.angle(x_freq)mag1=self.process1_mag(mag0)pha1=self.process1_pha(pha0)pha_cat=torch.cat((pha0,pha1),dim=1)pha_out=self.process2_pha(pha_cat)mag_out,mag0_weight=self.magGuideFusion(mag0,mag1)real=mag_out*torch.cos(pha_out)imag=mag_out*torch.sin(pha_out)x_out_freq=torch.complex(real,imag)x_out=torch.fft.irfft2(x_out_freq,s=(H,W),norm='backward')returnx_outclassMagGuidedFusion(nn.Module):def__init__(self,channels):super(MagGuidedFusion,self).__init__()self.channels=channels self.expand_conv=nn.Conv2d(1,channels,kernel_size=1,stride=1,padding=0)defforward(self,mag0,mag1):B,C,H,W=mag0.shape mag0_flat=mag0.view(B,C,-1)# (B, C, H*W)mag1_flat=mag1.view(B,C,-1)# (B, C, H*W)mag0_norm=F.normalize(mag0_flat,dim=-1)# (B, C, H*W)mag1_norm=F.normalize(mag1_flat,dim=-1)# (B, C, H*W)similarity_matrix=torch.bmm(mag0_norm,mag1_norm.transpose(1,2))# (B, C, C)similarity_scores=similarity_matrix.mean(dim=-1)# (B, C)top1_indices=torch.argmax(similarity_scores,dim=-1)# (B,)mag0_top1=torch.stack([mag0[b,top1_indices[b]]forbinrange(B)],dim=0).unsqueeze(1)# (B, 1, H, W)mag0_expanded=self.expand_conv(mag0_top1)# (B, C, H, W)mag0_weight=torch.sigmoid(mag0_expanded)# (B, C, H, W)fused_features=mag1*mag0_weight+mag1# (B, C, H, W)returnfused_features,mag0_weightclassRFGM(nn.Module):def__init__(self,inc,ouc):super(RFGM,self).__init__()self.conv0=nn.Sequential(nn.Conv2d(inc,inc,1,1,0),ProcessBlock(inc),)self.conv1=ProcessBlock(inc)self.conv2=ProcessBlock(inc)self.conv3=ProcessBlock(inc)self.conv4=nn.Sequential(ProcessBlock(inc*2),nn.Conv2d(inc*2,inc,1,1,0),)self.conv5=nn.Sequential(ProcessBlock(inc*2),nn.Conv2d(inc*2,inc,1,1,0),)self.convout=nn.Sequential(ProcessBlock(inc*2),nn.Conv2d(inc*2,ouc,1,1,0),)defforward(self,x):x=self.conv0(x)x1=self.conv1(x)x2=self.conv2(x1)x3=self.conv3(x2)x4=self.conv4(torch.cat((x2,x3),dim=1))x5=self.conv5(torch.cat((x1,x4),dim=1))xout=self.convout(torch.cat((x,x5),dim=1))returnxoutclassC3k_RFGM(C3k):def__init__(self,c1,c2,n=1,shortcut=False,g=1,e=0.5,k=3):super().__init__(c1,c2,n,shortcut,g,e,k)c_=int(c2*e)# hidden channelsself.m=nn.Sequential(*(RFGM(c_,c_)for_inrange(n)))classC3k2_RFGM(C3k2):def__init__(self,c1,c2,n=1,c3k=False,e=0.5,g=1,shortcut=True):super().__init__(c1,c2,n,c3k,e,g,shortcut)self.m=nn.ModuleList(C3k_RFGM(self.c,self.c,2,shortcut,g)ifc3kelseRFGM(self.c,self.c)for_inrange(n))

注册

ultralytics/nn/tasks.py中进行如下操作:

步骤1:

fromultralytics.nn.C3k2.C3k2_RFGMimportC3k2_RFGM

步骤2

修改def parse_model(d, ch, verbose=True):

C3k2_RFGM

配置yolo11-C3k2_RFGM.yaml

# Ultralytics YOLO 🚀, AGPL-3.0 license# YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parametersnc:80# number of classesscales:# model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'# [depth, width, max_channels]n:[0.50,0.25,1024]# summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPss:[0.50,0.50,1024]# summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPsm:[0.50,1.00,512]# summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPsl:[1.00,1.00,512]# summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPsx:[1.00,1.50,512]# summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs# YOLO11n backbonebackbone:# [from, repeats, module, args]-[-1,1,Conv,[64,3,2]]# 0-P1/2-[-1,1,Conv,[128,3,2]]# 1-P2/4-[-1,2,C3k2_RFGM,[256,False,0.25]]-[-1,1,Conv,[256,3,2]]# 3-P3/8-[-1,2,C3k2_RFGM,[512,False,0.25]]-[-1,1,Conv,[512,3,2]]# 5-P4/16-[-1,2,C3k2_RFGM,[512,True]]-[-1,1,Conv,[1024,3,2]]# 7-P5/32-[-1,2,C3k2_RFGM,[1024,True]]-[-1,1,SPPF,[1024,5]]# 9-[-1,2,C2PSA,[1024]]# 10# YOLO11n headhead:-[-1,1,nn.Upsample,[None,2,"nearest"]]-[[-1,6],1,Concat,[1]]# cat backbone P4-[-1,2,C3k2_RFGM,[512,False]]# 13-[-1,1,nn.Upsample,[None,2,"nearest"]]-[[-1,4],1,Concat,[1]]# cat backbone P3-[-1,2,C3k2_RFGM,[256,False]]# 16 (P3/8-small)-[-1,1,Conv,[256,3,2]]-[[-1,13],1,Concat,[1]]# cat head P4-[-1,2,C3k2_RFGM,[512,False]]# 19 (P4/16-medium)-[-1,1,Conv,[512,3,2]]-[[-1,10],1,Concat,[1]]# cat head P5-[-1,2,C3k2_RFGM,[1024,True]]# 22 (P5/32-large)-[[16,19,22],1,Detect,[nc]]# Detect(P3, P4, P5)

实验

脚本

importwarnings warnings.filterwarnings('ignore')fromultralyticsimportYOLOif__name__=='__main__':# 修改为自己的配置文件地址model=YOLO('./ultralytics/cfg/models/11/yolo11-C3k2_RFGM.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='SGD',amp=True,project='runs/train',name='C3k2_RFGM',)

结果

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询