Быстрый способ выполнить вычисление массива в Python - PullRequest
1 голос
/ 26 марта 2020

У меня есть изображение, на котором я хочу выполнить некоторые вычисления. Пиксели изображения будут представлены как f(x, y), где x - номер столбца, а y - номер строки каждого пикселя. Я хочу выполнить вычисление, используя следующую формулу:

enter image description here

Вот код, который выполняет вычисление:

import matplotlib.pyplot as plt
import numpy as np
import os.path
from PIL import Image

global image_width, image_height


# A. Blur Measurement
def  measure_blur(f):

    D_sub_h = [[0 for y in range(image_height)] for x in range(image_width)]

    for x in range(image_width):
        for y in range(image_height):
            if(y == 0):                
                f_x_yp1 = f[x][y+1]
                f_x_ym1 = 0 
            elif(y == (image_height -1)):

                f_x_yp1 = 0
                f_x_ym1 = f[x][y -1]
            else:                
                f_x_yp1 = f[x][y+1]
                f_x_ym1 = f[x][y -1]

            D_sub_h[x][y] = abs(f_x_yp1 - f_x_ym1)

    return D_sub_h

if __name__ == '__main__':

    image_counter = 1

    while True:

        if not os.path.isfile(str (image_counter) + '.jpg'):
            break

        image_path = str(image_counter) + '.jpg'
        image = Image.open(image_path )
        image_height, image_width = image.size

        print("Image Width : " + str(image_width))
        print("Image Height : " + str(image_height))

        f = np.array(image)
        D_sub_h = measure_blur(f)
        image_counter = image_counter + 1

Проблема с этим кодом в том, что когда размер изображения становится большим, например (5000, 5000), его выполнение занимает очень много времени. Есть ли способ или функция, которую я могу использовать, чтобы ускорить время выполнения, не делая одно за другим или вручную вычисляя?

1 Ответ

1 голос
/ 26 марта 2020

Поскольку вы специально конвертируете входные данные f в массив numpy, я предполагаю, что вы хотите использовать numpy. В этом случае распределение D_sub_h должно измениться из списка в массив:

D_sub_h = np.empty_like(f)

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

D_sub_h[0, :] = f[1, :]
D_sub_h[-1, :] = -f[-2, :]

Остальная часть данных - это просто разница между следующим и предыдущим индексом в каждом местоположении, которая идиоматически вычисляется путем сдвига представлений: f[2:, :] - f[:-2, :]. Эта формулировка создает временный массив. Вы можете избежать этого, используя np.subtract в явном виде:

np.subtract(f[2:, :], f[:-2, :], out=D_sub_h[1:-1, :])

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

def measure_blur(f):
    D_sub_h = np.empty_like(f)
    D_sub_h[0, :] = f[1, :]
    D_sub_h[-1, :] = -f[-2, :]
    np.subtract(f[2:, :], f[:-2, :], out=D_sub_h[1:-1, :])
    return D_sub_h

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

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

D_sub_h = np.concatenate((f[1, None], f[2:, :] - f[:-2, :], -f[-2, None]), axis=0)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...