_fourier.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # Copyright (C) 2003-2005 Peter J. Verveer
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions
  5. # are met:
  6. #
  7. # 1. Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. #
  10. # 2. Redistributions in binary form must reproduce the above
  11. # copyright notice, this list of conditions and the following
  12. # disclaimer in the documentation and/or other materials provided
  13. # with the distribution.
  14. #
  15. # 3. The name of the author may not be used to endorse or promote
  16. # products derived from this software without specific prior
  17. # written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
  20. # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  23. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  25. # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  27. # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. import numpy as np
  31. from scipy._lib._util import normalize_axis_index
  32. from . import _ni_support
  33. from . import _nd_image
  34. __all__ = ['fourier_gaussian', 'fourier_uniform', 'fourier_ellipsoid',
  35. 'fourier_shift']
  36. def _get_output_fourier(output, input):
  37. if output is None:
  38. if input.dtype.type in [np.complex64, np.complex128, np.float32]:
  39. output = np.zeros(input.shape, dtype=input.dtype)
  40. else:
  41. output = np.zeros(input.shape, dtype=np.float64)
  42. elif type(output) is type:
  43. if output not in [np.complex64, np.complex128,
  44. np.float32, np.float64]:
  45. raise RuntimeError("output type not supported")
  46. output = np.zeros(input.shape, dtype=output)
  47. elif output.shape != input.shape:
  48. raise RuntimeError("output shape not correct")
  49. return output
  50. def _get_output_fourier_complex(output, input):
  51. if output is None:
  52. if input.dtype.type in [np.complex64, np.complex128]:
  53. output = np.zeros(input.shape, dtype=input.dtype)
  54. else:
  55. output = np.zeros(input.shape, dtype=np.complex128)
  56. elif type(output) is type:
  57. if output not in [np.complex64, np.complex128]:
  58. raise RuntimeError("output type not supported")
  59. output = np.zeros(input.shape, dtype=output)
  60. elif output.shape != input.shape:
  61. raise RuntimeError("output shape not correct")
  62. return output
  63. def fourier_gaussian(input, sigma, n=-1, axis=-1, output=None):
  64. """
  65. Multidimensional Gaussian fourier filter.
  66. The array is multiplied with the fourier transform of a Gaussian
  67. kernel.
  68. Parameters
  69. ----------
  70. input : array_like
  71. The input array.
  72. sigma : float or sequence
  73. The sigma of the Gaussian kernel. If a float, `sigma` is the same for
  74. all axes. If a sequence, `sigma` has to contain one value for each
  75. axis.
  76. n : int, optional
  77. If `n` is negative (default), then the input is assumed to be the
  78. result of a complex fft.
  79. If `n` is larger than or equal to zero, the input is assumed to be the
  80. result of a real fft, and `n` gives the length of the array before
  81. transformation along the real transform direction.
  82. axis : int, optional
  83. The axis of the real transform.
  84. output : ndarray, optional
  85. If given, the result of filtering the input is placed in this array.
  86. Returns
  87. -------
  88. fourier_gaussian : ndarray
  89. The filtered input.
  90. Examples
  91. --------
  92. >>> from scipy import ndimage, datasets
  93. >>> import numpy.fft
  94. >>> import matplotlib.pyplot as plt
  95. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  96. >>> plt.gray() # show the filtered result in grayscale
  97. >>> ascent = datasets.ascent()
  98. >>> input_ = numpy.fft.fft2(ascent)
  99. >>> result = ndimage.fourier_gaussian(input_, sigma=4)
  100. >>> result = numpy.fft.ifft2(result)
  101. >>> ax1.imshow(ascent)
  102. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  103. >>> plt.show()
  104. """
  105. input = np.asarray(input)
  106. output = _get_output_fourier(output, input)
  107. axis = normalize_axis_index(axis, input.ndim)
  108. sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
  109. sigmas = np.asarray(sigmas, dtype=np.float64)
  110. if not sigmas.flags.contiguous:
  111. sigmas = sigmas.copy()
  112. _nd_image.fourier_filter(input, sigmas, n, axis, output, 0)
  113. return output
  114. def fourier_uniform(input, size, n=-1, axis=-1, output=None):
  115. """
  116. Multidimensional uniform fourier filter.
  117. The array is multiplied with the Fourier transform of a box of given
  118. size.
  119. Parameters
  120. ----------
  121. input : array_like
  122. The input array.
  123. size : float or sequence
  124. The size of the box used for filtering.
  125. If a float, `size` is the same for all axes. If a sequence, `size` has
  126. to contain one value for each axis.
  127. n : int, optional
  128. If `n` is negative (default), then the input is assumed to be the
  129. result of a complex fft.
  130. If `n` is larger than or equal to zero, the input is assumed to be the
  131. result of a real fft, and `n` gives the length of the array before
  132. transformation along the real transform direction.
  133. axis : int, optional
  134. The axis of the real transform.
  135. output : ndarray, optional
  136. If given, the result of filtering the input is placed in this array.
  137. Returns
  138. -------
  139. fourier_uniform : ndarray
  140. The filtered input.
  141. Examples
  142. --------
  143. >>> from scipy import ndimage, datasets
  144. >>> import numpy.fft
  145. >>> import matplotlib.pyplot as plt
  146. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  147. >>> plt.gray() # show the filtered result in grayscale
  148. >>> ascent = datasets.ascent()
  149. >>> input_ = numpy.fft.fft2(ascent)
  150. >>> result = ndimage.fourier_uniform(input_, size=20)
  151. >>> result = numpy.fft.ifft2(result)
  152. >>> ax1.imshow(ascent)
  153. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  154. >>> plt.show()
  155. """
  156. input = np.asarray(input)
  157. output = _get_output_fourier(output, input)
  158. axis = normalize_axis_index(axis, input.ndim)
  159. sizes = _ni_support._normalize_sequence(size, input.ndim)
  160. sizes = np.asarray(sizes, dtype=np.float64)
  161. if not sizes.flags.contiguous:
  162. sizes = sizes.copy()
  163. _nd_image.fourier_filter(input, sizes, n, axis, output, 1)
  164. return output
  165. def fourier_ellipsoid(input, size, n=-1, axis=-1, output=None):
  166. """
  167. Multidimensional ellipsoid Fourier filter.
  168. The array is multiplied with the fourier transform of an ellipsoid of
  169. given sizes.
  170. Parameters
  171. ----------
  172. input : array_like
  173. The input array.
  174. size : float or sequence
  175. The size of the box used for filtering.
  176. If a float, `size` is the same for all axes. If a sequence, `size` has
  177. to contain one value for each axis.
  178. n : int, optional
  179. If `n` is negative (default), then the input is assumed to be the
  180. result of a complex fft.
  181. If `n` is larger than or equal to zero, the input is assumed to be the
  182. result of a real fft, and `n` gives the length of the array before
  183. transformation along the real transform direction.
  184. axis : int, optional
  185. The axis of the real transform.
  186. output : ndarray, optional
  187. If given, the result of filtering the input is placed in this array.
  188. Returns
  189. -------
  190. fourier_ellipsoid : ndarray
  191. The filtered input.
  192. Notes
  193. -----
  194. This function is implemented for arrays of rank 1, 2, or 3.
  195. Examples
  196. --------
  197. >>> from scipy import ndimage, datasets
  198. >>> import numpy.fft
  199. >>> import matplotlib.pyplot as plt
  200. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  201. >>> plt.gray() # show the filtered result in grayscale
  202. >>> ascent = datasets.ascent()
  203. >>> input_ = numpy.fft.fft2(ascent)
  204. >>> result = ndimage.fourier_ellipsoid(input_, size=20)
  205. >>> result = numpy.fft.ifft2(result)
  206. >>> ax1.imshow(ascent)
  207. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  208. >>> plt.show()
  209. """
  210. input = np.asarray(input)
  211. if input.ndim > 3:
  212. raise NotImplementedError("Only 1d, 2d and 3d inputs are supported")
  213. output = _get_output_fourier(output, input)
  214. if output.size == 0:
  215. # The C code has a bug that can result in a segfault with arrays
  216. # that have size 0 (gh-17270), so check here.
  217. return output
  218. axis = normalize_axis_index(axis, input.ndim)
  219. sizes = _ni_support._normalize_sequence(size, input.ndim)
  220. sizes = np.asarray(sizes, dtype=np.float64)
  221. if not sizes.flags.contiguous:
  222. sizes = sizes.copy()
  223. _nd_image.fourier_filter(input, sizes, n, axis, output, 2)
  224. return output
  225. def fourier_shift(input, shift, n=-1, axis=-1, output=None):
  226. """
  227. Multidimensional Fourier shift filter.
  228. The array is multiplied with the Fourier transform of a shift operation.
  229. Parameters
  230. ----------
  231. input : array_like
  232. The input array.
  233. shift : float or sequence
  234. The size of the box used for filtering.
  235. If a float, `shift` is the same for all axes. If a sequence, `shift`
  236. has to contain one value for each axis.
  237. n : int, optional
  238. If `n` is negative (default), then the input is assumed to be the
  239. result of a complex fft.
  240. If `n` is larger than or equal to zero, the input is assumed to be the
  241. result of a real fft, and `n` gives the length of the array before
  242. transformation along the real transform direction.
  243. axis : int, optional
  244. The axis of the real transform.
  245. output : ndarray, optional
  246. If given, the result of shifting the input is placed in this array.
  247. Returns
  248. -------
  249. fourier_shift : ndarray
  250. The shifted input.
  251. Examples
  252. --------
  253. >>> from scipy import ndimage, datasets
  254. >>> import matplotlib.pyplot as plt
  255. >>> import numpy.fft
  256. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  257. >>> plt.gray() # show the filtered result in grayscale
  258. >>> ascent = datasets.ascent()
  259. >>> input_ = numpy.fft.fft2(ascent)
  260. >>> result = ndimage.fourier_shift(input_, shift=200)
  261. >>> result = numpy.fft.ifft2(result)
  262. >>> ax1.imshow(ascent)
  263. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  264. >>> plt.show()
  265. """
  266. input = np.asarray(input)
  267. output = _get_output_fourier_complex(output, input)
  268. axis = normalize_axis_index(axis, input.ndim)
  269. shifts = _ni_support._normalize_sequence(shift, input.ndim)
  270. shifts = np.asarray(shifts, dtype=np.float64)
  271. if not shifts.flags.contiguous:
  272. shifts = shifts.copy()
  273. _nd_image.fourier_shift(input, shifts, n, axis, output)
  274. return output