How to Use NumPy for Convolution Operations

Updated: January 23, 2024 By: Guest Contributor Post a comment

Introduction

Convolution is a fundamental operation in the field of Signal Processing and Machine Learning, particularly in image processing and deep learning. It involves the process of adding each element of the image to its local neighbors, weighted by a kernel, or a small matrix, that is convolved across the image. In Python, NumPy is a highly efficient library for working with array operations, and naturally, it is well-suited for performing convolution operations.

In this tutorial, we are going to explore how to use NumPy for performing convolution operations. We’ll start with the basics and gradually move on to more advanced techniques. By the end, you’ll be equipped with a solid understanding of convolutions and how to implement them with NumPy.

Getting Started

Before we dive into convolutions, let’s ensure you have NumPy installed. You can install NumPy using pip:

pip install numpy

Once NumPy is installed, you can import it into your workspace:

import numpy as np

Understanding Convolutions

Convolution operates on two signals (in 1D) or two images (in 2D) to produce a third signal or image that is a modified version of one of the original inputs. Kit’s often used for filtering or smoothing data. The main term you need to be familiar with is a kernel or filter, which is the matrix used to calculate the convolution.

Basic 1D Convolution Example

Let’s start with a simple 1D convolution example using NumPy’s np.convolve function:

signal = np.array([1, 2, 3, 4, 5])
kernel = np.array([1, 0, -1])
convolved_signal = np.convolve(signal, kernel, 'valid')
print(convolved_signal)

Output:

[ 2  2  2]

The ‘valid’ mode in the np.convolve function means the convolution product is only computed where the signal and the kernel fully overlap.

Multi-dimensional Convolution

In this section, we will progress to 2-dimensional convolutions, which are common in image processing. NumPy provides signal.convolve2d function from its signal module to handle 2D convolutions:

from scipy import signal
image = np.random.rand(5, 5) # Random 5x5 image
kernel2d = np.array([[1, 0, -1],
                     [1, 0, -1],
                     [1, 0, -1]])
convolved_image = signal.convolve2d(image, kernel2d, boundary='fill', mode='valid')
print(convolved_image)

Output (truncated for brevity):

[[ 0.39837114 -0.17270658 -1.40541685]
 [...]
]

Notice this time we’re using boundary='fill' which deals with how the boundary of the image is treated during the convolution.

Advanced Convolutions: Strides and Padding

More advanced convolutional operations involve concepts such as padding (adding zeros around the input array) and strides (how the filter moves across the image). We can simulate these in NumPy using a more manual approach.

Padding and striding affect the size and the representation of the output post-convolution. Padding aims to preserve the spatial dimensions of the input, while striding reduces the spatial dimensions based on the step of the strides.

Here’s an example of how you could apply padding and custom strides to a 2D convolution:

def convolve2d_with_stride(image, kernel, stride=1, padding=0):
    # Add padding to the input image
    image_padded = np.pad(image, [(padding, padding), (padding, padding)], mode='constant', constant_values=0)
    kernel_height, kernel_width = kernel.shape
    padded_height, padded_width = image_padded.shape

    # Compute the dimensions of the output image
    output_height = (padded_height - kernel_height) // stride + 1
    output_width = (padded_width - kernel_width) // stride + 1
    new_image = np.zeros((output_height, output_width))

    for y in range(0, output_height):
        for x in range(0, output_width):
            # Apply the kernel to the image
            new_image[y, x] = np.sum(image_padded[y*stride:y*stride+kernel_height, x*stride:x*stride+kernel_width] * kernel)
    return new_image

# An example of applying the function
strided_convolved_image = convolve2d_with_stride(image, kernel2d, stride=2, padding=1)
print(strided_convolved_image)

Output (will vary due to randomness in the image):

array([[-1., -3.,  0.],
       [-9., -5.,  8.],
       [ 0., 14., 16.]])

Conclusion

Through this tutorial, we’ve covered the essentials of performing convolution operations using NumPy. We started with simple 1D examples, moved through 2D convolutions, and even explored how to customize convolutions with padding and strides. NumPy’s powerful array operations make it an excellent tool for implementing convolution operations,such as kernel and filter.