Обрезать пустые массивы (отступы) из тома - PullRequest
1 голос
/ 10 октября 2019

Что я хочу сделать, это обрезать том, чтобы удалить все ненужные данные. Например, скажем, у меня есть объем 100x100x100, заполненный нулями, за исключением объема 50x50x50, который заполнен единицами. Как получить обрезанный том 50x50x50 из оригинала?

Вот наивный метод, который я придумал.

import numpy as np
import tensorflow as tf

test=np.zeros((100,100,100)) # create an empty 100x100x100 volume
rand=np.random.rand(66,25,34) # create a 66x25x34 filled volume
test[10:76, 20:45, 30:64] = rand # partially fill the empty volume

# initialize the cropping coordinates
minx=miny=minz=0
maxx=maxy=maxz=0
maxx,maxy,maxz=np.subtract(test.shape,1)

# compute the optimal cropping coordinates
dimensions=test.shape
while(tf.reduce_max(test[minx,:,:]) == 0): # check for empty slices along the x axis
    minx+=1
while(tf.reduce_max(test[:,miny,:]) == 0): # check for empty slices along the y axis
    miny+=1
while(tf.reduce_max(test[:,:,minz]) == 0): # check for empty slices along the z axis
    minz+=1
while(tf.reduce_max(test[maxx,:,:]) == 0):
    maxx-=1
while(tf.reduce_max(test[:,maxy,:]) == 0):
    maxy-=1
while(tf.reduce_max(test[:,:,maxz]) == 0):
    maxz-=1

maxx,maxy,maxz=np.add((maxx,maxy,maxz),1)
crop = test[minx:maxx,miny:maxy,minz:maxz]

print(minx,miny,minz,maxx,maxy,maxz)
print(rand.shape)
print(crop.shape)

Это печатает:

10 20 30 76 45 64
(66, 25, 34)
(66, 25, 34)

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

NB:

  • Подобъем не обязательно должен быть кубоидом, он может иметь любую форму.

  • Я хочу сохранить зазоры внутри подобъема, только удалить то, что находится "за пределами" обрезаемой формы.

Ответы [ 2 ]

1 голос
/ 10 октября 2019

(Правка) Упс, я не видел комментарий о сохранении так называемых "пробелов" между элементами! Это должно быть тем, наконец.

def get_nonzero_sub(arr):
    arr_slices = tuple(np.s_[curr_arr.min():curr_arr.max() + 1] for curr_arr in arr.nonzero())
    return arr[arr_slices]
0 голосов
/ 10 октября 2019

Пока вы ожидаете разумного ответа (я бы предположил, что это где-то встроенная функция в библиотеке обработки изображений), вот способ

y, x = np.where(np.any(test, 0))
z, _ = np.where(np.any(test, 1))
test[min(z):max(z)+1, min(y):max(y)+1, min(x):max(x)+1]

Я думаю, что отказ от tf из этого должен повысить вашу производительность.


Пояснение (на основе 2D-массива)

test = np.array([
    [0, 0, 0, 0, 0, ],
    [0, 0, 1, 2, 0, ],
    [0, 0, 3, 0, 0, ],
    [0, 0, 0, 0, 0, ],
    [0, 0, 0, 0, 0, ],
])

Мы хотим обрезать его, чтобы получить

[[1, 2]
 [3, 0]]
  1. np.any(..., 0) это будет "перебирать" по оси 0 и возвращать True, если любые элементов в срезе truey . Я показываю результат этого в комментариях здесь:

    np.array([
        [0, 0, 0, 0, 0, ],  # False
        [0, 0, 1, 2, 0, ],  # True
        [0, 0, 3, 0, 0, ],  # True
        [0, 0, 0, 0, 0, ],  # False
        [0, 0, 0, 0, 0, ],  # False
    ])
    

    т.е. он возвращает np.array([False, True, True, False, False])

  2. np.any(..., 1) делает то же самое, что шаг 2, но по оси1 вместо оси ноль, т.е.

    np.array([
        [0,     0,     0,     0,     0,    ], 
        [0,     0,     1,     2,     0,    ],
        [0,     0,     3,     0,     0,    ],
        [0,     0,     0,     0,     0,    ],
        [0,     0,     0,     0,     0,    ],
    #    False  False  True   True   False
    ])
    

    Обратите внимание, что в случае трехмерного массива эти шаги возвращают двумерные массивы

  3. (x,) = np.where(...) это возвращает значения индекса истинных значений в массиве. Так что np.where([False, True, True, False, False]) возвращает (array([1, 2]),). Обратите внимание, что это кортеж, поэтому в 2D случае нам нужно было бы вызвать (x,) = ..., поэтому x это просто массив array([1, 2]). Синтаксис лучше в 2D случае, так как мы можем использовать распаковку кортежей, то есть x, y = ...

  4. Обратите внимание, что в 3D случае np.where может дать нам значение для 2 осей ввремя. Я решил сделать XY за один раз, а затем Z-? во второй раз. ? это либо x, либо y, я не могу решить, какие из них нам не нужны, и я выбрасываю их в переменную с именем _, которая по соглашению является подходящим местом для хранения нежелательной информации, которую вы не делаете. на самом деле хочу. Примечание. Мне нужно сделать z, _ =, так как я хочу распаковать кортежи, а не просто z =, иначе z станет кортежем с обоими массивами.

  5. Ну, этот шаг довольнопочти так же, как то, что вы сделали в конце своего ответа, так что я полагаю, вы понимаете это. Простое нарезание в каждом измерении от первого элемента со значением в этом измерении до последнего. Вам нужен + 1, потому что нарезка в python не включает индекс после :.

Надеюсь, это понятно?

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