当前位置: 首页 > news >正文

#4【CVPR2024】SHIP:图像融合的一种协同高阶交互范式


📜 Probing Synergistic High-Order Interaction in Infrared and Visible Image Fusion


🍕 源码: https://github.com/zheng980629/SHIP

在这里插入图片描述
先前融合规则与我们提出的范式之间的比较。之前的工作要么(a)缺乏明确的交互,要么(b)仅实现了 2 n d 2^{n d} 2nd阶空间交互;而(c)我们的SHIP融合了高阶空间和通道交互,以探索模态之间在空间细粒度细节和全局统计中的协同相关性,逐步整合并区分互补信息

如何将二阶交互扩展到任意高阶而又不会显著增加计算量呢?论文并不仅仅只是将低阶计算平庸地升阶,而是考虑了计算量的问题,这就为以后潜在的实际应用增添了可能性

🍟 贡献:
  • 本研究中的新型协同高阶交互范式(SHIP)探索了红外与可见光图像融合中复杂的高阶交互。通过在空间和通道维度上融入高阶交互,SHIP作为一种开创性方法,研究了模态之间的协同相关性
  • 该范式研究了涉及空间细粒度细节和全局统计的高阶交互,协同整合互补信息,并从源模态中区分相互依赖性
  • 展示了其在全色锐化任务中的有效性

🍭 理解空间和通道上的阶数
  • 普通卷积(vanilla convolution):传统的卷积操作通过对输入图像的局部区域进行加权求和来提取特征。然而,这种操作本质上无法捕捉特定位置与其邻近区域之间的空间交互。换句话说,卷积核的权重是固定的,无法根据输入内容动态调整,因此难以建模复杂的空间依赖关系。

  • 动态卷积:动态卷积通过根据输入生成动态权重来改进普通卷积。这种改进引入了一阶空间交互,即卷积核的权重会根据输入内容自适应调整。

  • 自注意力机制:Transformer 通过自注意力机制实现了二阶空间交互。其核心是通过矩阵乘法计算查询(queries)、键(keys)和值(values)之间的关系。

  • SE 模块:SE 模块通过一阶统计量来重新校准通道响应。具体来说,它首先对每个通道进行全局平均池化(squeeze),然后通过全连接层学习通道权重(excitation),最后用这些权重对特征图进行加权。


在这里插入图片描述
所提出的协同高阶交互范式(SHIP)的详细框架包括在L次迭代中执行的交替空间和通道高阶交互。具体来说,空间高阶交互充分挖掘了两种模态之间的协作,并通过高阶建模整合了空间细粒度的互补信息。随后,基于全局一阶统计量(均值)的通道高阶相互作用进一步研究了全局统计量,区分了可见光和红外模态之间的相互依赖性


🍥 高阶空间交互

I F = SHIP ⁡ L ( ψ ( I R ) , ϕ ( I V ) ) I_{\mathcal{F}}=\operatorname{SHIP}_L\left(\psi\left(I_{\mathcal{R}}\right), \phi\left(I_{\mathcal{V}}\right)\right) IF=SHIPL(ψ(IR),ϕ(IV))整体结构也是比较简单的,就是一个相同的模块重复处理了好多次得到结果,效果好不好全靠模块设计

