
作者在文章中提出了,目前(2023)的伪造检测算法是不能够对浅层伪影特征进行有效提取的,这是因为这些模型存在着在未被篡改区域提取伪影特征,这是不现实。因此,作者提出了一个双流网络,分别提出
a Local Forgery Guided Attention (LFGA) module用于定位伪造区域,Multi-scale Patch Feature Fusion (MPFF) modules实现对伪造区域的伪影检测。
主要模型框架包含了:
🟢 Spatial Rich Model (SRM):用于提取高频特征信息;
🟢 Cross-Modality Consistency Enhancement (CMCE) module:融合空间和频域信息;
🟢 Local Forgery Guided Attention (LFGA) module:定位伪造特征;
🟢 Multi-scale Patch Feature Fusion (MPFF) modules:检测伪造特征;

⭐ 通过余弦相似度计算空间、频域特征的相关性,通过余弦相似度和Relu激活函数修正原有特征:F' =ReLIU(F+Cor \odot F_h) ,具体代码如下所示:
class CMCE(nn.Module):
def __init__(self, in_channel=3):
super(CMCE, self).__init__()
self.relu = nn.ReLU()
self.bn = nn.BatchNorm2d(in_channel)
self.stage1 = nn.Sequential(
nn.Conv2d(in_channel, in_channel, 3, 1, bias=False),
nn.BatchNorm2d(in_channel),
nn.ReLU()
)
self.stage2 = nn.Sequential(
nn.Conv2d(in_channel, in_channel, 3, 1, bias=False),
nn.BatchNorm2d(in_channel),
nn.ReLU()
)
def forward(self, fa, fb):
(b1, c1, h1, w1), (b2, c2, h2, w2) = fa.size(), fb.size()
assert c1 == c2
cos_sim = F.cosine_similarity(fa, fb, dim=1)
cos_sim = cos_sim.unsqueeze(1)
fa = fa + fb * cos_sim
fb = fb + fa * cos_sim
fa = self.relu(fa)
fb = self.relu(fb)
return fa, fb⭐ 该模块的主要作用是学习到注意力图的位置特征,防止出现对未篡改区域进行检测。作者提取定位流的中层特征图,并通过线性变换 (论文中是通过一个 1 \times 1 的卷积核实现) 和Softmax激活函数计算注意力值,最后将注意力值作用于分类特征上,具体公式为:F'_c = \text{ReLU(Reshape}(Att \cdot h(F_c))) + F_c ,本质上就是自注意力机制的变体。

