Source code for aiapy.psf.deconvolve
"""
Deconvolve an AIA image with the channel point spread function.
"""
import copy
import warnings
import numpy as np
from sunpy import log
try:
import cupy
HAS_CUPY = True
except ImportError:
HAS_CUPY = False
from aiapy.util import AiapyUserWarning
from .psf import psf as calculate_psf
__all__ = ["deconvolve"]
[docs]
def deconvolve(smap, *, psf=None, iterations=25, clip_negative=True, use_gpu=True):
"""
Deconvolve an AIA image with the point spread function.
Perform image deconvolution on an AIA image with the instrument
point spread function using the Richardson-Lucy deconvolution
algorithm [1]_.
.. note:: If the `~cupy` package is installed and your machine has an
NVIDIA GPU, the deconvolution will automatically be accelerated
with CUDA. This can lead to more than an order of magnitude in
performance increase compared to pure `numpy` on a CPU.
For more information on PSF deconvolution on a GPU, see [2]_.
Parameters
----------
smap : `~sunpy.map.Map`
An AIA image
psf : `~numpy.ndarray`, optional
The point spread function. If None, it will be calculated
iterations : `int`, optional
Number of iterations in the Richardson-Lucy algorithm
clip_negative : `bool`, optional
If the image has negative intensity values, set them to zero.
use_gpu : `bool`, optional
If True and `~cupy` is installed, do PSF deconvolution on the GPU
with `~cupy`.
Returns
-------
`~sunpy.map.Map`
Deconvolved AIA image
See Also
--------
psf
References
----------
.. [1] https://en.wikipedia.org/wiki/Richardson%E2%80%93Lucy_deconvolution
.. [2] Cheung, M., 2015, *GPU Technology Conference Silicon Valley*, `GPU-Accelerated Image Processing for NASA's Solar Dynamics Observatory <https://on-demand.gputechconf.com/gtc/2015/presentation/S5209-Mark-Cheung.pdf>`__
"""
# TODO: do we need a check to make sure this is a full-frame image?
img = smap.data
if clip_negative:
img = np.where(img < 0, 0, img)
if np.any(img < 0):
warnings.warn(
"Image contains negative intensity values. Consider setting clip_negative to True",
AiapyUserWarning,
stacklevel=3,
)
if psf is None:
psf = calculate_psf(smap.wavelength)
if use_gpu and not HAS_CUPY:
log.info("cupy not installed or working, falling back to CPU")
if HAS_CUPY and use_gpu:
log.info("Using a GPU via cupy")
img = cupy.array(img)
psf = cupy.array(psf)
# Center PSF at pixel (0,0)
psf = np.roll(np.roll(psf, psf.shape[0] // 2, axis=0), psf.shape[1] // 2, axis=1)
# Convolution requires FFT of the PSF
psf = np.fft.rfft2(psf)
psf_conj = psf.conj()
img_decon = np.copy(img)
for _ in range(iterations):
ratio = img / np.fft.irfft2(np.fft.rfft2(img_decon) * psf)
img_decon = img_decon * np.fft.irfft2(np.fft.rfft2(ratio) * psf_conj)
return smap._new_instance( # NOQA: SLF001
cupy.asnumpy(img_decon) if (HAS_CUPY and use_gpu) else img_decon,
copy.deepcopy(smap.meta),
plot_settings=copy.deepcopy(smap.plot_settings),
mask=smap.mask,
)