From de0cb9954e09c189072b9d3533e1662a5c4850ee Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Thu, 23 Apr 2026 15:49:15 +0200 Subject: [PATCH 1/6] Moved hd_bet to seperate file --- .../brain_extraction/__init__.py | 3 +- .../brain_extraction/brain_extractor.py | 72 +----------------- .../brain_extraction/hd_bet.py | 76 +++++++++++++++++++ 3 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 brainles_preprocessing/brain_extraction/hd_bet.py diff --git a/brainles_preprocessing/brain_extraction/__init__.py b/brainles_preprocessing/brain_extraction/__init__.py index 92a981b..a2c9b9e 100644 --- a/brainles_preprocessing/brain_extraction/__init__.py +++ b/brainles_preprocessing/brain_extraction/__init__.py @@ -1 +1,2 @@ -from .brain_extractor import HDBetExtractor +from .hd_bet import HDBetExtractor +from .synthstrip import SynthStripExtractor \ No newline at end of file diff --git a/brainles_preprocessing/brain_extraction/brain_extractor.py b/brainles_preprocessing/brain_extraction/brain_extractor.py index 03a716b..cd9d2b3 100644 --- a/brainles_preprocessing/brain_extraction/brain_extractor.py +++ b/brainles_preprocessing/brain_extraction/brain_extractor.py @@ -1,17 +1,11 @@ # TODO add typing and docs -import shutil -from abc import ABC, abstractmethod +from abc import abstractmethod from pathlib import Path -from typing import Optional, Union -from enum import Enum +from typing import Union from auxiliary.io import read_image, write_image -from brainles_hd_bet import run_hd_bet -class Mode(Enum): - FAST = "fast" - ACCURATE = "accurate" class BrainExtractor: @@ -77,65 +71,3 @@ def apply_mask( raise RuntimeError(f"Error writing output file: {e}") from e -class HDBetExtractor(BrainExtractor): - def extract( - self, - input_image_path: Union[str, Path], - masked_image_path: Union[str, Path], - brain_mask_path: Union[str, Path], - mode: Union[str, Mode] = Mode.ACCURATE, - device: Optional[Union[int, str]] = 0, - do_tta: bool = True, - **kwargs, - ) -> None: - # GPU + accurate + TTA - """ - Skull-strips images with HD-BET and generates a skull-stripped file and mask. - - Args: - input_image_path (str or Path): Path to the input image. - masked_image_path (str or Path): Path where the brain-extracted image will be saved. - brain_mask_path (str or Path): Path where the brain mask will be saved. - mode (str or Mode): Extraction mode ('fast' or 'accurate'). - device (str or int): Device to use for computation (e.g., 0 for GPU 0, 'cpu' for CPU). - do_tta (bool): whether to do test time data augmentation by mirroring along all axes. - """ - - # Ensure mode is a Mode enum instance - if isinstance(mode, str): - try: - mode_enum = Mode(mode.lower()) - except ValueError: - raise ValueError(f"'{mode}' is not a valid Mode.") - elif isinstance(mode, Mode): - mode_enum = mode - else: - raise TypeError("Mode must be a string or a Mode enum instance.") - - # Run HD-BET - run_hd_bet( - mri_fnames=[str(input_image_path)], - output_fnames=[str(masked_image_path)], - mode=mode_enum.value, - device=device, - # TODO consider postprocessing - postprocess=False, - do_tta=do_tta, - keep_mask=True, - overwrite=True, - ) - - # Construct the path to the generated mask - masked_image_path = Path(masked_image_path) - hdbet_mask_path = masked_image_path.with_name( - masked_image_path.name.replace(".nii.gz", "_mask.nii.gz") - ) - - if hdbet_mask_path.resolve() != Path(brain_mask_path).resolve(): - try: - shutil.copyfile( - src=str(hdbet_mask_path), - dst=str(brain_mask_path), - ) - except Exception as e: - raise RuntimeError(f"Error copying mask file: {e}") from e diff --git a/brainles_preprocessing/brain_extraction/hd_bet.py b/brainles_preprocessing/brain_extraction/hd_bet.py new file mode 100644 index 0000000..cd631ea --- /dev/null +++ b/brainles_preprocessing/brain_extraction/hd_bet.py @@ -0,0 +1,76 @@ +from pathlib import Path +from typing import Optional, Union +import shutil +from enum import Enum + +from brainles_preprocessing.brain_extraction.brain_extractor import BrainExtractor +from brainles_hd_bet import run_hd_bet + + +class Mode(Enum): + FAST = "fast" + ACCURATE = "accurate" + + +class HDBetExtractor(BrainExtractor): + def extract( + self, + input_image_path: Union[str, Path], + masked_image_path: Union[str, Path], + brain_mask_path: Union[str, Path], + mode: Union[str, Mode] = Mode.ACCURATE, + device: Optional[Union[int, str]] = 0, + do_tta: bool = True, + **kwargs, + ) -> None: + # GPU + accurate + TTA + """ + Skull-strips images with HD-BET and generates a skull-stripped file and mask. + + Args: + input_image_path (str or Path): Path to the input image. + masked_image_path (str or Path): Path where the brain-extracted image will be saved. + brain_mask_path (str or Path): Path where the brain mask will be saved. + mode (str or Mode): Extraction mode ('fast' or 'accurate'). + device (str or int): Device to use for computation (e.g., 0 for GPU 0, 'cpu' for CPU). + do_tta (bool): whether to do test time data augmentation by mirroring along all axes. + """ + + # Ensure mode is a Mode enum instance + if isinstance(mode, str): + try: + mode_enum = Mode(mode.lower()) + except ValueError: + raise ValueError(f"'{mode}' is not a valid Mode.") + elif isinstance(mode, Mode): + mode_enum = mode + else: + raise TypeError("Mode must be a string or a Mode enum instance.") + + # Run HD-BET + run_hd_bet( + mri_fnames=[str(input_image_path)], + output_fnames=[str(masked_image_path)], + mode=mode_enum.value, + device=device, + # TODO consider postprocessing + postprocess=False, + do_tta=do_tta, + keep_mask=True, + overwrite=True, + ) + + # Construct the path to the generated mask + masked_image_path = Path(masked_image_path) + hdbet_mask_path = masked_image_path.with_name( + masked_image_path.name.replace(".nii.gz", "_mask.nii.gz") + ) + + if hdbet_mask_path.resolve() != Path(brain_mask_path).resolve(): + try: + shutil.copyfile( + src=str(hdbet_mask_path), + dst=str(brain_mask_path), + ) + except Exception as e: + raise RuntimeError(f"Error copying mask file: {e}") from e From d8205f704428fce845981c12bf61456bb5f34dcc Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:56:18 +0000 Subject: [PATCH 2/6] Autoformat with black --- brainles_preprocessing/brain_extraction/__init__.py | 2 +- brainles_preprocessing/brain_extraction/brain_extractor.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/brainles_preprocessing/brain_extraction/__init__.py b/brainles_preprocessing/brain_extraction/__init__.py index a2c9b9e..7177a08 100644 --- a/brainles_preprocessing/brain_extraction/__init__.py +++ b/brainles_preprocessing/brain_extraction/__init__.py @@ -1,2 +1,2 @@ from .hd_bet import HDBetExtractor -from .synthstrip import SynthStripExtractor \ No newline at end of file +from .synthstrip import SynthStripExtractor diff --git a/brainles_preprocessing/brain_extraction/brain_extractor.py b/brainles_preprocessing/brain_extraction/brain_extractor.py index cd9d2b3..e02a015 100644 --- a/brainles_preprocessing/brain_extraction/brain_extractor.py +++ b/brainles_preprocessing/brain_extraction/brain_extractor.py @@ -6,8 +6,6 @@ from auxiliary.io import read_image, write_image - - class BrainExtractor: @abstractmethod def extract( @@ -69,5 +67,3 @@ def apply_mask( ) except Exception as e: raise RuntimeError(f"Error writing output file: {e}") from e - - From 703f8134bed186d9ba23fc4cc61e8997695bd16b Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Thu, 23 Apr 2026 16:04:45 +0200 Subject: [PATCH 3/6] fixed imports in preprocessor --- brainles_preprocessing/preprocessor/preprocessor.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/brainles_preprocessing/preprocessor/preprocessor.py b/brainles_preprocessing/preprocessor/preprocessor.py index 2983f39..797fafe 100644 --- a/brainles_preprocessing/preprocessor/preprocessor.py +++ b/brainles_preprocessing/preprocessor/preprocessor.py @@ -11,10 +11,8 @@ from loguru import logger -from brainles_preprocessing.brain_extraction.brain_extractor import ( - BrainExtractor, - HDBetExtractor, -) +from brainles_preprocessing.brain_extraction.brain_extractor import BrainExtractor +from brainles_preprocessing.brain_extraction.hd_bet import HDBetExtractor from brainles_preprocessing.constants import PreprocessorSteps from brainles_preprocessing.defacing import Defacer, QuickshearDefacer from brainles_preprocessing.modality import CenterModality, Modality From f71f59223a8a9296cf9e1fa427eed6c1a7cb6f8c Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Fri, 24 Apr 2026 10:43:44 +0200 Subject: [PATCH 4/6] Included ABC inheritance --- brainles_preprocessing/brain_extraction/brain_extractor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/brain_extraction/brain_extractor.py b/brainles_preprocessing/brain_extraction/brain_extractor.py index e02a015..908982a 100644 --- a/brainles_preprocessing/brain_extraction/brain_extractor.py +++ b/brainles_preprocessing/brain_extraction/brain_extractor.py @@ -1,12 +1,12 @@ # TODO add typing and docs -from abc import abstractmethod +from abc import abstractmethod, ABC from pathlib import Path from typing import Union from auxiliary.io import read_image, write_image -class BrainExtractor: +class BrainExtractor(ABC): @abstractmethod def extract( self, From 66042587b90ba00f9f4d3386082df0970d50815d Mon Sep 17 00:00:00 2001 From: nicmuenster Date: Fri, 24 Apr 2026 13:32:07 +0200 Subject: [PATCH 5/6] Made the Synthstrip import optional / as a try&catch in the __init__.py file --- brainles_preprocessing/brain_extraction/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/brainles_preprocessing/brain_extraction/__init__.py b/brainles_preprocessing/brain_extraction/__init__.py index 7177a08..bc1f009 100644 --- a/brainles_preprocessing/brain_extraction/__init__.py +++ b/brainles_preprocessing/brain_extraction/__init__.py @@ -1,2 +1,5 @@ from .hd_bet import HDBetExtractor -from .synthstrip import SynthStripExtractor +try: + from .synthstrip import SynthStripExtractor +except ImportError: + pass \ No newline at end of file From 4f9f8f556a977253eb90fca0af05638a89523868 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:41:55 +0000 Subject: [PATCH 6/6] Autoformat with black --- brainles_preprocessing/brain_extraction/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brainles_preprocessing/brain_extraction/__init__.py b/brainles_preprocessing/brain_extraction/__init__.py index bc1f009..8c97b5c 100644 --- a/brainles_preprocessing/brain_extraction/__init__.py +++ b/brainles_preprocessing/brain_extraction/__init__.py @@ -1,5 +1,6 @@ from .hd_bet import HDBetExtractor + try: from .synthstrip import SynthStripExtractor except ImportError: - pass \ No newline at end of file + pass