Skip to content

Commit efb3506

Browse files
authored
Merge pull request #112 from BrainLesion/feature/2025-algorithms
Feature/2025 algorithms
2 parents 3d407d9 + e498c75 commit efb3506

20 files changed

+1158
-465
lines changed

README.md

Lines changed: 142 additions & 94 deletions
Large diffs are not rendered by default.

brats/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
from brats.core.inpainting_algorithms import Inpainter
44
from brats.core.missing_mri_algorithms import MissingMRI
55
from brats.core.segmentation_algorithms import (
6-
AdultGliomaPostTreatmentSegmenter,
76
AdultGliomaPreTreatmentSegmenter,
7+
AdultGliomaPreAndPostTreatmentSegmenter,
88
AfricaSegmenter,
99
GoATSegmenter,
1010
MeningiomaSegmenter,
1111
MetastasesSegmenter,
12+
MeningiomaRTSegmenter,
1213
PediatricSegmenter,
1314
)
1415

brats/constants.py

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,17 @@ class Algorithms(str, Enum):
2626
pass
2727

2828

29-
class AdultGliomaPostTreatmentAlgorithms(Algorithms):
30-
"""Constants for the available adult glioma post treatment segmentation algorithms."""
29+
class AdultGliomaPreAndPostTreatmentAlgorithms(Algorithms):
30+
"""Constants for the available adult glioma pre and post treatment segmentation algorithms."""
31+
32+
BraTS25_1 = "BraTS25_1"
33+
""" BraTS25 Adult Glioma Segmentation 1st place """
34+
BraTS25_2 = "BraTS25_2"
35+
""" BraTS25 Adult Glioma Segmentation 2nd place """
36+
BraTS25_3A = "BraTS25_3A"
37+
""" BraTS25 Adult Glioma Segmentation 3rd place (tie) """
38+
BraTS25_3B = "BraTS25_3B"
39+
""" BraTS25 Adult Glioma Segmentation 3rd place (tie) """
3140

3241
BraTS24_1 = "BraTS24_1"
3342
""" BraTS24 Adult Glioma Segmentation 1st place """
@@ -48,8 +57,15 @@ class AdultGliomaPreTreatmentAlgorithms(Algorithms):
4857
"""BraTS23 Adult Glioma Segmentation 3rd place (GPU only)"""
4958

5059

51-
class MeningiomaAlgorithms(Algorithms):
52-
"""Constants for the available meningioma segmentation algorithms."""
60+
class MeningiomaRTAlgorithms(Algorithms):
61+
"""Constants for the available meningioma segmentation - Radio Therapy algorithms."""
62+
63+
BraTS25_1 = "BraTS25_1"
64+
""" BraTS25 Meningioma Segmentation 1st place """
65+
BraTS25_2 = "BraTS25_2"
66+
""" BraTS25 Meningioma Segmentation 2nd place """
67+
BraTS25_3 = "BraTS25_3"
68+
""" BraTS25 Meningioma Segmentation 3rd place """
5369

5470
BraTS24_1 = "BraTS24_1"
5571
""" BraTS24 Meningioma Segmentation 1st place """
@@ -58,6 +74,15 @@ class MeningiomaAlgorithms(Algorithms):
5874
BraTS24_3 = "BraTS24_3"
5975
""" BraTS24 Meningioma Segmentation 3rd place """
6076

77+
78+
class MeningiomaAlgorithms(Algorithms):
79+
"""Constants for the available meningioma segmentation algorithms."""
80+
81+
BraTS25_1 = "BraTS25_1"
82+
"""BraTS25 Meningioma Segmentation 1st place """
83+
BraTS25_2 = "BraTS25_2"
84+
"""BraTS25 Meningioma Segmentation 2nd place """
85+
6186
BraTS23_1 = "BraTS23_1"
6287
"""BraTS23 Meningioma Segmentation 1st place (GPU only)"""
6388
BraTS23_2 = "BraTS23_2"
@@ -69,6 +94,15 @@ class MeningiomaAlgorithms(Algorithms):
6994
class PediatricAlgorithms(Algorithms):
7095
"""Constants for the available pediatric segmentation algorithms."""
7196

97+
BraTS25_1A = "BraTS25_1A"
98+
""" BraTS25 Pediatric Segmentation 1st place (tie)"""
99+
BraTS25_1B = "BraTS25_1B"
100+
""" BraTS25 Pediatric Segmentation 1st place (tie)"""
101+
BraTS25_1C = "BraTS25_1C"
102+
""" BraTS25 Pediatric Segmentation 1st place (tie)"""
103+
BraTS25_1D = "BraTS25_1D"
104+
""" BraTS25 Pediatric Segmentation 1st place (tie)"""
105+
72106
BraTS24_1 = "BraTS24_1"
73107
""" BraTS24 Pediatric Segmentation 1st place """
74108
BraTS24_2 = "BraTS24_2"
@@ -87,6 +121,15 @@ class PediatricAlgorithms(Algorithms):
87121
class AfricaAlgorithms(Algorithms):
88122
"""Constants for the available africa segmentation algorithms."""
89123