传统的注意力机制
O S ( ( F V ) 2 ) = F V S 1 = softmax ⁡ ( Q ⊗ K T d k ) ⊗ V = A ⊗ V \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right)=F_{\mathcal{V}_S}^1=\operatorname{softmax}\left(\frac{\mathbf{Q} \otimes \mathbf{K}^T}{\sqrt{d_k}}\right) \otimes \mathbf{V}=\mathbf{A} \otimes \mathbf{V} OS((FV)2)=FVS1=softmax(dk QKT)V=AV文中提出了一种近似形式
A i j = ⟨ q i , k j ⟩ \mathbf{A}_{i j}=\left\langle\mathbf{q}_i, \mathbf{k}_j\right\rangle Aij=qi,kj A = F − 1 ( F ( F V W Q ) ⊙ F ( F R W K ) ‾ ) \mathbf{A}=\mathcal{F}^{-1}\left(\mathcal{F}\left(F_{\mathcal{V}} \mathbf{W}^Q\right) \odot \overline{\mathcal{F}\left(F_{\mathcal{R}} \mathbf{W}^K\right)}\right) A=F1(F(FVWQ)F(FRWK)) O S ( ( F V ) 2 ) = F V S 1 = Norm ⁡ ( A ) ⊙ ( F R W V ) \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right)=F_{\mathcal{V}_S}^1=\operatorname{Norm}(\mathbf{A}) \odot\left(\mathrm{F}_{\mathcal{R}} \mathbf{W}^{\mathrm{V}}\right) OS((FV)2)=FVS1=Norm(A)(FRWV)本质上就是把矩阵乘法转到频域中变为点乘,再转回去空间域,其实说不上创新,而且在计算上面是不是变得高效也难说呀 O S ( ( F V S i − 1 ) 2 ) = F V S i = Attention ⁡ ( Q i , K i , V i ) , Q i = F V S i − 1 W Q i , K i = F R W K i , V i = F R W v i \begin{aligned} & \mathcal{O}_S\left(\left(F_{\mathcal{V}_S}^{i-1}\right)^2\right)=F_{\mathcal{V}_S}^i=\operatorname{Attention}\left(\mathbf{Q}_i, \mathbf{K}_i, \mathbf{V}_i\right), \\ & \mathbf{Q}_i=F_{\mathcal{V}_S}^{i-1} \mathbf{W}^{\mathbf{Q}_i}, \mathbf{K}_i=F_{\mathcal{R}} \mathbf{W}^{\mathbf{K}_i}, \mathbf{V}_i=F_{\mathcal{R}} \mathbf{W}^{\mathbf{v}_i} \end{aligned} OS((FVSi1)2)=FVSi=Attention(Qi,Ki,Vi),Qi=FVSi1WQi,Ki=FRWKi,Vi=FRWvi F V → O S ( ( F V ) 2 ) → F V S 1 → O S ( ( F V S 1 ) 2 ) → F V S 2 ⋯ → O S ( ( F V S i ) 2 ) → F V S i + 1 … O S ( ( F V S L − 1 ) 2 ) → F V S L \begin{aligned} & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^1 \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^1\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^2 \cdots \\ & \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^i\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^{i+1} \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^{L-1}\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^L \end{aligned} FVOS((FV)2)FVS1OS((FVS1)2)FVS2OS((FVSi)2)FVSi+1OS((FVSL1)2)FVSL之前的多个自注意力模块堆叠集中在索引上,集中在一个模态,另一个模态的键值对并没有因为模块的堆叠而更新,而本文可以提高到任意阶:
O S ( ( F V i − 1 ) j ) = F V s j i = Norm ⁡ ( F V s j − 1 i W Q j ) ⊙ ( F R s j − 1 i W V ) F V s j − 1 i = Norm ⁡ ( F V s i − 2 i W Q j − 1 ) , F R s j − 1 i = F R s j − 2 i W V j − 1 i \begin{aligned} & \mathcal{O}_S\left(\left(F_{\mathcal{V}}^{i-1}\right)^j\right)=F_{\mathcal{V}_s^j}^i=\operatorname{Norm}\left(F_{\mathcal{V}_s^{j-1}}^i \mathbf{W}^{\mathbf{Q} \mathbf{j}}\right) \odot\left(F_{\mathcal{R}_s^{j-1}}^i \mathbf{W}^{\mathbf{V}}\right)\\ & F_{\mathcal{V}_s^{j-1}}^i=\operatorname{Norm}\left(F_{\mathcal{V}_s^{i-2}}^i \mathbf{W}^{\mathbf{Q} \mathbf{j}-1}\right), F_{\mathcal{R}_s^{j-1}}^i=F_{\mathcal{R}_s^{j-2}}^i \mathbf{W}^{\mathbf{V}_{\mathbf{j}-1}^{\mathbf{i}}} \end{aligned} OS((FVi1)j)=FVsji=Norm(FVsj1iWQj)(FRsj1iWV)FVsj1i=Norm(FVsi2iWQj1),FRsj1i=FRsj2iWVj1i F V → O S ( ( F V ) N ) → F V S 1 → O S ( ( F V s 1 ) N ) → F V S 2 … → O S ( ( F V S i ) N ) → F V S i + 1 … O S ( ( F V S L − 1 ) N ) → F V S L \begin{aligned} & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^1 \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_s}^1\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^2 \ldots \\ & \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_S}^i\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^{i+1} \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^{L-1}\right)^N\right) \rightarrow F_{\mathcal{V}_S}^L \end{aligned} FVOS((FV)N)FVS1OS((FVs1)N)FVS2OS((FVSi)N)FVSi+1OS((FVSL1)N)FVSL

