_fourier.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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
  31. from numpy.core.multiarray 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 [numpy.complex64, numpy.complex128,
  39. numpy.float32]:
  40. output = numpy.zeros(input.shape, dtype=input.dtype)
  41. else:
  42. output = numpy.zeros(input.shape, dtype=numpy.float64)
  43. elif type(output) is type:
  44. if output not in [numpy.complex64, numpy.complex128,
  45. numpy.float32, numpy.float64]:
  46. raise RuntimeError("output type not supported")
  47. output = numpy.zeros(input.shape, dtype=output)
  48. elif output.shape != input.shape:
  49. raise RuntimeError("output shape not correct")
  50. return output
  51. def _get_output_fourier_complex(output, input):
  52. if output is None:
  53. if input.dtype.type in [numpy.complex64, numpy.complex128]:
  54. output = numpy.zeros(input.shape, dtype=input.dtype)
  55. else:
  56. output = numpy.zeros(input.shape, dtype=numpy.complex128)
  57. elif type(output) is type:
  58. if output not in [numpy.complex64, numpy.complex128]:
  59. raise RuntimeError("output type not supported")
  60. output = numpy.zeros(input.shape, dtype=output)
  61. elif output.shape != input.shape:
  62. raise RuntimeError("output shape not correct")
  63. return output
  64. def fourier_gaussian(input, sigma, n=-1, axis=-1, output=None):
  65. """
  66. Multidimensional Gaussian fourier filter.
  67. The array is multiplied with the fourier transform of a Gaussian
  68. kernel.
  69. Parameters
  70. ----------
  71. input : array_like
  72. The input array.
  73. sigma : float or sequence
  74. The sigma of the Gaussian kernel. If a float, `sigma` is the same for
  75. all axes. If a sequence, `sigma` has to contain one value for each
  76. axis.
  77. n : int, optional
  78. If `n` is negative (default), then the input is assumed to be the
  79. result of a complex fft.
  80. If `n` is larger than or equal to zero, the input is assumed to be the
  81. result of a real fft, and `n` gives the length of the array before
  82. transformation along the real transform direction.
  83. axis : int, optional
  84. The axis of the real transform.
  85. output : ndarray, optional
  86. If given, the result of filtering the input is placed in this array.
  87. Returns
  88. -------
  89. fourier_gaussian : ndarray
  90. The filtered input.
  91. Examples
  92. --------
  93. >>> from scipy import ndimage, datasets
  94. >>> import numpy.fft
  95. >>> import matplotlib.pyplot as plt
  96. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  97. >>> plt.gray() # show the filtered result in grayscale
  98. >>> ascent = datasets.ascent()
  99. >>> input_ = numpy.fft.fft2(ascent)
  100. >>> result = ndimage.fourier_gaussian(input_, sigma=4)
  101. >>> result = numpy.fft.ifft2(result)
  102. >>> ax1.imshow(ascent)
  103. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  104. >>> plt.show()
  105. """
  106. input = numpy.asarray(input)
  107. output = _get_output_fourier(output, input)
  108. axis = normalize_axis_index(axis, input.ndim)
  109. sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
  110. sigmas = numpy.asarray(sigmas, dtype=numpy.float64)
  111. if not sigmas.flags.contiguous:
  112. sigmas = sigmas.copy()
  113. _nd_image.fourier_filter(input, sigmas, n, axis, output, 0)
  114. return output
  115. def fourier_uniform(input, size, n=-1, axis=-1, output=None):
  116. """
  117. Multidimensional uniform fourier filter.
  118. The array is multiplied with the Fourier transform of a box of given
  119. size.
  120. Parameters
  121. ----------
  122. input : array_like
  123. The input array.
  124. size : float or sequence
  125. The size of the box used for filtering.
  126. If a float, `size` is the same for all axes. If a sequence, `size` has
  127. to contain one value for each axis.
  128. n : int, optional
  129. If `n` is negative (default), then the input is assumed to be the
  130. result of a complex fft.
  131. If `n` is larger than or equal to zero, the input is assumed to be the
  132. result of a real fft, and `n` gives the length of the array before
  133. transformation along the real transform direction.
  134. axis : int, optional
  135. The axis of the real transform.
  136. output : ndarray, optional
  137. If given, the result of filtering the input is placed in this array.
  138. Returns
  139. -------
  140. fourier_uniform : ndarray
  141. The filtered input.
  142. Examples
  143. --------
  144. >>> from scipy import ndimage, datasets
  145. >>> import numpy.fft
  146. >>> import matplotlib.pyplot as plt
  147. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  148. >>> plt.gray() # show the filtered result in grayscale
  149. >>> ascent = datasets.ascent()
  150. >>> input_ = numpy.fft.fft2(ascent)
  151. >>> result = ndimage.fourier_uniform(input_, size=20)
  152. >>> result = numpy.fft.ifft2(result)
  153. >>> ax1.imshow(ascent)
  154. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  155. >>> plt.show()
  156. """
  157. input = numpy.asarray(input)
  158. output = _get_output_fourier(output, input)
  159. axis = normalize_axis_index(axis, input.ndim)
  160. sizes = _ni_support._normalize_sequence(size, input.ndim)
  161. sizes = numpy.asarray(sizes, dtype=numpy.float64)
  162. if not sizes.flags.contiguous:
  163. sizes = sizes.copy()
  164. _nd_image.fourier_filter(input, sizes, n, axis, output, 1)
  165. return output
  166. def fourier_ellipsoid(input, size, n=-1, axis=-1, output=None):
  167. """
  168. Multidimensional ellipsoid Fourier filter.
  169. The array is multiplied with the fourier transform of an ellipsoid of
  170. given sizes.
  171. Parameters
  172. ----------
  173. input : array_like
  174. The input array.
  175. size : float or sequence
  176. The size of the box used for filtering.
  177. If a float, `size` is the same for all axes. If a sequence, `size` has
  178. to contain one value for each axis.
  179. n : int, optional
  180. If `n` is negative (default), then the input is assumed to be the
  181. result of a complex fft.
  182. If `n` is larger than or equal to zero, the input is assumed to be the
  183. result of a real fft, and `n` gives the length of the array before
  184. transformation along the real transform direction.
  185. axis : int, optional
  186. The axis of the real transform.
  187. output : ndarray, optional
  188. If given, the result of filtering the input is placed in this array.
  189. Returns
  190. -------
  191. fourier_ellipsoid : ndarray
  192. The filtered input.
  193. Notes
  194. -----
  195. This function is implemented for arrays of rank 1, 2, or 3.
  196. Examples
  197. --------
  198. >>> from scipy import ndimage, datasets
  199. >>> import numpy.fft
  200. >>> import matplotlib.pyplot as plt
  201. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  202. >>> plt.gray() # show the filtered result in grayscale
  203. >>> ascent = datasets.ascent()
  204. >>> input_ = numpy.fft.fft2(ascent)
  205. >>> result = ndimage.fourier_ellipsoid(input_, size=20)
  206. >>> result = numpy.fft.ifft2(result)
  207. >>> ax1.imshow(ascent)
  208. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  209. >>> plt.show()
  210. """
  211. input = numpy.asarray(input)
  212. if input.ndim > 3:
  213. raise NotImplementedError("Only 1d, 2d and 3d inputs are supported")
  214. output = _get_output_fourier(output, input)
  215. if output.size == 0:
  216. # The C code has a bug that can result in a segfault with arrays
  217. # that have size 0 (gh-17270), so check here.
  218. return output
  219. axis = normalize_axis_index(axis, input.ndim)
  220. sizes = _ni_support._normalize_sequence(size, input.ndim)
  221. sizes = numpy.asarray(sizes, dtype=numpy.float64)
  222. if not sizes.flags.contiguous:
  223. sizes = sizes.copy()
  224. _nd_image.fourier_filter(input, sizes, n, axis, output, 2)
  225. return output
  226. def fourier_shift(input, shift, n=-1, axis=-1, output=None):
  227. """
  228. Multidimensional Fourier shift filter.
  229. The array is multiplied with the Fourier transform of a shift operation.
  230. Parameters
  231. ----------
  232. input : array_like
  233. The input array.
  234. shift : float or sequence
  235. The size of the box used for filtering.
  236. If a float, `shift` is the same for all axes. If a sequence, `shift`
  237. has to contain one value for each axis.
  238. n : int, optional
  239. If `n` is negative (default), then the input is assumed to be the
  240. result of a complex fft.
  241. If `n` is larger than or equal to zero, the input is assumed to be the
  242. result of a real fft, and `n` gives the length of the array before
  243. transformation along the real transform direction.
  244. axis : int, optional
  245. The axis of the real transform.
  246. output : ndarray, optional
  247. If given, the result of shifting the input is placed in this array.
  248. Returns
  249. -------
  250. fourier_shift : ndarray
  251. The shifted input.
  252. Examples
  253. --------
  254. >>> from scipy import ndimage, datasets
  255. >>> import matplotlib.pyplot as plt
  256. >>> import numpy.fft
  257. >>> fig, (ax1, ax2) = plt.subplots(1, 2)
  258. >>> plt.gray() # show the filtered result in grayscale
  259. >>> ascent = datasets.ascent()
  260. >>> input_ = numpy.fft.fft2(ascent)
  261. >>> result = ndimage.fourier_shift(input_, shift=200)
  262. >>> result = numpy.fft.ifft2(result)
  263. >>> ax1.imshow(ascent)
  264. >>> ax2.imshow(result.real) # the imaginary part is an artifact
  265. >>> plt.show()
  266. """
  267. input = numpy.asarray(input)
  268. output = _get_output_fourier_complex(output, input)
  269. axis = normalize_axis_index(axis, input.ndim)
  270. shifts = _ni_support._normalize_sequence(shift, input.ndim)
  271. shifts = numpy.asarray(shifts, dtype=numpy.float64)
  272. if not shifts.flags.contiguous:
  273. shifts = shifts.copy()
  274. _nd_image.fourier_shift(input, shifts, n, axis, output)
  275. return output