124+
BraTS25_1 = "BraTS25_1"
125+
""" BraTS25 BraTS-SSA Segmentation 1st place """
126+
BraTS25_2A = "BraTS25_2A"
127+
""" BraTS25 BraTS-SSA Segmentation 2nd place (Tie)"""
128+
BraTS25_2B = "BraTS25_2B"
129+
""" BraTS25 BraTS-SSA Segmentation 2nd place (Tie) """
130+
BraTS25_3 = "BraTS25_3"
131+
""" BraTS25 BraTS-SSA Segmentation 3rd place """
132+
90133
BraTS24_1 = "BraTS24_1"
91134
""" BraTS24 BraTS-Africa Segmentation 1st place """
92135
BraTS24_2 = "BraTS24_2"
@@ -105,6 +148,11 @@ class AfricaAlgorithms(Algorithms):
105148
class MetastasesAlgorithms(Algorithms):
106149
"""Constants for the available Inpainting algorithms."""
107150

151+
BraTS25_1 = "BraTS25_1"
152+
"""BraTS25 Brain Metastases Segmentation 1st place"""
153+
BraTS25_2 = "BraTS25_2"
154+
"""BraTS25 Brain Metastases Segmentation 2nd place"""
155+
108156
BraTS23_1 = "BraTS23_1"
109157
"""BraTS23 Brain Metastases Segmentation 1st place (GPU only)"""
110158
BraTS23_2 = "BraTS23_2"
@@ -116,6 +164,13 @@ class MetastasesAlgorithms(Algorithms):
116164
class InpaintingAlgorithms(Algorithms):
117165
"""Constants for the available BraTS Inpainting algorithms."""
118166

167+
BraTS25_1A = "BraTS25_1A"
168+
""" BraTS25 Inpainting shared 1st place (tie) """
169+
BraTS25_1B = "BraTS25_1B"
170+
""" BraTS25 Inpainting shared 1st place (tie)"""
171+
BraTS25_2 = "BraTS25_2"
172+
""" BraTS25 Inpainting 2nd place """
173+
119174
BraTS24_1 = "BraTS24_1"
120175
""" BraTS24 Inpainting 1st place """
121176
BraTS24_2 = "BraTS24_2"
@@ -132,21 +187,38 @@ class InpaintingAlgorithms(Algorithms):
132187

133188

134189
class MissingMRIAlgorithms(Algorithms):
135-
"""Constants for the available missing mri algorithms."""
190+
"""Constants for the available missing mri algorithms."""
191+
192+
BraTS25_1 = "BraTS25_1"
193+
""" BraTS25 MissingMRI 1st place """
194+
BraTS25_2 = "BraTS25_2"
195+
""" BraTS25 MissingMRI 2nd place """
196+
BraTS25_3 = "BraTS25_3"
197+
""" BraTS25 MissingMRI 3rd place """
136198

137199
BraTS24_1 = "BraTS24_1"
138200
""" BraTS24 MissingMRI 1st place """
139201
BraTS24_2 = "BraTS24_2"
140202
""" BraTS24 MissingMRI 2nd place """
141203
BraTS24_3 = "BraTS24_3"
142204
""" BraTS24 MissingMRI 3rd place """
205+
143206
BraTS23_1 = "BraTS23_1"
144207
""" BraTS23 MissingMRI 1st place """
145208

146209

147210
class GoATAlgorithms(Algorithms):
148211
"""Constants for the available missing mri algorithms."""
149212

213+
BraTS25_1A = "BraTS25_1A"
214+
""" BraTS25 Generalizability Across Tumors (BraTS-GoAT) 1st place (tie) """
215+
BraTS25_1B = "BraTS25_1B"
216+
""" BraTS25 Generalizability Across Tumors (BraTS-GoAT) 1st place (tie) """
217+
BraTS25_1C = "BraTS25_1C"
218+
""" BraTS25 Generalizability Across Tumors (BraTS-GoAT) 1st place (tie) """
219+
BraTS25_1D = "BraTS25_1D"
220+
""" BraTS25 Generalizability Across Tumors (BraTS-GoAT) 1st place (tie) """
221+
150222
BraTS24_1 = "BraTS24_1"
151223
""" BraTS24 Generalizability Across Tumors (BraTS-GoAT) 1st place (The only submission)"""
152224