具体代码如下所示:
class LFGA(nn.Module):
def __init__(self, in_channel=3, ratio=4):
super(LFGA, self).__init__()
self.chanel_in = in_channel
self.query_conv = nn.Conv2d(
in_channels=in_channel, out_channels=in_channel//ratio, kernel_size=1)
self.key_conv = nn.Conv2d(
in_channels=in_channel, out_channels=in_channel//ratio, kernel_size=1)
self.value_conv = nn.Conv2d(
in_channels=in_channel, out_channels=in_channel, kernel_size=1)
self.gamma = nn.Parameter(torch.zeros(1))
self.softmax = nn.Softmax(dim=-1)
self.relu = nn.ReLU()
self.bn = nn.BatchNorm2d(self.chanel_in)
def forward(self, fa, fb):
B, C, H, W = fa.size()
proj_query = self.query_conv(fb).view(
B, -1, H*W).permute(0, 2, 1) # B , HW, C
proj_key = self.key_conv(fb).view(
B, -1, H*W) # B X C x (*W*H)
attention = self.softmax(torch.bmm(proj_query, proj_key)) # BX (N) X (N)
proj_value = self.value_conv(fa).view(
B, -1, H*W) # B , C , HW
out = torch.bmm(proj_value, attention.permute(0, 2, 1))
out = out.view(B, C, H, W)
out = self.gamma*out + fa
return self.relu(out)分支 | 是否用了 MPFF | 为什么 |
|---|---|---|
分割分支(Segmentation) | ✅ 使用了 MPFF | 要保留局部空间细节、处理多尺度伪造痕迹,MPFF 融合不同尺度的空间信息有助于提升定位精度 |
分类分支(Classification) | ❌ 没有使用 MPFF | 更关注整体是否为伪造,所以只需要融合统一尺度下的浅层结构特征 + 高层语义信息,重点是全局信息,MPFF 这种细致的 patch 局部匹配不是必须 |
⭐ 目前的检测方法难以发挥浅层伪影特征的特点,为了更好的利用伪影的特征,作者提出在定位流中主要关注局部空间细节特征,分类头关注全局语义特征信息。作者在定位流和分类流中都引入了MPFF. 具体来说就是,将流的中间层特征图,将其分为了k个与定位流输出特征图相同分辨率的块(不满一个块用0填充)。为了能够更好的保留局部信息特征不由于感受野的减少而丧失信息,利用所有块和输出特征图计算最后的多尺度局部特征F'_l , 将这个局部特征输入到预测头中。
class MPFF(nn.Module):
def __init__(self, size=8):
super(MPFF, self).__init__()
# fa: b * c * h1 * w1
# fb: b * c * h2 * h2
self.size = size
def forward(self, fa, fb):
b1, c1, h1, w1 = fa.size()
b2, c2, h2, w2 = fb.size()
assert b1 == b2 and c1 == c2 and self.size == h2
padding = abs(h1 % self.size - self.size) % self.size
pad = nn.ReplicationPad2d(padding=(padding // 2, (padding + 1) // 2, padding // 2, (padding + 1) // 2)).to(fa.device)
fa = pad(fa)
b1, c1, h1, w1 = fa.size()
assert h1 % self.size == 0
window = h1 // self.size
unfold = nn.Unfold(kernel_size=window, dilation=1, padding=0, stride=window)
fb = fb.repeat_interleave(window, dim=2)
fb = fb.repeat_interleave(window, dim=3)
ff = torch.tanh(fa * fb)
ff = torch.sum(ff, dim=1, keepdim=True)
ff = unfold(ff).view(b1, -1, self.size, self.size)
return ff.to(fa.device)⭐ 分类流:对于分类头的输出,作者采用了Low-rank Bilinear Pooling来合并这些特征,具体的,通过卷积快得到特征图 F^*_c , 将所有的浅层特征图经过池化层并聚合在一起,得到F_s ,最后的输出为:F'_c=P(UF_s \odot VF_c^*) + B
y0m = self.pad_max_pool(y0)
y1m = self.pad_max_pool(y1)
y2m = self.pad_max_pool(y2)
y3m = self.pad_max_pool(y3)
y5m = self.pad_max_pool(y5)
# 拼接作为 shallow feature F_s
mul_feas = torch.cat((y0m, y1m, y2m, y3m, y5m), dim=1)
# 与最终语义特征 y6 进行 HBFusion(低秩双线性融合)
cls_feas = self.HBFusion(mul_feas, y6)⭐ Low-rank Bilinear Pooling 代码:
class HdmProdBilinearFusion(nn.Module):
def __init__(self, dim1, dim2,
hidden_dim=2048, output_dim=3072, bili_affine=None,
bili_dropout=0.5, **kwargs):
super(HdmProdBilinearFusion, self).__init__()
self.hidden_dim = hidden_dim
self.output_dim = output_dim
self.Trans1 = nn.Linear(dim1, hidden_dim)
self.Trans2 = nn.Linear(dim2, hidden_dim)
self.OutTrans = nn.Linear(hidden_dim, output_dim)
if bili_dropout is None:
self.Dropout = nn.Identity()
else:
self.Dropout = nn.Dropout(bili_dropout)
def forward(self, features1, features2, **kwargs):
b1, c1, h1, w1 = features1.size()
b2, c2, h2, w2 = features2.size()
assert b1 == b2 and h1 == h2 and w1 == w1
features1 = features1.view(b1, c1, -1).permute(0, 2, 1).contiguous().view(-1, c1)
features2 = features2.view(b2, c2, -1).permute(0, 2, 1).contiguous().view(-1, c2)
prod = self.Trans1(features1) * self.Trans2(features2)
prod = torch.tanh(prod)
prod = self.OutTrans(self.Dropout(prod))
prob = prod.view(b1, -1, self.output_dim).permute(0, 2, 1).contiguous().view(b1, -1, h1, w1)
return prob🟢 人脸提取:未提及人脸提取的方法,resize为(2299\times 299),采用了数据增强
🟢 超参数:我们采用用预训练权值初始化的backbone Xception,并使用betas为0.9和0.999,epsilon为1e-8的Adam优化器。初始学习率设置为5𝑒−4,每5个epoch衰减50%。位置流预测的伪造地图的大小设置为19 x 19。MPFF模块的超参数𝑚和𝑛设置为2048和4096。
🟢 跨数据集的实验结果:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。