Изменение формы представления Numpy без копирования (2d движущееся / скользящее окно, полосы, маскированные структуры памяти)

У меня есть изображение, хранящееся в виде массива 2d numpy (возможно, multi-d).

Я могу сделать вид на этот массив, который отражает 2-мерное скользящее окно, но когда я изменяю его так, чтобы каждая строка была сплющенным окном (строки - это окна, столбец - это пиксель в этом окне), python делает полную копию. Это происходит потому, что я использую типичный трюк с шагом, а новая форма не является непрерывной в памяти.

Мне это нужно, потому что я передаю целые большие изображения классификатору sklearn, который принимает 2-мерные матрицы, где нет процедуры пакетной / частичной подгонки, а полная развернутая копия слишком велика для памяти.

Мой вопрос: есть ли способ сделать это, не делая полную копию представления?

Я считаю, что ответом будет либо (1) что-то о шагах или управлении памятью numpy, что я упустил, либо (2) какая-то структура замаскированной памяти для python, которая может имитировать массив numpy даже для внешнего пакета, такого как sklearn, который включает Cython.

Эта задача обучения перемещению окон двумерного изображения в памяти является распространенной, но единственная известная мне попытка учесть исправления напрямую - это проект Vigra (http://ukoethe.github.io/vigra/).

Спасибо за помощь.

>>> A=np.arange(9).reshape(3,3)
>>> print A
[[0 1 2]
 [3 4 5]
 [6 7 8]]
>>> xstep=1;ystep=1; xsize=2; ysize=2
>>> window_view = np.lib.stride_tricks.as_strided(A, ((A.shape[0] - xsize + 1) / xstep, (A.shape[1] - ysize + 1) / ystep, xsize, ysize),
...       (A.strides[0] * xstep, A.strides[1] * ystep, A.strides[0], A.strides[1]))
>>> print window_view 
[[[[0 1]
   [3 4]]

  [[1 2]
   [4 5]]]


 [[[3 4]
   [6 7]]

  [[4 5]
   [7 8]]]]
>>> 
>>> np.may_share_memory(A,window_view)
True
>>> B=window_view.reshape(-1,xsize*ysize)
>>> np.may_share_memory(A,B)
False

person locallyoptimal    schedule 18.07.2014    source источник
comment
Я думаю, что это невозможно, даже если вы передаете массив as_strided классификатору sklearn, я думаю, что большинство (если не все) классификаторов будут копировать ваши данные, если они не непрерывны.   -  person HYRY    schedule 18.07.2014
comment
Да, я уверен, что это невозможно. Прости. Если вы найдете способ, дайте мне знать;) Также: прямой ввод изображения может быть не очень хорошей идеей, а вычислительные возможности могут решить вашу проблему.   -  person Andreas Mueller    schedule 18.07.2014
comment
Определенно исключите число (1), sklearn.feature_extraction.image.extract_patches дает вам именно то представление, о котором вы говорите, и его изменение определенно сделает копию в соответствии с правилами numpy. Вы уверены, что вам нужны сразу все патчи многих изображений? Вы можете изучить онлайн / пакетные алгоритмы для решения любых задач. Попробуйте, например, SGDClassifier.   -  person eickenberg    schedule 19.07.2014
comment
@HYRY На самом деле зависит от оценщика. Смежные данные обычно не требуются.   -  person Fred Foo    schedule 21.07.2014


Ответы (1)


Ваша задача невозможна, используя только шаги, но NumPy поддерживает один вид массива, который выполняет эту работу. С помощью strides и masked_array вы можете создать желаемое представление ваших данных. Однако не все функции NumPy поддерживают операции с masked_array, поэтому возможно, что scikit-learn не справляется и с ними.

Давайте сначала по-новому взглянем на то, что мы пытаемся здесь сделать. Рассмотрим входные данные вашего примера. По сути, данные - это всего лишь одномерный массив в памяти, и это будет проще, если мы подумаем о том, как с этим справиться. Массив только кажется двумерным, потому что мы определили его форму. Используя шаги, фигуру можно определить так:

from numpy.lib.stride_tricks import as_strided

base = np.arange(9)
isize = base.itemsize
A = as_strided(base, shape=(3, 3), strides=(3 * isize, isize))

Теперь цель состоит в том, чтобы сделать base такими шагами, чтобы он упорядочивал числа, как в конечном массиве, B. Другими словами, мы запрашиваем целые числа a и b такие, что

>>> as_strided(base, shape=(4, 4), strides=(a, b))
array([[0, 1, 3, 4],
       [1, 2, 4, 5],
       [3, 4, 6, 7],
       [4, 5, 7, 8]])

Но это явно невозможно. Ближайшее представление, которое мы можем получить таким образом, - это скользящее окно над base:

>>> C = as_strided(base, shape=(5, 5), strides=(isize, isize))
>>> C
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8]])

Но разница здесь в том, что у нас есть лишние столбцы и строки, от которых мы хотели бы избавиться. Таким образом, фактически мы просим скользящее окно, которое не является непрерывным, а также совершает скачки через равные промежутки времени. В этом примере мы хотим, чтобы каждый третий элемент был исключен из окна и перескакивал через один элемент после двух строк.

Мы можем описать это как masked_array:

>>> mask = np.zeros((5, 5), dtype=bool)
>>> mask[2, :] = True
>>> mask[:, 2] = True
>>> D = np.ma.masked_array(C, mask=mask)

Этот массив содержит именно те данные, которые нам нужны, и это только представление исходных данных. Мы можем подтвердить, что данные равны

>>> D.data[~D.mask].reshape(4, 4)
array([[0, 1, 3, 4],
       [1, 2, 4, 5],
       [3, 4, 6, 7],
       [4, 5, 7, 8]])

Но, как я сказал в начале, весьма вероятно, что scikit-learn не понимает замаскированные массивы. Если он просто преобразует это в массив, данные будут неверными:

>>> np.array(D)
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8]])
person jasaarim    schedule 24.05.2015
comment
Ваш ответ появился в виде связанной ссылки на stackoverflow.com/a/35805797/901925. OP хотел изменить вид блока без копирования. Ваша маскировка заманчива - за исключением того, что многие ma функции работают, используя filled для замены замаскированных значений на безобидные (например, filled(0) для ma.sum). Это временная копия для каждой замаскированной операции. - person hpaulj; 05.03.2016