summaryrefslogtreecommitdiff
path: root/silx/math/fit/filters.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'silx/math/fit/filters.pyx')
-rw-r--r--silx/math/fit/filters.pyx416
1 files changed, 0 insertions, 416 deletions
diff --git a/silx/math/fit/filters.pyx b/silx/math/fit/filters.pyx
deleted file mode 100644
index da1f6f5..0000000
--- a/silx/math/fit/filters.pyx
+++ /dev/null
@@ -1,416 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-"""This module provides background extraction functions and smoothing
-functions. These functions are extracted from PyMca module SpecFitFuns.
-
-Index of background extraction functions:
-------------------------------------------
-
- - :func:`strip`
- - :func:`snip1d`
- - :func:`snip2d`
- - :func:`snip3d`
-
-Smoothing functions:
---------------------
-
- - :func:`savitsky_golay`
- - :func:`smooth1d`
- - :func:`smooth2d`
- - :func:`smooth3d`
-
-References:
------------
-
-.. [Morhac97] Miroslav Morháč et al.
- Background elimination methods for multidimensional coincidence γ-ray spectra.
- Nucl. Instruments and Methods in Physics Research A401 (1997) 113-132.
- https://doi.org/10.1016/S0168-9002(97)01023-1
-
-.. [Ryan88] C.G. Ryan et al.
- SNIP, a statistics-sensitive background treatment for the quantitative analysis of PIXE spectra in geoscience applications.
- Nucl. Instruments and Methods in Physics Research B34 (1988) 396-402*.
- https://doi.org/10.1016/0168-583X(88)90063-8
-
-API documentation:
--------------------
-
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "15/05/2017"
-
-import logging
-import numpy
-
-_logger = logging.getLogger(__name__)
-
-cimport cython
-cimport silx.math.fit.filters_wrapper as filters_wrapper
-
-
-def strip(data, w=1, niterations=1000, factor=1.0, anchors=None):
- """Extract background from data using the strip algorithm, as explained at
- http://pymca.sourceforge.net/stripbackground.html.
-
- In its simplest implementation it is just as an iterative procedure
- depending on two parameters. These parameters are the strip background
- width ``w``, and the number of iterations. At each iteration, if the
- contents of channel ``i``, ``y(i)``, is above the average of the contents
- of the channels at ``w`` channels of distance, ``y(i-w)`` and
- ``y(i+w)``, ``y(i)`` is replaced by the average.
- At the end of the process we are left with something that resembles a spectrum
- in which the peaks have been stripped.
-
- :param data: Data array
- :type data: numpy.ndarray
- :param w: Strip width
- :param niterations: number of iterations
- :param factor: scaling factor applied to the average of ``y(i-w)`` and
- ``y(i+w)`` before comparing to ``y(i)``
- :param anchors: Array of anchors, indices of points that will not be
- modified during the stripping procedure.
- :return: Data with peaks stripped away
- """
- cdef:
- double[::1] input_c
- double[::1] output
- long[::1] anchors_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- input_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- output = numpy.empty(shape=(input_c.size,),
- dtype=numpy.float64)
-
- if anchors is not None and len(anchors):
- # numpy.int_ is the same as C long (http://docs.scipy.org/doc/numpy/user/basics.types.html)
- anchors_c = numpy.array(anchors,
- copy=False,
- dtype=numpy.int_,
- order='C')
- len_anchors = anchors_c.size
- else:
- # Make a dummy length-1 array, because if I use shape=(0,) I get the error
- # IndexError: Out of bounds on buffer access (axis 0)
- anchors_c = numpy.empty(shape=(1,),
- dtype=numpy.int_)
- len_anchors = 0
-
-
- status = filters_wrapper.strip(&input_c[0], input_c.size,
- factor, niterations, w,
- &anchors_c[0], len_anchors, &output[0])
-
- return numpy.asarray(output).reshape(data_shape)
-
-
-def snip1d(data, snip_width):
- """Estimate the baseline (background) of a 1D data vector by clipping peaks.
-
- Implementation of the algorithm SNIP in 1D is described in [Morhac97]_.
- The original idea for 1D and the low-statistics-digital-filter (lsdf) comes
- from [Ryan88]_.
-
- :param data: Data array, preferably 1D and of type *numpy.float64*.
- Else, the data array will be flattened and converted to
- *dtype=numpy.float64* prior to applying the snip filter.
- :type data: numpy.ndarray
- :param snip_width: Width of the snip operator, in number of samples.
- A sample will be iteratively compared to it's neighbors up to a
- distance of ``snip_width`` samples. This parameters has a direct
- influence on the speed of the algorithm.
- :type width: int
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip1d(&data_c[0], data_c.size, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def snip2d(data, snip_width):
- """Estimate the baseline (background) of a 2D data signal by clipping peaks.
-
- Implementation of the algorithm SNIP in 2D described in [Morhac97]_.
-
- :param data: 2D array
- :type data: numpy.ndarray
- :param width: Width of the snip operator, in number of samples. A wider
- snip operator will result in a smoother result (lower frequency peaks
- will be clipped), and a longer computation time.
- :type width: int
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__"):
- raise TypeError("data must be a 2D sequence (list, tuple) " +
- "or a 2D numpy array")
- nrows = len(data)
- ncolumns = len(data[0])
- data_shape = (len(data), len(data[0]))
-
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 2:
- ncolumns = data_shape[1]
- else:
- raise TypeError("data array must be 2-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip2d(&data_c[0], nrows, ncolumns, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def snip3d(data, snip_width):
- """Estimate the baseline (background) of a 3D data signal by clipping peaks.
-
- Implementation of the algorithm SNIP in 3D described in [Morhac97]_.
-
- :param data: 3D array
- :type data: numpy.ndarray
- :param width: Width of the snip operator, in number of samples. A wider
- snip operator will result in a smoother result (lower frequency peaks
- will be clipped), and a longer computation time.
- :type width: int
-
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__") or\
- not hasattr(data[0][0], "__len__"):
- raise TypeError("data must be a 3D sequence (list, tuple) " +
- "or a 3D numpy array")
- nx = len(data)
- ny = len(data[0])
- nz = len(data[0][0])
- data_shape = (len(data), len(data[0]), len(data[0][0]))
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 3:
- nx = data_shape[0]
- ny = data_shape[1]
- nz = data_shape[2]
- else:
- raise TypeError("data array must be 3-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip3d(&data_c[0], nx, ny, nz, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def savitsky_golay(data, npoints=5):
- """Smooth a curve using a Savitsky-Golay filter.
-
- :param data: Input data
- :type data: 1D numpy array
- :param npoints: Size of the smoothing operator in number of samples
- Must be between 3 and 100.
- :return: Smoothed data
- """
- cdef:
- double[::1] data_c
- double[::1] output
-
- data_c = numpy.array(data,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- output = numpy.empty(shape=(data_c.size,),
- dtype=numpy.float64)
-
- status = filters_wrapper.SavitskyGolay(&data_c[0], data_c.size,
- npoints, &output[0])
-
- if status:
- _logger.error("Smoothing failed. Check that npoints is greater " +
- "than 3 and smaller than 100.")
-
- return numpy.asarray(output).reshape(data.shape)
-
-
-def smooth1d(data):
- """Simple smoothing for 1D data.
-
- For a data array :math:`y` of length :math:`n`, the smoothed array
- :math:`ys` is calculated as a weighted average of neighboring samples:
-
- :math:`ys_0 = 0.75 y_0 + 0.25 y_1`
-
- :math:`ys_i = 0.25 (y_{i-1} + 2 y_i + y_{i+1})` for :math:`0 < i < n-1`
-
- :math:`ys_{n-1} = 0.25 y_{n-2} + 0.75 y_{n-1}`
-
-
- :param data: 1D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth1d(&data_c[0], data_c.size)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def smooth2d(data):
- """Simple smoothing for 2D data:
- :func:`smooth1d` is applied succesively along both axis
-
- :param data: 2D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__"):
- raise TypeError("data must be a 2D sequence (list, tuple) " +
- "or a 2D numpy array")
- nrows = len(data)
- ncolumns = len(data[0])
- data_shape = (len(data), len(data[0]))
-
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 2:
- ncolumns = data_shape[1]
- else:
- raise TypeError("data array must be 2-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth2d(&data_c[0], nrows, ncolumns)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def smooth3d(data):
- """Simple smoothing for 3D data:
- :func:`smooth2d` is applied on each 2D slice of the data volume along all
- 3 axis
-
- :param data: 2D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__") or\
- not hasattr(data[0][0], "__len__"):
- raise TypeError("data must be a 3D sequence (list, tuple) " +
- "or a 3D numpy array")
- nx = len(data)
- ny = len(data[0])
- nz = len(data[0][0])
- data_shape = (len(data), len(data[0]), len(data[0][0]))
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 3:
- nx = data_shape[0]
- ny = data_shape[1]
- nz = data_shape[2]
- else:
- raise TypeError("data array must be 3-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth3d(&data_c[0], nx, ny, nz)
-
- return numpy.asarray(data_c).reshape(data_shape)