NumPy 2D скорость итераций массива - PullRequest
0 голосов
/ 09 мая 2018

У меня есть цикл, который заполняет двумерный массив NumPy информацией о пикселях для PIL, этот массив называется 'Shadows'.Цвета - белый или синий.Я хочу создать окончательный образ из тех, где доминирует белый.т. е. если одно изображение в цикле имеет синий пиксель на координате x, y, а другое изображение в цикле имеет белый пиксель на той же координате, то конечный пиксель будет белым.

Этов настоящее время выполняется:

import math, random, copy
import numpy as np
from PIL import Image, ImageDraw

colours = {0: (255,255,255), 1: (0,0,255)}

#width and height of area of interest
w = 100 #100 meter
h = 200 #200 meter

NumberOfDots = 10
DotRadius = 20
NumberOfRuns = 3

Final = np.array([[colours[0] for x in range(w)] for y in range(h)])
Shadows = np.array([[colours[0] for x in range(w)] for y in range(h)])

for SensorNum in range(NumberOfRuns):

  Shadows = np.array([[colours[0] for x in range(w)] for y in range(h)])

  for dot in range(NumberOfDots):

    ypos = random.randint(DotRadius, h-DotRadius)
    xpos = random.randint(DotRadius, w-DotRadius)

    for i in range(xpos - DotRadius, xpos + DotRadius):
      for j in range(ypos - DotRadius, ypos + DotRadius):
          if math.sqrt((xpos - i)**2 + (ypos - j)**2) < DotRadius:
            Shadows[j][i] = colours[1]

  im = Image.fromarray(Shadows.astype('uint8')).convert('RGBA')
  im.save('result_test_image'+str(SensorNum)+'.png')

  #This for loop below is the bottle-neck. Can its speed be improved?
  if SensorNum > 0:
    for i in range(w):
      for j in range(h):
        #White space dominates.
        #(pixel by pixel) If the current images pixel is white and the unfinshed Final
        #images pixel is blue then set the final pixel to white.
        if np.all(Shadows[j][i]==colours[0]) and np.all(Final[j][i]==colours[1]):
          Final[j][i] = colours[0]
  else:
    Final = copy.deepcopy(Shadows)

im = Image.fromarray(Final.astype('uint8')).convert('RGBA')
im.save('result_final_test.png')

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

1 Ответ

0 голосов
/ 11 мая 2018

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

Мой подход примерно в 800-1000 раз быстрее вашего текущего цикла. Я заменил массив и имена переменных в верхнем регистре на имена в нижнем регистре, используя подчеркивания. Верхний регистр обычно зарезервирован для классов в Python. Вот причина странной раскраски кода в вашем вопросе.

if sensor_num > 0:
    mask = (  # create a mask where the condition is True
        ((shadows[:, :, 0] == 255) &  # R=255
         (shadows[:, :, 1] == 255) &  # G=255
         (shadows[:, :, 2] == 255)) &  # B=255
        ((final[:, :, 0] == 0) &  # R=0
         (final[:, :, 1] == 0) &  # G=0
         (final[:, :, 2] == 255)))  # B=255
    final[mask] = np.array([255, 255, 255])  # set Final to white where mask is True
else:
    final = copy.deepcopy(shadows)

RGB-значения, конечно, могут быть заменены поиском предопределенных значений, таких как colours dict. Но я бы предложил использовать массив для хранения цветов, особенно если вы планируете индексировать его с помощью чисел:

colours = np.array([[255, 255, 255], [0, 0, 255]])

так что маска будет выглядеть так:

mask = (  # create a mask where the condition is True
    ((shadows[:, :, 0] == colours[0, 0]) &  # R=255
     (shadows[:, :, 1] == colours[0, 1]) &  # G=255
     (shadows[:, :, 2] == colours[0, 2])) &  # B=255
    ((final[:, :, 0] == colours[1, 0]) &  # R=0
     (final[:, :, 1] == colours[1, 1]) &  # G=0
     (final[:, :, 2] == colours[1, 2])))  # B=255
final[mask] = colours[0]  # set Final to white where mask is True

Конечно, это также работает с использованием dict.

Чтобы ускорить этот процесс, вы можете заменить сравнение RGC в маскировании некоторым сравнением с самим массивом (вычисление по трафарету). Это примерно на 5% быстрее для вашего размера массива, при этом разница в скорости увеличивается с увеличением размера массива, но вы теряете гибкость сравнения других цветов, просто изменяя записи в colours array / dict. Маска с трафаретными операциями выглядит так:

mask = (  # create a mask where the condition is True
    ((shadows[:, :, 0] == shadows[:, :, 1]) &  # R=G
     (shadows[:, :, 1] == shadows[:, :, 2]) &  # G=B
     (shadows[:, :, 2] == colours[0, 2])) &  # R=G=B=255
    ((final[:, :, 0] == final[:, :, 1]) &  # R=G
     (final[:, :, 1] == colours[1, 1]) &  # G=0
     (final[:, :, 2] == colours[1, 2])))  # B=255

Это должно существенно ускорить ваши вычисления.

Части другого кода также могут быть оптимизированы. Но, конечно, это того стоит, только если это не узкое место. Только один пример: вместо вызова random.randint каждого цикла вы можете вызвать его один раз и создать случайный массив (а также массивы + - DotRadius), а затем выполнить цикл по этому массиву:

ypos = np.random.randint(DotRadius, h-DotRadius, size=NumberOfDots)
ypos_plus_dot_radius = ypos + DotRadius
ypos_minus_dot_radius = ypos - DotRadius
xpos = np.random.randint(DotRadius, w-DotRadius, size=NumberOfDots)
xpos_plus_dot_radius = xpos + DotRadius
xpos_minus_dot_radius = xpos - DotRadius
for dot in range(NumberOfDots):
    yrange = np.arange(ypos_minus_dot_radius[dot], ypos_plus_dot_radius[dot])  # make range instead of looping
    # looping over xrange imho can't be avoided without further matrix operations
    for i in range(xpos_minus_dot_radius[dot], xpos_plus_dot_radius[dot]):
        # make a mask for the y-positions where the condition is true and
        # index the y-axis of Shadows with this mask:
        Shadows[yrange[np.sqrt((xpos[dot] - i)**2 + (ypos[dot] - yrange)**2) < DotRadius], i] = colours[1]
        # colours[1] can of course be replaced with any 3-element array or single integer/float
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...