Filter Base Module#

Module: pyradise.process.base

General#

The filter base module provides the filter base classes (i.e. Filter and LoopEntryFilter) and their associated filter parameter classes (i.e. FilterParams and LoopEntryFilterParams). Furthermore, this module contains the implementation of the filter pipeline class (i.e. FilterPipeline), which is used to chain multiple parameterized filters such that they can be executed sequentially on the same subject.

Class Overview#

The following abstract base classes are provided by the base module:

Class

Description

FilterParams

Base class for FilterParams subclasses that parameterize LoopEntryFilter subclasses.

Filter

Base class for Filter subclasses that process the provided Subject instances.

LoopEntryFilterParams

Base class for LoopEntryFilterParams subclasses that parametrize LoopEntryFilter subclasses.

LoopEntryFilter

Base class for LoopEntryFilter subclasses that allow loop-based processing of image subsets.

FilterPipeline

A class for combining multiple Filter instances into a common pipeline.

Details#

class FilterParams[source]#

Bases: ABC

An abstract filter parameter class which provides the parameters used for the configuration of a certain filter. The derived subclasses can hold any set of parameters and is provided to the corresponding Filter via the execute() method. The FilterParams subclasses may incorporate also methods to calculate certain parameter values based on the given set of parameters.

The instances of FilterParams subclasses are stored inside a TransformInfo instance to keep track of the parameters used during the execution of a certain Filter such that invertibility can be guaranteed for Filter s feasible to be inverted. However, for the reason of reproducibility the FilterParams instances should be tracked always.

Example

An example of a FilterParams implementation for an intensity rescaling filter:

>>> from pyradise.process import FilterParams
>>>
>>>
>>> class ExampleRescaleFilterParams(FilterParams):
>>>
>>>     def __init__(self, min_out: float, max_out: float) -> None:
>>>         super().__init__()
>>>
>>>         # reverse the values if min_out > max_out
>>>         if min_out > max_out:
>>>             min_out, max_out = max_out, min_out
>>>
>>>         # the minimum and maximum output intensity values
>>>         self.min_out = min_out
>>>         self.max_out = max_out
class Filter(warning_on_non_invertible=False)[source]#

Bases: ABC

An abstract filter base class which is used to process a subject and its content. In PyRaDiSe a Filter is the main data processing object which is feasible to modify the structure and content of a Subject, the content of the subject-associated Image and other subject-associated data. Thus, filters can be used for pre-processing, DL-model inference, and post-processing.

The implemented filter design provides a standardized interface such that filters can be chained together in a FilterPipeline to form a processing pipeline. Furthermore, the extensible implementation renders the tracking of content changes feasible for the purpose of reproducibility and invertibility on invertible Filter.

The process package provides a set of implemented Filter s and associated FilterParams. However, the user may implement its own Filter s depending on the task specific needs. We recommend to share the user-implemented Filter s with the community via GitHub or by generating pull requests to the PyRaDiSe GitHub repository. We thank all contributors in advance for sharing their filter implementations!

In order to implement a new Filter the following steps are required:

  1. Always derive from the Filter class.

  2. Implement the execute() method and possible subsequent methods which are used to process the Subject.

  3. Make sure that your implementation tracks the changes and assign it to the TransformTape instance of the corresponding Image instance.

  4. Implement the execute_inverse() and is_invertible() methods if the filter is invertible. Please note that the implementation can access all information which was previously recorded on the corresponding TransformTape instance.

  5. Test the new Filter implementation and make sure that it works as expected.

Example

Example implementation of an intensity rescaling filter:

