Summary: This article shows how to create a simple band-pass filter that passes frequencies between the cutoff frequencies \(f_L\) and \(f_H\), and rejects frequencies outside of that interval. It also shows how to create a band-reject filter for those cutoff frequencies. The article is complemented by a Filter Design tool that allows you to create your own custom versions of the example filters that are shown below, and download the resulting filter coefficients.
Band-pass and band-reject filters can be created by combining low-pass and high-pass filters. To create these in the first place, have a look at How to Create a Simple Low-Pass Filter and How to Create a Simple High-Pass Filter.
To create band-pass and band-reject filters, you need two cutoff frequencies, a lower limit \(f_L\) and a higher limit \(f_H\). The combined filters inherit the transition bandwidth (or roll-off), which might be different at each end, from the low-pass and high-pass filters that were used to build it.
The windowed-sinc filters that are described in this article are both examples of Finite Impulse Response (FIR) filters.
Band-Pass Filter
A band-pass filter passes frequencies between the lower limit \(f_L\) and the higher limit \(f_H\), and rejects other frequencies. If you don’t create a specific filter for this, you can get this result in two steps. In the first step, you apply a low-pass filter with cutoff frequency \(f_H\),
\[x_\mathrm{lpf,H}[n]=x[n]*h_\mathrm{lpf,H}[n],\]
where \(x[n]\) is the original signal, \(h_\mathrm{lpf,H}[n]\) is the low-pass filter with cutoff frequency \(f_H\), and \(x_\mathrm{lpf,H}[n]\) is the low-pass-filtered signal. The asterisk represents convolution. The result is a signal in which the rejection of frequencies larger than \(f_H\) has been taken care of. You can then filter that signal again, with a high-pass filter with cutoff frequency \(f_L\),
\[x_\mathrm{bp,LH}[n]=x_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n],\]
where \(h_\mathrm{hpf,L}[n]\) is the high-pass filter with cutoff frequency \(f_L\), and \(x_\mathrm{bp,LH}[n]\) is the required band-pass-filtered signal.
However, you can do better and combine both of these filters into a single one. How does that work? You can write
\[x_\mathrm{bp,LH}[n]=(x[n]*h_\mathrm{lpf,H}[n])*h_\mathrm{hpf,L}[n]=x[n]*(h_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n]),\]
where the last step follows from the associative property of convolution. This means that the required band-pass filter is
\[h_\mathrm{bp,LH}[n]=h_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n].\]
Hence, a band-pass filter can be created from a low-pass and a high-pass filter with appropriate cutoff frequencies by convolving the two filters. The example band-pass filter of Figure 1 has \(f_L=0.1\) and \(f_H=0.4\), with \(b=0.08\) as in the articles on low-pass and high-pass filters.

Band-Reject Filter
A band-reject filter rejects frequencies between the lower limit \(f_L\) and the higher limit \(f_H\), and passes other frequencies. As for the band-pass filter, you can get this result in two steps. In the first step, you apply a low-pass filter with cutoff frequency \(f_L\),
\[x_\mathrm{lpf,L}[n]=x[n]*h_\mathrm{lpf,L}[n],\]
where \(x[n]\) is the original signal, \(h_\mathrm{lpf,L}[n]\) is the low-pass filter with cutoff frequency \(f_L\), and \(x_\mathrm{lpf,L}[n]\) is the low-pass-filtered signal.
The result is a signal in which the frequencies in the rejection interval have been eliminated, but in which the frequencies higher than \(f_H\) are also gone. This can be corrected by filtering the original signal again, with a high-pass filter with cutoff frequency \(f_H\), and adding the result to the first signal,
\[x_\mathrm{br,LH}[n]=x_\mathrm{lpf,L}+x[n]*h_\mathrm{hpf,H}[n],\]
where \(h_\mathrm{hpf,H}[n]\) is the high-pass filter with cutoff frequency \(f_H\), and \(x_\mathrm{br,LH}[n]\) is the required band-reject-filtered signal.
You can again to better and combine both operations into a single filter. You can write
\[x_\mathrm{br,LH}[n]=x[n]*h_\mathrm{lpf,L}[n]+x[n]*h_\mathrm{hpf,H}[n]=x[n]*(h_\mathrm{lpf,L}[n]+h_\mathrm{hpf,H}[n]),\]
where the last step follows from the distributive property of convolution. This means that the required band-reject filter is
\[h_\mathrm{br,LH}[n]=h_\mathrm{lpf,L}[n]+h_\mathrm{hpf,H}[n].\]
Hence, a band-reject filter can be created from a low-pass and a high-pass filter with appropriate cutoff frequencies by adding the two filters. The example band-reject filter of Figure 2 has \(f_L=0.1\) and \(f_H=0.4\), with again \(b=0.08\).

