308 lines
11 KiB
Python
308 lines
11 KiB
Python
|
# Copyright (C) 2003-2005 Peter J. Verveer
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions
|
||
|
# are met:
|
||
|
#
|
||
|
# 1. Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
#
|
||
|
# 2. Redistributions in binary form must reproduce the above
|
||
|
# copyright notice, this list of conditions and the following
|
||
|
# disclaimer in the documentation and/or other materials provided
|
||
|
# with the distribution.
|
||
|
#
|
||
|
# 3. The name of the author may not be used to endorse or promote
|
||
|
# products derived from this software without specific prior
|
||
|
# written permission.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||
|
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||
|
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
|
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
import numpy
|
||
|
from scipy._lib._util import normalize_axis_index
|
||
|
from . import _ni_support
|
||
|
from . import _nd_image
|
||
|
|
||
|
__all__ = ['fourier_gaussian', 'fourier_uniform', 'fourier_ellipsoid',
|
||
|
'fourier_shift']
|
||
|
|
||
|
|
||
|
def _get_output_fourier(output, input):
|
||
|
if output is None:
|
||
|
if input.dtype.type in [numpy.complex64, numpy.complex128,
|
||
|
numpy.float32]:
|
||
|
output = numpy.zeros(input.shape, dtype=input.dtype)
|
||
|
else:
|
||
|
output = numpy.zeros(input.shape, dtype=numpy.float64)
|
||
|
elif type(output) is type:
|
||
|
if output not in [numpy.complex64, numpy.complex128,
|
||
|
numpy.float32, numpy.float64]:
|
||
|
raise RuntimeError("output type not supported")
|
||
|
output = numpy.zeros(input.shape, dtype=output)
|
||
|
elif output.shape != input.shape:
|
||
|
raise RuntimeError("output shape not correct")
|
||
|
return output
|
||
|
|
||
|
|
||
|
def _get_output_fourier_complex(output, input):
|
||
|
if output is None:
|
||
|
if input.dtype.type in [numpy.complex64, numpy.complex128]:
|
||
|
output = numpy.zeros(input.shape, dtype=input.dtype)
|
||
|
else:
|
||
|
output = numpy.zeros(input.shape, dtype=numpy.complex128)
|
||
|
elif type(output) is type:
|
||
|
if output not in [numpy.complex64, numpy.complex128]:
|
||
|
raise RuntimeError("output type not supported")
|
||
|
output = numpy.zeros(input.shape, dtype=output)
|
||
|
elif output.shape != input.shape:
|
||
|
raise RuntimeError("output shape not correct")
|
||
|
return output
|
||
|
|
||
|
|
||
|
def fourier_gaussian(input, sigma, n=-1, axis=-1, output=None):
|
||
|
"""
|
||
|
Multidimensional Gaussian fourier filter.
|
||
|
|
||
|
The array is multiplied with the fourier transform of a Gaussian
|
||
|
kernel.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
input : array_like
|
||
|
The input array.
|
||
|
sigma : float or sequence
|
||
|
The sigma of the Gaussian kernel. If a float, `sigma` is the same for
|
||
|
all axes. If a sequence, `sigma` has to contain one value for each
|
||
|
axis.
|
||
|
n : int, optional
|
||
|
If `n` is negative (default), then the input is assumed to be the
|
||
|
result of a complex fft.
|
||
|
If `n` is larger than or equal to zero, the input is assumed to be the
|
||
|
result of a real fft, and `n` gives the length of the array before
|
||
|
transformation along the real transform direction.
|
||
|
axis : int, optional
|
||
|
The axis of the real transform.
|
||
|
output : ndarray, optional
|
||
|
If given, the result of filtering the input is placed in this array.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
fourier_gaussian : ndarray
|
||
|
The filtered input.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> from scipy import ndimage, datasets
|
||
|
>>> import numpy.fft
|
||
|
>>> import matplotlib.pyplot as plt
|
||
|
>>> fig, (ax1, ax2) = plt.subplots(1, 2)
|
||
|
>>> plt.gray() # show the filtered result in grayscale
|
||
|
>>> ascent = datasets.ascent()
|
||
|
>>> input_ = numpy.fft.fft2(ascent)
|
||
|
>>> result = ndimage.fourier_gaussian(input_, sigma=4)
|
||
|
>>> result = numpy.fft.ifft2(result)
|
||
|
>>> ax1.imshow(ascent)
|
||
|
>>> ax2.imshow(result.real) # the imaginary part is an artifact
|
||
|
>>> plt.show()
|
||
|
"""
|
||
|
input = numpy.asarray(input)
|
||
|
output = _get_output_fourier(output, input)
|
||
|
axis = normalize_axis_index(axis, input.ndim)
|
||
|
sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
|
||
|
sigmas = numpy.asarray(sigmas, dtype=numpy.float64)
|
||
|
if not sigmas.flags.contiguous:
|
||
|
sigmas = sigmas.copy()
|
||
|
|
||
|
_nd_image.fourier_filter(input, sigmas, n, axis, output, 0)
|
||
|
return output
|
||
|
|
||
|
|
||
|
def fourier_uniform(input, size, n=-1, axis=-1, output=None):
|
||
|
"""
|
||
|
Multidimensional uniform fourier filter.
|
||
|
|
||
|
The array is multiplied with the Fourier transform of a box of given
|
||
|
size.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
input : array_like
|
||
|
The input array.
|
||
|
size : float or sequence
|
||
|
The size of the box used for filtering.
|
||
|
If a float, `size` is the same for all axes. If a sequence, `size` has
|
||
|
to contain one value for each axis.
|
||
|
n : int, optional
|
||
|
If `n` is negative (default), then the input is assumed to be the
|
||
|
result of a complex fft.
|
||
|
If `n` is larger than or equal to zero, the input is assumed to be the
|
||
|
result of a real fft, and `n` gives the length of the array before
|
||
|
transformation along the real transform direction.
|
||
|
axis : int, optional
|
||
|
The axis of the real transform.
|
||
|
output : ndarray, optional
|
||
|
If given, the result of filtering the input is placed in this array.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
fourier_uniform : ndarray
|
||
|
The filtered input.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> from scipy import ndimage, datasets
|
||
|
>>> import numpy.fft
|
||
|
>>> import matplotlib.pyplot as plt
|
||
|
>>> fig, (ax1, ax2) = plt.subplots(1, 2)
|
||
|
>>> plt.gray() # show the filtered result in grayscale
|
||
|
>>> ascent = datasets.ascent()
|
||
|
>>> input_ = numpy.fft.fft2(ascent)
|
||
|
>>> result = ndimage.fourier_uniform(input_, size=20)
|
||
|
>>> result = numpy.fft.ifft2(result)
|
||
|
>>> ax1.imshow(ascent)
|
||
|
>>> ax2.imshow(result.real) # the imaginary part is an artifact
|
||
|
>>> plt.show()
|
||
|
"""
|
||
|
input = numpy.asarray(input)
|
||
|
output = _get_output_fourier(output, input)
|
||
|
axis = normalize_axis_index(axis, input.ndim)
|
||
|
sizes = _ni_support._normalize_sequence(size, input.ndim)
|
||
|
sizes = numpy.asarray(sizes, dtype=numpy.float64)
|
||
|
if not sizes.flags.contiguous:
|
||
|
sizes = sizes.copy()
|
||
|
_nd_image.fourier_filter(input, sizes, n, axis, output, 1)
|
||
|
return output
|
||
|
|
||
|
|
||
|
def fourier_ellipsoid(input, size, n=-1, axis=-1, output=None):
|
||
|
"""
|
||
|
Multidimensional ellipsoid Fourier filter.
|
||
|
|
||
|
The array is multiplied with the fourier transform of an ellipsoid of
|
||
|
given sizes.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
input : array_like
|
||
|
The input array.
|
||
|
size : float or sequence
|
||
|
The size of the box used for filtering.
|
||
|
If a float, `size` is the same for all axes. If a sequence, `size` has
|
||
|
to contain one value for each axis.
|
||
|
n : int, optional
|
||
|
If `n` is negative (default), then the input is assumed to be the
|
||
|
result of a complex fft.
|
||
|
If `n` is larger than or equal to zero, the input is assumed to be the
|
||
|
result of a real fft, and `n` gives the length of the array before
|
||
|
transformation along the real transform direction.
|
||
|
axis : int, optional
|
||
|
The axis of the real transform.
|
||
|
output : ndarray, optional
|
||
|
If given, the result of filtering the input is placed in this array.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
fourier_ellipsoid : ndarray
|
||
|
The filtered input.
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
This function is implemented for arrays of rank 1, 2, or 3.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> from scipy import ndimage, datasets
|
||
|
>>> import numpy.fft
|
||
|
>>> import matplotlib.pyplot as plt
|
||
|
>>> fig, (ax1, ax2) = plt.subplots(1, 2)
|
||
|
>>> plt.gray() # show the filtered result in grayscale
|
||
|
>>> ascent = datasets.ascent()
|
||
|
>>> input_ = numpy.fft.fft2(ascent)
|
||
|
>>> result = ndimage.fourier_ellipsoid(input_, size=20)
|
||
|
>>> result = numpy.fft.ifft2(result)
|
||
|
>>> ax1.imshow(ascent)
|
||
|
>>> ax2.imshow(result.real) # the imaginary part is an artifact
|
||
|
>>> plt.show()
|
||
|
"""
|
||
|
input = numpy.asarray(input)
|
||
|
if input.ndim > 3:
|
||
|
raise NotImplementedError("Only 1d, 2d and 3d inputs are supported")
|
||
|
output = _get_output_fourier(output, input)
|
||
|
if output.size == 0:
|
||
|
# The C code has a bug that can result in a segfault with arrays
|
||
|
# that have size 0 (gh-17270), so check here.
|
||
|
return output
|
||
|
axis = normalize_axis_index(axis, input.ndim)
|
||
|
sizes = _ni_support._normalize_sequence(size, input.ndim)
|
||
|
sizes = numpy.asarray(sizes, dtype=numpy.float64)
|
||
|
if not sizes.flags.contiguous:
|
||
|
sizes = sizes.copy()
|
||
|
_nd_image.fourier_filter(input, sizes, n, axis, output, 2)
|
||
|
return output
|
||
|
|
||
|
|
||
|
def fourier_shift(input, shift, n=-1, axis=-1, output=None):
|
||
|
"""
|
||
|
Multidimensional Fourier shift filter.
|
||
|
|
||
|
The array is multiplied with the Fourier transform of a shift operation.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
input : array_like
|
||
|
The input array.
|
||
|
shift : float or sequence
|
||
|
The size of the box used for filtering.
|
||
|
If a float, `shift` is the same for all axes. If a sequence, `shift`
|
||
|
has to contain one value for each axis.
|
||
|
n : int, optional
|
||
|
If `n` is negative (default), then the input is assumed to be the
|
||
|
result of a complex fft.
|
||
|
If `n` is larger than or equal to zero, the input is assumed to be the
|
||
|
result of a real fft, and `n` gives the length of the array before
|
||
|
transformation along the real transform direction.
|
||
|
axis : int, optional
|
||
|
The axis of the real transform.
|
||
|
output : ndarray, optional
|
||
|
If given, the result of shifting the input is placed in this array.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
fourier_shift : ndarray
|
||
|
The shifted input.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> from scipy import ndimage, datasets
|
||
|
>>> import matplotlib.pyplot as plt
|
||
|
>>> import numpy.fft
|
||
|
>>> fig, (ax1, ax2) = plt.subplots(1, 2)
|
||
|
>>> plt.gray() # show the filtered result in grayscale
|
||
|
>>> ascent = datasets.ascent()
|
||
|
>>> input_ = numpy.fft.fft2(ascent)
|
||
|
>>> result = ndimage.fourier_shift(input_, shift=200)
|
||
|
>>> result = numpy.fft.ifft2(result)
|
||
|
>>> ax1.imshow(ascent)
|
||
|
>>> ax2.imshow(result.real) # the imaginary part is an artifact
|
||
|
>>> plt.show()
|
||
|
"""
|
||
|
input = numpy.asarray(input)
|
||
|
output = _get_output_fourier_complex(output, input)
|
||
|
axis = normalize_axis_index(axis, input.ndim)
|
||
|
shifts = _ni_support._normalize_sequence(shift, input.ndim)
|
||
|
shifts = numpy.asarray(shifts, dtype=numpy.float64)
|
||
|
if not shifts.flags.contiguous:
|
||
|
shifts = shifts.copy()
|
||
|
_nd_image.fourier_shift(input, shifts, n, axis, output)
|
||
|
return output
|