Как я могу ускорить обработку множества патчей в изображении?

Я написал функцию для обработки изображения, в которой я извлекаю много патчей, а затем обрабатываю их с помощью той же функции (func) для создания нового изображения. Однако это очень медленно из-за двух циклов, функции, количества патчей и размера патчей. Я не знаю, как я могу ускорить этот код.

Функция, как показано ниже.

# code1
def filter(img, func, ksize, strides=1):
    height,width = img.shape
    f_height,f_width = ksize
    new_height = height - f_height + 1
    new_width = width - f_width + 1

    new_img = np.zeros((new_height,new_width))

    for i in range(new_height):
        for j in range(new_width):
            patch = img[i:i+f_height,j:j+f_width]
            new_img[i][j] = func(patch)

    return new_img

func может быть очень гибким и занимать много времени. Я беру один например. Функция ниже хочет вычислить центральную точку патча, делящего медиану патча. Однако я не хочу, чтобы те пиксели, значение которых равно 255, вычисляли медиану (255 — это значение по умолчанию для недопустимых пикселей). Поэтому я использую замаскированный массив в numpy. Маскированный массив замедляет код в несколько раз, и я понятия не имею, как это оптимизировать.

# code2
def relative_median_and_center_diff(patch, in_the_boundary, rectangle, center_point):
        mask = patch == 255
        mask[center_point] = True
        masked_patch = np.ma.array(patch, mask=mask)
        count = masked_patch.count()
        if count <= 1:
            return 0
        else:
            return patch[center_point]/(np.ma.median(masked_patch)+1)

идеи, которые я пробовал или получил:

  1. Я использовал некоторую функцию numpy для извлечения патчей перед циклом, ожидая, что это может быть быстрее, чем patch = img[i:i+f_height,j:j+f_width]. Я нашел функции для извлечения исправлений из патчи определенного размера из образа в питоне эффективно Сначала я попробовал view_as_windows из skimage.util.shape. Код был изменен, как показано ниже. Это занимает больше времени, чем code1. Я также попробовал sklearn.feature_extraction.image.extract_patches_2d и обнаружил, что это быстрее, чем code3, но все же медленнее, чем code1. (Кто-нибудь может сказать мне, почему это так?)
# code3
def filter(img, func, ksize, strides=1):
    height,width = img.shape
    f_height,f_width = ksize
    new_height = height - f_height + 1
    new_width = width - f_width + 1

    new_img = np.zeros((new_height,new_width))

    from skimage.util.shape import view_as_windows
    patches = view_as_windows(img, (f_height,f_width))

    for i in range(new_height):
        for j in range(new_width):
            patch = patches[i,j]
            new_img[i][j] = func(patch)

    return new_img
  1. Эта операция немного похожа на свертку или фильтр, за исключением функции func. Интересно, как эти библиотеки справляются с этим, и не могли бы вы, ребята, дать мне некоторые подсказки.

  2. Можем ли мы избежать двух петель в этой ситуации? Может быть, это может ускорить код.

  3. У меня есть гпу. Могу ли я изменить код, чтобы запустить его на gpus и заставить его обрабатывать патчи параллельно, чтобы сделать его быстрее?

  4. Измените код на C. Это последнее, что я хочу сделать, потому что, возможно, это немного беспорядочно.

Можете ли вы, ребята, дать мне некоторые идеи или некоторые предложения?


person 李悦城    schedule 19.07.2019    source источник
comment
Вам нужна вся информация для всех патчей? Поскольку я не знаю сценария, и это почти похоже на свертку, не можем ли мы иметь концепцию шагов, например, вы пропустите несколько патчей/или присвоите этому патчу среднее значение соседних патчей, чтобы вы уменьшили количество обрабатываемых патчей?   -  person venkata krishnan    schedule 19.07.2019
comment
@venkatkrishnan Спасибо за ответ! Я делаю это для чего-то вроде семантической сегментации. Поэтому я хочу, чтобы это было максимально точно. В большинстве случаев важнее качество. Я буду исследовать, насколько этот метод влияет на качество. Еще раз спасибо!   -  person 李悦城    schedule 19.07.2019
comment
Также нужно знать, почему каждый из них работает медленно, потому что они выполняют много проверок и т. д., чтобы извлечь патч. Вы можете посмотреть исходный код представления skimage в виде окон здесь - github.com/scikit-image/scikit-image/blob/master/skimage/util/ и маска numpy снова представляет собой последовательный итеративный процесс (тип движущегося окна), который, очевидно, сделает его медленным .   -  person venkata krishnan    schedule 19.07.2019


Ответы (1)


Если на вашем компьютере несколько процессоров, вы можете сделать этот процесс многопоточным, отправив его в ThreadPoolExecutor

Ваш код должен выглядеть примерно так:

from concurrent.futures import ThreadPoolExecutor
from multiprocessing import cpu_count()

executor = ThreadPoolExecutor(max_workers=cpu_count())
future = executor.submit(func, data, *args)
future_to_item[future] = data

for future in concurrent.futures.as_completed(future_to_item):
    # do something when you get the result

Я постоянно использую ThreadPoolExecutor для обработки изображений.

Поскольку у нас есть только функции и мы не знаем, как работает ваша программа (полностью), ознакомьтесь с параллелизмом в Python, чтобы вы могли лучше понять, как интегрировать это в свой код: https://docs.python.org/3/library/concurrent.futures.html

person Code Daddy    schedule 19.07.2019
comment
Я попытался сначала извлечь патчи, а затем использовать ProcessPoolExecutor для использования нескольких процессоров. Однако это заняло гораздо больше времени (6 с против 0,25 с). Я использую строку future_to_index = {executor.submit(func, patch): i for i,patch in enumerate(patches)}. Может быть, это потому, что патч слишком мал и не может полностью использовать несколько процессоров? или я не так сделал? - person 李悦城; 20.07.2019
comment
Еще код:with concurrent.futures.ProcessPoolExecutor(max_workers=cpu_count()) as executor: future_to_index = {executor.submit(func, patch): i for i,patch in enumerate(patches)} for future in concurrent.futures.as_completed(future_to_index): i = future_to_index[future] new_img[i//new_width][i%new_width] = future.result() - person 李悦城; 20.07.2019
comment
ProcessPoolExecutor сильно отличается от ThreadPoolExecutor. Посмотрите ссылку, которую я прикрепил к моему ответу - person Code Daddy; 23.07.2019