在这里插入图片描述
在不同的空间高阶交互步骤中,每次交互后的特征可视化。例如, F V s 3 2 F_{\mathcal{V}_s^3} ^2 FVs32表示 2 n d 2^{nd} 2nd空间高阶相互作用中三阶相互作用后的特征。这些可视化从两个角度说明了高阶空间相互作用的有效性:(1)在每个高阶相互作用中,特征响应随着顺序的增加而升级,突出了突出的对象;(2) 不同的高阶相互作用产生独特的响应,展示了特征表示的多样性

class spatialInteraction(nn.Module):def __init__(self, channelin, channelout):super(spatialInteraction, self).__init__()# 定义三个卷积序列用于处理融合特征self.reflashFused1 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),  # 3x3卷积,输入通道为channelin,输出通道为channelout,padding为1nn.ReLU(),  # ReLU激活函数nn.Conv2d(channelout, channelout, 3, 1, 1)  # 再次3x3卷积)self.reflashFused2 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashFused3 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))# 定义三个卷积序列用于处理红外特征self.reflashInfrared1 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashInfrared2 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashInfrared3 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))# 定义四个LayerNorm层,用于归一化self.norm1 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm2 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm3 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm4 = LayerNorm(channelout, LayerNorm_type='WithBias')def forward(self, vis, inf, i, j):# 获取可见光图像的尺寸_, C, H, W = vis.size()# 对可见光和红外图像进行快速傅里叶变换(FFT)vis_fft = torch.fft.rfft2(vis.float())inf_fft = torch.fft.rfft2(inf.float())# 计算可见光和红外图像的频域注意力图atten = vis_fft * inf_fftatten = torch.fft.irfft2(atten, s=(H, W))  # 逆FFT变换回空间域atten = self.norm1(atten)  # 归一化fused_OneOrderSpa = atten * inf  # 一阶空间融合特征# 通过第一个融合卷积序列处理一阶融合特征fused_OneOrderSpa = self.reflashFused1(fused_OneOrderSpa)fused_OneOrderSpa = self.norm2(fused_OneOrderSpa)  # 归一化infraredReflash1 = self.reflashInfrared1(inf)  # 处理红外特征fused_twoOrderSpa = fused_OneOrderSpa * infraredReflash1  # 二阶空间融合特征# 通过第二个融合卷积序列处理二阶融合特征fused_twoOrderSpa = self.reflashFused2(fused_twoOrderSpa)fused_twoOrderSpa = self.norm3(fused_twoOrderSpa)  # 归一化infraredReflash2 = self.reflashInfrared2(infraredReflash1)  # 处理红外特征fused_threeOrderSpa = fused_twoOrderSpa * infraredReflash2  # 三阶空间融合特征# 通过第三个融合卷积序列处理三阶融合特征fused_threeOrderSpa = self.reflashFused3(fused_threeOrderSpa)fused_threeOrderSpa = self.norm4(fused_threeOrderSpa)  # 归一化infraredReflash3 = self.reflashInfrared3(infraredReflash2)  # 处理红外特征fused_fourOrderSpa = fused_threeOrderSpa * infraredReflash3  # 四阶空间融合特征# 将最终融合特征与原始可见光图像相加fused = fused_fourOrderSpa + vis# 返回融合后的特征和处理后的红外特征return fused, infraredReflash3

🍉 高阶通道交互

