В тензорном потоке есть метод перемешивания пикселей, который называется deep_to_space. Он делает следующее: Предположим, у нас есть изображение (массив) размеров (4,4,4). Приведенный выше метод перемешивает значения этого массива, чтобы мы получили массив размером (16,16,1), как показано на изображении ниже:
В течение нескольких часов я пытался воссоздать этот метод в numpy, используя функции plane numpy, такие как изменение формы, транспонирование и т. д., однако мне это не удалось. Кто-нибудь знает, как это реализовать?
Очень похожую проблему можно найти в Как реализовать tf.space_to_length с помощью numpy?. Однако в этом вопросе рассматривается метод space_to_depth
, который является обратной операцией.
Вы можете сделать это с помощью чистого numpy, но я действительно рекомендую взглянуть на einops. Эту и многие более сложные операции можно выполнить за одну операцию.
@Меркурий Согласен. Я добавил einops.rearrange
решение к своему ответу.
Вот решение «сначала каналы» (т.е. при условии, что размеры вашего массива упорядочены каналы×высота×ширина):
import numpy as np
# Create some data ("channels-first" version)
a = (np.ones((1, 3, 3), dtype=int) *
np.arange(1,5)[:, np.newaxis, np.newaxis]) # 4×3×3
c, h, w = a.shape # channels, height, width
p = int(np.sqrt(c)) # height and width of one "patch"
assert p * p == c # Sanity-check
a_flat = a.reshape(p, p, h, w).transpose(2, 0, 3, 1).reshape(h * p, w * p) # 6×6
print(a)
# [[[1 1 1]
# [1 1 1]
# [1 1 1]]
# [[2 2 2]
# [2 2 2]
# [2 2 2]]
# [[3 3 3]
# [3 3 3]
# [3 3 3]]
# [[4 4 4]
# [4 4 4]
# [4 4 4]]]
print(a_flat)
# [[1 2 1 2 1 2]
# [3 4 3 4 3 4]
# [1 2 1 2 1 2]
# [3 4 3 4 3 4]
# [1 2 1 2 1 2]
# [3 4 3 4 3 4]]
А вот соответствующая версия «последние каналы» (т. е. при условии, что размеры вашего массива упорядочены по высоте×ширине×каналов):
import numpy as np
# Create some data ("channels-last" version)
a = np.ones((3, 3, 1), dtype=int) * np.arange(1, 5) # 3×3×4
h, w, c = a.shape # height, width, channels
p = int(np.sqrt(c)) # height and width of one "patch"
assert p * p == c # Sanity-check
a_flat = a.reshape(h, w, p, p).transpose(0, 2, 1, 3).reshape(h * p, w * p) # 6×6
print(a)
# [[[1 2 3 4]
# [1 2 3 4]
# [1 2 3 4]]
# [[1 2 3 4]
# [1 2 3 4]
# [1 2 3 4]]
# [[1 2 3 4]
# [1 2 3 4]
# [1 2 3 4]]]
print(a_flat)
# [[1 2 1 2 1 2]
# [3 4 3 4 3 4]
# [1 2 1 2 1 2]
# [3 4 3 4 3 4]
# [1 2 1 2 1 2]
# [3 4 3 4 3 4]]
В обоих случаях идея одна и та же:
reshape
мы разделяем размер канала (или «глубину») длины c на то, что станет патчем p×p (обратите внимание, что p·p=c, где p и c соответствуют t и t² в вопросе).transpose
мы размещаем высоту патча позади текущей высоты изображения, а ширину патча позади текущей ширины изображения.reshape
мы объединяем текущую высоту изображения и высоту патча с новой высотой изображения, а текущую ширину изображения и ширину патча с новой шириной изображения.einops
Используя rerange() из пакета einops
, как предложено в комментарии Mercury, соответствующие решения могут выглядеть следующим образом:
import numpy as np
from einops import rearrange
# Channels first
a = (np.ones((1, 3, 3), dtype=int) *
np.arange(1,5)[:, np.newaxis, np.newaxis]) # 4×3×3
p = int(np.sqrt(a.shape[0])) # height and width of one "patch"
a_flat = rearrange(a, "(hp wp) h w -> (h hp) (w wp)", hp=p)
# Channels last
a = np.ones((3, 3, 1), dtype=int) * np.arange(1, 5) # 3×3×4
p = int(np.sqrt(a.shape[-1])) # height and width of one "patch"
a_flat = rearrange(a, "h w (hp wp) -> (h hp) (w wp)", hp=p)
Можете ли вы предоставить некоторые примерные данные просто в качестве примера?