>>> import SimpleITK as sitk
>>> import numpy as np
>>>
>>> from pyradise.process import Filter, FilterParams
>>> from pyradise.data import Subject, IntensityImage, TransformInfo
>>>
>>>
>>> class ExampleRescaleFilterParams(FilterParams):
>>>
>>>     def __init__(self, min_out: float, max_out: float) -> None:
>>>         super().__init__()
>>>
>>>         # reverse the values if min_out > max_out
>>>         if min_out > max_out:
>>>             min_out, max_out = max_out, min_out
>>>
>>>         # the minimum and maximum output intensity values
>>>         self.min_out = min_out
>>>         self.max_out = max_out
>>>
>>>
>>> class ExampleRescaleFilter(Filter):
>>>
>>>     @staticmethod
>>>     def is_invertible() -> bool:
>>>         # return True because the filter is invertible
>>>         return True
>>>
>>>     def execute(self,
>>>                 subject: Subject,
>>>                 params: ExampleRescaleFilterParams
>>>                 ) -> Subject:
>>>         # loop through the images
>>>         for image in subject.get_images():
>>>
>>>             # exclude segmentation images
>>>             if not isinstance(image, IntensityImage):
>>>                 continue
>>>
>>>             # retrieve the image data
>>>             original_image_sitk = image.get_image_data()
>>>
>>>             # rescale the intensity
>>>             new_image_sitk = sitk.RescaleIntensity(original_image_sitk,
>>>                                                    params.min_out,
>>>                                                    params.max_out)
>>>
>>>             # update the image data
>>>             image.set_image_data(new_image_sitk)
>>>
>>>             # track the necessary information
>>>             original_image_np = sitk.GetArrayFromImage(original_image_sitk)
>>>             self.tracking_data['min_'] = float(np.min(original_image_np))
>>>             self.tracking_data['max_'] = float(np.max(original_image_np))
>>>             self._register_tracked_data(image, original_image_sitk,
>>>                                         new_image_sitk, params)
>>>
>>>         return subject
>>>
>>>     def execute_inverse(self,
>>>                         subject: Subject,
>>>                         transform_info: TransformInfo,
>>>                         target_image: Optional[Union[SegmentationImage, IntensityImage]] = None
>>>                         ) -> Subject:
>>>         # loop through the images
>>>         for image in subject.get_images():
>>>
>>>             # exclude segmentation images
>>>             if not isinstance(image, IntensityImage):
>>>                 continue
>>>
>>>             # retrieve the tracked data
>>>             min_intensity = transform_info.get_data('min_')
>>>             max_intensity = transform_info.get_data('max_')
>>>
>>>             # undo the intensity rescaling
>>>             original_image_sitk = image.get_image_data()
>>>             new_image_sitk = sitk.RescaleIntensity(original_image_sitk,
>>>                                                    min_intensity,
>>>                                                    max_intensity)
>>>
>>>             # update the image data
>>>             image.set_image_data(new_image_sitk)
>>>
>>>             # there is no need to track information because
>>>             # the operation is inverted
>>>
>>>         return subject
Parameters:

warning_on_non_invertible (bool) – If True, a warning is printed to the console if a filter is called to execute the invertible process but is not invertible (default: False).

abstract static is_invertible()[source]#

Check if the filter is invertible.

Returns:

True if the filter is invertible, otherwise False.

Return type:

bool

set_verbose(verbose)[source]#

Set the verbose state.

Parameters:

verbose (bool) – If True, the filter outputs information to the console, otherwise not.

Return type:

None

Returns:

None

set_warning_on_non_invertible(warn)[source]#

Set the warning state.

Parameters:

warn (bool) – If True, the filter outputs a warning if the filter is called and is not invertible, otherwise not.

Return type:

None

Returns:

None

abstract execute(subject, params)[source]#

Execute the filter on the provided Subject instance.

Note

For the ease of use, the filter provides a private _create_transform_info() method which can be used to create the TransformInfo instances.

Important

The filter is responsible to record the transformations applied to each image such that the invertibility is ensured. Even if the filter is not invertible, the transformations should be recorded such that the order of filter applications can be reconstructed from the transform tapes of the images. In case the filter is not invertible, the is_invertible() must return False.

Parameters:
  • subject (Subject) – The subject to be processed.

  • params (Optional[FilterParams]) – The filter parameters, if required.

Returns:

The processed subject.

Return type:

Subject

abstract execute_inverse(subject, transform_info, target_image=None)[source]#

Execute the filter inversely if possible. Typically, this method gets a temporary subject which contains a single image because the recording of the transformations is image dependent and inappropriate inverse transformations would be applied to the other images. However, this method can also be applied to a whole subject to apply the inverse transformations to all images. This approach provides a more flexible way to handle invertibility of transformations.

Important

If the filter is not invertible, the subject must be returned unchanged and the is_invertible() must return False.

Parameters:
  • subject (Subject) – The subject to be processed.

  • transform_info (TransformInfo) – The TransformInfo instance.

  • target_image (Optional[Union[SegmentationImage, IntensityImage]]) – The target image to which the inverse transformation should be applied. If None, the inverse transformation is applied to all images (default: None).

Returns:

The processed subject.

Return type:

Subject

class LoopEntryFilterParams(loop_axis=None)[source]#

Bases: FilterParams

An abstract filter parameter class which provides in addition to FilterParams the loop_axis parameter which is used to specify the axis to loop over in the LoopEntryFilter.

Parameters:

loop_axis (Optional[int]) – The axis along which the data transformation is performed. If None, the transformation is performed on the whole image at once. If a value is given, the transformation is performed by looping over the corresponding image dimension.

