- ECCV 2018 斯坦福-李飞飞团队
一、问题描述
受以下启发:
对抗性例子的发现:深度学习模型在图像识别任务中对微小的、难以察觉的输入扰动非常敏感,这些扰动可以导致模型错误分类图像。这一现象表明,神经网络可以在图像中编码信息,而这些信息对人类观察者来说是不可见的。
深度网络的表征能力:深度学习模型,尤其是卷积神经网络,已经证明在图像处理任务中非常有效,因为它们能够学习到复杂的图像特征。这种强大的特征提取能力可以用来开发更高级的数据隐藏技术。
二、解决方法
HiDDeN(Hiding Data with Deep Networks)方法的意义在于它提出了一种新颖的、基于深度学习的框架END,用于在数字图像中隐藏信息。这种方法在隐写术和数字水印领域具有重要意义。
2.1信息如何嵌入?
import torch
import torch.nn as nn
from options import HiDDenConfiguration
from model.conv_bn_relu import ConvBNRelu
class Encoder(nn.Module):
"""
Inserts a watermark into an image.
"""
def __init__(self, config: HiDDenConfiguration):
super(Encoder, self).__init__()
self.H = config.H
self.W = config.W
self.conv_channels = config.encoder_channels
self.num_blocks = config.encoder_blocks
layers = [ConvBNRelu(3, self.conv_channels)]
for _ in range(config.encoder_blocks-1):
layer = ConvBNRelu(self.conv_channels, self.conv_channels)
layers.append(layer)
self.conv_layers = nn.Sequential(*layers)
self.after_concat_layer = ConvBNRelu(self.conv_channels + 3 + config.message_length,
self.conv_channels)
self.final_layer = nn.Conv2d(self.conv_channels, 3, kernel_size=1)
def forward(self, image, message):
#1、扩展秘密信息:message 张量首先通过 unsqueeze 操作增加了两个维度,使其成为四维张量,这样可以匹配图像的高度和宽度维度。然后,使用 expand 方法将 message 张量扩展到整个图像的空间尺寸(self.H x self.W),确保每个像素位置都包含秘密信息的一部分。
expanded_message = message.unsqueeze(-1)
expanded_message.unsqueeze_(-1)
expanded_message = expanded_message.expand(-1,-1, self.H, self.W)
#2、特征提取:载体图像通过一系列卷积层(ConvBNRelu 块)进行处理,这些层提取图像的特征并将其编码。这些特征包含了图像的视觉信息,并且是通过深度学习模型训练得到的。
encoded_image = self.conv_layers(image)
#3、信息融合:扩展后的秘密信息(expanded_message)、编码后的图像特征(encoded_image)和原始载体图像(image)沿着通道维度(dim=1)合并。这样,每个像素点不仅包含了原始图像的信息,还包含了秘密信息和编码后的特征图,实现了秘密信息与图像特征的融合。
concat = torch.cat([expanded_message, encoded_image, image], dim=1)
#4、最终编码:合并后的特征图通过另一个 ConvBNRelu 层(after_concat_layer)进一步处理,最后通过一个1x1卷积层(final_layer)生成最终的含密图像。这个过程确保了秘密信息被嵌入到图像的像素中,同时保持了图像的视觉质量。
im_w = self.after_concat_layer(concat)
im_w = self.final_layer(im_w)
return im_w
在代码中,秘密信息
message
首先通过unsqueeze
操作增加了两个维度,这样原本可能是一维或二维的秘密信息张量变成了四维张量。这个四维张量的形状是[batch_size, message_length, 1, 1]
,其中batch_size
可以是1(表示单个图像),message_length
是秘密信息的长度。接着,使用
expand
方法,秘密信息被扩展到与图像相同的空间尺寸(self.H
xself.W
)。这里的关键是expand
方法是如何工作的:它不会复制秘密信息的实际值到每个像素,而是创建一个新的视图(view),在这个视图中,秘密信息似乎在每个像素位置上都有出现,但实际上是在内存中共享同一份数据。因此,当
expanded_message
与图像和编码后的图像特征图沿着通道维度合并时,秘密信息的每个字节(或相应的部分)似乎都出现在了图像的每个像素上。这意味着秘密信息不是简单地复制多份,而是在空间上“展开”,使得每个像素点的通道中都包含了秘密信息的一部分。这样,当秘密信息与图像特征合并后,秘密信息就被嵌入到了图像的每个像素中。即使图像的某部分被剪切,剩余部分的像素仍然包含了秘密信息的完整表示,因为每个像素都“参与”了秘密信息的表示。因此,即使只有图像的一部分存在,也可以从中恢复出完整的秘密信息。
expend:
补充:“视图”(view),我们通常是指对数据的一种引用或表示方式,它允许我们以不同的方式访问和操作原始数据,而不需要复制数据本身。
在PyTorch中,
expand
方法创建的是一个新视图,它允许秘密信息张量在不同的空间位置重复使用相同的数据。这意味着,尽管看起来秘密信息被复制到了图像的每个像素上,实际上在内存中,秘密信息的数据只存储了一次,并且这个数据被多个位置引用。例如,如果秘密信息是一个长度为100的一维张量,使用
expand
方法可以创建一个新的四维张量,其形状可能是[1, 100, 100, 100]
。在这个新张量中,秘密信息似乎在每个100x100的平面上都出现了,但实际上,秘密信息的数据并没有被复制100x100次。它们在内存中仍然是一份,只是在访问时表现得像是在每个位置上都有出现。
2.2 噪声层怎么设计?
(1)噪声层类型
1)Identity Layer(身份层)
- 功能:这是最基本的噪声层,它不改变编码图像 Ien,即直接传递图像而不进行任何处理。
- 应用:作为基准,用于比较其他噪声层的效果。
2) Dropout Layer(随机丢弃层)
- 功能:随机将图像中的一部分像素置为零,这部分像素是从原始载体图像 Ico中保留的,其余像素则从编码图像 Ien中保留。
- 控制参数:保留像素的比例 p。
3)Cropout Layer(裁剪失活层)
- 功能:与 Dropout 类似,但以块的方式进行,随机选择图像的一部分(如一个方块区域)进行保留,其余部分用原始图像的相应部分替换。
- 控制参数:保留区域的比例p。
4)Gaussian Layer(高斯模糊层)
- 功能:使用高斯核对编码图像进行模糊处理。
- 控制参数:高斯核的宽度 σ。
5)Crop Layer(裁剪层)
- 功能:随机裁剪编码图像的一个区域,可能包含图像的部分内容。
- 控制参数:裁剪后图像与原图像的大小比例 p。
6)JPEG Layer(JPEG压缩层)
- 功能:模拟 JPEG 压缩对图像的影响。
- 控制参数:JPEG 压缩的质量因子 Q。在JPEG压缩中,质量因子 Q 是一个重要的参数,它决定了图像的压缩比率和图像质量之间的平衡:
- 高质量 Q:较低的压缩率,保留更多的图像细节,文件大小较大。
- 低质量 Q:较高的压缩率,丢失更多的图像细节,文件大小较小。
(2)JPEG压缩模拟
JPEG 压缩通过执行离散余弦变换(DCT)将图像分解为 8x8 的频率组件网格,然后对这些频率组件进行量化。高频组件的量化更激进,即在压缩过程中丢失更多细节。
1)JPEG压缩原理
JPEG压缩通过以下步骤实现:
- 离散余弦变换(DCT):将图像分割成8x8的块,并为每个块进行DCT变换,将空间域数据转换为频率域数据。
- 量化:对DCT系数进行量化,高频部分的量化步长较大,导致高频信息丢失较多,这部分是压缩过程中的主要信息损失来源。
2)非可微分问题
量化步骤是非线性的,且不可微分,这意味着我们不能使用传统的梯度下降法来优化涉及JPEG压缩的网络。为了解决这个问题,论文中提出了两种可微分的近似方法:
①JPEG-Mask
目的:模拟JPEG压缩中的量化过程,通过保留低频DCT系数,将高频系数置零。
操作步骤:
保留低频系数:低频DCT系数通常包含了图像的主要信息,如图像的整体结构和平滑区域。在
JPEG-Mask
操作中,这些系数被保留(不被置零),因为它们对图像的重建质量至关重要。置零高频系数:高频DCT系数通常包含图像的细节信息,如边缘和纹理。在JPEG-Mask操作中,这些系数被置零,模拟了JPEG压缩中高频系数的丢失。这种操作有助于压缩图像,因为人眼对这些细节的敏感度较低。
效果:通过这种方式,图像的大致轮廓和平滑区域得以保留,而一些细微的特征可能会丢失。这有助于减小文件大小,同时对视觉质量的影响较小。
②JPEG-Drop
目的:通过随机丢弃高频DCT系数,模拟JPEG压缩中的量化不确定性。
操作步骤:
随机丢弃:根据预定的概率模型,每个高频DCT系数有一定概率被置零。这种操作模拟了JPEG压缩中高频系数的量化丢失。
保留低频系数:与
JPEG-Mask
类似,JPEG-Drop
操作通常也会保留低频系数,因为它们包含了图像的主要信息。效果:
JPEG-Drop
提供了一种更为灵活的模拟方式,因为它引入了随机性。这意味着不是所有的高频系数都会被丢弃,而是根据一定的概率进行。这可以更好地模拟真实世界中的JPEG压缩效果,其中不同图像的压缩结果可能会有所不同。
③留下的信息如何操作
在
JPEG-Mask
和JPEG-Drop
操作之后,留下的信息(主要是低频系数)将用于图像的重建:
逆DCT变换:使用留下的DCT系数进行逆DCT变换,从频率域转换回空间域。
图像重建:重建的图像保留了主要的结构信息,但可能会丢失一些细节。这种权衡是JPEG压缩的关键,它允许在显著减小文件大小的同时保持可接受的图像质量。
训练深度学习模型:在模拟JPEG压缩的深度学习模型中,这些操作使得模型能够学习到如何在压缩和重建过程中保持图像的重要特征。
通过这两种方法,可以在深度学习模型中有效地模拟JPEG压缩的效果,从而训练出对JPEG压缩具有鲁棒性的模型。
2.3 信息如何提取?
import torch.nn as nn
from options import HiDDenConfiguration
from model.conv_bn_relu import ConvBNRelu
class Decoder(nn.Module):
def __init__(self, config: HiDDenConfiguration):
super(Decoder, self).__init__()
self.channels = config.decoder_channels
# 创建包含7个Conv-BN-ReLU块的网络结构
layers = [ConvBNRelu(3, self.channels)]
for _ in range(config.decoder_blocks - 1):
layers.append(ConvBNRelu(self.channels, self.channels))
# 最后一个Conv-BN-ReLU块将通道数从self.channels转换为消息长度L
layers.append(ConvBNRelu(self.channels, config.message_length))
# 添加全局平均池化层
layers.append(nn.AdaptiveAvgPool2d(output_size=(1, 1)))
self.layers = nn.Sequential(*layers)
# 线性层将池化后的输出映射到消息空间
self.linear = nn.Linear(config.message_length, config.message_length)
def forward(self, image_with_wm):
x = self.layers(image_with_wm)
# 压缩维度以匹配线性层的输入要求
x.squeeze_(3).squeeze_(2)
x = self.linear(x)
return x
2.4 损失函数设计
在训练过程中,模型通过最小化综合损失函数来优化编码器、解码器和鉴别器的参数。这包括:
- 使用随机梯度下降(SGD)或其变体(如Adam优化器)来更新参数。
- 通过计算损失函数的梯度并应用这些梯度来调整参数,以减少损失。
(1)消息失真损失:用于衡量解码后的消息与原始消息之间的差异。其目的是确保解码器能准确地从编码图像中恢复出原始消息。
(2)图像失真损失:用于衡量编码图像与原始载体图像之间的视觉差异。目的是使编码图像在视觉上与原始图像尽可能相似,以避免引起观察者的注意。
(3)对抗损失(损失函数使用了二元交叉熵损失(Binary Cross-Entropy Loss),它是分类问题中常用的损失函数,特别是当输出是一个概率值时。):对抗损失用于训练编码器以生成能够欺骗鉴别器的编码图像。鉴别器的目标是区分编码图像和原始载体图像,而编码器的目标是使鉴别器难以进行这种区分。
三、实验部分
数字水印技术无开放源代码的实现。作为基线,论文与Digimarc进行了比较,这是一个用于数字水印的闭源商业软件包。实验中将容量、保密性、鲁棒性作为评估标准。隐写术方面侧重于嵌入的容量与保密性,水印方面侧重鲁棒性。
为了有效检测隐写图像,隐写分析器通常需要一个训练过程,这个过程需要大量的标记数据,即已知含有隐藏信息的图像(载密图像)和不含有隐藏信息的图像(载体图像)。然而,在实际操作中,获取大量准确标记的载密图像是比较困难的,因为这些图像中隐藏的信息是未知的。
隐蔽性测试实验步骤:
1、未标记的测试数据集:ATS从一个包含未知是否含有隐藏信息的图像集合开始。
2、反复应用隐写算法:ATS对这些未标记的图像反复应用隐写算法,将随机生成的信息隐藏进去。
3、生成人工训练集:通过上述过程,ATS能够创建出一组新的图像,其中一半理论上含有隐藏信息(因为算法被应用了),另一半理论上没有(因为没有应用算法或信息被隐藏得当,使得算法检测不出来)。
4、训练监督分类器:使用这个人工生成的训练集,ATS训练出一个分类器,这个分类器的目标是学会区分哪些图像是载体图像,哪些是载密图像。
5、评估模型:最后,这个训练好的分类器被用来测试其在实际未知图像上的检测能力。
顶部:展示了Digimarc和HiDDeN模型在不同噪声层训练下,编码图像与封面图像之间的平均PSNR。 底部:展示了原始封面图像和使用Digimarc以及HiDDeN模型(经过Crop, Gaussian, 和Combined噪声层训练)编码的图像。 底部右侧:展示了一个使用组合噪声层训练但没有对抗性训练的模型生成的编码图像,显示了明显的伪影。
对比实验的实验流程:
(1)估算Digimarc的容量:首先估计Digimarc能处理的信息量。
(2)应用纠错代码:将HiDDeN的比特率与Digimarc的容量匹配,使用纠错代码来比较两者的性能。
(3)性能比较:研究者们将HiDDeN的比特准确率转换成解码成功率,并与Digimarc的解码成功率进行比较。
(4)性能评估:如果HiDDeN的比特准确率是95%或更高,就认为等同于Digimarc的成功解码;如果比特准确率是90%或更低,则认为是解码失败。
实验结果:在图中,研究者们展示了在不同噪声干扰和强度下HiDDeN模型的性能,并与Digimarc进行比较。图中展示了没有经过噪声处理训练的模型(蓝色)、专门针对特定噪声训练的模型(橙色)以及对所有噪声类型都训练过的综合模型(绿色)的性能。同时,还展示了Digimarc在256x256图像上的解码成功率(紫色)。
四、后续更多的工作
这篇论文比较经典,对比实验少,具有一定的意义。后续研究者开展了很多工作。END、MBRS、De-END、FIN.....
付章杰, 王帆, 孙星明, 等. 基于深度学习的图像隐写方法研究[J]. 计算机学报, 2020, 43(9): 1656-1672.