Source code for pyradise.process.invertibility

import warnings
from copy import deepcopy
from typing import Optional, Tuple, Union

from pyradise.data import (IntensityImage, Modality, OrganAnnotatorCombination,
                           SegmentationImage, Subject, TransformInfo,
                           seq_to_modalities,
                           seq_to_organ_annotator_combinations)
from pyradise.process import Filter, FilterParams

__all__ = ["PlaybackTransformTapeFilterParams", "PlaybackTransformTapeFilter"]


[docs]class PlaybackTransformTapeFilterParams(FilterParams): """A filter parameter class for the :class:`~pyradise.process.invertibility.PlaybackTransformTapeFilter` class. Args: modalities (Optional[Tuple[Union[str, Modality], ...]]): A tuple of modalities for which the transform tape should be played back. If None, the transform tape will be played back for all modalities (default: None). organ_annotator_combinations (Optional[Tuple[Union[Tuple[str, str], OrganRaterCombination], ...]]): A tuple of organ-annotator combinations for which the transform tape should be played back. If None, the transform tape will be played back for all organ-annotator combinations (default: None). """ def __init__( self, modalities: Optional[Tuple[Union[str, Modality], ...]] = None, organ_annotator_combinations: Optional[Tuple[Union[Tuple[str, str], OrganAnnotatorCombination], ...]] = None, ) -> None: super().__init__() if modalities is not None: self.modalities = seq_to_modalities(modalities) else: self.modalities = None if organ_annotator_combinations is not None: self.organ_annotator_combinations = seq_to_organ_annotator_combinations(organ_annotator_combinations) else: self.organ_annotator_combinations = organ_annotator_combinations
[docs]class PlaybackTransformTapeFilter(Filter): """A filter class for playing back the transform tape of specific or all :class:`~pyradise.data.image.Image` instances of the provided :class:`~pyradise.data.subject.Subject` instance. This filter is helpful for restoring the spatial properties of the loaded data such that the output data of the processing pipeline has identical spatial properties as the input data. """
[docs] @staticmethod def is_invertible() -> bool: """Return False because the filter is not invertible. Returns: bool: False. """ return False
[docs] def execute(self, subject: Subject, params: Optional[PlaybackTransformTapeFilterParams] = None) -> Subject: """Execute the filter on the provided :class:`~pyradise.data.subject.Subject` instance. Args: subject (Subject): The :class:`~pyradise.data.subject.Subject` instance. params (Optional[FilterParams]): The filter parameters. Returns: Subject: The :class:`~pyradise.data.subject.Subject` instance with played back images. """ original_images = [] changed_images = [] images = subject.get_images() for image in images: # exclude images not matching the provided criteria if isinstance(image, IntensityImage) and params.modalities is not None: if not image.get_modality() in params.modalities: continue if isinstance(image, SegmentationImage) and params.organ_annotator_combinations is not None: if not image.get_organ_annotator_combination() in params.organ_annotator_combinations: continue # copy the original subject temp_subject = deepcopy(subject) transform_tape = image.get_transform_tape() transform_infos = transform_tape.get_recorded_elements(reverse=True) # play back the transform tape for transform_info in transform_infos: filter_ = transform_info.get_filter() # activate warnings if the filter is not invertible if self.warn_on_non_invertible: filter_.set_warning_on_non_invertible(True) temp_subject = filter_.execute_inverse(temp_subject, transform_info, image) # collect the modified images changed_image_candidates = [img for img in temp_subject.get_images() if img == image] changed_images.append(changed_image_candidates[0]) original_images.append(image) # replace the original images with the modified images and reset the transform tape for original_image, changed_image in zip(original_images, changed_images): original_image.set_image_data(changed_image.get_image_data()) original_image.get_transform_tape().reset() return subject
[docs] def execute_inverse( self, subject: Subject, transform_info: TransformInfo, target_image: Optional[Union[SegmentationImage, IntensityImage]] = None, ) -> Subject: """Return the provided :class:`~pyradise.data.subject.Subject` instance without any processing because :class:`~pyradise.data.taping.TransformTape` playback is not invertible. Args: subject (Subject): The :class:`~pyradise.data.subject.Subject` instance. transform_info (TransformInfo): The :class:`~pyradise.data.taping.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: Subject: The provided :class:`~pyradise.data.subject.Subject` instance. """ # potentially warn the user that the operation is not invertible if self.warn_on_non_invertible and not self.is_invertible(): warnings.warn( "WARNING: " f"The {self.__class__.__name__} is called to invert its operation for the following image: \n" f"\t{target_image.__str__()} \nHowever, the filter is not invertible. The provided subject " "is returned without modification." ) return subject