@@ -161,10 +233,11 @@ class GoATAlgorithms(Algorithms):
161233
ADULT_GLIOMA_PRE_TREATMENT_SEGMENTATION_ALGORITHMS = (
162234
META_DIR / "adult_glioma_pre_treatment.yml"
163235
)
164-
ADULT_GLIOMA_POST_TREATMENT_SEGMENTATION_ALGORITHMS = (
165-
META_DIR / "adult_glioma_post_treatment.yml"
236+
ADULT_GLIOMA_PRE_AND_POST_TREATMENT_SEGMENTATION_ALGORITHMS = (
237+
META_DIR / "adult_glioma_pre_and_post_treatment.yml"
166238
)
167239
MENINGIOMA_SEGMENTATION_ALGORITHMS = META_DIR / "meningioma.yml"
240+
MENINGIOMA_RT_SEGMENTATION_ALGORITHMS = META_DIR / "meningioma_rt.yml"
168241
PEDIATRIC_SEGMENTATION_ALGORITHMS = META_DIR / "pediatric.yml"
169242
AFRICA_SEGMENTATION_ALGORITHMS = META_DIR / "africa.yml"
170243
METASTASES_SEGMENTATION_ALGORITHMS = META_DIR / "metastases.yml"

brats/core/brats_algorithm.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from brats.utils.algorithm_config import load_algorithms
1212
from brats.constants import OUTPUT_NAME_SCHEMA, Algorithms, Task
1313
from brats.utils.data_handling import InferenceSetup
14+
from brats.utils.exceptions import AlgorithmConfigException
1415

1516

1617
class BraTSAlgorithm(ABC):
@@ -23,7 +24,7 @@ def __init__(
2324
algorithm: Algorithms,
2425
algorithms_file_path: Path,
2526
task: Task,
26-
cuda_devices: Optional[str] = "0",
27+
cuda_devices: str = "0",
2728
force_cpu: bool = False,
2829
):
2930
# inference device setup
@@ -34,6 +35,10 @@ def __init__(
3435
self.algorithm_list = load_algorithms(file_path=algorithms_file_path)
3536
# save algorithm identifier for logging etc.
3637
self.algorithm_key = algorithm.value
38+
if self.algorithm_key not in self.algorithm_list:
39+
raise AlgorithmConfigException(
40+
f"Algorithm {self.algorithm_key} not found in {algorithms_file_path}"
41+
)
3742
# data for selected algorithm
3843
self.algorithm = self.algorithm_list[algorithm.value]
3944

@@ -63,8 +68,21 @@ def _standardize_batch_inputs(
6368
"""
6469
pass
6570

71+
def extract_identifier_from_subject_id(self, subject_id: str) -> str:
72+
"""
73+
Extract the index from the subject ID
74+
Args:
75+
subject_id (str): Subject ID of the input
76+
Returns:
77+
str: Extracted identifier
78+
"""
79+
return "-".join(subject_id.split("-")[-2:])
80+
6681
def _process_single_output(
67-
self, tmp_output_folder: Path | str, subject_id: str, output_file: Path
82+
self,
83+
tmp_output_folder: Path | str,
84+
subject_id: str,
85+
output_file: Path,
6886
) -> None:
6987
"""
7088
Process the output of a single inference run and save it in the specified file.
@@ -79,9 +97,14 @@ def _process_single_output(
7997
# Missing MRI has no fixed names since the missing modality differs and is included in the name
8098
algorithm_output = Path(tmp_output_folder).iterdir().__next__()
8199
else:
82-
algorithm_output = Path(tmp_output_folder) / OUTPUT_NAME_SCHEMA[
83-
self.task
84-
].format(subject_id=subject_id)
100+
# extract id from subject id, i.e. BraTS-MEN-00000-000 => 00000-000
101+
identifier = self.extract_identifier_from_subject_id(subject_id)
102+
possible_output = list(Path(tmp_output_folder).glob(f"*{identifier}*"))
103+
if len(possible_output) == 0:
104+
raise FileNotFoundError(
105+
f"No output found for subject {subject_id} in {tmp_output_folder}"
106+
)
107+
algorithm_output = possible_output[0]
85108

86109
# ensure path exists and rename output to the desired path
87110
output_file = Path(output_file).absolute()
@@ -123,9 +146,15 @@ def _process_batch_output(
123146
/ f"{external_name}{'-' + modality if modality else ''}.nii.gz"
124147
)
125148
else:
126-
algorithm_output = Path(tmp_output_folder) / OUTPUT_NAME_SCHEMA[
127-
self.task
128-
].format(subject_id=internal_name)
149+
identifier = self.extract_identifier_from_subject_id(internal_name)
150+
possible_outputs = list(Path(tmp_output_folder).glob(f"*{identifier}*"))
151+
if len(possible_outputs) == 0:
152+
logger.error(
153+
f"No output found for subject {internal_name} in {tmp_output_folder}"
154+
)
155+
continue
156+
algorithm_output = possible_outputs[0]
157+
129158
output_file = output_folder / f"{external_name}.nii.gz"
130159
shutil.move(algorithm_output, output_file)
131160

@@ -166,7 +195,7 @@ def _infer_single(
166195
self._process_single_output(
167196
tmp_output_folder=tmp_output_folder,
168197
subject_id=subject_id,
169-
output_file=output_file,
198+
output_file=Path(output_file),
170199
)
171200
logger.info(f"Saved output to: {Path(output_file).absolute()}")
172201

0 commit comments

Comments
 (0)