# 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