Numpy - создание перекрывающихся трехмерных подмассивов в качестве векторов с эффективным использованием памяти - PullRequest
2 голосов
/ 01 марта 2012

Я пытаюсь создать список всех перекрывающихся подмассивов одинакового размера из большого трехмерного массива (для сегментации на основе патчей), где каждый подмассив должен быть сведен (как одномерный вектор), чтобы я мог использовать шариковое дерево в sklearn.neighbours.BallTree.

Так, например, учитывая изображение 100x100x100, если бы я разбил его на перекрывающиеся патчи 5x5x5 (подрешетки), у меня было бы 96x96x96 = 884 736 из них.

Однако я не нашел никакого способа сделать это без большого выделения памяти для каждого уплощенного / векторизованного подмассива. Похоже, это связано с тем, что каждый подмассив не является непрерывным в памяти.

например. Для изображения размером 100x100x100, если я хочу, чтобы каждый патч 5x5x5 представлял собой одномерный вектор (длиной 125), numpy решает выделить новый массив в памяти для всех 884 736 из них, который затем становится довольно большим, особенно если я хочу работать с более чем одно изображение размером 100x100x100!

Я бы приветствовал любые решения для преодоления этой проблемы с памятью в python / numpy. Я рассматривал возможность создания подкласса объекта numpy.ndarray, который хранит указатель на расположение патча на большом изображении, но возвращает данные в виде 1D-массива только при вызове (и затем он снова удаляется, когда не используется) но я не нашел достаточно подробностей о создании подклассов ndarray объектов для этого. Я буду очень разочарован, если единственным решением будет реализовать все на C / C ++. Я ценю любую помощь, которая может быть оказана, спасибо!

1 Ответ

1 голос
/ 02 марта 2012

Исходя из вашего вопроса, вы уже можете знать обо всем этом. Тем не менее, я публикую этот «ответ» как более подробное обсуждение проблем, потому что многие люди могут не знать о них ....

Если вы этого не сделаете, вы можете создать массив 96x96x96x5x5x5 из изображения 100x100x100, который действует как движущееся окно 5x5x5, без выделения дополнительной памяти.

Однако, поскольку вы можете иметь только один шаг на измерение, нет способа преобразовать его в массив 96x96x96x125 без создания копии.

Во всяком случае, вот пример (в основном взят прямо из одного из моих предыдущих ответов ):

import numpy as np

def rolling_window_lastaxis(a, window):
    """Directly taken from Erik Rigtorp's post to numpy-discussion.
    <http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html>"""
    if window < 1:
       raise ValueError, "`window` must be at least 1."
    if window > a.shape[-1]:
       raise ValueError, "`window` is too long."
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

def rolling_window(a, window):
    """Takes a numpy array *a* and a sequence of (or single) *window* lengths
    and returns a view of *a* that represents a moving window."""
    if not hasattr(window, '__iter__'):
        return rolling_window_lastaxis(a, window)
    for i, win in enumerate(window):
        if win > 1:
            a = a.swapaxes(i, -1)
            a = rolling_window_lastaxis(a, win)
            a = a.swapaxes(-2, i)
    return a

x = np.zeros((100,100,100), dtype=np.uint8)
y = rolling_window(x, (5,5,5))
print 'Now *y* will be a 96x96x96x5x5x5 array...'
print y.shape
print 'Representing a "rolling window" into *x*...'
y[0,0,0,...] = 1
y[1,1,0,...] = 2
print x[:10,:10,0] # Note that *x* and *y* share the same memory!

Это дает:

Now *y* will be a 96x96x96x5x5x5 array...
(96, 96, 96, 5, 5, 5)
Representing a "rolling window" into *x*...
[[1 1 1 1 1 0 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [0 2 2 2 2 2 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]

Однако, как вы уже заметили, мы не можем изменить это на 96x96x96x125 без создания копии. y.shape = (96,96,96,-1) вызовет ошибку, и z = y.reshape((96,96,96,-1)) будет работать, но вернет копию.

(Соответствующая документация находится в numpy.reshape, если это кажется сбивающим с толку. Обычно reshape избегает копирования, когда это возможно, и возвращает копию, если это не так, тогда как установка атрибута shape вызовет ошибку, когда копирование не выполняется. не возможно.)

Однако, даже если вы создадите более эффективный контейнер массивов, sklearn.neighbors.BallTree почти наверняка сделает временные промежуточные копии.

Вы упоминаете, что делаете сегментацию изображения. Почему бы не взглянуть на более эффективный алгоритм, чем "грубая сила", которую вы пытаетесь использовать? (Или, если это неосуществимо, дайте нам еще несколько подробностей о том, почему ... Может быть, у кого-то будет идея получше?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...