使用 Python 解码隐写术图像(维基百科上的示例图像)

Using Python to Decode Steganography Images (example images at Wikipedia)

Wikipedia's Steganography Article 处有一个带有隐藏图像数据的示例图像。

维基百科注释:

Image of a tree with a steganographically hidden image. The hidden image is revealed by removing all but the two least significant bits of each color component and a subsequent normalization. The hidden image is shown (here).

问题: 我对 "subsequent normalisation" 感到困惑;假设工作 Python 2.x 基于 PIL 模块的代码,规范化因素如何影响检索?

后续归一化是每个颜色分量linear interpolation

比如说,像素 1,1 的红色分量是 234。

234的二进制表示是

In [1]: bin(234)
Out[1]: '0b11101010'

我们可以通过一些按位运算删除除两个最低有效位之外的所有内容:

In [2]: bin(234 & 0b11)
Out[2]: '0b10'

8 位图像的范围是 8 位或 256 种可能的阴影。但是我们的颜色值的范围只是 2 位或 4 种可能的色调。

归一化部分正在做 linear interpolation 拉伸 2 位值以填充 8 位 space:

In [3]: (234 & 0b11) * (256/4)
Out[2]: 128

在每个颜色分量上都这样做,猫就会出现。

规范化是将一个值范围更改为另一个值范围的过程。

由于二进制值为 00、01、10 和 11,因此范围为 [0,3]。之所以要将其标准化为 [0,255] 是为了覆盖整个像素强度范围。如果您只是从数组中保存一张图像,其值在 [0,3] 范围内,它会显示为黑色,因为所有这些值都非常接近 0 而远离 255。

image normalization 上的维基百科页面为您提供了通用公式。它还显示了将范围 [50,180](将此范围称为 A)转换为 [0,255](范围 B)的示例。

首先,我们移动 A,使其从零开始。实际上,您减去最低值 (50),使 [50,180] 变为 [0,130](称之为 C)。 C 的最大距离为 130-0 = 130,B 的最大距离为 255-0 = 255。因此您需要将 C 的所有值乘以 255/130。由于 C 中的最低值为 0,因此 C * 255/130。但是,如果 B 不是从 0 开始,您只需添加必要的偏移量即可。

为了防止单词混淆,下面是将 [2,5] 转换为 [-4,4] 的可视化。

在您的情况下,标准化是从 [0,3] 到 [0,255]。由于两个范围都从 0 开始,您不需要任何偏移量,只需乘以 255/3 即可。此结果将 {0,1,2,3} 转换为 {0,85,170,255}。

请注意,255/3 是一个整数。但是,如果您正在规范化,比如 [0,7],最准确的转换将需要浮点比例因子 255/7。无论您将其保留为浮点数还是将其四舍五入为整数,都超出了此答案的范围。

实现此目的的最简单代码是

import Image
import numpy as np

# The array will be of type `np.uint8`. For integer computations that exceed
# this range, use `np.asarray(Image.open(...), dtype=int)`.
stego = np.asarray(Image.open('Steganography_original.png'))
extracted = stego & 0b00000011
extracted *= (255 / 3)

# Compare result with the one from wikipedia
wiki_extracted = np.asarray(Image.open('Steganography_recovered.png').convert('RGB'))
if np.all(wiki_extracted == extracted):
    print 'All pixels match'