Извлечь и объединить 3-е измерение массива Numpy - PullRequest
0 голосов
/ 01 февраля 2019

Фон

Из видео computerphile мне пришла идея поиграть с Стеганография наименее значимого бита .Теперь я пытаюсь извлечь и объединить все значения RGB в битовом формате изображения, используя Numpy.В конце концов, мне нужен только каждый 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-й оси на 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 голосов
/ 02 февраля 2019

Это XY проблема .Вам не нужно преобразовывать пиксели в двоичные или извлекать какие-либо конкретные биты с помощью явных методов, потому что тогда вам придется снова сшивать все заново.Вы можете напрямую делать то, что хотите, с помощью побитовых операций , потому что «двоичное» и десятичное - это два представления одного и того же числа.Комбинация AND s и SHIFT s позволит вам обнулить любую часть целого числа или выделить определенный диапазон битов

Например,

>> (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, мы должны сгладить наш массив изображений, который, как я полагаю, имеет форму (высота, ширина, 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)
...