传统的SE(squeeze and excitation)模块
Z i = 1 H × W ∑ x = 1 H ∑ y = 1 W F i ( x , y ) O C ( ( F i ) 1 ) = F C i = σ ( W Z 1 i Z i ) ⋅ F i \begin{gathered} Z^i=\frac{1}{H \times W} \sum_{x=1}^H \sum_{y=1}^W F^i(x, y) \\ \mathcal{O}_C\left(\left(F^i\right)^1\right)=F_C^i=\sigma\left(\mathbf{W}^{\mathbf{Z}_1^i} Z^i\right) \cdot F^i \end{gathered} Zi=H×W1x=1Hy=1WFi(x,y)OC((Fi)1)=FCi=σ(WZ1iZi)Fi其中 F i = concat ⁡ [ F V S i , F R S i ] F^i = \operatorname{concat}\left[F_{\mathcal{V}_S}^i, F_{\mathcal{R}_S}^i\right] Fi=concat[FVSi,FRSi] Z c Z_c Zc 表示一阶统计量, σ \sigma σ 表示 Sigmoid 函数。 W Z \mathbf{W}^{\mathbf{Z}} WZ 包括两个线性变换和一个 ReLU 函数。作者将其扩展到了高阶形式: O C ( ( F i ) j ) = F C j i = σ ( W Z j i Z j − 1 i ) ⋅ ( W F j i F j − 1 i ) , Z j − 1 i = σ ( W F j − 1 i Z j − 2 i ) , F j − 1 i = W F j − 1 i F j − 2 i F V → O S ( ( F V ) N ) → O C ( ( F V S 1 ) N ) → O S ( ( F V C 1 ) N ) → O C ( ( F V S 2 ) N ) → … O S ( ( F V C L − 1 ) N ) → O C ( ( F V S L ) N ) \begin{aligned} & \mathcal{O}_C\left(\left(F^i\right)^j\right)=F_{C j}^i=\sigma\left(\mathbf{W}^{\mathbf{Z}_j^i} Z_{j-1}^i\right) \cdot\left(\mathbf{W}^{\mathbf{F}_j^i} F_{j-1}^i\right), \\ & Z_{j-1}^i=\sigma\left(\mathbf{W}^{\mathbf{F}_{j-1}^i} Z_{j-2}^i\right), F_{j-1}^i=\mathbf{W}^{\mathbf{F}_{j-1}^i} F_{j-2}^i \\ & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^N\right) \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^1\right)^N\right) \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{C}}}^1\right)^N\right) \\ & \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^2\right)^N\right) \rightarrow \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_C}^{L-1}\right)^N\right) \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^L\right)^N\right) \end{aligned} OC((Fi)j)=FCji=σ(WZjiZj1i)(WFjiFj1i),Zj1i=σ(WFj1iZj2i),Fj1i=WFj1iFj2iFVOS((FV)N)OC((FVS1)N)OS((FVC1)N)OC((FVS2)N)OS((FVCL1)N)OC((FVSL)N)具体原理不是很懂,但好像是做一次变换就可以升一次阶数,具体可能需要结合代码来看
在这里插入图片描述
不同阶次在通道索引上的通道交互。这个观察结果为有力证据,表明不同阶次的交互探索了红外和可见模态之间的多样化相互依赖关系

