Удаление индексов из трехмерного массива - PullRequest
1 голос
/ 29 сентября 2019

Из изображения мне нужно удалить один пиксель из каждой строки. Поэтому для изображения 50 x 60 я должен удалить 50 пикселей с изображения. Расположение пикселей уже определено и доступно в виде списка индексов в форме:

[(0, 6), (1,2), (2,9), (3,12), .... , (49,23)]

, где, например, (0,6) можно прочитать как, удалить пиксель, который находится в 0-й строке и 6-м столбце. Как я могу удалить соответствующие пиксели из 3-канального (цветного) изображения?

Я знаю способ удаления из двумерного массива, например:

np.delete(gray_image[0], 6)
np.delete(gray_image[1], 2)
np.delete(gray_image[2], 9)
.
.
np.delete(gray_image[49], 23)

Но как я могу это сделать? это на изображении, которое имеет 3 канала (3D-массив)?

Ответы [ 3 ]

1 голос
/ 29 сентября 2019

Вдохновленный this post, мы расширим его до 3D регистра массива, помня, что строка будет означать, что первая ось для 3D массивов изображений и столбцов будет второй,Таким образом, удаление одного элемента в строке даст нам на один элемент меньше вдоль второй оси.

Решение будет выглядеть примерно так -

def delete_per_row_3D(a, remove_idx):
    mask = np.ones(a.shape[:-1],dtype=bool)
    mask[np.arange(len(remove_idx)), remove_idx] = False
    return a[mask].reshape(np.array(a.shape)-[0,1,0])

Пример выполнения -

In [43]: np.random.seed(0)
    ...: a = np.random.randint(10,100,(2,4,3))

In [44]: a
Out[44]: 
array([[[54, 57, 74],
        [77, 77, 19],
        [93, 31, 46],
        [97, 80, 98]],

       [[98, 22, 68],
        [75, 49, 97],
        [56, 98, 91],
        [47, 35, 87]]])

In [45]: remove_idx = np.array([2,0]) # length be a.shape[0],
         # as no. of rows in 3D image array would be that

In [46]: delete_per_row_3D(a, remove_idx)
Out[46]: 
array([[[54, 57, 74],
        [77, 77, 19],
        [97, 80, 98]],

       [[75, 49, 97],
        [56, 98, 91],
        [47, 35, 87]]])

Визуализация с каналами

Чтобы помочь читателям лучше визуализировать его, рассмотрим три фрагмента для каналов RGB / BGR -

In [47]: a[...,0]
Out[47]: 
array([[54, 77, 93, 97],
       [98, 75, 56, 47]])

In [48]: a[...,1]
Out[48]: 
array([[57, 77, 31, 80],
       [22, 49, 98, 35]])

In [49]: a[...,2]
Out[49]: 
array([[74, 19, 46, 98],
       [68, 97, 91, 87]])

Мы хотим удалитьcolumn-2 из первого ряда и column-0 из второго ряда. Итак, рассмотрим те, которые удаляются из всех трех каналов, то есть 93,31,46 и 98,22,68 удалены. Таким образом, мы закончим с показанным выводом ранее с одним элементом меньше вдоль второй оси.

Альтернативы

Снова из ранее связанного поста, еще две альтернативыbe -

def delete_per_row_3D_v2(a, remove_idx):
    mask = remove_idx[:,None]!=np.arange(a.shape[1])
    return a[mask].reshape(np.array(a.shape)-[0,1,0])

def delete_per_row_3D_v3(a, remove_idx):
    m,n = a.shape[:-1]
    rm = remove_idx+n*np.arange(m)
    return np.delete(a.reshape(-1,a.shape[-1]),rm,axis=0).reshape(m,n-1,-1)

Переместите его на следующий уровень с использованием подхода на основе вида

Мы будем использовать метод на основе вида для маскировки пикселей в группе на основе и, следовательно, достижения основных преимуществ в отношении памяти и, следовательно, производительности, как показано ниже -

def delete_per_row_3D_view(a, remove_idx):
    m,n,r = a.shape
    mask = np.ones((m,n),dtype=bool)
    mask[np.arange(len(remove_idx)), remove_idx] = False
    vd = np.dtype((np.void, a.dtype.itemsize * r))
    a_masked = a.view(vd).ravel()[mask.flat].view(a.dtype)
    return a_masked.reshape(m,-1,r)

Сравнительный анализ

