Максимальное и минимальное значение массива Numpy 3D

У меня есть массив Numpy 3d, который представляет собой просто список серых изображений:

images = np.zeros((xlen, height, width), dtype=int)
for i in range (5):
   images[i] = cv2.imread(filename[i], cv2.IMREAD_GRAYSCALE)

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

Итак, мне нужно:

  1. Найдите минимальное и максимальное значения для каждого пикселя
  2. Рассчитайте среднее значение для каждого пикселя между всеми изображениями без этих максимальных и минимальных значений.
  3. Заменить все минимальные и максимальные значения расчетным средним

Я реализовал это наивным способом, используя стандартные функции Python, но это слишком медленно:

   #remove highest and lowest values for each pixel
   for el in range (height):
      for em in range (width):
         mylist = []
         for j in range (0, xlen):
            mylist.append(images[j][el][em])
         indmin = mylist.index(min(mylist))
         indmax = mylist.index(max(mylist))
         temp_counterx=0
         temp_sum = 0
         for j in range (0, xlen):
            if (j!=indmin) and (j!=indmax):
               temp_counterx +=1
               temp_sum += mylist[j]
         temp_val = int(temp_sum/temp_counterx)
         images[indmin][el][em]=temp_val
         images[indmax][el][em]=temp_val

Можно ли ускорить это с помощью Numpy?

UPD: Принято решение, предложенное faultr, с небольшими изменениями:

   mins = np.min(images, axis=0)
   maxs = np.max(images, axis=0)
   sums = np.sum(images, axis=0)
   # compute the mean without the extremes
   mean_without_extremes = (sums - mins - maxs) / (xlen - 2)
   mean_without_extremes = mean_without_extremes.astype(int)

   # replace maxima with the mean
   images = np.where((mins==images), images, mean_without_extremes)
   images = np.where((maxs==images), images, mean_without_extremes)

...и получили 30-кратное увеличение скорости! Кажется, что numpy предоставляет действительно быстрый и мощный вычислительный движок, просто его использование иногда может быть сложным из-за сложной структуры данных, с которой он работает.


person Cupera    schedule 09.05.2021    source источник


Ответы (2)


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

Используя циклы python, вы теряете все преимущества numpy, потому что они по своей сути медленные, по крайней мере, по сравнению с базовым скомпилированным кодом, который выполняется при вызове функций numpy. Если вы хотите, чтобы ваш код был достаточно быстрым, вам следует использовать векторизацию. Рассмотрим следующий код, который делает то, что вы просите, но без каких-либо циклов в python:

# compute minima, maxima and sum
mins = np.min(images, axis=0)
maxs = np.max(images, axis=0)
sums = np.sum(images, axis=0)
# compute the mean without the extremes
mean_without_extremes = (sums - mins - maxs) / (xlen - 2)
# replace maxima with the mean
images[images == mins] = mean_without_extremes.reshape(-1)
images[images == maxs] = mean_without_extremes.reshape(-1)

Поскольку вы, вероятно, не знакомы с этим, я рекомендую прочитать введение в документации об индексировании и трансляции, чтобы эффективно использовать numpy:


РЕДАКТИРОВАТЬ: Как указано в комментариях, приведенное выше решение работает только для xlen > 2 и если экстремумы достигаются только один раз для каждого пикселя. это можно исправить, заменив эти строки на

images = np.where(images == mins, images, mean_without_extremes)
images[np.isnan(images)] = 0  # set "empty mean" to zero
# using "np.where" as suggested by OP
# we can actually reduce that to one "np.where" call which might be slightly faster
images = np.where(np.logical_or(images == mins, images == maxs), images, mean_without_extremes)
person flawr    schedule 09.05.2021
comment
Ого, не ожидал, что будет так просто. Пытался придумать что-то громоздкое с помощью unravel_index. К сожалению, ваш код не запускается из-за ошибки в строке 'images[images == mins]...': назначение индексации логического массива NumPy не может назначить входные значения X выходным значениям Y, где X - размер изображения , а Y примерно на 5% больше размера изображения. Все фигуры печатают X, поэтому я не могу понять, откуда взялся Y. Любые идеи? - person Cupera; 10.05.2021
comment
Я предполагаю, что дополнительные 5% увеличенного размера массива приходятся на строки, которые имеют несколько равных значений, которые являются минимальными или максимальными. Numpy.ma. может быть решение здесь, не так ли? - person Cupera; 10.05.2021
comment
Изменены последние 2 строки на np.where(), и все работает гладко. Спасибо за ваш ответ. У вас еще нет представителей, чтобы проголосовать, поэтому вернитесь к этому чуть позже. Ваша помощь приветствуется. - person Cupera; 10.05.2021
comment
Хорошо, я не рассматривал случай, когда несколько значений являются минимальными или максимальными. Я рад, что ты смог это понять! - person flawr; 10.05.2021

Убедитесь, что все, что вы используете, является numpy array, а НЕ Python list, и убедитесь, что все элементы имеют одинаковый тип данных. В вашем случае это верно.

Теперь вы можете использовать библиотеку под названием numba. Он использует JIT.

Видео, демонстрирующее это, можно посмотреть здесь.

Документацию по numba можно посмотреть здесь

person John Brookfields    schedule 09.05.2021
comment
Хотя numba может помочь в некоторых случаях, я считаю, что здесь это совершенно не нужно. Первое, что нужно сделать, это научиться эффективно использовать numpy, прежде чем пытаться это сделать. - person flawr; 10.05.2021
comment
Конечно, я старался быть как можно дальше от списка Python. Использовал слово «список» для объяснения организации данных. Numba выглядит как интересная альтернатива медленному выполнению кода на Python, обязательно нужно попробовать в своих проектах. - person Cupera; 10.05.2021