Каков наилучший способ итерации по массивному двумерному массиву по кадру NxN для фильтрации изображения? - PullRequest
0 голосов
/ 09 марта 2019

Существует простой фильтр размытия, который считает светлые и темные окружающие пиксели. Вот код Но моя реализация довольно медленная (около 3 секунд для массива 780x1200). Конечно, это может быть намного быстрее

import time
import numpy as np
from skimage.data import imread

def _filter_step(in_img, out_img, n, pos=(0,0)):
    y = (pos[0] - n//2, pos[0]+n//2+1)     # frame borders
    x = (pos[1] - n//2, pos[1]+n//2+1)

    frame = in_img[y[0]:y[1],  x[0]:x[1]]  # get frame

    whites = np.count_nonzero(frame)       # count light pixels
    k = whites/(n*n)                       # calculate proportion
    out_img[pos[0], pos[1]] = int(k * 255) # write new pixel


def make_filter(img, n):
    if not n % 2:
        raise ValueError("n must be odd")
    img = img > 180                        # binarize 

    out_img = np.empty_like(img)           # output array

    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            _filter_step(img, out_img, n, (i, j))
    return out_img

if __name__ == "__main__":
    image = imread("img780x1200.jpg", as_gray=True) 
    n = 11
    time_start = time.time()
    image1 = make_filter(image, n)
    print(time.time() - time_start) # ~3 sec

Я пытался провести параллельные вычисления:

import multiprocessing as mp
import ctypes as ct

def iter_image(x1, y1, x2, y2, img, out_img, n, mode=0):
    out_img = np.frombuffer(out_img, dtype=ct.c_int).reshape(img.shape)
    for y in range(img.shape[0])[::(-1)**mode]:
        for x in range(img.shape[1])[::(-1)**mode]:
            if mode:
                y2.value, x2.value = y, x
            else:
                y1.value, x1.value = y, x

            if y1.value < y2.value or x1.value < x2.value:
                _filter_step(img, out_img, n, ((y1.value,x1.value), (y2.value,x2.value))[mode])
            else:
                return ((y1, x1), (y2, x2))[mode]
    return ((y1, x1), (y2, x2))[mode]


def mp_make_filter(img, n):
    if not n % 2:
        raise ValueError("n must be odd")

    img = img > 180

    x1 = mp.Value('i', 0, lock=False)
    y1 = mp.Value('i', 0, lock=False)
    x2 = mp.Value('i', 0, lock=False)
    y2 = mp.Value('i', 0, lock=False)
    out_img = mp.Array('i', np.empty(img.shape[0] * img.shape[1], dtype=ct.c_int), lock=False)

    p1 = mp.Process(target=iter_image, args=(x1, y1, x2, y2, img, out_img, n, 0))
    p2 = mp.Process(target=iter_image, args=(x1, y1, x2, y2, img, out_img, n, 1))

    p1.start()
    p2.start()
    p1.join()
    p2.join()

    return np.frombuffer(out_img, dtype=ct.c_int).reshape(img.shape)

Этот код повторяет массив в 2 потока, пока они не "встречаются" друг с другом. Но это делает производительность еще медленнее (около 5 секунд) Как я могу ускорить код?

1 Ответ

0 голосов
/ 09 марта 2019

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

    y = (max(pos[0] - n//2, 0), pos[0]+n//2+1)     # frame borders
    x = (max(pos[1] - n//2, 0), pos[1]+n//2+1)

Кроме того, вы, вероятно, захотите переместить строку out_img = np.empty_like(img) до того, как сделать img логическим массивом.

Вот более быстрый метод с использованием cumsum:

y, x = image.shape
padded = np.zeros((y+k, x+k), 'i1')
padded[k//2+1:-k//2+1, k//2+1:-k//2+1] = image > 180
dint = padded.cumsum(1).cumsum(0)
result = dint[k:, k:] + dint[:-k, :-k] - dint[k:, :-k] - dint[:-k, k:]
result = (result * 255 / (k*k)).astype('u1')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...