1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
| import torch import torch.nn as nn from mamba_ssm import Mamba
class MS_Mamba(nn.Module): """ 多光谱Mamba块 论文Section III-C: 1. 时间轴扫描:对每个光谱特征沿时间轴做双向Mamba 2. 通道轴扫描:沿通道轴双向扫描捕获跨光谱交互 """ def __init__(self, d_model: int, d_state: int = 16, d_conv: int = 4, expand: int = 2): super().__init__() self.d_model = d_model self.temporal_mamba_rgb = Mamba( d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand, bidirectional=True ) self.temporal_mamba_nir = Mamba( d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand, bidirectional=True ) self.channel_mamba = Mamba( d_model=d_model * 2, d_state=d_state, d_conv=d_conv, expand=expand, bidirectional=True ) self.norm1 = nn.LayerNorm(d_model) self.norm2 = nn.LayerNorm(d_model) self.norm3 = nn.LayerNorm(d_model * 2) self.proj = nn.Linear(d_model * 2, d_model) def forward(self, rgb_feat: torch.Tensor, nir_feat: torch.Tensor): """ Args: rgb_feat: [B, T, C] RGB时序特征 nir_feat: [B, T, C] NIR时序特征 Returns: fused_feat: [B, T, C] 融合后特征 """ B, T, C = rgb_feat.shape rgb_temporal = self.temporal_mamba_rgb( self.norm1(rgb_feat) ) + rgb_feat nir_temporal = self.temporal_mamba_nir( self.norm2(nir_feat) ) + nir_feat concat_feat = torch.cat([rgb_temporal, nir_temporal], dim=-1) concat_feat = self.norm3(concat_feat) channel_input = concat_feat.transpose(1, 2) channel_out = self.channel_mamba(channel_input) channel_out = channel_out.transpose(1, 2) fused = self.proj(channel_out + concat_feat) return fused
class MS_rPPG(nn.Module): """ MS-rPPG完整模型 论文Section III: 多光谱rPPG估计框架 输入:RGB+NIR面部视频 → 输出:心率估计值 """ def __init__(self, num_classes: int = 1): super().__init__() self.rgb_encoder = nn.Conv3d(3, 64, kernel_size=(3,3,3), padding=(1,1,1)) self.nir_encoder = nn.Conv3d(1, 64, kernel_size=(3,3,3), padding=(1,1,1)) self.cslm = CrossSpectralLinearModulation(64) self.ms_mamba_blocks = nn.ModuleList([ MS_Mamba(d_model=64) for _ in range(3) ]) self.head = nn.Sequential( nn.AdaptiveAvgPool3d(1), nn.Flatten(), nn.Linear(64, 32), nn.GELU(), nn.Linear(32, num_classes) ) def forward(self, rgb: torch.Tensor, nir: torch.Tensor) -> torch.Tensor: """ Args: rgb: [B, 3, T, H, W] RGB面部视频 nir: [B, 1, T, H, W] NIR面部视频 Returns: hr: [B, 1] 估计心率值 """ rgb_feat = self.rgb_encoder(rgb) nir_feat = self.nir_encoder(nir) fused = self.cslm(rgb_feat, nir_feat) B, C, T, H, W = fused.shape fused = fused.view(B, C, T, H * W).permute(0, 3, 2, 1) fused = fused.mean(1) for block in self.ms_mamba_blocks: fused = block(fused, fused) fused_4d = fused.transpose(1, 2).unsqueeze(-1).unsqueeze(-1) hr = self.head(fused_4d) return hr
if __name__ == "__main__": model = MS_rPPG() rgb_input = torch.randn(2, 3, 300, 64, 64) nir_input = torch.randn(2, 1, 300, 64, 64) hr_output = model(rgb_input, nir_input) print(f"输入RGB shape: {rgb_input.shape}") print(f"输入NIR shape: {nir_input.shape}") print(f"输出心率估计: {hr_output.shape}") print(f"模型参数量: {sum(p.numel() for p in model.parameters()):,}")
|