提取并连接Numpy数组的第3维

时间:2019-02-01 15:19:45

标签: python arrays numpy split

背景

computerphile的视频中,我想到了与Least Significant Bit Steganography一起玩的想法。 现在,我尝试使用Numpy提取并连接图像的所有RGB值以位格式。最终,我只需要数组的第7位和第8位。

设置

我用Pillow加载图像,并通过以下方式提取位:

from PIL import Image
import numpy as np

img = Image.open('test.png')
arr = np.array(img)
bits = np.unpackbits(arr, axis=2)

问题

bits数组的形状现在为(1600、1200、24)以获得1600x1200像素的图像。我现在需要的是

  1. 提取每个像素的24位
  2. 将所有24位的块连接到一个1d数组中。
  3. 仅提取第7、8、15、16、23和24位,因此每个颜色分量仅提取后2位。

到目前为止的方法

我试图将{2}沿第二轴的3d数组分成3组。然后,我可以遍历1200个包含3个列表的列表,并提取最后2位,如下所示:

sp = np.split(bits, 3, axis=2)
for i in range(0, 1200):
    for j in range(0, 3):
        print(sp[j][0][i][-2:])

问题

尽管我上面的方法行得通,但我觉得必须仅使用Numpy Magic®才能有更有效的解决方案。你知道更好的方法吗?

1 个答案:

答案 0 :(得分:1)

这是XY problem。您无需使用显式方法将像素转换为二进制图像或提取任何特定位,因为那样您就必须再次将其全部缝合起来。您可以直接使用bitwise operations进行所需的操作,因为“二进制”和十进制是相同数字的两个表示形式。 ANDSHIFT的组合将使您可以将整数的任何部分归零,或隔离特定范围的位

例如,

>> (107 >> 3) & 7
5

因为

Decimal: 107      >> 3 = 13       & 7        = 5
Binary : 01101011 >> 3 = 00001101 & 00000111 = 00000101
           |-|                |-| 
         we want
         these 3

现在,假设您的信息是世界“你好”。您可以像这样方便地将字节分成四个部分。

secret = b'hello'
bits = []
for byte in secret:
    for i in range(6, -1, -2):
        bits.append((byte >> i) & 3)
bits = np.array(bits)

由于每个bits元素都包含两位,因此值的范围可以在0到3之间。如果您想到二进制中的字母“ h”,即“ 01 | 10 | 10 | 00”,则可以了解bits的前几个值是1、2、2、0等。

要利用numpy中的矢量化操作,我们应该展平图像数组,我认为图像数组的形状为(height,width,3)。

np.random.seed(0)
img = np.random.randint(0, 255, (1600, 1200, 3)).astype(np.uint8)
shape = img.shape
# this specific type of flattening puts the pixels in your desired order, i.e.,
# pixel (0, 0) red-green-blue, pixel (0, 1) red-green-blue, etc
flat_img = img.reshape(-1).copy()

现在嵌入很简单

length = len(bits)
flat_img[:length] = (flat_img[:length] & 252) + bits
stego_img = flat_img.reshape(shape)