Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Convolutions and Fourier Transforms

Open in Colab

A convolution is a linear operator of the form

(f∗g)(t)=∫f(τ)g(t−τ)dτ(f \ast g)(t) = \int f(\tau) g(t - \tau ) d\tau

In a discrete space, this turns into a sum

∑τf(τ)g(t−τ)\sum_\tau f(\tau) g(t - \tau)

Convolutions are shift invariant, or time invariant. They frequently appear in temporal and spatial image processing, as well as in probability.

If we want to represent the discrete convolution operator as a matrix, we get a toeplitz matrix GG

G=[g(0)g(1)g(2)…g(−1)g(0)g(1)…g(−2)g(−1)g(0)…⋮⋱]G = \begin{bmatrix} g(0) & g(1) & g(2) & \dots\\ g(-1) & g(0) & g(1) &\dots\\ g(-2) & g(-1) & g(0) & \dots\\ \vdots & & &\ddots \end{bmatrix}

A circulant matrix acting on a discrete signal of length NN is a toeplitz matrix where g(−i)=g(N−i)g(-i) = g(N-i).

array([[ 0, 1, 2, 3], [-1, 0, 1, 2], [-2, -1, 0, 1], [-3, -2, -1, 0]])
array([[0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3], [3, 2, 1, 0]])

both toeplitz and circulant matrices have special solve function in scipy.linalg

standard solve
CPU times: user 2.36 ms, sys: 1.16 ms, total: 3.52 ms
Wall time: 15.3 ms

toeplitz solve
CPU times: user 238 µs, sys: 0 ns, total: 238 µs
Wall time: 203 µs
2.5310720385211843e-11
standard solve
CPU times: user 633 µs, sys: 69 µs, total: 702 µs
Wall time: 670 µs

circulant solve
CPU times: user 184 µs, sys: 0 ns, total: 184 µs
Wall time: 198 µs
4.503661378908039e-15

Example

One place where convolutions appear is in combining probability distributions. For instance, if we take a sample x∼y+zx \sim y + z where y∼fy \sim f and z∼gz \sim g and f,gf, g are probability density functions, then the probability density function that describes the random variable xx is f∗gf\ast g.

Fourier Transforms

A Fourier transform takes functions back and forth between time and frequency domains.

f^(ω)=∫−∞∞f(x)e−2πixωdx\hat{f}(\omega) = \int_{-\infty}^\infty f(x) e^{-2\pi i x \omega} dx

A discrete Fourier transform (DFT) operates on a signal represented as a finite dimensional vector. On a signal of length NN, we have

f^k=1N∑n=0N−1fne−2πikn/N\hat{f}_k = \frac{1}{N} \sum_{n=0}^{N-1} f_n e^{-2\pi i k n / N}

In practice, discrete Fourier transforms are often computed using the Fast Fourier Transform (FFT) algorithm, which runs in O(Nlog⁡N)O(N\log N) time for a signal of length NN. Scipy provides funcitons for FFT as well as the inverse iFFT.

<Figure size 432x288 with 1 Axes>

the function in frequency space typically has real and imaginary components.

<Figure size 864x288 with 3 Axes>

real-valued signals should have anti-symmetric complex components and symmetric real components.

<Figure size 432x288 with 1 Axes>

Fourier transforms can reveal a variety of useful information and have many useful properties. One of which is that convolutions in the time/spatial domain become pointwise multiplication in the frequency domain.

h=f∗g⇔h^=f^g^h = f \ast g \Leftrightarrow \hat{h} = \hat{f} \hat{g}

This means that circulant matrices and their inverses can be applied in O(Nlog⁡N)O(N \log N) time using the FFT.

CPU times: user 32 µs, sys: 3 µs, total: 35 µs
Wall time: 38.1 µs
CPU times: user 496 µs, sys: 0 ns, total: 496 µs
Wall time: 452 µs
3.7281796402185724e-14
CPU times: user 1.04 ms, sys: 5 µs, total: 1.05 ms
Wall time: 1.15 ms
CPU times: user 1.17 ms, sys: 11 µs, total: 1.18 ms
Wall time: 880 µs
3.55434477453188e-15

Exercise 1

Create a plot that demonstrates the asymptotic scaling of the FFT is O(Nlog⁡N)O(N \log N)

Exercise 2

Let CC be a circulant matrix.

Given that the inverse of a circulant matrix can also be applied using a Fourier transform, is C−1C^{-1} also a circulant matrix?

Write a function to compute the inverse of CC.

Higher Dimensions

In 2 dimensions, you can use scipy’s fft2 and ifft2 for 2-dimensional signals such as images. For an m×nm \times n image, the time complexity is O(mnlog⁡(mn))O(mn \log(mn))

For general image processing, you may find it useful to install scikit-image

(pycourse) conda install scikit-image

Sparse Convolutions

In image processing, you may often encounter sparse convolutions, particularly convolutions that are very localized (e.g. only the first few entries of the firs row c are nonzero). One example is that many camera lenses cause a slight blur which mixes light from nearby sources. In deep learning, these sparse convolutions have been very effective in image processing tasks. Perhaps the simplest explanation of why learning parameters for a convolution is a good idea in this situation is that convolutions are translation invariant allowing the neural net to learn regardless of where a target appears on an image.

In this case, we typically think of convolutions as k×kk\times k patches which slide over a 2-dimensional image.

The time complexity of applying this convolution using standard for-loops to a m×nm\times n image is O(k2mn)O(k^2 mn), which is typically faster than using a Fourier transform. GPUs are also very good at this sort of operation.

<Figure size 432x288 with 1 Axes>
CPU times: user 88.6 ms, sys: 0 ns, total: 88.6 ms
Wall time: 89.4 ms
<Figure size 432x288 with 1 Axes>

If you’re using this sort of convolution in PyTorch, you can use torch.nn.Conv2d or torch.conv2d

CPU times: user 42.4 ms, sys: 13.7 ms, total: 56.2 ms
Wall time: 14.8 ms
<Figure size 432x288 with 1 Axes>

Discrete Cosine Transform (DCT)

One of the potential annoyances with Fourier transforms is that even with real-valued signals they produce complex output. If you want to stick to real output with a similar interpretation, you can use the Discrete Cosine Transform (DCT) or Discrete Sine Transform (DST). Both are implemented in Scipy.

Scipy DCT

<Figure size 720x360 with 2 Axes>
<Figure size 720x360 with 2 Axes>

The DCT can also be used for a simple method of image compression - you can simply remove the high frequency componenents of an image and retain most of the large structure while losing some detail.

<Figure size 432x288 with 1 Axes>
<Figure size 432x288 with 1 Axes>

Now, we’ll cut out the highest-frequency DCT coefficients.

<Figure size 432x288 with 1 Axes>

alternatively, we can make the image the original size by just setting the high-frequency DCT coefficients to 0.

<Figure size 432x288 with 1 Axes>

Now, let’s really compress the image...

<Figure size 432x288 with 1 Axes>