Мы включаем все векторизованные методы здесь (включая все из этого поста)и из поста @Paul Panzer).

1) Корпус 3D-матрицы OP 640x340:

In [180]: np.random.seed(0)
     ...: a = np.random.randint(0,256,(640,340,3)).astype(np.uint8)
     ...: remove_idx = np.random.randint(0,340,(640))

In [181]: %timeit delete_per_row_3D(a, remove_idx)
     ...: %timeit delete_per_row_3D_v2(a, remove_idx)
     ...: %timeit delete_per_row_3D_v3(a, remove_idx)
     ...: %timeit delete_per_row_3D_view(a, remove_idx)
     ...: %timeit pp(a, remove_idx)
     ...: %timeit pp_flat(a, remove_idx)
100 loops, best of 3: 5.09 ms per loop
100 loops, best of 3: 5.31 ms per loop
100 loops, best of 3: 3.58 ms per loop
1000 loops, best of 3: 211 µs per loop
100 loops, best of 3: 4.19 ms per loop
1000 loops, best of 3: 1.23 ms per loop

2) Корпус 3D-массива Small 64x34:

In [182]: np.random.seed(0)
     ...: a = np.random.randint(0,256,(64,34,3)).astype(np.uint8)
     ...: remove_idx = np.random.randint(0,34,(64))

In [183]: %timeit delete_per_row_3D(a, remove_idx)
     ...: %timeit delete_per_row_3D_v2(a, remove_idx)
     ...: %timeit delete_per_row_3D_v3(a, remove_idx)
     ...: %timeit delete_per_row_3D_view(a, remove_idx)
     ...: %timeit pp(a, remove_idx)
     ...: %timeit pp_flat(a, remove_idx)
10000 loops, best of 3: 61.9 µs per loop
10000 loops, best of 3: 61.7 µs per loop
10000 loops, best of 3: 61.3 µs per loop
100000 loops, best of 3: 12.9 µs per loop
10000 loops, best of 3: 49.8 µs per loop
10000 loops, best of 3: 22.2 µs per loop

3)Большой корпус массива 6400x3400 3D:

In [184]: np.random.seed(0)
     ...: a = np.random.randint(0,256,(6400,3400,3)).astype(np.uint8)
     ...: remove_idx = np.random.randint(0,3400,(6400))

In [185]: %timeit delete_per_row_3D(a, remove_idx)
     ...: %timeit delete_per_row_3D_v2(a, remove_idx)
     ...: %timeit delete_per_row_3D_v3(a, remove_idx)
     ...: %timeit delete_per_row_3D_view(a, remove_idx)
     ...: %timeit pp(a, remove_idx)
     ...: %timeit pp_flat(a, remove_idx)
1 loop, best of 3: 649 ms per loop
1 loop, best of 3: 669 ms per loop
1 loop, best of 3: 434 ms per loop
10 loops, best of 3: 47.5 ms per loop
1 loop, best of 3: 415 ms per loop
10 loops, best of 3: 127 ms per loop

Итак, метод view-based: delete_per_row_3D_view уничтожает все другие подходы всех размеров.

0 голосов
/ 29 сентября 2019

Адаптировано с https://stackoverflow.com/a/58157198/7207392

import numpy as np
import operator as op

a = np.arange(60.0).reshape(4,5,3)
idxs = [(0,1), (1,3), (2, 1), (3,4)]

m,n,_ = a.shape

# extract column indices
# there are simpler ways but this is fast
columns = np.fromiter(map(op.itemgetter(1),idxs),int,m)

# build decimated array
result = np.where((columns[...,None]>np.arange(n-1))[...,None],a[:,:-1],a[:,1:])

Если вы хотите, чтобы это было как можно быстрее, возможно, стоит временно объединить последние два измерения - (pp_flat против pp в эти моменты времени):

Обновление: Никогда не сбрасывайте со счетов @Divakar (новая запись delete_per_row_3D_view) ...

enter image description here

Если вас интересует этот зигзаг, простониже 10 ^ 3, где я переключаюсь с прореживания изображения на повторяющиеся пиксели. Последний генерирует смежные массивы. Интересно, что непрерывный массив в 4 раза обрабатывается быстрее, чем меньший несмежный ... (точнее, метод не работает с несмежными массивами, поэтому мы должны сделать непрерывную копию)

ОБНОВЛЕНИЕ: добавленоверсия, которая может обрабатывать несмежные массивы, если цветовые тройки являются смежными.

Код:

from simple_benchmark import BenchmarkBuilder, MultiArgument
import numpy as np
import operator as op
from scipy.misc import face

B = BenchmarkBuilder()

@B.add_function()
def delete_per_row_3D(a, remove_idx):
    mask = np.ones(a.shape[:-1],dtype=bool)
    mask[np.arange(len(remove_idx)), remove_idx] = False
    return a[mask].reshape(np.array(a.shape)-[0,1,0])

@B.add_function()
def delete_per_row_3D_v2(a, remove_idx):
    mask = remove_idx[:,None]!=np.arange(a.shape[1])
    return a[mask].reshape(np.array(a.shape)-[0,1,0])

@B.add_function()
def delete_per_row_3D_v3(a, remove_idx):
    m,n = a.shape[:-1]
    rm = remove_idx+n*np.arange(m)
    return np.delete(a.reshape(-1,a.shape[-1]),rm,axis=0).reshape(m,n-1,-1)

@B.add_function()
def pp(a,columns):
    m,n,_ = a.shape
    return np.where((columns[...,None]>np.arange(n-1))[...,None],a[:,:-1],a[:,1:])

@B.add_function()
def pp_flat(a,columns):
    m,n,d = a.shape
    af = a.reshape(m,-1)
    return np.where(columns[...,None]>np.arange(n-1).repeat(d),af[...,:-d],af[...,d:]).reshape(m,n-1,d)


@B.add_function()
def delete_per_row_3D_view(a, remove_idx):
    m,n,r = a.shape
    mask = np.ones((m,n),dtype=bool)
    mask[np.arange(len(remove_idx)), remove_idx] = False
    vd = np.dtype((np.void, a.dtype.itemsize * r))
    a_masked = np.ascontiguousarray(a).view(vd).ravel()[mask.flat].view(a.dtype)
    return a_masked.reshape(m,-1,r)


from numpy.lib.stride_tricks import as_strided

@B.add_function()
def delete_per_row_3D_view_2(a, remove_idx):
    m,n,r = a.shape
    mask = np.ones((m,n),dtype=bool)
    mask[np.arange(len(remove_idx)), remove_idx] = False
    vd = np.dtype((np.void, a.dtype.itemsize * r))
    a_masked = as_strided(a[0,0,...].view(vd),a.shape[:-1],a.strides[:-1])[mask].view(a.dtype)
    return a_masked.reshape(m,-1,r)

@B.add_arguments('array size')
def argument_provider():
    orig = face()
    for dec in (128,64,32,16,8,4,2,1,-2,-4):
        if dec>0:
            a = orig[::dec,::dec]
        else:
            a = orig.repeat(-dec,axis=0).repeat(-dec,axis=1)
        dim_size,w,_ = a.shape
        idxs = np.random.randint(0,w,dim_size)
#        idxs = [*enumerate(np.random.randint(0,w,dim_size)),]
        yield dim_size, MultiArgument([a,idxs])

r = B.run()
r.plot()

import pylab
pylab.savefig('delunif.png')
0 голосов
/ 29 сентября 2019

Вы можете удалить пиксели, рассчитав одномерный индекс каждого пикселя, который будет удален из списка индексов, как показано ниже, а затем удалите элементы, используя индекс, затем измените массив обратно на новый размер

ar = np.arange(60).reshape(5,4,3)
indx = [(0,3), (1,1), (2,0), (3,2), (4,1)]
indx_1d = [ar.shape[2]*ar.shape[1]*i+ar.shape[2]*j + k for item in indx for k,(i,j) in enumerate([item]*3)]

deleted = np.delete(ar, indx_1d).reshape(5,3,3)

print(ar)
print("\n\n******************************")
print(deleted)

Выход

[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]
  [ 9 10 11]]

 [[12 13 14]
  [15 16 17]
  [18 19 20]
  [21 22 23]]

 [[24 25 26]
  [27 28 29]
  [30 31 32]
  [33 34 35]]

 [[36 37 38]
  [39 40 41]
  [42 43 44]
  [45 46 47]]

 [[48 49 50]
  [51 52 53]
  [54 55 56]
  [57 58 59]]]


******************************
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]]

 [[12 13 14]
  [18 19 20]
  [21 22 23]]

 [[27 28 29]
  [30 31 32]
  [33 34 35]]

 [[36 37 38]
  [39 40 41]
  [45 46 47]]

 [[48 49 50]
  [54 55 56]
  [57 58 59]]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...