class channelInteraction(nn.Module):def __init__(self, channelin, channelout):super(channelInteraction, self).__init__()# 初始化各个卷积层,包括通道注意力模块和融合模块self.chaAtten = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),  # 1x1卷积nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True)  # 1x1卷积)# 重构通道注意力模块1、2、3,用于逐步改进注意力特征self.reflashChaAtten1 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))self.reflashChaAtten2 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))self.reflashChaAtten3 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))# 融合模块,用于融合不同通道的特征self.reflashFused1 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),  # 3x3卷积nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1)  # 3x3卷积)self.reflashFused2 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1))self.reflashFused3 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1))# 自适应平均池化层,将输入大小调整为 (batch_size, channels, 1, 1)self.avgpool = nn.AdaptiveAvgPool2d(1)# 后处理模块,用于处理融合后的特征self.postprocess = nn.Sequential(InvBlock(DenseBlock, 2 * channelin, channelout),nn.Conv2d(2 * channelout, channelout, 1, 1, 0)  # 1x1卷积,减少通道数)def forward(self, vis, inf, i, j):# 输入:vis和inf是两个不同的特征图(例如,视觉和信息特征图)# 首先将这两个特征图按通道维度拼接vis_cat = torch.cat([vis, inf], 1)# 使用通道注意力机制对拼接后的特征图进行处理chanAtten = self.chaAtten(self.avgpool(vis_cat)).softmax(1)channel_response = self.chaAtten(self.avgpool(vis_cat))# 使用通道注意力对特征图进行加权fused_OneOrderCha = vis_cat * chanAtten# 通过第一个重构模块(reflashFused1)进一步处理fused_OneOrderCha = self.reflashFused1(fused_OneOrderCha)chanAttenReflash1 = self.reflashChaAtten1(chanAtten).softmax(1)fused_twoOrderCha = fused_OneOrderCha * chanAttenReflash1# 通过第二个重构模块(reflashFused2)进一步处理fused_twoOrderCha = self.reflashFused2(fused_twoOrderCha)chanAttenReflash2 = self.reflashChaAtten2(chanAttenReflash1).softmax(1)fused_threeOrderCha = fused_twoOrderCha * chanAttenReflash2# 通过第三个重构模块(reflashFused3)进一步处理fused_threeOrderCha = self.reflashFused3(fused_threeOrderCha)chanAttenReflash3 = self.reflashChaAtten3(chanAttenReflash2).softmax(1)fused_fourOrderCha = fused_threeOrderCha * chanAttenReflash3# 最终的后处理模块,生成最终的输出output = self.postprocess(fused_fourOrderCha)return output

🍫 损失函数

L = L int  + λ L gra  \mathcal{L}=\mathcal{L}_{\text {int }}+\lambda \mathcal{L}_{\text {gra }} L=Lint +λLgra  L i n t = ∥ ( ω V ∘ I V + ω R ∘ I R ) − I F ∥ 1 \mathcal{L}_{\mathrm{int}}=\left\|\left(\omega_{\mathcal{V}} \circ I_{\mathcal{V}}+\omega_{\mathcal{R}} \circ I_{\mathcal{R}}\right)-I_{\mathcal{F}}\right\|_1 Lint=(ωVIV+ωRIR)IF1 ω V = S V / ( S V − S R ) and  S R = 1 − S V \omega_{\mathcal{V}}=S_{\mathcal{V}} /\left(S_{\mathcal{V}}-S_{\mathcal{R}}\right) \text { and } S_{\mathcal{R}}=1-S_{\mathcal{V}} ωV=SV/(SVSR) and SR=1SV L gra  = 1 H W ∥ ∇ I F − max ⁡ ( ∇ I R , ∇ I V ) ∥ 1 \mathcal{L}_{\text {gra }}=\frac{1}{H W}\left\|\nabla I_{\mathcal{F}}-\max \left(\nabla I_{\mathcal{R}}, \nabla I_{\mathcal{V}}\right)\right\|_1 Lgra =HW1IFmax(IR,IV)1损失函数中规中矩,因为重点不在这边,重点在于前面的模块设计。实验部分有兴趣的可以自行去看,我觉得没有什么亮点,就不说了


本文的源代码中,作者引用了Facebook研究的一个注册器,可以用来组织代码结构,使得整个代码部分更加容易管理:

