# Copyright (C) 2024 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations
from functools import partial
from logging import getLogger
from typing import TYPE_CHECKING
import scipy.ndimage as scipy_ndimage
from mantidimaging import helper as h
from mantidimaging.core.operations.base_filter import BaseFilter
from mantidimaging.core.parallel import shared as ps
from mantidimaging.core.utility.progress_reporting import Progress
from mantidimaging.gui.utility import add_property_to_form
from mantidimaging.gui.utility.qt_helpers import Type
if TYPE_CHECKING:
from mantidimaging.core.data import ImageStack
[docs]
class GaussianFilter(BaseFilter):
"""Applies Gaussian filter to the data.
Intended to be used on: Projections or reconstructed slices
When: As a pre-processing or post-reconstruction step to reduce noise.
"""
filter_name = "Gaussian"
link_histograms = True
[docs]
@staticmethod
def filter_func(data: ImageStack, size=None, mode=None, order=None, progress=None):
"""
:param data: Input data as a 3D numpy.ndarray
:param size: Size of the kernel
:param mode: The mode with which to handle the edges.
One of [reflect, constant, nearest, mirror, wrap].
Modes are described in the `SciPy documentation
<https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html>`_.
:param order: The order of the filter along each axis is given as a
sequence of integers, or as a single number.
An order of 0 corresponds to convolution with a Gaussian
kernel.
An order of 1, 2, or 3 corresponds to convolution
with the first, second or third derivatives of a Gaussian.
Higher order derivatives are not implemented
:return: The processed 3D numpy.ndarray
"""
h.check_data_stack(data)
if not size or not size > 1:
raise ValueError(f'Size parameter must be greater than 1, but value provided was {size}')
_execute(data, size, mode, order, progress)
h.check_data_stack(data)
return data
[docs]
@staticmethod
def register_gui(form, on_change, view):
_, size_field = add_property_to_form('Kernel Size',
Type.INT,
3, (2, 1000),
form=form,
on_change=on_change,
tooltip="Size of the median filter kernel")
_, order_field = add_property_to_form('Order',
Type.INT,
0, (0, 3),
form=form,
on_change=on_change,
tooltip="Order of the Gaussian filter")
_, mode_field = add_property_to_form('Edge Mode',
Type.CHOICE,
valid_values=modes(),
form=form,
on_change=on_change,
tooltip="Mode to handle the edges of the image")
return {'size_field': size_field, 'order_field': order_field, 'mode_field': mode_field}
[docs]
@staticmethod
def execute_wrapper(size_field=None, order_field=None, mode_field=None):
return partial(GaussianFilter.filter_func,
size=size_field.value(),
mode=mode_field.currentText(),
order=order_field.value())
[docs]
def modes():
return ['reflect', 'constant', 'nearest', 'mirror', 'wrap']
def _execute(images: ImageStack, size, mode, order, progress=None):
log = getLogger(__name__)
progress = Progress.ensure_instance(progress, task_name='Gaussian filter')
f = ps.create_partial(scipy_ndimage.gaussian_filter, ps.return_to_self, sigma=size, mode=mode, order=order)
log.info("Starting PARALLEL gaussian filter, with pixel data type: {0}, "
"filter size/width: {1}.".format(images.dtype, size))
progress.update()
ps.execute(f, [images.shared_array], images.data.shape[0], progress, msg="Gaussian filter")
progress.mark_complete()
log.info("Finished gaussian filter, with pixel data type: {0}, "
"filter size/width: {1}.".format(images.dtype, size))