Search code examples
pythonnumpyscipyconvolutionsmoothing

Smoothing a 2D array along only one axis


I wonder if anyone could help me extend the smoothing example in the SciPy cookbook to a 2D problem.

This script works great for smoothing a 1D function, and they also give code for a 2D smoothing in both axis (i.e. blurring an image).

However, I'd like to apply this function to a 2D dataset, but only along one axis (x direction). I could do this in a loop, by inspecting each slice in y, applying the 1D convolution, then rebuilding the array. But this seems poor coding technique.

Therefore, I wonder how to do it in 2D? I imagine I need to make a 2D kernel with weights changing along one direction only, but I'm not sure how to do this, or which convolve function to use (numpy.convolve, scipy.signal.convolve, scipy.ndimage.filters.convolve1d etc.)


Solution

  • Perhaps the simplest option is to use one of the 1D filters in 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)
    

    You could also use the non-1D versions of the filters like this: ndimage.gaussian_filter(img, (0, 10)) (i.e. set the filter width to 0 for the axes that you don't want to smooth along).

    To smooth with an arbitrary kernel, you could use 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)
    

    Here's what the various outputs look like:

    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()
    

    enter image description here