Note
Go to the end to download the full example code or to run this example in your browser via Binder
Butterworth Filters#
The Butterworth filter is implemented in the frequency domain and is designed
to have no passband or stopband ripple. It can be used in either a lowpass or
highpass variant. The cutoff_frequency_ratio
parameter is used to set the
cutoff frequency as a fraction of the sampling frequency. Given that the
Nyquist frequency is half the sampling frequency, this means that this
parameter should be a positive floating point value < 0.5. The order
of the
filter can be adjusted to control the transition width, with higher values
leading to a sharper transition between the passband and stopband.
Butterworth filtering example#
Here we define a get_filtered helper function to repeat lowpass and highpass filtering at a specified series of cutoff frequencies.
import matplotlib.pyplot as plt
from skimage import data, filters
image = data.camera()
# cutoff frequencies as a fraction of the maximum frequency
cutoffs = [.02, .08, .16]
def get_filtered(image, cutoffs, squared_butterworth=True, order=3.0, npad=0):
"""Lowpass and highpass butterworth filtering at all specified cutoffs.
Parameters
----------
image : ndarray
The image to be filtered.
cutoffs : sequence of int
Both lowpass and highpass filtering will be performed for each cutoff
frequency in `cutoffs`.
squared_butterworth : bool, optional
Whether the traditional Butterworth filter or its square is used.
order : float, optional
The order of the Butterworth filter
Returns
-------
lowpass_filtered : list of ndarray
List of images lowpass filtered at the frequencies in `cutoffs`.
highpass_filtered : list of ndarray
List of images highpass filtered at the frequencies in `cutoffs`.
"""
lowpass_filtered = []
highpass_filtered = []
for cutoff in cutoffs:
lowpass_filtered.append(
filters.butterworth(
image,
cutoff_frequency_ratio=cutoff,
order=order,
high_pass=False,
squared_butterworth=squared_butterworth,
npad=npad,
)
)
highpass_filtered.append(
filters.butterworth(
image,
cutoff_frequency_ratio=cutoff,
order=order,
high_pass=True,
squared_butterworth=squared_butterworth,
npad=npad,
)
)
return lowpass_filtered, highpass_filtered
def plot_filtered(lowpass_filtered, highpass_filtered, cutoffs):
"""Generate plots for paired lists of lowpass and highpass images."""
fig, axes = plt.subplots(2, 1 + len(cutoffs), figsize=(12, 8))
fontdict = dict(fontsize=14, fontweight='bold')
axes[0, 0].imshow(image, cmap='gray')
axes[0, 0].set_title('original', fontdict=fontdict)
axes[1, 0].set_axis_off()
for i, c in enumerate(cutoffs):
axes[0, i + 1].imshow(lowpass_filtered[i], cmap='gray')
axes[0, i + 1].set_title(f'lowpass, c={c}', fontdict=fontdict)
axes[1, i + 1].imshow(highpass_filtered[i], cmap='gray')
axes[1, i + 1].set_title(f'highpass, c={c}', fontdict=fontdict)
for ax in axes.ravel():
ax.set_xticks([])
ax.set_yticks([])
plt.tight_layout()
return fig, axes
# Perform filtering with the (squared) Butterworth filter at a range of
# cutoffs.
lowpasses, highpasses = get_filtered(image, cutoffs, squared_butterworth=True)
fig, axes = plot_filtered(lowpasses, highpasses, cutoffs)
titledict = dict(fontsize=18, fontweight='bold')
fig.text(0.5, 0.95, '(squared) Butterworth filtering (order=3.0, npad=0)',
fontdict=titledict, horizontalalignment='center')
Text(0.5, 0.95, '(squared) Butterworth filtering (order=3.0, npad=0)')
Avoiding boundary artifacts#
It can be seen in the images above that there are artifacts near the edge of
the images (particularly for the smaller cutoff values). This is due to the
periodic nature of the DFT and can be reduced by applying some amount of
padding to the edges prior to filtering so that there are not sharp eges in
the periodic extension of the image. This can be done via the npad
argument to butterworth
.
Note that with padding, the undesired shading at the image edges is substantially reduced.
Text(0.5, 0.95, '(squared) Butterworth filtering (order=3.0, npad=32)')
True Butterworth filter#
To use the traditional signal processing definition of the Butterworth filter,
set squared_butterworth=False
. This variant has an amplitude profile in
the frequency domain that is the square root of the default case. This causes
the transition from the passband to the stopband to be more gradual at any
given order. This can be seen in the following images which appear a bit
sharper in the lowpass case than their squared Butterworth counterparts
above.
Total running time of the script: (0 minutes 4.406 seconds)