From 25b162601cf1fccd1e3f3863175736900ad71f25 Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Wed, 14 Jan 2026 15:36:18 +0100 Subject: [PATCH 1/8] Updated defacer to either use minimum or custom value --- brainles_preprocessing/defacing/defacer.py | 32 +++++++++++++------ .../defacing/quickshear/quickshear.py | 6 ++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 9c54253..a9df702 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -1,17 +1,26 @@ from abc import ABC, abstractmethod from pathlib import Path -from typing import Union - +from typing import Union, Optional +import numpy as np from auxiliary.io import read_image, write_image class Defacer(ABC): - """ - Base class for defacing medical images using brain masks. + def __init__(self, + masking_value: Optional[Union[int, float]] = None, + ): + + """ + Base class for defacing medical images using brain masks. + + Subclasses should implement the `deface` method to generate a defaced image + based on the provided input image and mask. + """ + # Here, masking value functions across a global value across all images and modalities + # If no value is passed, the minimum of a given input image is chosen + self.masking_value = masking_value + - Subclasses should implement the `deface` method to generate a defaced image - based on the provided input image and mask. - """ @abstractmethod def deface( @@ -63,8 +72,13 @@ def apply_mask( if input_data.shape != mask_data.shape: raise ValueError("Input image and mask must have the same dimensions.") - # Apply mask (element-wise multiplication) - masked_data = input_data * mask_data + # check whether a global masking value was passed, otherwise choose minimum + if self.masking_value is None: + current_masking_value = np.min(input_data) + else: + current_masking_value = self.masking_value.astype(input_data.dtype) + # Apply mask (element-wise either input or masking value) + masked_data = np.where(masked_data.astype(bool), input_data, current_masking_value) # Save the defaced image write_image( diff --git a/brainles_preprocessing/defacing/quickshear/quickshear.py b/brainles_preprocessing/defacing/quickshear/quickshear.py index 754b593..5f64e5c 100644 --- a/brainles_preprocessing/defacing/quickshear/quickshear.py +++ b/brainles_preprocessing/defacing/quickshear/quickshear.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Union +from typing import Union, Optional import nibabel as nib from auxiliary.io import read_image, write_image @@ -35,6 +35,7 @@ def __init__( buffer: float = 10.0, force_atlas_registration: bool = True, atlas_image_path: Union[str, Path, Atlas] = Atlas.SRI24, + masking_value: Optional[Union[int, float]] = None, ): """Initialize Quickshear defacer @@ -42,8 +43,9 @@ def __init__( buffer (float, optional): buffer parameter from quickshear algorithm. Defaults to 10.0. force_atlas_registration (bool, optional): If True, forces atlas registration of the BET mask before defacing to potentially boost quickshear performance. Defaults to True. atlas_image_path (Union[str, Path, Atlas], optional): Path to the atlas image or an Atlas enum value that will be used for the optional atlas registrations. Defaults to Atlas.SRI24. + masking_value (Union[int, float], optional): global value to be inserted in the masked areas. Default is None which leads to the minimum of each respective image. """ - super().__init__() + super().__init__(masking_value=masking_value) self.buffer = buffer self.force_atlas_registration = force_atlas_registration self.atlas_image_path = atlas_image_path From 0f49f4c3b9fe2ac06beb743a03dfa66b6e523192 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:41:37 +0000 Subject: [PATCH 2/8] Autoformat with black --- brainles_preprocessing/defacing/defacer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index a9df702..9cc4e6c 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -6,10 +6,10 @@ class Defacer(ABC): - def __init__(self, - masking_value: Optional[Union[int, float]] = None, - ): - + def __init__( + self, + masking_value: Optional[Union[int, float]] = None, + ): """ Base class for defacing medical images using brain masks. @@ -20,8 +20,6 @@ def __init__(self, # If no value is passed, the minimum of a given input image is chosen self.masking_value = masking_value - - @abstractmethod def deface( self, @@ -78,7 +76,9 @@ def apply_mask( else: current_masking_value = self.masking_value.astype(input_data.dtype) # Apply mask (element-wise either input or masking value) - masked_data = np.where(masked_data.astype(bool), input_data, current_masking_value) + masked_data = np.where( + masked_data.astype(bool), input_data, current_masking_value + ) # Save the defaced image write_image( From 1c9b56ce51a3794e84c1e2941913cb16112f84aa Mon Sep 17 00:00:00 2001 From: nicmuenster <80422089+nicmuenster@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:54:34 +0100 Subject: [PATCH 3/8] Update brainles_preprocessing/defacing/defacer.py --- brainles_preprocessing/defacing/defacer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 9cc4e6c..57f4bf8 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -74,7 +74,7 @@ def apply_mask( if self.masking_value is None: current_masking_value = np.min(input_data) else: - current_masking_value = self.masking_value.astype(input_data.dtype) + current_masking_value = cast(self.masking_value, input_data.dtype) # Apply mask (element-wise either input or masking value) masked_data = np.where( masked_data.astype(bool), input_data, current_masking_value From 86d158c94d7ed3726711633a73984cf7ab7a5da4 Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Wed, 14 Jan 2026 16:04:07 +0100 Subject: [PATCH 4/8] Fixed typing and minor bugs --- brainles_preprocessing/defacing/defacer.py | 24 +++++++++---------- .../defacing/quickshear/quickshear.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 57f4bf8..fe984db 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -16,24 +16,24 @@ def __init__( Subclasses should implement the `deface` method to generate a defaced image based on the provided input image and mask. """ - # Here, masking value functions across a global value across all images and modalities + # Here, masking value functions as a global value across all images and modalities # If no value is passed, the minimum of a given input image is chosen self.masking_value = masking_value - @abstractmethod - def deface( + @abstractmethod + def deface( self, input_image_path: Union[str, Path], mask_image_path: Union[str, Path], - ) -> None: - """ - Generate a defacing mask provided an input image. + ) -> None: + """ + Generate a defacing mask provided an input image. - Args: - input_image_path (str or Path): Path to the input image (NIfTI format). - mask_image_path (str or Path): Path to the output mask image (NIfTI format). - """ - pass + Args: + input_image_path (str or Path): Path to the input image (NIfTI format). + mask_image_path (str or Path): Path to the output mask image (NIfTI format). + """ + pass def apply_mask( self, @@ -77,7 +77,7 @@ def apply_mask( current_masking_value = cast(self.masking_value, input_data.dtype) # Apply mask (element-wise either input or masking value) masked_data = np.where( - masked_data.astype(bool), input_data, current_masking_value + mask_data.astype(bool), input_data, current_masking_value ) # Save the defaced image diff --git a/brainles_preprocessing/defacing/quickshear/quickshear.py b/brainles_preprocessing/defacing/quickshear/quickshear.py index 5f64e5c..a33b82a 100644 --- a/brainles_preprocessing/defacing/quickshear/quickshear.py +++ b/brainles_preprocessing/defacing/quickshear/quickshear.py @@ -43,7 +43,7 @@ def __init__( buffer (float, optional): buffer parameter from quickshear algorithm. Defaults to 10.0. force_atlas_registration (bool, optional): If True, forces atlas registration of the BET mask before defacing to potentially boost quickshear performance. Defaults to True. atlas_image_path (Union[str, Path, Atlas], optional): Path to the atlas image or an Atlas enum value that will be used for the optional atlas registrations. Defaults to Atlas.SRI24. - masking_value (Union[int, float], optional): global value to be inserted in the masked areas. Default is None which leads to the minimum of each respective image. + masking_value (Optional[Union[int, float]], optional): global value to be inserted in the masked areas. Default is None which leads to the minimum of each respective image. """ super().__init__(masking_value=masking_value) self.buffer = buffer From 2fbf914b43cfd43d46ab416d3bfd92012fdae732 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:06:49 +0000 Subject: [PATCH 5/8] Autoformat with black --- brainles_preprocessing/defacing/defacer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index fe984db..54e4260 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -22,9 +22,9 @@ def __init__( @abstractmethod def deface( - self, - input_image_path: Union[str, Path], - mask_image_path: Union[str, Path], + self, + input_image_path: Union[str, Path], + mask_image_path: Union[str, Path], ) -> None: """ Generate a defacing mask provided an input image. From 7a9b7b54b1b6777b24ccf76f5379270dd7c22fe7 Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Wed, 14 Jan 2026 16:14:42 +0100 Subject: [PATCH 6/8] Second attempt at fixing the typing issue --- brainles_preprocessing/defacing/defacer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 54e4260..5cb4dd0 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -74,7 +74,7 @@ def apply_mask( if self.masking_value is None: current_masking_value = np.min(input_data) else: - current_masking_value = cast(self.masking_value, input_data.dtype) + current_masking_value = np.array(self.masking_value).astype(input_data.dtype).item() # Apply mask (element-wise either input or masking value) masked_data = np.where( mask_data.astype(bool), input_data, current_masking_value From f282cdf5df7309737eafee9b4d00376ef7f5ab08 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:49:45 +0000 Subject: [PATCH 7/8] Autoformat with black --- brainles_preprocessing/defacing/defacer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 5cb4dd0..1de3efb 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -74,7 +74,9 @@ def apply_mask( if self.masking_value is None: current_masking_value = np.min(input_data) else: - current_masking_value = np.array(self.masking_value).astype(input_data.dtype).item() + current_masking_value = ( + np.array(self.masking_value).astype(input_data.dtype).item() + ) # Apply mask (element-wise either input or masking value) masked_data = np.where( mask_data.astype(bool), input_data, current_masking_value From 1f9cf2db5bf7debc22c1e57bd35ea391e63bbe69 Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Thu, 15 Jan 2026 17:58:39 +0100 Subject: [PATCH 8/8] Slight reformatting and added TODO for modality specific masking values --- brainles_preprocessing/defacing/defacer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/brainles_preprocessing/defacing/defacer.py b/brainles_preprocessing/defacing/defacer.py index 1de3efb..0e0e876 100644 --- a/brainles_preprocessing/defacing/defacer.py +++ b/brainles_preprocessing/defacing/defacer.py @@ -18,6 +18,8 @@ def __init__( """ # Here, masking value functions as a global value across all images and modalities # If no value is passed, the minimum of a given input image is chosen + # TODO: Consider extending this to modality-specific masking values in the future, this should + # probably be implemented as a property of the the specific modality self.masking_value = masking_value @abstractmethod