Эффективно итеративно подмножать двумерный массив numpy

У меня есть большое изображение, на котором я хочу выполнить операцию из движущегося окна по всему окну. Вот воспроизводимый пример:

Учитывая массив с именем image формы (5, 5), мне нужно извлечь подмножества из массива в окнах 3x3.

import numpy as np

# example dada
image = np.array([[1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]])

Выход[1]:

array([[1, 2, 3, 4, 5],
       [1, 2, 3, 4, 5],
       [1, 2, 3, 4, 5],
       [1, 2, 3, 4, 5],
       [1, 2, 3, 4, 5]])

Для окна 3x3 первое подмножество:

# first iteration
window_size = 3
image[0:window_size, 0:window_size] # from 1st to 3th row and from 1st to 3th col

Выход[2]:

array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

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

for i in range(0, image.shape[0]-window_size+1):
   for j in range(0, image.shape[1]-window_size+1):
      a = (j,i)     # top left value
      b = (j,i+window_size)   # top right value
      c = (j+window_size,i)   # bottom left value
      d = (j+window_size,i+window_size) # bottom right value
      print('Window position', a,b,c,d)
      subset = image[i:i+window_size, j:j+window_size]
      print(subset)

Есть ли более эффективный способ выполнить эту операцию и избежать выполнения этих двух циклов?

numpy.org/devdocs/reference/generated/… — Я не знаю, намного ли это быстрее, в документации предупреждают, что это медленнее, чем специализированные решения, такие как свертка.
Cris Luengo 05.07.2023 16:27

Вы можете использовать np.lib.stride_tricks.sliding_window_view(image, (3, 3)), но это зависит от того, что вы хотите с этим делать…

mozway 05.07.2023 16:28

Кстати, ваш код для нарезки неверен, это должно быть: image[i:i+window_size, j:j+window_size]

mozway 05.07.2023 16:32

Ваш подход - это то, чего я жду. Большое спасибо. PS: Исправил ошибку в воспроизводимом коде.

sermomon 05.07.2023 16:38

Есть несколько других вопросов о векторизации движущихся окон в массивах, на которые вы должны ссылаться, здесь , здесь и здесь

astroChance 05.07.2023 16:56

Я не знал, что вы можете использовать кортежи для индексации таким образом, поэтому я попытался запустить ваш код. Как я и ожидал, image[a:b,a:c] не работает. Наверное, так и должно быть image[a[0]:d[0],a[1]:d[1]].

Cris Luengo 05.07.2023 17:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
67
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте выполнить следующие действия:

import numpy as np

# example data
image = np.array([[1, 2, 3, 4, 5],
                  [1, 2, 3, 4, 5],
                  [1, 2, 3, 4, 5],
                  [1, 2, 3, 4, 5],
                  [1, 2, 3, 4, 5]])

# Define the window size
window_size = 3

# Calculate the number of rows and columns in the output array
output_shape = (image.shape[0] - window_size + 1, image.shape[1] - 
window_size + 1)

# Create an empty array to store the subsets
subsets = np.empty(output_shape, dtype=object)

# Extract subsets using nested loops
for i in range(output_shape[0]):
    for j in range(output_shape[1]):
        subsets[i, j] = image[i:i + window_size, j:j + window_size]

# Print the extracted subsets
for i in range(output_shape[0]):
    for j in range(output_shape[1]):
        print(f"Subset at position ({i}, {j}):\n{subsets[i, j]}\n")

Этот ответ был бы более полезным, если бы он объяснял, как он отвечает на вопрос - ответы только на код никогда не бывают такими полезными, как ответы, объясняющие решение. Тем не менее, вопрос заключался в следующем: «Есть ли более эффективный способ выполнить эту операцию и избежать выполнения этих двух циклов?», поэтому код, использующий циклы, вероятно, не отвечает на вопрос.

Cris Luengo 05.07.2023 17:27

Можете ли вы объяснить, почему ваш ответ так похож на ответ, сгенерированный ChatGPT?

Sebastian Wozny 05.07.2023 20:01

как это лучше?

juanpa.arrivillaga 06.07.2023 23:22
Ответ принят как подходящий

Благодаря комментариям @CrisLuengo и @mozway я могу ответить на свой вопрос. Действительно, выделенная функция np.lib.stride_tricks.sliding_window_view более эффективна, чем вложенный цикл. Вот небольшой тест тайминга с размером окна 3х3 для арий 100х100, 1000х1000 и 10000х10000.

import numpy as np
import time

image = np.random.random((10000, 10000))
window_size = 3

# nested loop
start = time.time()
subsets = []
for i in range(0, image.shape[0]-window_size+1):
   for j in range(0, image.shape[1]-window_size+1):
      subset = image[i:i+window_size, j:j+window_size]
      subsets.append(subset)
end = time.time()
print(end - start)

# dedicated function
start = time.time()
subset = np.lib.stride_tricks.sliding_window_view(image, (window_size, window_size))
subsets = subset.reshape(subset.shape[0]*subset.shape[1], window_size, window_size)
end = time.time()
print(end - start)

Однако может быть еще более эффективное решение с использованием scipy.ndimage.generic_filter. Эта функция позволяет применять определяемую пользователем функцию к различным позициям окна. Если я найду лучшее решение с этим подходом, я обновлю свой ответ.

Подумайте об использовании time.perf_counter(), если вы пытаетесь измерить эффективность подходов.

astroChance 06.07.2023 20:16

Другие вопросы по теме