Свести 3D-массив в 2D-массив, используя вторую матрицу для выбора элементов в третьем измерении - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть два входных массива: data_arr измерений (i, j, k) и index_arr измерений (i, j).Записи в index_arr являются целыми числами в диапазоне [0, k-1].Я хотел бы создать выходной массив (output_arr) измерений (i, j), где для каждого элемента output_arr index_arr сообщает мне, какой из элементов выбрать.

Другими словами output_arr [i, j] = data_arr [i, j, index_arr [i, j]]

Очевидно, я мог бы сделать это в ледниковом темпе с двойным циклом for.Я бы предпочел что-то более быстрое, используя умную индексацию.В настоящее время лучшее, что я мог придумать, - это создание двух дополнительных 2D матриц размера (i, j).

Ниже приведен простой MWE, созданный с точки зрения создания мозаичного изображения из изображения RGB с использованием стандартного шаблона Байера.Я хотел бы иметь возможность избавиться от X_ind и Y_ind

import numpy as np
import time


if __name__ == '__main__':
    img_width = 1920
    img_height = 1080
    img_num_colours = 3

    red_arr = np.ones([img_height, img_width], dtype=np.uint16) * 10
    green_arr = np.ones([img_height, img_width], dtype=np.uint16) * 20
    blue_arr = np.ones([img_height, img_width], dtype=np.uint16) * 30

    img_arr = np.dstack((red_arr, green_arr, blue_arr))

    bayer_arr = np.ones([img_height, img_width], dtype=np.uint16)
    bayer_arr[0::2,0::2] = 0 # Red entries in bater patter
                             # Green entries are already set by np.ones intialisation
    bayer_arr[1::2,1::2] = 2 # blue entries in bayer patter
    print("bayer\n",bayer_arr[:8,:12], "\n")

    mosaiced_arr = np.zeros([img_height, img_width], dtype=np.uint16)
    Y_ind = np.repeat(np.arange(0, img_width).reshape(1, img_width), img_height, 0)
    X_ind = np.repeat(np.arange(0, img_height).reshape(img_height, 1), img_width, 1)

    start_time = time.time()
    demos_arr = img_arr[X_ind, Y_ind, bayer_arr]
    end_time = time.time()

    print(demos_arr.shape)
    print("demos\n",demos_arr[:8,:12], "\n")
    print("Mosaic took {:.3f}s".format(end_time - start_time)) 

Редактировать: Как отметил @ Georgy, этот вопрос похож на этот , который я не сделалне могу найти в моих терминах поиска, так что, возможно, этот пост будет подписывать этот пост.Ответы в другом посте применимы, хотя арифметика сплющенного индекса отличается, так как порядок моих измерений отличается.Ответ выше эквивалентен версии ogrid в другом вопросе.Фактически ogrid можно использовать, заменив следующее изменение кода:

# Y_ind = np.repeat(np.arange(0, img_width).reshape(1, img_width), img_height, 0)
# X_ind = np.repeat(np.arange(0, img_height).reshape(img_height, 1), img_width, 1)
X_ind, Y_ind = np.ogrid[0:img_height, 0:img_width]

Вы можете реализовать опцию выбора (ограниченную выбором из 32 опций) следующим образом:

start_time = time.time()
demos_arr = bayer_arr.choose((img_arr[...,0], img_arr[...,1], img_arr[...,2]))
end_time = time.time()

Решение ogrid работает за 12 мс, а решение выбора за 34 мс на моей машине

1 Ответ

0 голосов
/ 07 декабря 2018

Вы хотите numpy.take_along_axis:

output_arr = numpy.take_along_axis(data_arr, index_arr[:, :, numpy.newaxis], axis=2)
output_arr = output_arr[:,:,0]  # Since take_along_axis keeps the same number of dimensions

Эта функция является новой в numpy 1.15.0.

https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.take_along_axis.html

Обратите внимание, что data_arr и index_arr необходимоимеют одинаковое количество измерений.Таким образом, вам нужно изменить форму index_array на 3 измерения и впоследствии изменить форму результата на 2 измерения.Т.е.:

start_time = time.time()
demos_arr = np.take_along_axis(img_arr, bayer_arr.reshape([img_height, img_width, 1]), axis=2).reshape([img_height, img_width])
end_time = time.time()

Результаты синхронизации для взятия вдоль оси такие же, как и в реализации ogrid.

...