Поэлементное применение операций с кортежами к массиву кортежей numpy

#python #numpy

#python #numpy

Вопрос:

Я работаю с 2d-массивом «пикселей» (кортежи rgb int), и мне нужен эффективный способ применить некоторые операции numpy к каждому элементу. В частности, я пытаюсь найти пиксели в пределах нескольких цветовых оттенков целевого цвета, чтобы позже изолировать их с помощью numpy.nonzero

Использование циклов For в этом случае занимает десятки секунд, поэтому я собираюсь поэлементно применить операции numpy для достижения того же результата.

Я хочу применить

Вычитание кортежей :

 pixel_diff = numpy.subtract( pixel_a, pixel_b) 
  

Абсолютное значение кортежа:

 pixel_abs = numpy.abs( pixel_diff )
  

Сравнение кортежей:

 pixel_bool = pixel_abs < int_tolerance
  

Tuple all ():

 is_similar = numpy.all(pixel_bool)
  

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

Комментарии:

1. Почему вы продолжаете ссылаться на кортежи? Если вы передаете кортежи методам numpy, они преобразуются в массивы до того, как действительно может быть выполнена какая-либо работа.

2. как именно выглядит ваш pixels массив?

3. Массив (на моей машине) имеет 2d-форму (1920, 1080) с dtype [('x0', int), ('x1', int), ('x2', int)] . До сих пор я только слышал, что этот формат упоминается как «массив кортежей». — если 3d-массив был бы для меня более полезным, я могу легко изменить форму.

4. Ммм, это больше похоже на структурированный массив , и мне непонятно, почему библиотека возвращает такой объект, а не 2D-массив одного типа. Что это за библиотека?

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

Ответ №1:

    import numpy as np
   #create a RGB array of 1000x1000x3 and separate into colors
   R, G, B = np.random.randint(0, 255, size = (1000, 1000, 3))
   #find all pixels less than 100, 100, 100
   np.logical_and((R<100), (G<100), (B<100))
  

Вы можете изменить последнюю строку в соответствии с вашими потребностями в цвете. Как есть, последняя строка занимает около 1,5 мс на одном ядре.

Комментарии:

1. Кажется надежным решением — хотя, чтобы было ясно, это применение операции путем реструктуризации массива в 3 2d массива целых чисел? (Мне придется изменить форму и разделить, чтобы протестировать мой вариант использования)

2. Да, чтобы получить максимальную отдачу от векторизации numpy, вам следует реструктурировать вашу проблему таким образом, чтобы обеспечить операции с массивом на месте, которые намного быстрее, чем переход от элемента к элементу.

3. @Mike: (a) Строка присваивания выдает ошибку. Это сработало бы, только если rhs имели форму (3,1000,1000) . (b) Ссылаясь на ваш комментарий выше, «поэлементно» не означает, что он не может быть «на месте»

4. @fountainhead а) приведенный выше код сработал для меня. возможно, вы столкнулись с проблемой, связанной с соглашением об определении изображений rgb. б) это правда, это была неосторожная формулировка с моей стороны.

5. @Mike: Я попробовал еще раз, и я получаю ошибку: «Ошибка значения: слишком много значений для распаковки (ожидается 3)». R, G, B = np.random.randint(0, 255, size = (1000, 1000, 3)) . В этой строке просто используется randint() функция, которой наплевать на соглашения rgb. Он пытается создать массив 1000x1000x3 , который эквивалентен 1000 подмассивам каждого размера 1000x3 , и распаковать его в 3 переменные. Несоответствие находится между 1000 и 3 . Это просто не сработает.

Ответ №2:

Вы можете преобразовать в эквивалентное неструктурированное представление (без каких-либо дополнительных затрат на создание копии базовых данных):

 my_3dview_of_ints = my_2d_of_3tuples.view(dtype=int)
  

где my_2d_of_3tuples находится ваш текущий структурированный массив (массив кортежей)

Затем вы можете выполнять свои обычные операции с массивом numpy в этом представлении, не сталкиваясь с какими-либо ошибками типа.

Например, если ваш массив выглядит следующим образом:

  [[(207,  27, 185) ( 90, 197,  52) ( 58, 153, 145) (239,  42,  39)]
 [(218,  23, 195) (226,  92, 170) ( 21, 114, 190) (192, 145,  48)]]
  

тогда представление, созданное выше, будет выглядеть следующим образом:

 [[[207  27 185]
  [ 90 197  52]
  [ 58 153 145]
  [239  42  39]]

 [[218  23 195]
  [226  92 170]
  [ 21 114 190]
  [192 145  48]]]
  

Например:

 pixel_a = my_3dview_of_ints[0,0] # pixel [207,27,185] at [0,0]
pixel_b = my_3dview_of_ints[1,1] # pixel [226,92,170] at [1,1]

pixel_diff = numpy.subtract( pixel_a, pixel_b) # Gives [-12,-65,5]
  

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

 my_3dview_of_ints[3,3] = pixel_a # Assign [207, 27,185] to location [3,3]