# 修改自: https://github.com/facebookresearch/fvcore/blob/master/fvcore/common/registry.py  # noqa: E501class Registry():"""提供名称 -> 对象映射的注册表,用于支持第三方用户的自定义模块。创建一个注册表(例如,创建一个骨干网络的注册表):.. code-block:: pythonBACKBONE_REGISTRY = Registry('BACKBONE')注册一个对象:.. code-block:: python@BACKBONE_REGISTRY.register()class MyBackbone():...或者:.. code-block:: pythonBACKBONE_REGISTRY.register(MyBackbone)"""def __init__(self, name):"""构造函数参数:name (str): 注册表的名称"""self._name = name# key: 数据集/模型等的类名,value: 对应的类对象self._obj_map = {}def _do_register(self, name, obj):# 注册一个对象assert (name not in self._obj_map), (f"名为 '{name}' 的对象已经在 '{self._name}' 注册表中注册过了!")self._obj_map[name] = objdef register(self, obj=None):"""使用给定对象的名称 `obj.__name__` 注册该对象。可以作为装饰器使用,也可以不使用装饰器。参见本类的文档字符串了解使用方法。"""if obj is None:# 用作装饰器def deco(func_or_class):name = func_or_class.__name__# print(name)self._do_register(name, func_or_class)return func_or_classreturn deco# 作为函数调用时name = obj.__name__self._do_register(name, obj)def get(self, name):# 获取对应类的对象ret = self._obj_map.get(name)if ret is None:raise KeyError(f"在 '{self._name}' 注册表中没有找到名为 '{name}' 的对象!")return retdef __contains__(self, name):# 判断某个对象是否已经注册return name in self._obj_mapdef __iter__(self):# 迭代器方法return iter(self._obj_map.items())def keys(self):# 获取所有注册的对象的名称return self._obj_map.keys()# 以下是不同模块的注册表实例DATASET_REGISTRY = Registry('dataset')  # 数据集注册表
ARCH_REGISTRY = Registry('arch')        # 网络架构注册表
MODEL_REGISTRY = Registry('model')      # 模型注册表
LOSS_REGISTRY = Registry('loss')        # 损失函数注册表
METRIC_REGISTRY = Registry('metric')    # 指标注册表

相关文章:

#4【CVPR2024】SHIP:图像融合的一种协同高阶交互范式

📜 Probing Synergistic High-Order Interaction in Infrared and Visible Image Fusion 🍕 源码: https://github.com/zheng980629/SHIP 先前融合规则与我们提出的范式之间的比较。之前的工作要么(a)缺乏明确的交互&a…...

虚拟机从零实现机器人控制

1. 系统安装 因Docker不适合需要图形界面的开发,因此使用虚拟机VMware方便可视化界面方式查看效果,相关软件可以从官网下载,这里有一整套免费安装文件百度网盘地址: 2. ROS安装 Ubuntu 22.04:https://docs.ros.org…...

趣味数学300题1981版-八个等式、五个5等于24

八个等式 分析:此问题的求解思路是按照最后一步运算的运算符号进行分类。示例中最后一步的运算是除法,只要被除数与除数相等且不为0,就可以得到结果1.因此我们还可以对于结果等于1的情况列出其他的算式。如果保持最后一步运算为除法运算&…...

Microsoft Office 2024 软件安装教程(免费)

1.通过百度网盘下载Microsoft Office 2024安装包 下载地址为: https://pan.baidu.com/s/1jk1kvQsKFH9dZGF5xfGgiQ?pwdjbkv 提取码: jbkv 。 2.安装环境 Win10~Win11或更高。 3.安装步骤 (1)下载压缩包,解压缩。 (2&#xf…...

Linux 常见指令