Python Code
In Python, all these formulas can be implemented concisely. The first code fragment shows how to implement a band-pass filter.
from __future__ import division import numpy as np fL = 0.1 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). fH = 0.4 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). b = 0.08 # Transition band, as a fraction of the sampling rate (in (0, 0.5)). N = int(np.ceil((4 / b))) if not N % 2: N += 1 # Make sure that N is odd. n = np.arange(N) # Compute a low-pass filter with cutoff frequency fH. hlpf = np.sinc(2 * fH * (n - (N - 1) / 2)) hlpf *= np.blackman(N) hlpf = hlpf / np.sum(hlpf) # Compute a high-pass filter with cutoff frequency fL. hhpf = np.sinc(2 * fL * (n - (N - 1) / 2)) hhpf *= np.blackman(N) hhpf = hhpf / np.sum(hhpf) hhpf = -hhpf hhpf[(N - 1) // 2] += 1 # Convolve both filters. h = np.convolve(hlpf, hhpf)
The second code fragment shows how to implement a band-reject filter.
from __future__ import division import numpy as np fL = 0.1 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). fH = 0.4 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). b = 0.08 # Transition band, as a fraction of the sampling rate (in (0, 0.5)). N = int(np.ceil((4 / b))) if not N % 2: N += 1 # Make sure that N is odd. n = np.arange(N) # Compute a low-pass filter with cutoff frequency fL. hlpf = np.sinc(2 * fL * (n - (N - 1) / 2)) hlpf *= np.blackman(N) hlpf /= np.sum(hlpf) # Compute a high-pass filter with cutoff frequency fH. hhpf = np.sinc(2 * fH * (n - (N - 1) / 2)) hhpf *= np.blackman(N) hhpf /= np.sum(hhpf) hhpf = -hhpf hhpf[(N - 1) // 2] += 1 # Add both filters. h = hlpf + hhpf
Applying the filter \(h\) to a signal \(s\) is done by convolution, as for the low-pass and high-pass filters, and can again be as simple as writing the single line:
s = np.convolve(s, h)
Filter Design Tool
This article is complemented with a Filter Design tool. Experiment with different values for \(f_L\) and \(f_H\), visualize the resulting filters, and download the filter coefficients. Try it now!
# Add both filters.
h = hlpf + hhpf
This line:
h = hlpf + hhpf
should be changed to:
h = np.convolve(hlpf, hhpf)
No, the code as given is correct. Note that the the filters are combined in a different way for band-pass and band-reject. The mathematical reasoning behind this is given in the body of the article.
Thanks so much for this tutorial! Its very helpful. One quick comment:
Based on running this code, it seems like there could be a slight correction
hhpf[(N - 1) // 2] += 1
should be
hhpf[(N - 1) // 2 + 1] += 1
Thanks for your kind words! I think the code is correct as I wrote it. Let's look at an example: I make sure that
N
is odd, for example,N=5
. This means that the coefficients are numbered 0, 1, 2, 3, 4.(N-1)//2
equals two, so I indeed add one to the middle coefficient.Hi Tom,
Thanks for the article. It's very much helpful:)
But the results(I mean Filter Plots), I got, are pretty much different as shown above with same Cutoff Frequency.
Thanks again:)
Could you please give me the code on how to plot the above plots
I have a post on that: How to Plot the Frequency Response of a Filter.
Hi Tom
Thanks for the article! Could you also implement a band pass filter by subtracting two low pass filters with cutoffs fH and fL?
Hi Marcello,
Yes, that is indeed also a possibility. And it even results in a shorter filter... The approach that I describe above is the most used one, I think, probably because it follows directly from the procedure of removing the low and the high frequencies in two steps. I'm considering writing a short new blog post about this, as I've done for the alternative approach to compute a high-pass filter in Spectral Reversal to Create a High-Pass Filter. Thanks for the hint!
Add new comment