deconvolution.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. """Implementation of various restoration functions."""
  2. import numpy as np
  3. from scipy.signal import convolve
  4. from .._shared.utils import _supported_float_type
  5. from . import uft
  6. def wiener(image, psf, balance, reg=None, is_real=True, clip=True):
  7. r"""Restore image using Wiener–Hunt deconvolution.
  8. Wiener–Hunt deconvolution is a restoration method which follows a Bayesian
  9. approach [1]_.
  10. Parameters
  11. ----------
  12. image : (N1, N2, ..., ND) ndarray
  13. Degraded image.
  14. psf : ndarray
  15. Point spread function (PSF). Assumed to be the impulse
  16. response (input image space) if the data type is real, or the
  17. transfer function (Fourier or frequency space) if the data type is
  18. complex. There is no constraint on the shape of the impulse
  19. response. The transfer function though must be of shape
  20. `(N1, N2, ..., ND)` if `is_real is True`,
  21. `(N1, N2, ..., ND // 2 + 1)` otherwise (see :func:`numpy.fft.rfftn`).
  22. balance : float
  23. Regularization parameter. Denoted by :math:`\lambda`: in the Notes
  24. section below, its value lets you balance data adequacy (improving
  25. frequency restoration) with respect to prior adequacy (reducing
  26. frequency restoration and avoiding noise artifacts). A larger value for
  27. this parameter favors the regularization/prior.
  28. reg : ndarray, optional
  29. Regularization operator. Laplacian by default. It can
  30. be an impulse response or a transfer function, as for the PSF.
  31. Shape constraints are the same as for `psf`.
  32. is_real : bool, optional
  33. True by default. Specify if `psf` and `reg` are provided over just half
  34. the frequency space (thanks to the redundancy of the Fourier transform
  35. for real signals). Applies only if `psf` and/or `reg` are
  36. provided as transfer functions.
  37. See ``uft`` module and :func:`np.fft.rfftn`.
  38. clip : bool, optional
  39. True by default. If True, pixel values of the deconvolved image (which
  40. is the return value) above 1 (resp. below -1) are clipped to 1 (resp.
  41. to -1). Be careful to set `clip=False` if you do not want this clipping
  42. and/or if your data range is not [0, 1] or [-1,1].
  43. Returns
  44. -------
  45. im_deconv : (N1, N2, ..., ND) ndarray
  46. The deconvolved image.
  47. Examples
  48. --------
  49. >>> import skimage as ski
  50. >>> import scipy as sp
  51. >>> img = ski.color.rgb2gray(ski.data.astronaut())
  52. >>> psf = np.ones((5, 5)) / 25
  53. >>> img = sp.signal.convolve2d(img, psf, 'same')
  54. >>> rng = np.random.default_rng()
  55. >>> img += 0.1 * img.std() * rng.standard_normal(img.shape)
  56. >>> deconvolved_img = ski.restoration.wiener(img, psf, 0.1)
  57. Notes
  58. -----
  59. This function applies the Wiener filter to a noisy (degraded)
  60. image by an impulse response (or PSF). If the data model is
  61. .. math:: y = Hx + n
  62. where :math:`n` is noise, :math:`H` the PSF, and :math:`x` the
  63. unknown original image, the Wiener filter is
  64. .. math::
  65. \hat x = F^\dagger \left( |\Lambda_H|^2 + \lambda |\Lambda_D|^2 \right)^{-1}
  66. \Lambda_H^\dagger F y
  67. where :math:`F` and :math:`F^\dagger` are the Fourier and inverse
  68. Fourier transforms respectively, :math:`\Lambda_H` the transfer
  69. function (or the Fourier transform of the PSF, see [2]_),
  70. and :math:`\Lambda_D` the regularization operator, which is a filter
  71. penalizing the restored image frequencies (Laplacian by default, that is,
  72. penalization of high frequencies). The parameter :math:`\lambda` tunes the
  73. balance between data (which tends to increase high frequencies, even those
  74. coming from noise) and regularization/prior (which tends to avoid noise
  75. artifacts).
  76. These methods are then specific to a prior model. Consequently,
  77. the application or the true image nature must correspond to the
  78. prior model. By default, the prior model (Laplacian) introduces
  79. image smoothness or pixel correlation. It can also be interpreted
  80. as high-frequency penalization to compensate for the instability of
  81. the solution with respect to the data (sometimes called noise
  82. amplification or "explosive" solution).
  83. Finally, the use of Fourier space implies a circulant property of
  84. :math:`H`, see [2]_.
  85. References
  86. ----------
  87. .. [1] François Orieux, Jean-François Giovannelli, and Thomas
  88. Rodet, "Bayesian estimation of regularization and point
  89. spread function parameters for Wiener–Hunt deconvolution",
  90. J. Opt. Soc. Am. A 27, 1593–1607 (2010)
  91. https://www.osapublishing.org/josaa/abstract.cfm?URI=josaa-27-7-1593
  92. https://hal.archives-ouvertes.fr/hal-00674508
  93. .. [2] B. R. Hunt "A matrix theory proof of the discrete
  94. convolution theorem", IEEE Trans. on Audio and
  95. Electroacoustics, vol. au-19, no. 4, pp. 285–288, dec. 1971
  96. """
  97. if reg is None:
  98. reg, _ = uft.laplacian(image.ndim, image.shape, is_real=is_real)
  99. if not np.iscomplexobj(reg):
  100. reg = uft.ir2tf(reg, image.shape, is_real=is_real)
  101. float_type = _supported_float_type(image.dtype)
  102. image = image.astype(float_type, copy=False)
  103. psf = psf.real.astype(float_type, copy=False)
  104. reg = reg.real.astype(float_type, copy=False)
  105. if psf.shape != reg.shape:
  106. trans_func = uft.ir2tf(psf, image.shape, is_real=is_real)
  107. else:
  108. trans_func = psf
  109. wiener_filter = np.conj(trans_func) / (
  110. np.abs(trans_func) ** 2 + balance * np.abs(reg) ** 2
  111. )
  112. if is_real:
  113. deconv = uft.uirfftn(wiener_filter * uft.urfftn(image), shape=image.shape)
  114. else:
  115. deconv = uft.uifftn(wiener_filter * uft.ufftn(image))
  116. if clip:
  117. deconv[deconv > 1] = 1
  118. deconv[deconv < -1] = -1
  119. return deconv
  120. def unsupervised_wiener(
  121. image, psf, reg=None, user_params=None, is_real=True, clip=True, *, rng=None
  122. ):
  123. """Unsupervised Wiener-Hunt deconvolution.
  124. Return the deconvolution with a Wiener-Hunt approach, where the
  125. hyperparameters are automatically estimated. The algorithm is a
  126. stochastic iterative process (Gibbs sampler) described in the
  127. reference below. See also ``wiener`` function.
  128. Parameters
  129. ----------
  130. image : (M, N) ndarray
  131. The input degraded image.
  132. psf : ndarray
  133. The impulse response (input image's space) or the transfer
  134. function (Fourier space). Both are accepted. The transfer
  135. function is automatically recognized as being complex
  136. (``np.iscomplexobj(psf)``).
  137. reg : ndarray, optional
  138. The regularisation operator. The Laplacian by default. It can
  139. be an impulse response or a transfer function, as for the psf.
  140. user_params : dict, optional
  141. Dictionary of parameters for the Gibbs sampler. Accepted keys are:
  142. threshold : float
  143. The stopping criterion: the norm of the difference between to
  144. successive approximated solution (empirical mean of object
  145. samples, see Notes section). 1e-4 by default.
  146. burnin : int
  147. The number of sample to ignore to start computation of the
  148. mean. 15 by default.
  149. min_num_iter : int
  150. The minimum number of iterations. 30 by default.
  151. max_num_iter : int
  152. The maximum number of iterations if ``threshold`` is not
  153. satisfied. 200 by default.
  154. callback : callable
  155. A user provided callable to which is passed, if the function
  156. exists, the current image sample for whatever purpose. The user
  157. can store the sample, or compute other moments than the
  158. mean. It has no influence on the algorithm execution and is
  159. only for inspection.
  160. clip : bool, optional
  161. True by default. If true, pixel values of the result above 1 or
  162. under -1 are thresholded for skimage pipeline compatibility.
  163. rng : {`numpy.random.Generator`, int}, optional
  164. Pseudo-random number generator.
  165. By default, a PCG64 generator is used (see :func:`numpy.random.default_rng`).
  166. If `rng` is an int, it is used to seed the generator.
  167. .. versionadded:: 0.19
  168. Returns
  169. -------
  170. x_postmean : (M, N) ndarray
  171. The deconvolved image (the posterior mean).
  172. chains : dict
  173. The keys ``noise`` and ``prior`` contain the chain list of
  174. noise and prior precision respectively.
  175. Examples
  176. --------
  177. >>> from skimage import color, data, restoration
  178. >>> img = color.rgb2gray(data.astronaut())
  179. >>> from scipy.signal import convolve2d
  180. >>> psf = np.ones((5, 5)) / 25
  181. >>> img = convolve2d(img, psf, 'same')
  182. >>> rng = np.random.default_rng()
  183. >>> img += 0.1 * img.std() * rng.standard_normal(img.shape)
  184. >>> deconvolved_img = restoration.unsupervised_wiener(img, psf)
  185. Notes
  186. -----
  187. The estimated image is design as the posterior mean of a
  188. probability law (from a Bayesian analysis). The mean is defined as
  189. a sum over all the possible images weighted by their respective
  190. probability. Given the size of the problem, the exact sum is not
  191. tractable. This algorithm use of MCMC to draw image under the
  192. posterior law. The practical idea is to only draw highly probable
  193. images since they have the biggest contribution to the mean. At the
  194. opposite, the less probable images are drawn less often since
  195. their contribution is low. Finally, the empirical mean of these
  196. samples give us an estimation of the mean, and an exact
  197. computation with an infinite sample set.
  198. References
  199. ----------
  200. .. [1] François Orieux, Jean-François Giovannelli, and Thomas
  201. Rodet, "Bayesian estimation of regularization and point
  202. spread function parameters for Wiener-Hunt deconvolution",
  203. J. Opt. Soc. Am. A 27, 1593-1607 (2010)
  204. https://www.osapublishing.org/josaa/abstract.cfm?URI=josaa-27-7-1593
  205. https://hal.archives-ouvertes.fr/hal-00674508
  206. """
  207. params = {
  208. 'threshold': 1e-4,
  209. 'max_num_iter': 200,
  210. 'min_num_iter': 30,
  211. 'burnin': 15,
  212. 'callback': None,
  213. }
  214. params.update(user_params or {})
  215. if reg is None:
  216. reg, _ = uft.laplacian(image.ndim, image.shape, is_real=is_real)
  217. if not np.iscomplexobj(reg):
  218. reg = uft.ir2tf(reg, image.shape, is_real=is_real)
  219. float_type = _supported_float_type(image.dtype)
  220. image = image.astype(float_type, copy=False)
  221. psf = psf.real.astype(float_type, copy=False)
  222. reg = reg.real.astype(float_type, copy=False)
  223. if psf.shape != reg.shape:
  224. trans_fct = uft.ir2tf(psf, image.shape, is_real=is_real)
  225. else:
  226. trans_fct = psf
  227. # The mean of the object
  228. x_postmean = np.zeros(trans_fct.shape, dtype=float_type)
  229. # The previous computed mean in the iterative loop
  230. prev_x_postmean = np.zeros(trans_fct.shape, dtype=float_type)
  231. # Difference between two successive mean
  232. delta = np.nan
  233. # Initial state of the chain
  234. gn_chain, gx_chain = [1], [1]
  235. # The correlation of the object in Fourier space (if size is big,
  236. # this can reduce computation time in the loop)
  237. areg2 = np.abs(reg) ** 2
  238. atf2 = np.abs(trans_fct) ** 2
  239. # The Fourier transform may change the image.size attribute, so we
  240. # store it.
  241. if is_real:
  242. data_spectrum = uft.urfft2(image)
  243. else:
  244. data_spectrum = uft.ufft2(image)
  245. rng = np.random.default_rng(rng)
  246. # Gibbs sampling
  247. for iteration in range(params['max_num_iter']):
  248. # Sample of Eq. 27 p(circX^k | gn^k-1, gx^k-1, y).
  249. # weighting (correlation in direct space)
  250. precision = gn_chain[-1] * atf2 + gx_chain[-1] * areg2 # Eq. 29
  251. # Note: Use astype instead of dtype argument to standard_normal to get
  252. # similar random values across precisions, as needed for
  253. # reference data used by test_unsupervised_wiener.
  254. _rand1 = rng.standard_normal(data_spectrum.shape)
  255. _rand1 = _rand1.astype(float_type, copy=False)
  256. _rand2 = rng.standard_normal(data_spectrum.shape)
  257. _rand2 = _rand2.astype(float_type, copy=False)
  258. excursion = np.sqrt(0.5 / precision) * (_rand1 + 1j * _rand2)
  259. # mean Eq. 30 (RLS for fixed gn, gamma0 and gamma1 ...)
  260. wiener_filter = gn_chain[-1] * np.conj(trans_fct) / precision
  261. # sample of X in Fourier space
  262. x_sample = wiener_filter * data_spectrum + excursion
  263. if params['callback']:
  264. params['callback'](x_sample)
  265. # sample of Eq. 31 p(gn | x^k, gx^k, y)
  266. gn_chain.append(
  267. rng.gamma(
  268. image.size / 2,
  269. 2 / uft.image_quad_norm(data_spectrum - x_sample * trans_fct),
  270. )
  271. )
  272. # sample of Eq. 31 p(gx | x^k, gn^k-1, y)
  273. gx_chain.append(
  274. rng.gamma((image.size - 1) / 2, 2 / uft.image_quad_norm(x_sample * reg))
  275. )
  276. # current empirical average
  277. if iteration > params['burnin']:
  278. x_postmean = prev_x_postmean + x_sample
  279. if iteration > (params['burnin'] + 1):
  280. current = x_postmean / (iteration - params['burnin'])
  281. previous = prev_x_postmean / (iteration - params['burnin'] - 1)
  282. delta = (
  283. np.sum(np.abs(current - previous))
  284. / np.sum(np.abs(x_postmean))
  285. / (iteration - params['burnin'])
  286. )
  287. prev_x_postmean = x_postmean
  288. # stop of the algorithm
  289. if (iteration > params['min_num_iter']) and (delta < params['threshold']):
  290. break
  291. # Empirical average \approx POSTMEAN Eq. 44
  292. x_postmean = x_postmean / (iteration - params['burnin'])
  293. if is_real:
  294. x_postmean = uft.uirfft2(x_postmean, shape=image.shape)
  295. else:
  296. x_postmean = uft.uifft2(x_postmean)
  297. if clip:
  298. x_postmean[x_postmean > 1] = 1
  299. x_postmean[x_postmean < -1] = -1
  300. return (x_postmean, {'noise': gn_chain, 'prior': gx_chain})
  301. def richardson_lucy(image, psf, num_iter=50, clip=True, filter_epsilon=None):
  302. """Richardson-Lucy deconvolution.
  303. Parameters
  304. ----------
  305. image : ([P, ]M, N) ndarray
  306. Input degraded image (can be n-dimensional). If you keep the
  307. default `clip=True` parameter, you may want to normalize
  308. the image so that its values fall in the [-1, 1] interval to avoid
  309. information loss.
  310. psf : ndarray
  311. The point spread function.
  312. num_iter : int, optional
  313. Number of iterations. This parameter plays the role of
  314. regularisation.
  315. clip : bool, optional
  316. True by default. If true, pixel value of the result above 1 or
  317. under -1 are thresholded for skimage pipeline compatibility.
  318. filter_epsilon : float, optional
  319. Value below which intermediate results become 0 to avoid division
  320. by small numbers.
  321. Returns
  322. -------
  323. im_deconv : ndarray
  324. The deconvolved image.
  325. Examples
  326. --------
  327. >>> from skimage import img_as_float, data, restoration
  328. >>> camera = img_as_float(data.camera())
  329. >>> from scipy.signal import convolve2d
  330. >>> psf = np.ones((5, 5)) / 25
  331. >>> camera = convolve2d(camera, psf, 'same')
  332. >>> rng = np.random.default_rng()
  333. >>> camera += 0.1 * camera.std() * rng.standard_normal(camera.shape)
  334. >>> deconvolved = restoration.richardson_lucy(camera, psf, 5)
  335. References
  336. ----------
  337. .. [1] https://en.wikipedia.org/wiki/Richardson%E2%80%93Lucy_deconvolution
  338. """
  339. float_type = _supported_float_type(image.dtype)
  340. image = image.astype(float_type, copy=False)
  341. psf = psf.astype(float_type, copy=False)
  342. im_deconv = np.full(image.shape, 0.5, dtype=float_type)
  343. psf_mirror = np.flip(psf)
  344. # Small regularization parameter used to avoid 0 divisions
  345. eps = 1e-12
  346. for _ in range(num_iter):
  347. conv = convolve(im_deconv, psf, mode='same') + eps
  348. if filter_epsilon:
  349. relative_blur = np.where(conv < filter_epsilon, 0, image / conv)
  350. else:
  351. relative_blur = image / conv
  352. im_deconv *= convolve(relative_blur, psf_mirror, mode='same')
  353. if clip:
  354. im_deconv[im_deconv > 1] = 1
  355. im_deconv[im_deconv < -1] = -1
  356. return im_deconv