Преоформяне на Numpy View без копиране (2d движещ се/плъзгащ се прозорец, крачки, маскирани структури на паметта)

Имам изображение, съхранено като 2d numpy масив (вероятно multi-d).

Мога да направя изглед върху този масив, който отразява 2d плъзгащ се прозорец, но когато го преоформя така, че всеки ред да е сплескан прозорец (редовете са прозорци, колоната е пиксел в този прозорец), Python прави пълно копие. Прави това, защото използвам типичния трик за крачка и новата форма не е съседна в паметта.

Имам нужда от това, защото предавам цели големи изображения на sklearn класификатор, който приема 2d матрици, където няма процедура за пакетно/частично напасване и пълното разширено копие е твърде голямо за памет.

Моят въпрос: Има ли начин да направите това, без да правите пълно копие на изгледа?

Вярвам, че отговорът ще бъде (1) нещо относно крачки или управление на паметта numpy, което съм пренебрегнал, или (2) някакъв вид маскирана структура на паметта за python, която може да емулира масив numpy дори към външен пакет като sklearn, който включва цитон.

Тази задача за обучение върху движещи се прозорци на 2d изображение в паметта е често срещана, но единственият опит, за който знам, за директно отчитане на кръпки, е проектът 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 поддържа един вид масив, който върши работата. Със стъпки и masked_array можете да създадете желания изглед на вашите данни. Въпреки това, не всички функции на NumPy поддържат операции с masked_array, така че е възможно scikit-learn да не се справя добре и с тях.

Нека първо погледнем наново какво се опитваме да направим тук. Помислете за входните данни на вашия пример. По същество данните са просто 1-d масив в паметта и е по-просто, ако помислим за крачките с това. Масивът изглежда само 2-d, защото сме дефинирали формата му. Използвайки крачки, формата може да се дефинира по следния начин:

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