linux 常见指令 Alt Enter 全屏 退出全屏 pwd: 显示用户所处路径 ls :显示当前路径下的文件或者目录名称 [ltVM-8-13-centos ~]$ ls 106 [ltVM-8-13-centos ~]$ ls -l ll :显示当前路径下的文件或者目录名称更多属性信息 [ltVM-8-13-cen…...

HTML Application(hta)入门教程

简介 HTA是HTML Application的缩写,又称为HTML应用程序。 hta是一个可执行文件,双击可以直接运行 hta与html非常相似,可直接将文件后缀改为.hta来获得HTA格式的文件。 支持VBS和JavaScript html的权限被限制在网页浏览器内,只有操…...

pytest运行用例的常见方式及参数

标题pytest运行用例方式及参数 用例结构目录 “”" 在最外层目录下执行所有的用例 参数说明: -s:显示用例的打印信息 -v:显示用例执行的详细信息 –alluredir:指定allure报告的路径 –clean-alluredir:清除allure报告的路径 -n:指定并发的进程数 -x:出现一条用…...

XML Schema 元素替换

XML Schema 元素替换 引言 XML(可扩展标记语言)是一种用于存储和传输数据的标记语言。XML Schema 是一种用于定义 XML 文档结构的语言,它描述了 XML 文档的结构、数据类型和约束。在处理 XML 文档时,有时需要对特定的元素进行替换,以满足特定的需求。本文将介绍 XML Sch…...

OpenBMC:BmcWeb app.run

1.监听用户移除signal //src\webserver_run.cpp int run() {...bmcweb::registerUserRemovedSignal();... } //include\user_monitor.hpp inline void onUserRemoved(sdbusplus::message_t& msg) {sdbusplus::message::object_path p;msg.read(p);std::string username …...

hot100_74. 搜索二维矩阵

hot100_74. 搜索二维矩阵 思路 给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否…...

光明谷推出AT指令版本的蓝牙音箱SOC 开启便捷智能音频开发新体验

前言 在蓝牙音箱市场竞争日益激烈的当下,开发一款性能卓越且易于上手的蓝牙音箱,成为众多厂商追求的目标。而光明谷科技有限公司推出的 AT 指令版本的蓝牙音箱 SOC,无疑为行业带来了全新的解决方案,以其诸多独特卖点,迅…...

基于windows的docker-desktop安装kubenetes以及dashboard

我们需要k8s环境做各种小实验可以本地安装一个,这里介绍win11如何通过docker-desktop安装k8s以及通过helm安装dashboard。 下载docker-desktop地址https://www.docker.com/get-started/打开【控制面板】->打开【启用和关闭windows功能】->分别勾选【hyper-v】…...

MT7628基于原厂的SDK包, 修改ra1网卡的MAC方法。

1、在/etc/config/wireless文件添加多个WIFI网卡的方法。 2、修改WIFI驱动,在src/embedded/ap/ap.c文件里面,从系统文件信息来修改ra1网卡的MAC内容,添加红色部分源代码。 RTMP_IO_WRITE32(pAd, RMAC_RMACDR, Value); if (idx > 0) …...

网络安全第三次练习

一、实验拓扑 二、实验要求 配置真实DNS服务信息,创建虚拟服务,配置DNS透明代理功能 三、需求分析 1.创建用户并配置认证策略 2.安全策略划分接口 3.ip与策略配置 四、实验步骤 1.划分安全策略接口 2.创建用户并进行策略认证 3.配置安全策略 4.NAT配…...

BFS 和 DFS(深度优先搜索、广度优先搜索)

深度优先搜索(DFS)和广度优先搜索(BFS)是两种常用的图遍历算法,用于解决图相关的问题。它们在搜索问题中具有广泛的应用,如路径搜索、连通性检测等。 以下是具体区别: (图片引自&am…...

Casbin 权限管理介绍及在 Go 语言中的使用入门

引言 在现代软件开发过程中,权限管理是一个至关重要的环节,它关系到系统的安全性和用户体验。Casbin 是一个强大的访问控制库,支持多种访问控制模型,如 ACL(访问控制列表)、RBAC(基于角色的访问…...

Two Sum

声明:博主为算法新手,博客仅作为个人学习记录 作为新手我的做法 (1)数组nums遍历一遍挑选出小于target的值及其下标,值存入temp,下标存到indices (2)遍历temp找到符合temp[i]temp[j]target的两个…...

3.3.2 交易体系构建——缠论操作思路

本节我们基于交易目标(规避下跌趋势,参与上涨趋势)来构建基于上涨趋势的缠论交易体系。建立上涨趋势的缠论交易体系需要以下几个步骤: 识别下跌走势大概率完成的位置 等待出现转折结构 确定交易模型并交易 从概率的角度来说,判断走势结束是个概率事件。为构建成功较高的交…...

[SQL] 事务的四大特性(ACID)

🎄事务的四大特性 以下就是事务的四大特性,简称ACID。 原子性📢事务时不可分割的最小操作单元,要么全部成功,要么全部失败。一致性📢事务完成后,必须使所有的数据都保持一致隔离性&#x1f4e2…...

使用 Three.js 实现流光特效

大家好!我是 [数擎AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | AI…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...