|
Packit |
67cb25 |
*****************
|
|
Packit |
67cb25 |
Digital Filtering
|
|
Packit |
67cb25 |
*****************
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Introduction
|
|
Packit |
67cb25 |
============
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The filters discussed in this chapter are based on the following moving data
|
|
Packit |
67cb25 |
window which is centered on :math:`i`-th sample:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: W_i^H = \left\{ x_{i-H}, \dots, x_i, \dots, x_{i+H} \right\}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
W_i^H = { x_{i-H}, ..., x_i, ..., x_{i+H} }
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Here, :math:`H` is a non-negative integer called the *window half-length*, which
|
|
Packit |
67cb25 |
represents the number of samples before and after sample :math:`i`.
|
|
Packit |
67cb25 |
The total window length is :math:`K = 2 H + 1`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Handling Endpoints
|
|
Packit |
67cb25 |
==================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
When processing samples near the ends of the input signal, there will not
|
|
Packit |
67cb25 |
be enough samples to fill the window :math:`W_i^H` defined above.
|
|
Packit |
67cb25 |
Therefore the user must specify how to construct the windows near the end points.
|
|
Packit |
67cb25 |
This is done by passing an input argument of type :type:`gsl_filter_end_t`:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_filter_end_t
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This data type specifies how to construct windows near end points and can
|
|
Packit |
67cb25 |
be selected from the following choices:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_END_PADZERO
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
With this option, a full window of length :math:`K` will be constructed
|
|
Packit |
67cb25 |
by inserting zeros into the window near the signal end points. Effectively,
|
|
Packit |
67cb25 |
the input signal is modified to
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: \tilde{x} = \{ \underbrace{0, \dots, 0}_{H \textrm{ zeros}}, x_1, x_2, \dots, x_{n-1}, x_n, \underbrace{0, \dots, 0}_{H \textrm{ zeros} } \}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x~ = { 0, ..., 0, x_1, x_2, ..., x_{n-1}, x_n, 0, ..., 0 }
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
to ensure a well-defined window for all :math:`x_i`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_END_PADVALUE
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
With this option, a full window of length :math:`K` will be constructed
|
|
Packit |
67cb25 |
by padding the window with the first and last sample in the input signal.
|
|
Packit |
67cb25 |
Effectively, the input signal is modified to
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: \tilde{x} = \{ \underbrace{x_1, \dots, x_1}_{H}, x_1, x_2, \dots, x_{n-1}, x_n, \underbrace{x_n, \dots, x_n}_{H} \}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x~ = { x_1, ..., x_1, x_1, x_2, ..., x_{n-1}, x_n, x_n, ..., x_n }
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_END_TRUNCATE
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
With this option, no padding is performed, and the windows are simply truncated
|
|
Packit |
67cb25 |
as the end points are approached.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Linear Digital Filters
|
|
Packit |
67cb25 |
======================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Gaussian Filter
|
|
Packit |
67cb25 |
---------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The Gaussian filter convolves the input signal with a Gaussian kernel or window. This filter
|
|
Packit |
67cb25 |
is often used as a smoothing or noise reduction filter. The Gaussian kernel is
|
|
Packit |
67cb25 |
defined by
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: G(k) = e^{-\frac{1}{2} \left( \alpha \frac{k}{(K-1)/2} \right)^2} = e^{-k^2/2\sigma^2}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
G(k) = e^{-1/2 ( \alpha k/((K-1)/2) )^2} = e^{-k^2/2\sigma^2}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for :math:`-(K-1)/2 \le k \le (K-1)/2`, and :math:`K` is the size of the kernel. The
|
|
Packit |
67cb25 |
parameter :math:`\alpha` specifies the number of standard deviations :math:`\sigma` desired
|
|
Packit |
67cb25 |
in the kernel. So for example setting :math:`\alpha = 3` would define a Gaussian window
|
|
Packit |
67cb25 |
of length :math:`K` which spans :math:`\pm 3 \sigma`. It is often more convenient to specify
|
|
Packit |
67cb25 |
the parameter :math:`\alpha` rather than the standard deviation :math:`\sigma` when constructing
|
|
Packit |
67cb25 |
the kernel, since a fixed value of :math:`\alpha` would correspond to the same shape of
|
|
Packit |
67cb25 |
Gaussian regardless of the size :math:`K`. The appropriate value of the standard deviation
|
|
Packit |
67cb25 |
depends on :math:`K` and is related to :math:`\alpha` as
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: \sigma = \frac{K - 1}{2\alpha}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
\sigma = (K - 1)/(2 \alpha)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The routines below accept :math:`\alpha` as an input argument instead of :math:`\sigma`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The Gaussian filter offers a convenient way of differentiating and smoothing an input signal
|
|
Packit |
67cb25 |
in a single pass. Using the derivative property of a convolution,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: \frac{d}{dt} \left( G * x \right) = \frac{dG}{dt} * x
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
d/dt ( G * x ) = dG/dt * x
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the input signal :math:`x(t)` can be smoothed and differentiated at the same time by
|
|
Packit |
67cb25 |
convolution with a derivative Gaussian kernel, which can be readily computed from the
|
|
Packit |
67cb25 |
analytic expression above. The same principle applies to higher order derivatives.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_filter_gaussian_workspace * gsl_filter_gaussian_alloc(const size_t K)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function initializes a workspace for Gaussian filtering using a kernel of
|
|
Packit |
67cb25 |
size :data:`K`. Here, :math:`H = K / 2`. If :math:`K` is even, it is rounded up to the next
|
|
Packit |
67cb25 |
odd integer to ensure a symmetric window. The size of the workspace is :math:`O(K)`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_filter_gaussian_free(gsl_filter_gaussian_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function frees the memory associated with :data:`w`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_filter_gaussian(const gsl_filter_end_t endtype, const double alpha, const size_t order, const gsl_vector * x, gsl_vector * y, gsl_filter_gaussian_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function applies a Gaussian filter parameterized by :data:`alpha` to the input vector :data:`x`,
|
|
Packit |
67cb25 |
storing the output in :data:`y`. The derivative order is specified by :data:`order`, with
|
|
Packit |
67cb25 |
:code:`0` corresponding to a Gaussian, :code:`1` corresponding to a first derivative
|
|
Packit |
67cb25 |
Gaussian, and so on. The parameter :data:`endtype` specifies how the signal end points are handled.
|
|
Packit |
67cb25 |
It is allowed for :data:`x` = :data:`y` for an in-place filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_filter_gaussian_kernel(const double alpha, const size_t order, const int normalize, gsl_vector * kernel)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function constructs a Gaussian kernel parameterized by :data:`alpha` and
|
|
Packit |
67cb25 |
stores the output in :data:`kernel`. The parameter :data:`order` specifies the
|
|
Packit |
67cb25 |
derivative order, with :code:`0` corresponding to a Gaussian, :code:`1` corresponding
|
|
Packit |
67cb25 |
to a first derivative Gaussian, and so on. If :data:`normalize` is set to :code:`1`, then
|
|
Packit |
67cb25 |
the kernel will be normalized to sum to one on output. If :data:`normalize` is set to
|
|
Packit |
67cb25 |
:code:`0`, no normalization is performed.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Nonlinear Digital Filters
|
|
Packit |
67cb25 |
=========================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The nonlinear digital filters described below are based on the window median, which is given
|
|
Packit |
67cb25 |
by
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: m_i = \textrm{median} \left\{ W_i^H \right\} = \textrm{median} \left\{ x_{i-H}, \dots, x_i, \dots, x_{i+H} \right\}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
m_i = median { W_i^H } = median { x_{i-H}, ..., x_i, ..., x_{i+H} }
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The median is considered robust to local outliers, unlike the mean.
|
|
Packit |
67cb25 |
Median filters can preserve sharp edges while at the same removing signal noise, and are used
|
|
Packit |
67cb25 |
in a wide range of applications.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Standard Median Filter
|
|
Packit |
67cb25 |
----------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The *standard median filter* (SMF) simply replaces the sample :math:`x_i` by the median
|
|
Packit |
67cb25 |
:math:`m_i` of the window :math:`W_i^H`: This filter has one tuning parameter given
|
|
Packit |
67cb25 |
by :math:`H`. The standard median filter is considered highly resistant to
|
|
Packit |
67cb25 |
local outliers and local noise in the data sequence :math:`\{x_i\}`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_filter_median_workspace * gsl_filter_median_alloc(const size_t K)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function initializes a workspace for standard median filtering using a symmetric centered moving window of
|
|
Packit |
67cb25 |
size :data:`K`. Here, :math:`H = K / 2`. If :math:`K` is even, it is rounded up to the next
|
|
Packit |
67cb25 |
odd integer to ensure a symmetric window. The size of the workspace is :math:`O(7K)`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_filter_median_free(gsl_filter_median_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function frees the memory associated with :data:`w`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_filter_median(const gsl_filter_end_t endtype, const gsl_vector * x, gsl_vector * y, gsl_filter_median_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function applies a standard median filter to the input :data:`x`, storing the output in :data:`y`.
|
|
Packit |
67cb25 |
The parameter :data:`endtype` specifies how the signal end points are handled. It
|
|
Packit |
67cb25 |
is allowed to have :data:`x` = :data:`y` for an in-place filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Recursive Median Filter
|
|
Packit |
67cb25 |
-----------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The *recursive median filter* (RMF) is a modification of the SMF to include previous filter outputs
|
|
Packit |
67cb25 |
in the window before computing the median. The filter's response is
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: y_i = \textrm{median} \left( y_{i-H}, \dots, y_{i-1}, x_i, x_{i+1}, \dots, x_{i+H} \right)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
y_i = median ( y_{i-H}, ..., y_{i-1}, x_i, x_{i+1}, ..., x_{i+H} )
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Sometimes, the SMF must be applied several times in a row to achieve adequate smoothing (i.e. a cascade filter).
|
|
Packit |
67cb25 |
The RMF, on the other hand, converges to a *root sequence* in one pass,
|
|
Packit |
67cb25 |
and can sometimes provide a smoother result than several passes of the SMF. A root sequence is an input which is
|
|
Packit |
67cb25 |
left unchanged by the filter. So there is no need to apply a recursive median filter twice to an input vector.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_filter_rmedian_workspace * gsl_filter_rmedian_alloc(const size_t K)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function initializes a workspace for recursive median filtering using a symmetric centered moving window of
|
|
Packit |
67cb25 |
size :data:`K`. Here, :math:`H = K / 2`. If :math:`K` is even, it is rounded up to the next
|
|
Packit |
67cb25 |
odd integer to ensure a symmetric window. The size of the workspace is :math:`O(K)`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_filter_rmedian_free(gsl_filter_rmedian_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function frees the memory associated with :data:`w`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_filter_rmedian(const gsl_filter_end_t endtype, const gsl_vector * x, gsl_vector * y, gsl_filter_rmedian_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function applies a recursive median filter to the input :data:`x`, storing the output in :data:`y`.
|
|
Packit |
67cb25 |
The parameter :data:`endtype` specifies how the signal end points are handled. It
|
|
Packit |
67cb25 |
is allowed to have :data:`x` = :data:`y` for an in-place filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Impulse Detection Filter
|
|
Packit |
67cb25 |
------------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Impulsive noise is characterized by short sequences of data points distinct from those in the
|
|
Packit |
67cb25 |
surrounding neighborhood. This section describes a powerful class of filters, also known as
|
|
Packit |
67cb25 |
*impulse rejection filters* and *decision-based filters*, designed to detect and remove such outliers from data.
|
|
Packit |
67cb25 |
The filter's response is given by
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: y_i = \left\{
|
|
Packit |
67cb25 |
\begin{array}{cc}
|
|
Packit |
67cb25 |
x_i, & |x_i - m_i| \le t S_i \\
|
|
Packit |
67cb25 |
m_i, & |x_i - m_i| > t S_i
|
|
Packit |
67cb25 |
\end{array}
|
|
Packit |
67cb25 |
\right.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
y_i = { x_i, |x_i - m_i| <= t * S_i
|
|
Packit |
67cb25 |
{ m_i, |x_i - m_i| > t * S_i
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where :math:`m_i` is the median value of the window :math:`W_i^H`, :math:`S_i` is a robust estimate
|
|
Packit |
67cb25 |
of the scatter or dispersion for the window :math:`W_i^H`, and :math:`t` is a tuning parameter specifying
|
|
Packit |
67cb25 |
the number of scale factors needed to determine that a point is an outlier. The main idea is that the median
|
|
Packit |
67cb25 |
:math:`m_i` will be unaffected by a small number of outliers in the window, and so a given
|
|
Packit |
67cb25 |
sample :math:`x_i` is tested to determine how far away it is from the median in terms of the local
|
|
Packit |
67cb25 |
scale estimate :math:`S_i`. Samples which are more than :math:`t` scale estimates away from the median
|
|
Packit |
67cb25 |
are labeled as outliers and replaced by the window median :math:`m_i`. Samples which are less than
|
|
Packit |
67cb25 |
:math:`t` scale estimates from the median are left unchanged by the filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Note that when :math:`t = 0`, the impulse detection filter is equivalent to the standard median filter. When
|
|
Packit |
67cb25 |
:math:`t \rightarrow \infty`, it becomes the identity filter. This means the impulse detection filter can
|
|
Packit |
67cb25 |
be viewed as a "less aggressive" version of the standard median filter, becoming less aggressive as :math:`t` is
|
|
Packit |
67cb25 |
increased. Note that this filter modifies only samples identified as outliers, while the standard median
|
|
Packit |
67cb25 |
filter changes all samples to the local median, regardless of whether they are outliers. This fact, plus
|
|
Packit |
67cb25 |
the additional flexibility offered by the additional tuning parameter :math:`t` can make the impulse detection filter
|
|
Packit |
67cb25 |
a better choice for some applications.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
It is important to have a robust and accurate scale estimate :math:`S_i` in order to
|
|
Packit |
67cb25 |
detect impulse outliers even in the presence of noise. The window standard deviation is not
|
|
Packit |
67cb25 |
typically a good choice, as it can be significantly perturbed by the presence of even one outlier.
|
|
Packit |
67cb25 |
GSL offers the following choices (specified by a parameter of type :type:`gsl_filter_scale_t`) for
|
|
Packit |
67cb25 |
computing the scale estimate :math:`S_i`, all of which are robust to the presence of impulse outliers.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_filter_scale_t
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This type specifies how the scale estimate :math:`S_i` of the window :math:`W_i^H` is calculated.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_SCALE_MAD
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This option specifies the median absolute deviation (MAD) scale estimate, defined by
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: S_i = 1.4826 \times \textrm{median} \left\{ | W_i^H - m_i | \right\}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
S_i = 1.4826 median { | W_i^H - m_i | }
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This choice of scale estimate is also known as the *Hampel filter* in the statistical literature.
|
|
Packit |
67cb25 |
See :ref:`here <sec_mad-statistic>` for more information.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_SCALE_IQR
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This option specifies the interquartile range (IQR) scale estimate, defined as the difference between
|
|
Packit |
67cb25 |
the 75th and 25th percentiles of the window :math:`W_i^H`,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: S_i = 0.7413 \left( Q_{0.75} - Q_{0.25} \right)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
S_i = 0.7413 ( Q_{0.75} - Q_{0.25} )
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where :math:`Q_p` is the p-quantile of the window :math:`W_i^H`. The idea is to throw away the largest
|
|
Packit |
67cb25 |
and smallest 25% of the window samples (where the outliers would be), and estimate a scale from the middle 50%.
|
|
Packit |
67cb25 |
The factor :math:`0.7413` provides an unbiased estimate of the standard deviation for Gaussian data.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_SCALE_SN
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This option specifies the so-called :math:`S_n` statistic proposed by Croux and Rousseeuw.
|
|
Packit |
67cb25 |
See :ref:`here <sec_Sn-statistic>` for more information.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. macro:: GSL_FILTER_SCALE_QN
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This option specifies the so-called :math:`Q_n` statistic proposed by Croux and Rousseeuw.
|
|
Packit |
67cb25 |
See :ref:`here <sec_Qn-statistic>` for more information.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. warning::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
While the scale estimates defined above are much less sensitive to outliers than the standard deviation,
|
|
Packit |
67cb25 |
they can suffer from an effect called *implosion*. The standard deviation of a window :math:`W_i^H` will be zero
|
|
Packit |
67cb25 |
if and only if all samples in the window are equal. However, it is possible for the MAD of a window
|
|
Packit |
67cb25 |
to be zero even if all the samples in the window are not equal. For example, if :math:`K/2 + 1` or more
|
|
Packit |
67cb25 |
of the :math:`K` samples in the window are equal to some value :math:`x^{*}`, then the window median will
|
|
Packit |
67cb25 |
be equal to :math:`x^{*}`. Consequently, at least :math:`K/2 + 1` of the absolute deviations
|
|
Packit |
67cb25 |
:math:`|x_j - x^{*}|` will be zero, and so the MAD will be zero. In such a case, the Hampel
|
|
Packit |
67cb25 |
filter will act like the standard median filter regardless of the value of :math:`t`. Caution should also
|
|
Packit |
67cb25 |
be exercised if dividing by :math:`S_i`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_filter_impulse_workspace * gsl_filter_impulse_alloc(const size_t K)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function initializes a workspace for impulse detection filtering using a symmetric moving window of
|
|
Packit |
67cb25 |
size :data:`K`. Here, :math:`H = K / 2`. If :math:`K` is even, it is rounded up to the next
|
|
Packit |
67cb25 |
odd integer to ensure a symmetric window. The size of the workspace is :math:`O(6K)`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_filter_impulse_free(gsl_filter_impulse_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function frees the memory associated with :data:`w`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_filter_impulse(const gsl_filter_end_t endtype, const gsl_filter_scale_t scale_type, const double t, const gsl_vector * x, gsl_vector * y, gsl_vector * xmedian, gsl_vector * xsigma, size_t * noutlier, gsl_vector_int * ioutlier, gsl_filter_impulse_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
These functions apply an impulse detection filter to the input vector :data:`x`, storing the filtered output
|
|
Packit |
67cb25 |
in :data:`y`. The tuning parameter :math:`t` is provided in :data:`t`.
|
|
Packit |
67cb25 |
The window medians :math:`m_i` are stored in :data:`xmedian` and the :math:`S_i` are stored in :data:`xsigma` on output.
|
|
Packit |
67cb25 |
The number of outliers detected is stored in :data:`noutlier` on output, while
|
|
Packit |
67cb25 |
the locations of flagged outliers are stored in the boolean array :data:`ioutlier`. The input
|
|
Packit |
67cb25 |
:data:`ioutlier` may be :code:`NULL` if not desired. It is allowed to have :data:`x` = :data:`y` for an
|
|
Packit |
67cb25 |
in-place filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Examples
|
|
Packit |
67cb25 |
========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Gaussian Example 1
|
|
Packit |
67cb25 |
------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This example program illustrates the Gaussian filter applied to smoothing a time series of length
|
|
Packit |
67cb25 |
:math:`N = 500` with a kernel size of :math:`K = 51`. Three filters are applied with
|
|
Packit |
67cb25 |
parameters :math:`\alpha = 0.5, 3, 10`. The results are shown in :numref:`fig_filt-gaussian`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _fig_filt-gaussian:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. figure:: /images/gaussfilt.png
|
|
Packit |
67cb25 |
:scale: 60%
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Top panel: Gaussian kernels (unnormalized) for :math:`\alpha = 0.5, 3, 10`.
|
|
Packit |
67cb25 |
Bottom panel: Time series (gray) with Gaussian filter output for same :math:`\alpha`
|
|
Packit |
67cb25 |
values.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
We see that the filter corresponding to :math:`\alpha = 0.5` applies the most smoothing,
|
|
Packit |
67cb25 |
while :math:`\alpha = 10` corresponds to the least amount of smoothing.
|
|
Packit |
67cb25 |
The program is given below.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: examples/gaussfilt.c
|
|
Packit |
67cb25 |
:code:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Gaussian Example 2
|
|
Packit |
67cb25 |
------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
A common application of the Gaussian filter is to detect edges, or sudden jumps, in a noisy
|
|
Packit |
67cb25 |
input signal. It is used both for 1D edge detection in time series, as well as 2D edge
|
|
Packit |
67cb25 |
detection in images. Here we will examine a noisy time series of length :math:`N = 1000`
|
|
Packit |
67cb25 |
with a single edge. The input signal is defined as
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: x(n) = e(n) +
|
|
Packit |
67cb25 |
\left\{
|
|
Packit |
67cb25 |
\begin{array}{cc}
|
|
Packit |
67cb25 |
0, & n \le N/2 \\
|
|
Packit |
67cb25 |
0.5, & n > N/2
|
|
Packit |
67cb25 |
\end{array}
|
|
Packit |
67cb25 |
\right.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x(n) = e(n) + { 0, n <= N/2
|
|
Packit |
67cb25 |
{ 0.5, n > N/2
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
where :math:`e(n)` is Gaussian random noise. The program smooths the input signal
|
|
Packit |
67cb25 |
with order :math:`0,1,` and :math:`2` Gaussian filters of length :math:`K = 61` with
|
|
Packit |
67cb25 |
:math:`\alpha = 3`. For comparison, the program also computes finite differences
|
|
Packit |
67cb25 |
of the input signal without smoothing. The results are shown in :numref:`fig_filt-gaussian2`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _fig_filt-gaussian2:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. figure:: /images/gaussfilt2.png
|
|
Packit |
67cb25 |
:scale: 60%
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Top row: original input signal :math:`x(n)` (black) with Gaussian smoothed signal in red.
|
|
Packit |
67cb25 |
Second row: First finite differences of input signal.
|
|
Packit |
67cb25 |
Third row: Input signal smoothed with a first order Gaussian filter.
|
|
Packit |
67cb25 |
Fourth row: Input signal smoothed with a second order Gaussian filter.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The finite difference approximation of the first derivative (second row) shows
|
|
Packit |
67cb25 |
the common problem with differentiating a noisy signal. The noise is amplified
|
|
Packit |
67cb25 |
and makes it extremely difficult to detect the sharp gradient at sample :math:`500`.
|
|
Packit |
67cb25 |
The third row shows the first order Gaussian smoothed signal with a clear peak
|
|
Packit |
67cb25 |
at the location of the edge. Alternatively, one could examine the second order
|
|
Packit |
67cb25 |
Gaussian smoothed signal (fourth row) and look for zero crossings to determine
|
|
Packit |
67cb25 |
the edge location.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The program is given below.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: examples/gaussfilt2.c
|
|
Packit |
67cb25 |
:code:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Square Wave Signal Example
|
|
Packit |
67cb25 |
--------------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following example program illustrates the median filters on a noisy
|
|
Packit |
67cb25 |
square wave signal. Median filters are well known for preserving sharp
|
|
Packit |
67cb25 |
edges in the input signal while reducing noise. The program constructs
|
|
Packit |
67cb25 |
a 5 Hz square wave signal with Gaussian noise added. Then the signal is
|
|
Packit |
67cb25 |
filtered with a standard median filter and recursive median filter using
|
|
Packit |
67cb25 |
a symmetric window of length :math:`K = 7`. The results are shown in
|
|
Packit |
67cb25 |
:numref:`fig_filt-edge`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _fig_filt-edge:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. figure:: /images/filt_edge.png
|
|
Packit |
67cb25 |
:scale: 60%
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Original time series is in gray. The standard median filter output is in
|
|
Packit |
67cb25 |
green and the recursive median filter output is in red.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Both filters preserve the sharp signal edges while reducing the noise. The
|
|
Packit |
67cb25 |
recursive median filter achieves a smoother result than the standard median
|
|
Packit |
67cb25 |
filter. The "blocky" nature of the output is characteristic of all median
|
|
Packit |
67cb25 |
filters. The program is given below.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: examples/filt_edge.c
|
|
Packit |
67cb25 |
:code:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Impulse Detection Example
|
|
Packit |
67cb25 |
-------------------------
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following example program illustrates the impulse detection filter. First,
|
|
Packit |
67cb25 |
it constructs a sinusoid signal of length :math:`N = 1000` with Gaussian noise
|
|
Packit |
67cb25 |
added. Then, about 1% of the data are perturbed to represent large outliers. An
|
|
Packit |
67cb25 |
impulse detecting filter is applied with a window size :math:`K = 25` and
|
|
Packit |
67cb25 |
tuning parameter :math:`t = 4`, using the :math:`Q_n` statistic as the robust
|
|
Packit |
67cb25 |
measure of scale. The results are plotted in :numref:`fig_impulse`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _fig_impulse:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. figure:: /images/impulse.png
|
|
Packit |
67cb25 |
:scale: 60%
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Original time series is in blue, filter output is in green, upper and
|
|
Packit |
67cb25 |
lower intervals for detecting outliers are in red and yellow respectively.
|
|
Packit |
67cb25 |
Detected outliers are marked with squares.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The program is given below.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: examples/impulse.c
|
|
Packit |
67cb25 |
:code:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
References and Further Reading
|
|
Packit |
67cb25 |
==============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following publications are relevant to the algorithms described
|
|
Packit |
67cb25 |
in this chapter,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* F. J. Harris, *On the use of windows for harmonic analysis with the discrete Fourier transform*,
|
|
Packit |
67cb25 |
Proceedings of the IEEE, 66 (1), 1978.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* S-J. Ko, Y-H. Lee, and A. T. Fam. *Efficient implementation of one-dimensional recursive median filters*,
|
|
Packit |
67cb25 |
IEEE transactions on circuits and systems 37.11 (1990): 1447-1450.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* R. K. Pearson and M. Gabbouj, *Nonlinear Digital Filtering with Python: An Introduction*.
|
|
Packit |
67cb25 |
CRC Press, 2015.
|