class LoopEntryFilter(warning_on_non_invertible=False)[source]#

Bases: Filter

An abstract filter base class which is feasible to process images slice-wise in a loop over a defined loop_axis. The loop_axis must be specified in the appropriate FilterParams instance and if it takes a value of None, the filter is executed on the whole image extent at once.

Reference:

The implementation of this class is inspired by an earlier version of the pymia package.

abstract static is_invertible()[source]#

Check if the filter is invertible.

Returns:

True if the filter is invertible, otherwise False.

Return type:

bool

static loop_entries(data, params, filter_fn, loop_axis)[source]#

Apply the function filter_fn() by looping over the image using the provided parameters (i.e. params).

Parameters:
  • data (np.ndarray) – The data to be processed.

  • params (Any) – The parameters for the filter function.

  • filter_fn (Callable[[np.ndarray, Any], np.ndarray]) – The filter function.

  • loop_axis (Optional[int]) – The axis to loop over. If None the whole image is taken, otherwise the respective dimension.

Returns:

The processed data.

Return type:

np.ndarray

abstract execute(subject, params)[source]#

Execute the filter on the provided Subject instance.

Note

For the ease of use, the filter provides a private _create_transform_info() method which can be used to create the TransformInfo instances.

Important

The filter is responsible to record the transformations applied to each image such that the invertibility is ensured. Even if the filter is not invertible, the transformations should be recorded such that the order of filter applications can be reconstructed from the transform tapes of the images. In case the filter is not invertible, the is_invertible() must return False.

Parameters:
Returns:

The processed subject.

Return type:

Subject

abstract execute_inverse(subject, transform_info, target_image=None)[source]#

Execute the filter inversely if possible. Typically, this method gets a temporary subject which contains a single image because the recording of the transformations is image dependent and inappropriate inverse transformations would be applied to the other images. However, this method can also be applied to a whole subject to apply the inverse transformations to all images. This approach provides a more flexible way to handle invertibility of transformations.

Important

If the filter is not invertible, the subject must be returned unchanged and the is_invertible() must return False.

Parameters:
  • subject (Subject) – The subject to be processed.

  • transform_info (TransformInfo) – The TransformInfo instance.

  • target_image (Optional[Union[SegmentationImage, IntensityImage]]) – The target image to which the inverse transformation should be applied. If None, the inverse transformation is applied to all images (default: None).

Returns:

The processed subject.

Return type:

Subject

set_verbose(verbose)#

Set the verbose state.

Parameters:

verbose (bool) – If True, the filter outputs information to the console, otherwise not.

Return type:

None

Returns:

None

set_warning_on_non_invertible(warn)#

Set the warning state.

Parameters:

warn (bool) – If True, the filter outputs a warning if the filter is called and is not invertible, otherwise not.

Return type:

None

Returns:

None

class FilterPipeline(filters=None, params=None, warning_on_non_invertible=False)[source]#

Bases: object

A filter pipeline class which can combine multiple Filter instances into one pipeline of sequential filter. This reduces the amount of boilerplate code for the user and provides a nice way to chain multiple filters together.

Parameters:
  • filters (Optional[Tuple[Filter, ...]]) – The filters of the pipeline (default: None).

  • params (Optional[Tuple[FilterParams, ...]]) – The parameters for the filters in the pipeline.

  • warning_on_non_invertible (bool) – If True, a warning is printed to the console if a filter is called to execute the invertible process but is not invertible (default: False).

set_verbose_all(verbose)[source]#

Set the verbose state for all Filter instances.

Parameters:

verbose (bool) – If True the filters print information to the console, otherwise not.

Return type:

None

Returns:

None

add_filter(filter_, params=None)[source]#

Add a Filter instance and its corresponding FilterParams to the pipeline.

Parameters:
Return type:

None

Returns:

None

set_param(params, filter_index)[source]#

Set the FilterParams for a specific Filter instance at index filter_index.

Parameters:
Return type:

None

Returns:

None

add_logger(logger)[source]#

Add a logger to the filter pipeline.

Parameters:

logger (logging.Logger) – The logger to use with the pipeline.

Return type:

None

Returns:

None

execute_iteratively(subject)[source]#

Execute iteratively in the filter pipeline on the provided Subject instance.

Parameters:

subject (Subject) – The Subject instance to be processed by the pipeline.

Returns:

The currently processed Subject iteration.

Return type:

Subject

execute(subject)[source]#

Execute the filter pipeline on the provided Subject instance.

Parameters:

subject (Subject) – The Subject instance to be processed by the pipeline.

Returns:

The processed subject.

Return type:

Subject