Поменять размеры массива с помощью Ellipsis? - PullRequest
0 голосов
/ 13 ноября 2018

Этот код меняет местами первый и последние каналы RBG изображения, которое загружается в массив Numpy:

img = imread('image1.jpg')

# Convert from RGB -> BGR
img = img[..., [2, 1, 0]]

Хотя я понимаю использование Ellipsis для нарезки в массивах Numpy, я не мог понять, как использовать Ellipsis здесь.Кто-нибудь может объяснить, что именно здесь происходит?

1 Ответ

0 голосов
/ 13 ноября 2018

ТЛ; др

img[..., [2, 1, 0]] дает тот же результат, что и взятие срезов img[:, :, i] для каждого i в массиве индекса [2, 1, 0], а затем размещение результатов по последнему измерению img. Другими словами:

img[..., [2,1,0]]

выдаст тот же результат, что и:

np.stack([img[:,:,2], img[:,:,1], img[:,:,0]], axis=2)

Многоточие ... - это заполнитель, который сообщает numpy, к какой оси применять массив индекса. Без ... индексный массив будет применен к первой оси img вместо последней. Таким образом, без ..., указатель индекса:

img[[2,1,0]]

выдаст тот же результат, что и:

np.stack([img[2,:,:], img[1,:,:], img[0,:,:]], axis=0)

Что говорят документы

Это пример того, что в документах называется "Объединение расширенной и базовой индексации" :

Если в индексе есть хотя бы один фрагмент (:), многоточие (...) или np.newaxis (или массив имеет больше измерений, чем расширенных индексов), то поведение может быть более сложным. Это похоже на объединение результатов индексации для каждого элемента расширенного индекса.

Далее описывается, что в этом

В случае

измерения из расширенных операций индексации [в вашем примере [2, 1, 0]] вставляются в массив результатов в том же месте, в котором они находились в исходном массиве (последняя логика - это то, что делает простое расширенное индексирование таким же образом нарезка).

2D корпус

Документы не самые простые для понимания, но в этом случае их не так уж сложно разобрать. Начните с более простого 2D-кейса:

arr = np.arange(12).reshape(4,3)

array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

Использование такого же вида расширенной индексации с одним значением индекса дает:

arr[:, [1]]

array([[ 1],
       [ 4],
       [ 7],
       [10]])

который является первым столбцом arr. Другими словами, вы получили все возможные значения от arr, удерживая индекс последней оси фиксированным. Как сказал @hpaulj в своем комментарии, многоточие должно быть заполнителем. Он фактически говорит numpy свободно выполнять итерации по всем осям, кроме последней, к которой применяется индексный массив.

Вы также можете использовать этот синтаксис индексации для перемещения столбцов arr по желанию:

arr[..., [1,0,2]]

array([[ 1,  0,  2],
       [ 4,  3,  5],
       [ 7,  6,  8],
       [10,  9, 11]])

По сути, это та же операция, что и в вашем примере, но для двумерного массива вместо трехмерного.

Вы можете объяснить, что происходит с arr[..., [1,0,2]], разбив его на более простые операции индексирования. Это как если бы вы сначала взяли возвращаемое значение arr[..., [1]]:

array([[ 1],
       [ 4],
       [ 7],
       [10]])

тогда возвращаемое значение arr[..., [0]]:

array([[0],
       [3],
       [6],
       [9]])

затем возвращаемое значение arr[..., [1]]:

array([[ 2],
       [ 5],
       [ 8],
       [11]])

и, наконец, объединяет все эти результаты в один массив формы (*arr.shape[:-1], len(ix)), где ix = [2, 0, 1] - индексный массив. Данные по последней оси упорядочены в соответствии с их порядком в ix.

Один хороший способ точно понять, что делает многоточие, - выполнить ту же операцию без него:

arr[[1,0,2]]

array([[6, 7, 8],
       [0, 1, 2],
       [3, 4, 5]])

В этом случае массив индекса применяется к первой оси arr, поэтому на выходе получается массив, содержащий [1,0,2] строки arr. Добавление ... перед массивом индекса говорит numpy о применении массива индекса к последней оси arr.

Ваш 3D чехол

Случай, о котором вы спрашивали, является трехмерным эквивалентом примера 2D arr[..., [1,0,2]], приведенного выше. Скажите, что img.shape - это (480, 640, 3). Вы можете думать о img[..., [2, 1, 0]] как о цикле по каждому значению i в ix=[2, 1, 0]. Для каждого i операция индексирования будет собирать пластинку с формой (480, 640, 1), которая находится вдоль i-го индекса последней оси img. Как только все три плиты собраны, конечный результат будет эквивалентен объединению вдоль их последней оси (и в порядке, в котором они были найдены).

примечания

  • Единственная разница между arr[..., [1]] и arr[:,1] в том, что arr[..., [1]] сохраняет форму данных из исходного массива.

  • Для двумерного массива arr[:, [1]] эквивалентно arr[..., [1]]. : действует как заполнитель, как и ..., но только для одного измерения.

...