Сглаживание 2D-массива только по одной оси

Интересно, может ли кто-нибудь помочь мне распространить пример сглаживания в кулинарной книге SciPy на двумерную задачу.

Этот скрипт отлично работает для сглаживания 1D-функции, а также дает код для 2D-сглаживания по обеим осям (т. е. размытие изображения).

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

Поэтому мне интересно, как это сделать в 2D? Я предполагаю, что мне нужно сделать 2D-ядро с весами, изменяющимися только в одном направлении, но я не уверен, как это сделать или какую функцию свертки использовать (numpy.convolve, scipy.signal.convolve, scipy.ndimage.filters.convolve1d и т. д.)


person IanRoberts    schedule 22.08.2015    source источник


Ответы (2)


Возможно, самый простой вариант — использовать один из 1D-фильтров в scipy.ndimage.filters:

from scipy import ndimage
from scipy.misc import lena

img = lena()

# a uniform (boxcar) filter with a width of 50
boxcar = ndimage.uniform_filter1d(img, 50, 1)

# a Gaussian filter with a standard deviation of 10
gauss = ndimage.gaussian_filter1d(img, 10, 1)

Вы также можете использовать неодномерные версии фильтров, например: ndimage.gaussian_filter(img, (0, 10)) (т. е. установить ширину фильтра на 0 для осей, по которым вы не хотите сглаживать).

Чтобы сгладить произвольное ядро, вы можете использовать scipy.ndimage.convolve1d:

import numpy as np

kern = np.hanning(50)   # a Hanning window with width 50
kern /= kern.sum()      # normalize the kernel weights to sum to 1

hanning = ndimage.convolve1d(img, kern, 1)

Вот как выглядят различные выходные данные:

from matplotlib import pyplot as plt

fig, ax = plt.subplots(2, 2, figsize=(8, 8))
ax[0, 0].imshow(img)
ax[0, 0].set_title('Original')
ax[0, 1].imshow(boxcar)
ax[0, 1].set_title('Boxcar filter (width = 50)')
ax[1, 0].imshow(gauss)
ax[1, 0].set_title(r'Gaussian filter ($\sigma$ = 10)')
ax[1, 1].imshow(hanning)
ax[1, 1].set_title(r'Hanning window (width = 50)')

for aa in ax.flat:
    aa.set_axis_off()

fig.tight_layout()
plt.show()

введите здесь описание изображения

person ali_m    schedule 22.08.2015

Введение

Кажется, что вы должны быть в состоянии сделать ny=1 для выполнения одномерной свертки двумерного изображения, но это показывает, что функции кулинарных книг на самом деле используют ядра длины 2 * n + 1. Это заставило меня подумать, что вы могли бы использовать ny=0, но это создает 0/0 в определении ядра. Так что тут тоже не повезло. :( Исходя из этого, я считаю, что поваренная книга не подходит для ваших целей, поэтому я предоставил альтернативный метод выполнения того, что вы просите.

Чтобы выполнить сглаживание 2D-массива путем свертки только по одному измерению, все, что вам нужно сделать, это создать 2D-массив (ядро), который имеет форму 1 по одному из измерений,

import numpy as np

kern = np.ones((11, 1))  # This will smooth along columns

И нормализовать его так, чтобы он суммировался с единицей,

kern /= kern.sum()

Затем сверните его с вашим сигналом,

import scipy.signal as signal

X, Y = np.mgrid[-70:70, -70:70]
Z = np.cos((X**2+Y**2)/200.) + np.random.normal(size=X.shape)

Z_smooth = signal.convolve(Z, kern)

Это должно дать вам что-то вроде этого: Окно товарного вагона.

Лучшее ядро

Выше я использовал ядро ​​'boxcar' (постоянные значения), которое многие люди считают несколько грубым. Люди часто предпочитают использовать более резкие или более плавные фильтры (например, «Ханнинг» или «Гаусс», как в поваренной книге).

kern_hanning = signal.hanning(11)[:, None]
kern_hanning /= kern_hanning.sum()
kern_gauss7 = signal.gaussian(11, 7)[:, None]
kern_gauss7 /= kern_gauss7.sum()
kern_gauss3 = signal.gaussian(11, 3)[:, None]
kern_gauss3 /= kern_gauss3.sum()

Эти разные окна выглядят так,

«Ядра»

После применения этих фильтров вы получите что-то вроде

«Ядро Gauss3 Kernel Ядро Gauss7

Обратите внимание, что ядро ​​«Gauss7» во многом похоже на коробочный вагон, поэтому на выходе оно дает очень похожие результаты. Окно Ханнинга, с другой стороны, намного тоньше и поэтому обеспечивает более четкую фильтрацию данных (гораздо меньшее размытие по кольцам).

person farenorth    schedule 22.08.2015
comment
Это сработало хорошо, спасибо. Можно было принять любое решение, но я выбрал другое, поскольку оно автоматически сохраняло форму массива (по крайней мере, в моем тестовом примере). - person IanRoberts; 22.08.2015
comment
Да, это, вероятно, лучшее решение. Вы можете сохранить форму массива с помощью signal.convolve(Z, kern, 'same'), но есть краевые эффекты. - person farenorth; 22.08.2015