diff --git a/README.md b/README.md
index 3c89385..c4ebaf7 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ from brats import AdultGliomaSegmenter
from brats.constants import AdultGliomaAlgorithms
segmenter = AdultGliomaSegmenter(algorithm=AdultGliomaAlgorithms.BraTS23_1, cuda_devices="0")
-# these parameters are optional, by default the winning algorithm will be used on cuda:0
+# these parameters are optional, by default the winning algorithm of 2023 will be used on cuda:0
segmenter.infer_single(
t1c="path/to/t1c.nii.gz",
t1n="path/to/t1n.nii.gz",
@@ -65,6 +65,9 @@ segmenter.infer_single(
| Year | Rank | Author | Paper | CPU Support | Key Enum |
| ---- | ---- | --------------------------------- | ------------------------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------- |
+| 2024 | 1st | _André Ferreira, et al._ | N/A | ❌ | [BraTS24_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS24_1) |
+| 2024 | 2nd | _Team kimbab_ | N/A | ❌ | [BraTS24_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS24_2) |
+| 2024 | 3rd | _Adrian Celaya_ | N/A | ✅ | [BraTS24_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS24_3) |
| 2023 | 1st | _André Ferreira, et al._ | [Link](https://arxiv.org/abs/2402.17317v1) | ❌ | [BraTS23_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS23_1) |
| 2023 | 2nd | _Andriy Myronenko, et al._ | N/A | ❌ | [BraTS23_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS23_2) |
| 2023 | 3rd | _Fadillah Adamsyah Maani, et al._ | N/A | ❌ | [BraTS23_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AdultGliomaAlgorithms.BraTS23_3) |
@@ -94,6 +97,9 @@ segmenter.infer_single(
| Year | Rank | Author | Paper | CPU Support | Key Enum |
| ---- | ---- | -------------------------- | ----- | ----------- | --------------------------------------------------------------------------------------------------------------- |
+| 2024 | 1st | _Zhifan Jiang et al._ | N/A | ❌ | [BraTS24_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS24_1) |
+| 2024 | 2nd | _Long Bai, et al._ | N/A | ✅ | [BraTS24_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS24_2) |
+| 2024 | 1st | _Sarim Hashmi, et al._ | N/A | ❌ | [BraTS24_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS24_3) |
| 2023 | 1st | _Andriy Myronenko, et al._ | TODO | ❌ | [BraTS23_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS23_1) |
| 2023 | 2nd | _Alyssa R Amod, et al._ | N/A | ❌ | [BraTS23_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS23_2) |
| 2023 | 3rd | _Ziyan Huang, et al._ | N/A | ✅ | [BraTS23_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.AfricaAlgorithms.BraTS23_3) |
@@ -104,10 +110,18 @@ segmenter.infer_single(
Meningioma Segmentation
+**Note**
+Unlike other segmentation challenges the expected inputs for the Meningioma Segmentation Algorithms differ between years.
+- _2023_: All 4 modalities are used (t1c, t1n, t2f, t2w)
+- _2024_: Only t1c is used
+
+Therefore the usage differs slightly, depending on which algorithm is used. To understand why, please refer to the [2024 challenge manuscript](https://arxiv.org/abs/2405.18383).
+
```python
from brats import MeningiomaSegmenter
from brats.constants import MeningiomaAlgorithms
+### Example for 2023 algorithms
segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS23_1, cuda_devices="0")
# these parameters are optional, by default the winning algorithm will be used on cuda:0
segmenter.infer_single(
@@ -115,7 +129,14 @@ segmenter.infer_single(
t1n="path/to/t1n.nii.gz",
t2f="path/to/t2f.nii.gz",
t2w="path/to/t2w.nii.gz",
- output_file="segmentation.nii.gz",
+ output_file="segmentation_23.nii.gz",
+)
+
+### Example for 2024 algorithms
+segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS24_1, cuda_devices="0")
+segmenter.infer_single(
+ t1c="path/to/t1c.nii.gz",
+ output_file="segmentation_24.nii.gz",
)
```
@@ -123,6 +144,9 @@ segmenter.infer_single(
| Year | Rank | Author | Paper | CPU Support | Key Enum |
| ---- | ---- | -------------------------- | ----- | ----------- | ------------------------------------------------------------------------------------------------------------------- |
+| 2024 | 1st | _Valeria Abramova_ | N/A | ❌ | [BraTS24_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS24_1) |
+| 2024 | 2nd | _Mehdi Astaraki_ | N/A | ❌ | [BraTS24_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS24_2) |
+| 2024 | 3rd | _Andre Ferreira, et al._ | N/A | ✅ | [BraTS24_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS24_3) |
| 2023 | 1st | _Andriy Myronenko, et al._ | N/A | ❌ | [BraTS23_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS23_1) |
| 2023 | 2nd | _Ziyan Huang, et al._ | N/A | ✅ | [BraTS23_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS23_2) |
| 2023 | 3rd | _Zhifan Jiang et al._ | N/A | ❌ | [BraTS23_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.MeningiomaAlgorithms.BraTS23_3) |
@@ -181,6 +205,9 @@ segmenter.infer_single(
| Year | Rank | Author | Paper | CPU Support | Key Enum |
| ---- | ---- | -------------------------- | ----- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
+| 2024 | 1st | _Tim Mulvany, et al._ | N/A | ❌ | [BraTS24_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS24_1) |
+| 2024 | 2nd | _Mehdi Astaraki_ | N/A | ❌ | [BraTS24_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS24_2) |
+| 2024 | 3rd | _Sarim Hashmi, et al._ | N/A | ❌ | [BraTS24_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS24_3) |
| 2023 | 1st | _Zhifan Jiang et al._ | N/A | ❌ | [BraTS23_1](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS23_1) |
| 2023 | 2nd | _Andriy Myronenko, et al._ | N/A | ❌ | [BraTS23_2](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS23_2) |
| 2023 | 3rd | _Yubo Zhou_ | N/A | ❌ | [BraTS23_3](https://brats.readthedocs.io/en/latest/utils/utils.html#brats.constants.PediatricAlgorithms.BraTS23_3) |
diff --git a/brats/constants.py b/brats/constants.py
index 3bdf5ba..56aae2e 100644
--- a/brats/constants.py
+++ b/brats/constants.py
@@ -29,6 +29,13 @@ class Algorithms(str, Enum):
class AdultGliomaAlgorithms(Algorithms):
"""Constants for the available adult glioma segmentation algorithms."""
+ BraTS24_1 = "BraTS24_1"
+ """ BraTS24 Adult Glioma Segmentation 1st place """
+ BraTS24_2 = "BraTS24_2"
+ """ BraTS24 Adult Glioma Segmentation 2nd place """
+ BraTS24_3 = "BraTS24_3"
+ """ BraTS24 Adult Glioma Segmentation 3rd place """
+
BraTS23_1 = "BraTS23_1"
"""BraTS23 Adult Glioma Segmentation 1st place (GPU only)"""
BraTS23_2 = "BraTS23_2"
@@ -40,6 +47,13 @@ class AdultGliomaAlgorithms(Algorithms):
class MeningiomaAlgorithms(Algorithms):
"""Constants for the available meningioma segmentation algorithms."""
+ BraTS24_1 = "BraTS24_1"
+ """ BraTS24 Meningioma Segmentation 1st place """
+ BraTS24_2 = "BraTS24_2"
+ """ BraTS24 Meningioma Segmentation 2nd place """
+ BraTS24_3 = "BraTS24_3"
+ """ BraTS24 Meningioma Segmentation 3rd place """
+
BraTS23_1 = "BraTS23_1"
"""BraTS23 Meningioma Segmentation 1st place (GPU only)"""
BraTS23_2 = "BraTS23_2"
@@ -51,6 +65,13 @@ class MeningiomaAlgorithms(Algorithms):
class PediatricAlgorithms(Algorithms):
"""Constants for the available pediatric segmentation algorithms."""
+ BraTS24_1 = "BraTS24_1"
+ """ BraTS24 Pediatric Segmentation 1st place """
+ BraTS24_2 = "BraTS24_2"
+ """ BraTS24 Pediatric Segmentation 2nd place """
+ BraTS24_3 = "BraTS24_3"
+ """ BraTS24 Pediatric Segmentation 3rd place """
+
BraTS23_1 = "BraTS23_1"
"""BraTS23 Pediatric Segmentation 1st place (GPU only)"""
BraTS23_2 = "BraTS23_2"
@@ -62,6 +83,13 @@ class PediatricAlgorithms(Algorithms):
class AfricaAlgorithms(Algorithms):
"""Constants for the available africa segmentation algorithms."""
+ BraTS24_1 = "BraTS24_1"
+ """ BraTS24 BraTS-Africa Segmentation 1st place """
+ BraTS24_2 = "BraTS24_2"
+ """ BraTS24 BraTS-Africa Segmentation 2nd place """
+ BraTS24_3 = "BraTS24_3"
+ """ BraTS24 BraTS-Africa Segmentation 3rd place """
+
BraTS23_1 = "BraTS23_1"
"""BraTS23 BraTS-Africa Segmentation 1st place (GPU only)"""
BraTS23_2 = "BraTS23_2"
@@ -90,6 +118,7 @@ class InpaintingAlgorithms(Algorithms):
""" BraTS24 Inpainting 2nd place """
BraTS24_3 = "BraTS24_3"
""" BraTS24 Inpainting 3rd place """
+
BraTS23_1 = "BraTS23_1"
""" BraTS23 Inpainting 1st place """
BraTS23_2 = "BraTS23_2"
diff --git a/brats/core/brats_algorithm.py b/brats/core/brats_algorithm.py
index 4bc4bed..37836d4 100644
--- a/brats/core/brats_algorithm.py
+++ b/brats/core/brats_algorithm.py
@@ -4,7 +4,7 @@
import sys
from abc import ABC, abstractmethod
from pathlib import Path
-from typing import Optional
+from typing import Dict, Optional
from loguru import logger
@@ -51,7 +51,11 @@ def __init__(
@abstractmethod
def _standardize_single_inputs(
- self, data_folder: Path, subject_id: str, inputs: dict[str, Path | str]
+ self,
+ data_folder: Path,
+ subject_id: str,
+ inputs: dict[str, Path | str],
+ subject_modality_separator: str,
) -> None:
"""
Standardize the input data to match the requirements of the selected algorithm.
@@ -61,7 +65,7 @@ def _standardize_single_inputs(
@abstractmethod
def _standardize_batch_inputs(
self, data_folder: Path, subjects: list[Path], input_name_schema: str
- ) -> None:
+ ) -> Dict[str, str]:
"""
Standardize the input data to match the requirements of the selected algorithm.
"""
@@ -157,6 +161,7 @@ def _infer_single(
data_folder=tmp_data_folder,
subject_id=subject_id,
inputs=inputs,
+ subject_modality_separator=self.algorithm.run_args.subject_modality_separator,
)
run_container(
@@ -208,6 +213,7 @@ def _infer_batch(
output_path=tmp_output_folder,
cuda_devices=self.cuda_devices,
force_cpu=self.force_cpu,
+ internal_external_name_map=internal_external_name_map,
)
self._process_batch_output(
diff --git a/brats/core/docker.py b/brats/core/docker.py
index 2de1b1c..4943a4b 100644
--- a/brats/core/docker.py
+++ b/brats/core/docker.py
@@ -124,11 +124,13 @@ def _get_additional_files_path(algorithm: AlgorithmData) -> Path:
Returns:
Path to the additional files
"""
- # ensure weights are present and get path
- if algorithm.weights is not None:
- return check_additional_files_path(record_id=algorithm.weights.record_id)
+ # ensure additional_files are present and get path
+ if algorithm.additional_files is not None:
+ return check_additional_files_path(
+ record_id=algorithm.additional_files.record_id
+ )
else:
- # if no weights are directly specified a dummy weights folder will be mounted
+ # if no additional_files are directly specified a dummy additional_files folder will be mounted
return get_dummy_path()
@@ -195,12 +197,12 @@ def _build_args(
"""
# Build command that will be run in the docker container
command_args = f"--data_path=/mlcube_io0 --output_path=/mlcube_io2"
- if algorithm.weights is not None:
- for i, param in enumerate(algorithm.weights.param_name):
- weights_arg = f"--{param}=/mlcube_io1"
- if algorithm.weights.checkpoint_path:
- weights_arg += f"/{algorithm.weights.checkpoint_path[i]}"
- command_args += f" {weights_arg}"
+ if algorithm.additional_files is not None:
+ for i, param in enumerate(algorithm.additional_files.param_name):
+ additional_files_arg = f"--{param}=/mlcube_io1"
+ if algorithm.additional_files.param_path:
+ additional_files_arg += f"/{algorithm.additional_files.param_path[i]}"
+ command_args += f" {additional_files_arg}"
# Add parameters file arg if required
params_arg = _get_parameters_arg(algorithm=algorithm)
@@ -245,7 +247,10 @@ def _observe_docker_output(container: docker.models.containers.Container) -> str
def _sanity_check_output(
- data_path: Path, output_path: Path, container_output: str
+ data_path: Path,
+ output_path: Path,
+ container_output: str,
+ internal_external_name_map: Optional[Dict[str, str]] = None,
) -> None:
"""Sanity check that the number of output files matches the number of input files and the output is not empty.
@@ -253,6 +258,7 @@ def _sanity_check_output(
data_path (Path): The path to the input data
output_path (Path): The path to the output data
container_output (str): The output of the docker container
+ internal_external_name_map (Optional[Dict[str, str]]): Dictionary mapping internal name (in standardized format) to external subject name provided by user (only used for batch inference)
Raises:
BraTSContainerException: If not enough output files exist
@@ -262,7 +268,6 @@ def _sanity_check_output(
# (should result in only counting actual inputs)
inputs = [e for e in data_path.iterdir() if e.name.startswith("BraTS")]
outputs = list(output_path.iterdir())
-
if len(outputs) < len(inputs):
logger.error(f"Docker container output: \n\r{container_output}")
raise BraTSContainerException(
@@ -272,8 +277,18 @@ def _sanity_check_output(
for i, output in enumerate(outputs, start=1):
content = nib.load(output).get_fdata()
if np.count_nonzero(content) == 0:
+ name = ""
+ if internal_external_name_map is not None:
+ name_key = [
+ k
+ for k in internal_external_name_map.keys()
+ if output.name.startswith(k)
+ ]
+ if name_key:
+ name = internal_external_name_map[name_key[0]]
+
logger.warning(
- f"""Output file {i} contains only zeros.
+ f"""Output file for subject {name + " "}contains only zeros.
Potentially the selected algorithm might not work properly with your data unless this behavior is correct for your use case.
If this seems wrong please try to use one of the other provided algorithms and file an issue on GitHub if the problem persists."""
)
@@ -286,7 +301,7 @@ def _log_algorithm_info(algorithm: AlgorithmData):
algorithm (AlgorithmData): algorithm data
"""
logger.opt(colors=True).info(
- f"Running algorithm: {algorithm.meta.challenge} [{algorithm.meta.rank} place]>"
+ f"Running algorithm: BraTS {algorithm.meta.year} {algorithm.meta.challenge} [{algorithm.meta.rank} place]>"
)
logger.opt(colors=True).info(
f"(Paper)> Consider citing the corresponding paper: {algorithm.meta.paper} by {algorithm.meta.authors}"
@@ -300,6 +315,7 @@ def run_container(
output_path: Path,
cuda_devices: str,
force_cpu: bool,
+ internal_external_name_map: Optional[Dict[str, str]] = None,
):
"""Run a docker container for the provided algorithm.
@@ -309,6 +325,7 @@ def run_container(
output_path (Path | str): The path to save the output
cuda_devices (str): The CUDA devices to use
force_cpu (bool): Whether to force CPU execution
+ internal_external_name_map (Dict[str, str]): Dictionary mapping internal name (in standardized format) to external subject name provided by user (only used for batch inference)
"""
_log_algorithm_info(algorithm=algorithm)
@@ -353,7 +370,10 @@ def run_container(
)
container_output = _observe_docker_output(container=container)
_sanity_check_output(
- data_path=data_path, output_path=output_path, container_output=container_output
+ data_path=data_path,
+ output_path=output_path,
+ container_output=container_output,
+ internal_external_name_map=internal_external_name_map,
)
logger.debug(f"Docker container output: \n\r{container_output}")
diff --git a/brats/core/inpainting_algorithms.py b/brats/core/inpainting_algorithms.py
index 665a0e5..a8fc7a6 100644
--- a/brats/core/inpainting_algorithms.py
+++ b/brats/core/inpainting_algorithms.py
@@ -3,7 +3,7 @@
from pathlib import Path
import shutil
import sys
-from typing import Optional
+from typing import Dict, Optional
from loguru import logger
@@ -29,7 +29,11 @@ def __init__(
)
def _standardize_single_inputs(
- self, data_folder: Path, subject_id: str, inputs: dict[str, Path | str]
+ self,
+ data_folder: Path,
+ subject_id: str,
+ inputs: dict[str, Path | str],
+ subject_modality_separator: str,
) -> None:
"""
Standardize the input data to match the requirements of the selected algorithm.
@@ -38,6 +42,7 @@ def _standardize_single_inputs(
data_folder (Path): Path to the data folder
subject_id (str): Subject ID
inputs (dict[str, Path | str]): Dictionary with the input data
+ subject_modality_separator (str): Separator between the subject ID and the modality
"""
subject_folder = data_folder / subject_id
@@ -45,8 +50,15 @@ def _standardize_single_inputs(
# TODO: investigate usage of symlinks (might cause issues on windows and would probably require different volume handling)
t1n, mask = inputs["t1n"], inputs["mask"]
try:
- shutil.copy(t1n, subject_folder / f"{subject_id}-t1n-voided.nii.gz")
- shutil.copy(mask, subject_folder / f"{subject_id}-mask.nii.gz")
+ shutil.copy(
+ t1n,
+ subject_folder
+ / f"{subject_id}{subject_modality_separator}t1n-voided.nii.gz",
+ )
+ shutil.copy(
+ mask,
+ subject_folder / f"{subject_id}{subject_modality_separator}mask.nii.gz",
+ )
except FileNotFoundError as e:
logger.error(f"Error while standardizing files: {e}")
sys.exit(1)
@@ -56,7 +68,7 @@ def _standardize_single_inputs(
def _standardize_batch_inputs(
self, data_folder: Path, subjects: list[Path], input_name_schema: str
- ) -> None:
+ ) -> Dict[str, str]:
"""Standardize the input images for a list of subjects to match requirements of all algorithms and save them in @tmp_data_folder/@subject_id.
Args:
@@ -80,6 +92,7 @@ def _standardize_batch_inputs(
"t1n": subject / f"{subject.name}-t1n-voided.nii.gz",
"mask": subject / f"{subject.name}-mask.nii.gz",
},
+ subject_modality_separator=self.algorithm.run_args.subject_modality_separator,
)
return internal_external_name_map
diff --git a/brats/core/missing_mri_algorithms.py b/brats/core/missing_mri_algorithms.py
index 22bc73c..caf6f3d 100644
--- a/brats/core/missing_mri_algorithms.py
+++ b/brats/core/missing_mri_algorithms.py
@@ -3,7 +3,7 @@
from pathlib import Path
import shutil
import sys
-from typing import Optional, Union
+from typing import Dict, List, Optional, Union
from loguru import logger
@@ -29,7 +29,11 @@ def __init__(
)
def _standardize_single_inputs(
- self, data_folder: Path, subject_id: str, inputs: dict[str, Path | str]
+ self,
+ data_folder: Path,
+ subject_id: str,
+ inputs: dict[str, Path | str],
+ subject_modality_separator: str,
) -> None:
"""
Standardize the input data to match the requirements of the selected algorithm.
@@ -38,6 +42,7 @@ def _standardize_single_inputs(
data_folder (Path): Path to the data folder
subject_id (str): Subject ID
inputs (dict[str, Path | str]): Dictionary with the input data
+ subject_modality_separator (str): Separator between the subject ID and the modality
"""
subject_folder = data_folder / subject_id
@@ -45,7 +50,11 @@ def _standardize_single_inputs(
try:
for modality, path in inputs.items():
- shutil.copy(path, subject_folder / f"{subject_id}-{modality}.nii.gz")
+ shutil.copy(
+ path,
+ subject_folder
+ / f"{subject_id}{subject_modality_separator}{modality}.nii.gz",
+ )
except FileNotFoundError as e:
logger.error(f"Error while standardizing files: {e}")
sys.exit(1)
@@ -58,7 +67,12 @@ def _standardize_single_inputs(
t2w=inputs.get("t2w"),
)
- def _standardize_batch_inputs(self, data_folder, subjects, input_name_schema):
+ def _standardize_batch_inputs(
+ self,
+ data_folder: Path,
+ subjects: List[Path],
+ input_name_schema: str,
+ ) -> Dict[str, str]:
"""Standardize the input images for a list of subjects to match requirements of all algorithms and save them in @tmp_data_folder/@subject_id.
Args:
@@ -90,6 +104,7 @@ def _standardize_batch_inputs(self, data_folder, subjects, input_name_schema):
data_folder=data_folder,
subject_id=internal_name,
inputs=valid_inputs,
+ subject_modality_separator=self.algorithm.run_args.subject_modality_separator,
)
return internal_external_name_map
@@ -106,7 +121,7 @@ def infer_single(
Perform synthesis of the missing modality for a single subject with the provided images and save the result to the output file.
Note:
- Exactly 3 inputs are required to perform synthesis of the missing modality.
+ Exactly 3 input modalities are required to perform synthesis of the missing modality.
Args:
output_file (Path | str): Output file to save the synthesized image
diff --git a/brats/core/segmentation_algorithms.py b/brats/core/segmentation_algorithms.py
index e514299..f78ad5a 100644
--- a/brats/core/segmentation_algorithms.py
+++ b/brats/core/segmentation_algorithms.py
@@ -3,7 +3,7 @@
import shutil
import sys
from pathlib import Path
-from typing import Dict, List, Optional
+from typing import Dict, List, Optional, Union
from loguru import logger
@@ -44,7 +44,11 @@ def __init__(
)
def _standardize_single_inputs(
- self, data_folder: Path, subject_id: str, inputs: Dict[str, Path | str]
+ self,
+ data_folder: Path,
+ subject_id: str,
+ inputs: Dict[str, Path | str],
+ subject_modality_separator: str,
) -> None:
"""Standardize the input images for a single subject to match requirements of all algorithms and save them in @data_folder/@subject_id.
Example:
@@ -59,17 +63,19 @@ def _standardize_single_inputs(
data_folder (Path): Parent folder where the subject folder will be created
subject_id (str): Subject ID to be used for the folder and filenames
inputs (Dict[str, Path | str]): Dictionary with the input images
+ subject_modality_separator (str): Separator between the subject ID and the modality
"""
subject_folder = data_folder / subject_id
subject_folder.mkdir(parents=True, exist_ok=True)
# TODO: investigate usage of symlinks (might cause issues on windows and would probably require different volume handling)
- t1c, t1n, t2f, t2w = inputs["t1c"], inputs["t1n"], inputs["t2f"], inputs["t2w"]
try:
- shutil.copy(t1c, subject_folder / f"{subject_id}-t1c.nii.gz")
- shutil.copy(t1n, subject_folder / f"{subject_id}-t1n.nii.gz")
- shutil.copy(t2f, subject_folder / f"{subject_id}-t2f.nii.gz")
- shutil.copy(t2w, subject_folder / f"{subject_id}-t2w.nii.gz")
+ for modality, path in inputs.items():
+ shutil.copy(
+ path,
+ subject_folder
+ / f"{subject_id}{subject_modality_separator}{modality}.nii.gz",
+ )
except FileNotFoundError as e:
logger.error(f"Error while standardizing files: {e}")
logger.error(
@@ -78,13 +84,19 @@ def _standardize_single_inputs(
sys.exit(1)
# sanity check inputs
- input_sanity_check(t1c=t1c, t1n=t1n, t2f=t2f, t2w=t2w)
+ input_sanity_check(
+ t1c=inputs.get("t1c"),
+ t1n=inputs.get("t1n"),
+ t2f=inputs.get("t2f"),
+ t2w=inputs.get("t2w"),
+ )
def _standardize_batch_inputs(
self,
data_folder: Path,
subjects: List[Path],
input_name_schema: str,
+ only_t1c: bool = False,
) -> Dict[str, str]:
"""Standardize the input images for a list of subjects to match requirements of all algorithms and save them in @tmp_data_folder/@subject_id.
@@ -92,6 +104,7 @@ def _standardize_batch_inputs(
subjects (List[Path]): List of subject folders, each with a t1c, t1n, t2f, t2w image in standard format
data_folder (Path): Parent folder where the subject folders will be created
input_name_schema (str): Schema to be used for the subject folder and filenames depending on the BraTS Challenge
+ only_t1c (bool, optional): If True, only the t1c image will be used. Defaults to False.
Returns:
Dict[str, str]: Dictionary mapping internal name (in standardized format) to external subject name provided by user
@@ -100,17 +113,20 @@ def _standardize_batch_inputs(
for i, subject in enumerate(subjects):
internal_name = input_name_schema.format(id=i)
internal_external_name_map[internal_name] = subject.name
- # TODO Add support for .nii files
+
+ inputs = {
+ "t1c": subject / f"{subject.name}-t1c.nii.gz",
+ }
+ if not only_t1c:
+ inputs["t1n"] = subject / f"{subject.name}-t1n.nii.gz"
+ inputs["t2f"] = subject / f"{subject.name}-t2f.nii.gz"
+ inputs["t2w"] = subject / f"{subject.name}-t2w.nii.gz"
self._standardize_single_inputs(
data_folder=data_folder,
subject_id=internal_name,
- inputs={
- "t1c": subject / f"{subject.name}-t1c.nii.gz",
- "t1n": subject / f"{subject.name}-t1n.nii.gz",
- "t2f": subject / f"{subject.name}-t2f.nii.gz",
- "t2w": subject / f"{subject.name}-t2w.nii.gz",
- },
+ inputs=inputs,
+ subject_modality_separator=self.algorithm.run_args.subject_modality_separator,
)
return internal_external_name_map
@@ -213,6 +229,129 @@ def __init__(
force_cpu=force_cpu,
)
+ def _standardize_batch_inputs(
+ self,
+ data_folder: Path,
+ subjects: List[Path],
+ input_name_schema: str,
+ only_t1c: bool = False,
+ ) -> Dict[str, str]:
+ """Standardize the input images for a list of subjects to match requirements of all algorithms and save them in @tmp_data_folder/@subject_id.
+
+ Args:
+ subjects (List[Path]): List of subject folders, each with a t1c, t1n, t2f, t2w image in standard format
+ data_folder (Path): Parent folder where the subject folders will be created
+ input_name_schema (str): Schema to be used for the subject folder and filenames depending on the BraTS Challenge
+
+ Returns:
+ Dict[str, str]: Dictionary mapping internal name (in standardized format) to external subject name provided by user
+ """
+ only_t1c = self.algorithm.meta.year == 2024
+ return super()._standardize_batch_inputs(
+ data_folder=data_folder,
+ subjects=subjects,
+ input_name_schema=input_name_schema,
+ only_t1c=only_t1c,
+ )
+
+ def infer_single(
+ self,
+ output_file: Path | str,
+ t1c: Union[Path, str] = None,
+ t1n: Optional[Union[Path, str]] = None,
+ t2f: Optional[Union[Path, str]] = None,
+ t2w: Optional[Union[Path, str]] = None,
+ log_file: Optional[Path | str] = None,
+ ) -> None:
+ """
+ Perform segmentation on a single subject with the provided images and save the result to the output file.
+
+ Note:
+ Unlike other segmentation challenges, not all modalities are required for all meningioma segmentation algorithms.
+ Differences by year:
+
+ - **2024**: Only `t1c` is required and used by the algorithms.
+ - **2023**: All (`t1c`, `t1n`, `t2f`, `t2w`) are required.
+
+
+ Args:
+ output_file (Path | str): Output file to save the segmentation.
+ t1c (Union[Path, str]): Path to the T1c image. Defaults to None.
+ t1n (Optional[Union[Path, str]], optional): Path to the T1n image. Defaults to None.
+ t2f (Optional[Union[Path, str]], optional): Path to the T2f image. Defaults to None.
+ t2w (Optional[Union[Path, str]], optional): Path to the T2w image. Defaults to None.
+ log_file (Optional[Path | str], optional): Save logs to this file. Defaults to None
+ """
+ inputs = {"t1c": t1c, "t1n": t1n, "t2f": t2f, "t2w": t2w}
+ # filter out None values
+ inputs = {k: v for k, v in inputs.items() if v is not None}
+
+ year = self.algorithm.meta.year
+ if year == 2024:
+ if "t1c" not in inputs or len(inputs) > 1:
+ raise ValueError(
+ (
+ "Only the T1C modality is required and used by the 2024 meningioma segmentation challenge and its algorithms. "
+ "Please provide only the T1C modality - Aborting to avoid confusion."
+ )
+ )
+ elif year == 2023:
+ if len(inputs) != 4:
+ raise ValueError(
+ (
+ "All modalities (t1c, t1n, t2f, t2w) are required for the 2023 meningioma segmentation challenge and its algorithms. "
+ "Please provide all modalities"
+ )
+ )
+ else:
+ raise NotImplementedError(
+ f"Invalid algorithm {year=} .Only 2023 and 2024 are supported as of now"
+ )
+ self._infer_single(
+ inputs=inputs,
+ output_file=output_file,
+ log_file=log_file,
+ )
+
+ def infer_batch(
+ self,
+ data_folder: Path | str,
+ output_folder: Path | str,
+ log_file: Path | str | None = None,
+ ) -> None:
+ """
+ Perform segmentation on a batch of subjects with the provided images and save the results to the output folder. \n
+
+ Note:
+ Unlike other segmentation challenges, not all modalities are required for all meningioma segmentation algorithms.
+ Differences by year:
+
+ - **2024**: Only `t1c` is required and used by the algorithms.
+ - **2023**: All (`t1c`, `t1n`, `t2f`, `t2w`) are required.
+
+ Requires the following structure (example for 2023, only t1c for 2024!):\n
+ data_folder\n
+ ┣ A\n
+ ┃ ┣ A-t1c.nii.gz\n
+ ┃ ┣ A-t1n.nii.gz\n
+ ┃ ┣ A-t2f.nii.gz\n
+ ┃ ┗ A-t2w.nii.gz\n
+ ┣ B\n
+ ┃ ┣ B-t1c.nii.gz\n
+ ┃ ┣ ...\n
+
+
+ Args:
+ data_folder (Path | str): Folder containing the subjects with required structure
+ output_folder (Path | str): Output folder to save the segmentations
+ log_file (Path | str, optional): Save logs to this file
+ """
+ return self._infer_batch(
+ data_folder=data_folder,
+ output_folder=output_folder,
+ log_file=log_file,
+ )
+
class PediatricSegmenter(SegmentationAlgorithm):
"""Provides algorithms to perform tumor segmentation on pediatric MRI data
diff --git a/brats/data/meta/adult_glioma.yml b/brats/data/meta/adult_glioma.yml
index 8036662..5e849a2 100644
--- a/brats/data/meta/adult_glioma.yml
+++ b/brats/data/meta/adult_glioma.yml
@@ -1,15 +1,80 @@
+constants:
+ input_name_schema: &input_name_schema "BraTS-GLI-{id:05d}-000"
+ challenge: &challenge "Adult Glioma Segmentation"
+ years:
+ 2024: &year_2024 2024
+ 2023: &year_2023 2023
+ ranks:
+ 1st: &rank_1 1st
+ 2nd: &rank_2 2nd
+ 3rd: &rank_3 3rd
+
+
algorithms:
+##### 2024 #####
+ BraTS24_1:
+ meta:
+ authors: André Ferreira, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_adult_glioma_faking_it:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: false
+ shm_size: "4gb"
+
+ BraTS24_2:
+ meta:
+ authors: Team kimbab
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_adult_glioma_kimbab:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: false
+ shm_size: "2gb"
+ additional_files:
+ record_id: "14413387"
+ param_name: ["nnunet_env_path"]
+
+ BraTS24_3:
+ meta:
+ authors: Adrian Celaya
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_adult_glioma_mist:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "2gb"
+ cpu_compatible: true
+ additional_files:
+ record_id: "14411467"
+ param_name: ["mist_models", "mist_config", "mist_dataset"]
+ param_path: ["models", "config.json", "dataset.json"]
+
+##### 2023 #####
+
BraTS23_1:
meta:
authors: André Ferreira, et al.
paper: https://arxiv.org/abs/2402.17317v1
- challenge: BraTS23 Adult Glioma Segmentation
- rank: 1st
- year: 2023
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2023
run_args:
docker_image: brainles/brats23_faking_it:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: false
shm_size: "2gb"
@@ -18,12 +83,12 @@ algorithms:
meta:
authors: Andriy Myronenko, et al.
paper: N/A
- challenge: BraTS23 Adult Glioma Segmentation
- rank: 2nd
- year: 2023
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2023
run_args:
docker_image: brainles/brats23_nvauto:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
shm_size: "32gb"
@@ -32,15 +97,15 @@ algorithms:
meta:
authors: Fadillah Adamsyah Maani, et al.
paper: N/A
- challenge: BraTS23 Adult Glioma Segmentation
- rank: 3rd
- year: 2023
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2023
run_args:
docker_image: brainles/brats23_biomedmbz:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: true
shm_size: "2gb"
- weights:
+ additional_files:
record_id: "11573315"
param_name: ["checkpoint_dir"]
diff --git a/brats/data/meta/africa.yml b/brats/data/meta/africa.yml
index 6ac0709..978fbed 100644
--- a/brats/data/meta/africa.yml
+++ b/brats/data/meta/africa.yml
@@ -1,15 +1,75 @@
+constants:
+ input_name_schema: &input_name_schema "BraTS-SSA-{id:05d}-000"
+ challenge: &challenge "BraTS-Africa Segmentation"
+ years:
+ 2024: &year_2024 2024
+ 2023: &year_2023 2023
+ ranks:
+ 1st: &rank_1 1st
+ 2nd: &rank_2 2nd
+ 3rd: &rank_3 3rd
+
+
algorithms:
+##### 2024 #####
+ BraTS24_1:
+ meta:
+ authors: Zhifan Jiang et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_africa_cnmc_pmi:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: false
+ shm_size: "2gb"
+
+ BraTS24_2:
+ meta:
+ authors: Long Bai, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_africa_cuhk_rpai
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "2gb"
+ cpu_compatible: true
+
+ BraTS24_3:
+ meta:
+ authors: Sarim Hashmi, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_africa_biomedia-mbzu
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "2gb"
+ additional_files:
+ record_id: "14414932"
+ param_name: ["checkpoint_dir"]
+
+##### 2023 #####
BraTS23_1:
meta:
authors: Andriy Myronenko, et al.
paper: N/A
- challenge: BraTS23 BraTS-Africa Segmentation
- rank: 1st
- year: 2023
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2023
run_args:
docker_image: brainles/brats23_africa_nvauto:latest
- input_name_schema: "BraTS-SSA-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
shm_size: "32gb"
@@ -18,27 +78,28 @@ algorithms:
meta:
authors: Alyssa R Amod, et al.
paper: N/A
- challenge: BraTS23 BraTS-Africa Segmentation
- rank: 2nd
- year: 2023
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2023
run_args:
docker_image: brainles/brats23_africa_sparkunn:latest
- input_name_schema: "BraTS-SSA-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: true
- weights:
+ additional_files:
record_id: "13373752"
param_name: ["ckpts_path"]
+
BraTS23_3:
meta:
authors: Ziyan Huang, et al.
paper: N/A
- challenge: BraTS23 BraTS-Africa Segmentation
- rank: 3rd
- year: 2023
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2023
run_args:
docker_image: brainles/brats23_africa_blackbean:latest
- input_name_schema: "BraTS-SSA-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
cpu_compatible: true
diff --git a/brats/data/meta/inpainting.yml b/brats/data/meta/inpainting.yml
index fe40c84..2339213 100644
--- a/brats/data/meta/inpainting.yml
+++ b/brats/data/meta/inpainting.yml
@@ -1,3 +1,14 @@
+constants:
+ input_name_schema: &input_name_schema "BraTS-GLI-{id:05d}-000"
+ challenge: &challenge "Inpainting"
+ years:
+ 2024: &year_2024 2024
+ 2023: &year_2023 2023
+ ranks:
+ 1st: &rank_1 1st
+ 2nd: &rank_2 2nd
+ 3rd: &rank_3 3rd
+
algorithms:
######## 2024 Algorithms ########
@@ -6,29 +17,29 @@ algorithms:
meta:
authors: Ke Chen, Juexin Zhang, Ying Weng
paper: N/A
- challenge: BraTS24 Inpainting
- rank: 1st
- year: 2024
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
run_args:
docker_image: brainles/brats24_inpainting_ying_weng:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: true
cpu_compatible: true
- weights:
+ additional_files:
record_id: "14230865"
- param_name: ["checkpoint_path"]
+ param_name: ["param_path"]
BraTS24_2:
meta:
authors: André Ferreira, et al.
paper: N/A
- challenge: BraTS24 Inpainting
- rank: 2nd
- year: 2024
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2024
run_args:
docker_image: brainles/brats24_inpainting_faking_it:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: false
shm_size: "4gb"
@@ -37,17 +48,17 @@ algorithms:
meta:
authors: Team SMINT
paper: N/A
- challenge: BraTS24 Inpainting
- rank: 3rd
- year: 2024
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2024
run_args:
docker_image: brainles/brats24_inpainting_smint:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: true
- weights:
+ additional_files:
record_id: "14231079"
- checkpoint_path: ["savedmodel395000.pt"]
+ param_path: ["savedmodel395000.pt"]
######## 2023 Algorithms ########
@@ -55,49 +66,49 @@ algorithms:
meta:
authors: Juexin Zhang, et al.
paper: N/A
- challenge: BraTS23 Inpainting
- rank: 1st
- year: 2023
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2023
run_args:
docker_image: brainles/brats23_inpainting_ying_weng:latest
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: true
cpu_compatible: true
- weights:
+ additional_files:
record_id: "13382922"
- param_name: ["checkpoint_path"]
+ param_name: ["param_path"]
BraTS23_2:
meta:
authors: Alicia Durrer, et al.
paper: N/A
- challenge: BraTS23 Inpainting
- rank: 2nd
- year: 2023
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2023
run_args:
docker_image: brainles/brats23_inpainting_domaso
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
- weights:
+ additional_files:
record_id: "13383452"
- checkpoint_path: ["savedmodel2850000.pt"]
+ param_path: ["savedmodel2850000.pt"]
BraTS23_3:
meta:
authors: Jiayu Huo, et al.
paper: N/A
- challenge: BraTS23 Inpainting
- rank: 3rd
- year: 2023
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2023
run_args:
docker_image: brainles/brats23_inpainting_medsegctrl
- input_name_schema: "BraTS-GLI-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
cpu_compatible: true
- weights:
+ additional_files:
record_id: "13383287"
param_name: ["weight_path"]
- checkpoint_path: ["epoch-19-step-197499.ckpt"]
+ param_path: ["epoch-19-step-197499.ckpt"]
diff --git a/brats/data/meta/meningioma.yml b/brats/data/meta/meningioma.yml
index 36d7df3..16f4ccd 100644
--- a/brats/data/meta/meningioma.yml
+++ b/brats/data/meta/meningioma.yml
@@ -1,14 +1,77 @@
+constants:
+ input_name_schema_by_year:
+ input_name_schema_2024: &input_name_schema_2024 "BraTS-MEN-RT-{id:04d}-1"
+ input_name_schema_2023: &input_name_schema_2023 "BraTS-MEN-{id:05d}-000"
+ challenge: &challenge "Meningioma Segmentation"
+ years:
+ 2024: &year_2024 2024
+ 2023: &year_2023 2023
+ ranks:
+ 1st: &rank_1 1st
+ 2nd: &rank_2 2nd
+ 3rd: &rank_3 3rd
+
+
algorithms:
+
+##### 2024 #####
+ BraTS24_1:
+ meta:
+ authors: Valeria Abramova
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_meningioma_nic_vicorob
+ input_name_schema: *input_name_schema_2024
+ requires_root: false
+ parameters_file: true
+ shm_size: "16gb"
+ subject_modality_separator: "_"
+
+ BraTS24_2:
+ meta:
+ authors: Mehdi Astaraki
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_meningioma_astaraki
+ input_name_schema: *input_name_schema_2024
+ requires_root: false
+ parameters_file: true
+ shm_size: "2gb"
+
+ BraTS24_3:
+ meta:
+ authors: Andre Ferreira, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_meningioma_faking_it
+ input_name_schema: *input_name_schema_2024
+ requires_root: false
+ parameters_file: false
+ shm_size: "4gb"
+ cpu_compatible: true # ~8 hours
+ subject_modality_separator: "_"
+
+
+##### 2023 #####
BraTS23_1:
meta:
authors: Andriy Myronenko, et al.
paper: N/A
- challenge: BraTS23 Meningioma Segmentation
- rank: 1st
- year: 2023
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2023
run_args:
docker_image: brainles/brats23_meningioma_nvauto:latest
- input_name_schema: "BraTS-MEN-{id:05d}-000"
+ input_name_schema: *input_name_schema_2023
requires_root: true
parameters_file: true
shm_size: "32gb"
@@ -17,12 +80,12 @@ algorithms:
meta:
authors: Ziyan Huang, et al.
paper: N/A
- challenge: BraTS23 Meningioma Segmentation
- rank: 2nd
- year: 2023
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2023
run_args:
docker_image: brainles/brats23_meningioma_blackbean:latest
- input_name_schema: "BraTS-MEN-{id:05d}-000"
+ input_name_schema: *input_name_schema_2023
requires_root: true
parameters_file: true
shm_size: "4gb"
@@ -32,12 +95,12 @@ algorithms:
meta:
authors: Zhifan Jiang et al.
paper: N/A
- challenge: BraTS23 Meningioma Segmentation
- rank: 3rd
- year: 2023
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2023
run_args:
docker_image: brainles/brats23_meningioma_cnmc_pmi2023:latest
- input_name_schema: "BraTS-MEN-{id:05d}-000"
+ input_name_schema: *input_name_schema_2023
requires_root: true
parameters_file: false
shm_size: "2gb"
diff --git a/brats/data/meta/metastases.yml b/brats/data/meta/metastases.yml
index cf09c42..6929eb0 100644
--- a/brats/data/meta/metastases.yml
+++ b/brats/data/meta/metastases.yml
@@ -3,7 +3,7 @@ algorithms:
meta:
authors: Andriy Myronenko, et al.
paper: N/A
- challenge: BraTS23 Brain Metastases Segmentation
+ challenge: Brain Metastases Segmentation
rank: 1st
year: 2023
run_args:
@@ -17,7 +17,7 @@ algorithms:
meta:
authors: Siwei Yang, et al.
paper: N/A
- challenge: BraTS23 Brain Metastases Segmentation
+ challenge: Brain Metastases Segmentation
rank: 2nd
year: 2023
run_args:
@@ -25,14 +25,14 @@ algorithms:
input_name_schema: "BraTS-MET-{id:05d}-000"
requires_root: true
parameters_file: false
- weights:
+ additional_files:
record_id: "13380822"
BraTS23_3:
meta:
authors: Ziyan Huang, et al.
paper: N/A
- challenge: BraTS23 Brain Metastases Segmentation
+ challenge: Brain Metastases Segmentation
rank: 3rd
year: 2023
run_args:
diff --git a/brats/data/meta/missing_mri.yml b/brats/data/meta/missing_mri.yml
index 4859fef..6ac3773 100644
--- a/brats/data/meta/missing_mri.yml
+++ b/brats/data/meta/missing_mri.yml
@@ -6,7 +6,7 @@ algorithms:
meta:
authors: Jihoon Cho, Seunghyuck Park, Jinah Park
paper: N/A
- challenge: BraTS24 Missing MRI
+ challenge: Missing MRI
rank: 1st
year: 2024
run_args:
@@ -14,16 +14,16 @@ algorithms:
input_name_schema: "BraTS-GLI-{id:05d}-000"
requires_root: false
parameters_file: true
- weights:
+ additional_files:
record_id: "14287969"
param_name: ["first_weights", "second_weights"]
- checkpoint_path: ["first_weight.bin", "second_weight.pth"]
+ param_path: ["first_weight.bin", "second_weight.pth"]
BraTS24_2:
meta:
authors: Haowen Pang
paper: N/A
- challenge: BraTS24 Missing MRI
+ challenge: Missing MRI
rank: 2nd
year: 2024
run_args:
@@ -31,14 +31,14 @@ algorithms:
input_name_schema: "BraTS-GLI-{id:05d}-000"
requires_root: false
parameters_file: true
- weights:
+ additional_files:
record_id: "14288120"
BraTS24_3:
meta:
authors: Minjoo Lim, Bogyeong Kang
paper: N/A
- challenge: BraTS24 Missing MRI
+ challenge: Missing MRI
rank: 3rd
year: 2024
run_args:
diff --git a/brats/data/meta/pediatric.yml b/brats/data/meta/pediatric.yml
index df8d194..0c3b7bf 100644
--- a/brats/data/meta/pediatric.yml
+++ b/brats/data/meta/pediatric.yml
@@ -1,16 +1,75 @@
+constants:
+ input_name_schema: &input_name_schema "BraTS-PED-{id:05d}-000"
+ challenge: &challenge "Pediatric Segmentation"
+ years:
+ 2024: &year_2024 2024
+ 2023: &year_2023 2023
+ ranks:
+ 1st: &rank_1 1st
+ 2nd: &rank_2 2nd
+ 3rd: &rank_3 3rd
+
algorithms:
+##### 2024 #####
+ BraTS24_1:
+ meta:
+ authors: Tim Mulvany, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_pediatric_aipni:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "8gb"
+ additional_files:
+ record_id: "14446259"
+
+ BraTS24_2:
+ meta:
+ authors: Mehdi Astaraki
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_pediatric_astaraki:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "8gb"
+ BraTS24_3:
+ meta:
+ authors: Sarim Hashmi, et al.
+ paper: N/A
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2024
+ run_args:
+ docker_image: brainles/brats24_pediatric_biomedia-mbzu:latest
+ input_name_schema: *input_name_schema
+ requires_root: false
+ parameters_file: true
+ shm_size: "2gb"
+ additional_files:
+ record_id: "14446377"
+ param_name: ["checkpoint_dir"]
+
+##### 2023 #####
BraTS23_1:
meta:
authors: Zhifan Jiang et al.
paper: N/A
- challenge: BraTS23 Pediatric Segmentation
- rank: 1st
- year: 2023
+ challenge: *challenge
+ rank: *rank_1
+ year: *year_2023
run_args:
docker_image: brainles/brats23_pediatric_cnmc_pmi2023:latest
- input_name_schema: "BraTS-PED-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: false
shm_size: "2gb"
@@ -19,12 +78,12 @@ algorithms:
meta:
authors: Andriy Myronenko, et al.
paper: N/A
- challenge: BraTS23 Pediatric Segmentation
- rank: 2nd
- year: 2023
+ challenge: *challenge
+ rank: *rank_2
+ year: *year_2023
run_args:
docker_image: brainles/brats23_pediatric_nvauto:latest
- input_name_schema: "BraTS-PED-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: true
parameters_file: true
shm_size: "32gb"
@@ -33,12 +92,12 @@ algorithms:
meta:
authors: Yubo Zhou
paper: N/A
- challenge: BraTS23 Pediatric Segmentation
- rank: 3rd
- year: 2023
+ challenge: *challenge
+ rank: *rank_3
+ year: *year_2023
run_args:
docker_image: brainles/brats23_pediatric_sherlock_zyb:latest
- input_name_schema: "BraTS-PED-{id:05d}-000"
+ input_name_schema: *input_name_schema
requires_root: false
parameters_file: false
shm_size: "10gb"
diff --git a/brats/data/parameters/brats24_adult_glioma_mist.yml b/brats/data/parameters/brats24_adult_glioma_mist.yml
new file mode 100644
index 0000000..b68df27
--- /dev/null
+++ b/brats/data/parameters/brats24_adult_glioma_mist.yml
@@ -0,0 +1,6 @@
+fast_inference: False
+overlap: 0.5
+blend_mode: "gaussian"
+tta: True
+no_preprocess: False
+output_std: False
\ No newline at end of file
diff --git a/brats/utils/algorithm_config.py b/brats/utils/algorithm_config.py
index c0de34e..6d4aab9 100644
--- a/brats/utils/algorithm_config.py
+++ b/brats/utils/algorithm_config.py
@@ -39,18 +39,20 @@ class RunArgs:
"""The required shared memory size for the Docker container"""
cpu_compatible: Optional[bool] = False
"""Whether the algorithm is compatible with CPU"""
+ subject_modality_separator: Optional[str] = "-"
+ """The separator between the subject ID and the modality, differs e.g. for BraTS24 Meningioma Challenge"""
@dataclass
-class WeightsData:
- """Dataclass for the weights data"""
+class AdditionalFilesData:
+ """Dataclass for the additional files data"""
record_id: str
- """The Zenodo record ID of the weights"""
+ """The Zenodo record ID of the additional files"""
param_name: Optional[List[str]] = field(default_factory=lambda: ["weights"])
- """The parameter(s) that specify the weights folder(s) in the algorithm execution, typically 'weights' but differs for some and can even be multiple"""
- checkpoint_path: Optional[List[str]] = None
- """The path(s) to specific checkpoint file(s) in the weights folder. Not required since some algorithms accept the entire weights folder"""
+ """The parameter(s) that specify additional file(s) in the algorithm execution, typically 'weights' but differs for some and can be multiple"""
+ param_path: Optional[List[str]] = None
+ """The path(s) to specific file(s) / folder(s) in the additional files folder. Not required since some algorithms accept the entire additional files folder"""
@dataclass
@@ -61,8 +63,8 @@ class AlgorithmData:
"""The meta data of the algorithm"""
run_args: RunArgs
"""The run arguments of the algorithm"""
- weights: Optional[WeightsData]
- """The weights data of the algorithm. Optional since some algorithms include weights in the docker image"""
+ additional_files: Optional[AdditionalFilesData]
+ """The additional files of the algorithm. Optional since some algorithms include them in the docker image"""
@dataclass
diff --git a/brats/utils/zenodo.py b/brats/utils/zenodo.py
index 8fa5a90..c801a0b 100644
--- a/brats/utils/zenodo.py
+++ b/brats/utils/zenodo.py
@@ -34,18 +34,22 @@ def check_additional_files_path(record_id: str) -> Path:
record_id=record_id
)
- record_weights_pattern = f"{record_id}_v*.*.*"
- matching_folders = list(ADDITIONAL_FILES_FOLDER.glob(record_weights_pattern))
- # Get the latest downloaded weights
- latest_downloaded_weights = _get_latest_version_folder_name(matching_folders)
+ record_additional_files_pattern = f"{record_id}_v*.*.*"
+ matching_folders = list(
+ ADDITIONAL_FILES_FOLDER.glob(record_additional_files_pattern)
+ )
+ # Get the latest downloaded additional_files
+ latest_downloaded_additional_files = _get_latest_version_folder_name(
+ matching_folders
+ )
- if not latest_downloaded_weights:
+ if not latest_downloaded_additional_files:
if not zenodo_metadata:
logger.error(
- "Model weights not found locally and Zenodo could not be reached. Exiting..."
+ "Additional files not found locally and Zenodo could not be reached. Exiting..."
)
sys.exit()
- logger.info(f"Model weights not found locally")
+ logger.info(f"Additional files not found locally")
return _download_additional_files(
zenodo_metadata=zenodo_metadata,
@@ -53,27 +57,29 @@ def check_additional_files_path(record_id: str) -> Path:
archive_url=archive_url,
)
- logger.info(f"Found downloaded local weights: {latest_downloaded_weights}")
+ logger.info(
+ f"Found downloaded local additional_files: {latest_downloaded_additional_files}"
+ )
if not zenodo_metadata:
logger.warning(
- "Zenodo server could not be reached. Using the latest downloaded weights."
+ "Zenodo server could not be reached. Using the latest downloaded additional files."
)
- return ADDITIONAL_FILES_FOLDER / latest_downloaded_weights
+ return ADDITIONAL_FILES_FOLDER / latest_downloaded_additional_files
- # Compare the latest downloaded weights with the latest Zenodo version
- if zenodo_metadata["version"] == latest_downloaded_weights.split("_v")[1]:
+ # Compare the latest downloaded additional files with the latest Zenodo version
+ if zenodo_metadata["version"] == latest_downloaded_additional_files.split("_v")[1]:
logger.info(
- f"Latest model weights ({latest_downloaded_weights}) are already present."
+ f"Latest additional files ({latest_downloaded_additional_files}) are already present."
)
- return ADDITIONAL_FILES_FOLDER / latest_downloaded_weights
+ return ADDITIONAL_FILES_FOLDER / latest_downloaded_additional_files
logger.info(
- f"New model weights available on Zenodo ({zenodo_metadata['version']}). Deleting old and fetching new weights..."
+ f"New additional files available on Zenodo ({zenodo_metadata['version']}). Deleting old and fetching new additional files..."
)
- # delete old weights
+ # delete old additional files
shutil.rmtree(
- ADDITIONAL_FILES_FOLDER / latest_downloaded_weights,
+ ADDITIONAL_FILES_FOLDER / latest_downloaded_additional_files,
onerror=lambda func, path, excinfo: logger.warning(
f"Failed to delete {path}: {excinfo}"
),
@@ -115,7 +121,7 @@ def _get_zenodo_metadata_and_archive_url(record_id: str) -> Dict | None:
response = requests.get(f"{ZENODO_RECORD_BASE_URL}/{record_id}")
if response.status_code != 200:
logger.error(
- f"Cant find model weights for record_id '{record_id}' on Zenodo. Exiting..."
+ f"Cant find additional files for record_id '{record_id}' on Zenodo. Exiting..."
)
# TODO add proper exit exception
data = response.json()
@@ -145,13 +151,13 @@ def _download_additional_files(
# ensure folder exists
record_folder.mkdir(parents=True, exist_ok=True)
- logger.info(f"Downloading model weights from Zenodo. This might take a while...")
+ logger.info(f"Downloading additional files from Zenodo. This might take a while...")
# Make a GET request to the URL
response = requests.get(archive_url, stream=True)
# Ensure the request was successful
if response.status_code != 200:
logger.error(
- f"Failed to download model weights. Status code: {response.status_code}"
+ f"Failed to download additional files. Status code: {response.status_code}"
)
return
@@ -168,7 +174,7 @@ def _extract_archive(response: requests.Response, record_folder: Path):
with Progress(
SpinnerColumn(),
- TextColumn("[cyan]Downloading weights..."),
+ TextColumn("[cyan]Downloading additional files..."),
TextColumn("[cyan]{task.completed:.2f} MB"),
transient=True,
) as progress:
diff --git a/tests/core/test_docker.py b/tests/core/test_docker.py
index 5889767..bcfc2ae 100644
--- a/tests/core/test_docker.py
+++ b/tests/core/test_docker.py
@@ -48,8 +48,8 @@ def setUp(self):
shm_size="1g",
cpu_compatible=False,
),
- weights=MagicMock(
- param_name=["weights"], checkpoint_path=["checkpoint.pth"]
+ additional_files=MagicMock(
+ param_name=["weights"], param_path=["checkpoint.pth"]
),
meta=MagicMock(
challenge="Challenge",
@@ -66,8 +66,8 @@ def setUp(self):
shm_size="1g",
cpu_compatible=True,
),
- weights=MagicMock(
- param_name=["weights"], checkpoint_path=["checkpoint.pth"]
+ additional_files=MagicMock(
+ param_name=["weights"], param_path=["checkpoint.pth"]
),
meta=MagicMock(
challenge="Challenge",
@@ -301,7 +301,9 @@ def test_sanity_check_output_not_enough_outputs(self, mock_nib_load, mock_logger
@patch("brats.core.docker.logger")
@patch("brats.core.docker.nib.load")
- def test_sanity_check_output_empty_warning(self, mock_nib_load, mock_logger):
+ def test_sanity_check_output_empty_warning_single_inference(
+ self, mock_nib_load, mock_logger
+ ):
# Create mock paths
mock_data_path = MagicMock(spec=Path)
mock_output_path = MagicMock(spec=Path)
@@ -336,6 +338,46 @@ def test_sanity_check_output_empty_warning(self, mock_nib_load, mock_logger):
# assertions
mock_logger.warning.assert_called_once()
+ @patch("brats.core.docker.logger")
+ @patch("brats.core.docker.nib.load")
+ def test_sanity_check_output_empty_warning_batch_inference(
+ self, mock_nib_load, mock_logger
+ ):
+ # Create mock paths
+ mock_data_path = MagicMock(spec=Path)
+ mock_output_path = MagicMock(spec=Path)
+
+ # Simulate input files starting with "BraTS" and output files
+ mock_data_path.iterdir.return_value = [
+ MagicMock(name="external_file_1", spec=Path),
+ ]
+ mock_output_path.iterdir.return_value = [
+ MagicMock(name="internal_file_1", spec=Path),
+ ]
+
+ # Create a mock object for the fdata
+ mock_nifti_img = MagicMock()
+ # zeros!
+ mock_nifti_img.get_fdata.return_value = np.zeros((2, 2, 2))
+
+ # Mock the nib.load to return the mock nifti image
+ mock_nib_load.return_value = mock_nifti_img
+
+ # Define container_output
+ container_output = "Sample container output"
+
+ # Check that no exception is raised
+
+ _sanity_check_output(
+ data_path=mock_data_path,
+ output_path=mock_output_path,
+ container_output=container_output,
+ internal_external_name_map={"internal_file_1": "external_file_1"},
+ )
+
+ # assertions
+ mock_logger.warning.assert_called_once()
+
@patch("brats.core.docker.logger.debug")
def test_log_algorithm_info(self, MockLoggerDebug):
_log_algorithm_info(algorithm=self.algorithm_gpu)
diff --git a/tests/core/test_inpainting_algorithms.py b/tests/core/test_inpainting_algorithms.py
index 78b3124..8b38031 100644
--- a/tests/core/test_inpainting_algorithms.py
+++ b/tests/core/test_inpainting_algorithms.py
@@ -46,6 +46,7 @@ def test_successful_single_standardization(self, mock_input_sanity_check):
"t1n": self.t1n,
"mask": self.mask,
},
+ subject_modality_separator="-",
)
subject_folder = self.tmp_data_folder / subject_id
self.assertTrue(subject_folder.exists())
@@ -68,6 +69,7 @@ def test_single_standardize_handle_file_not_found_error(
"t1n": t1n,
"mask": self.mask,
},
+ subject_modality_separator="-",
)
mock_logger.assert_called()
mock_exit.assert_called_with(1)
diff --git a/tests/core/test_missing_mri_algorithms.py b/tests/core/test_missing_mri_algorithms.py
index bf49ebf..2749a06 100644
--- a/tests/core/test_missing_mri_algorithms.py
+++ b/tests/core/test_missing_mri_algorithms.py
@@ -48,6 +48,7 @@ def test_successful_single_standardization(self, mock_input_sanity_check):
"t1c": self.t1c,
"t2w": self.t2w,
},
+ subject_modality_separator="-",
)
subject_folder = self.tmp_data_folder / subject_id
self.assertTrue(subject_folder.exists())
@@ -72,6 +73,7 @@ def test_single_standardize_handle_file_not_found_error(
"t1c": self.t1c,
"t2w": self.t2w,
},
+ subject_modality_separator="-",
)
mock_logger.assert_called()
mock_exit.assert_called_with(1)
diff --git a/tests/core/test_segmentation_algorithms.py b/tests/core/test_segmentation_algorithms.py
index db341a1..fd0602f 100644
--- a/tests/core/test_segmentation_algorithms.py
+++ b/tests/core/test_segmentation_algorithms.py
@@ -62,6 +62,7 @@ def test_successful_single_standardization(self, mock_input_sanity_check):
"t2f": self.t2f,
"t2w": self.t2w,
},
+ subject_modality_separator="-",
)
subject_folder = self.tmp_data_folder / subject_id
self.assertTrue(subject_folder.exists())
@@ -88,6 +89,7 @@ def test_single_standardize_handle_file_not_found_error(
"t2f": self.t2f,
"t2w": self.t2w,
},
+ subject_modality_separator="-",
)
mock_logger.assert_called()
mock_exit.assert_called_with(1)
@@ -166,3 +168,137 @@ def test_metastases_segmenter_initialization(self):
algorithm=MetastasesAlgorithms.BraTS23_2, cuda_devices="1", force_cpu=True
)
self.assertIsInstance(custom_segmenter, MetastasesSegmenter)
+
+ ## Test MeningiomaSegmenter specialty
+
+ @patch("brats.core.segmentation_algorithms.MeningiomaSegmenter._infer_single")
+ def test_meningioma_segmenter_infer_single_2023_valid(self, mock_infer_single):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS23_1)
+
+ segmenter.infer_single(
+ t1c=self.t1c,
+ t1n=self.t1n,
+ t2f=self.t2f,
+ t2w=self.t2w,
+ output_file=self.tmp_data_folder / "output.nii.gz",
+ )
+
+ mock_infer_single.assert_called_once()
+
+ @patch("brats.core.segmentation_algorithms.MeningiomaSegmenter._infer_single")
+ def test_meningioma_segmenter_infer_single_2023_invalid_missing_modalities(
+ self, mock_infer_single
+ ):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS23_1)
+
+ with self.assertRaises(ValueError):
+ segmenter.infer_single(
+ t1c=self.t1c,
+ # t1n=self.t1n, # Missing modality
+ t2f=self.t2f,
+ t2w=self.t2w,
+ output_file=self.tmp_data_folder / "output.nii.gz",
+ )
+
+ mock_infer_single.assert_not_called()
+
+ @patch("brats.core.segmentation_algorithms.MeningiomaSegmenter._infer_single")
+ def test_meningioma_segmenter_infer_single_2024_valid(self, mock_infer_single):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS24_1)
+
+ segmenter.infer_single(
+ t1c=self.t1c,
+ output_file=self.tmp_data_folder / "output.nii.gz",
+ )
+
+ mock_infer_single.assert_called_once()
+
+ @patch("brats.core.segmentation_algorithms.MeningiomaSegmenter._infer_single")
+ def test_meningioma_segmenter_infer_single_2024_invalid_missing_t1c(
+ self, mock_infer_single
+ ):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS24_1)
+
+ with self.assertRaises(ValueError):
+ segmenter.infer_single(
+ # t1c=self.t1c,
+ # t1n=self.t1n,
+ # t2f=self.t2f,
+ t2w=self.t2w,
+ output_file=self.tmp_data_folder / "output.nii.gz",
+ )
+
+ mock_infer_single.assert_not_called()
+
+ @patch("brats.core.segmentation_algorithms.MeningiomaSegmenter._infer_single")
+ def test_meningioma_segmenter_infer_single_2024_invalid_too_many_files(
+ self, mock_infer_single
+ ):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS24_1)
+
+ with self.assertRaises(ValueError):
+ segmenter.infer_single(
+ t1c=self.t1c,
+ # t1n=self.t1n,
+ # t2f=self.t2f,
+ t2w=self.t2w,
+ output_file=self.tmp_data_folder / "output.nii.gz",
+ )
+
+ mock_infer_single.assert_not_called()
+
+ @patch("brats.core.brats_algorithm.BraTSAlgorithm._process_batch_output")
+ @patch("brats.core.brats_algorithm.run_container")
+ @patch(
+ "brats.core.segmentation_algorithms.MeningiomaSegmenter._standardize_single_inputs"
+ )
+ def test_meningioma_segmenter_infer_batch_2024(
+ self,
+ mock_standardize_single_inputs,
+ mock_run_container,
+ mock_process_batch_output,
+ ):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS24_1)
+
+ segmenter.infer_batch(
+ data_folder=self.data_folder,
+ output_folder=self.tmp_data_folder,
+ log_file=self.tmp_data_folder / "log.txt",
+ )
+
+ self.assertEqual(mock_standardize_single_inputs.call_count, 1)
+ # assert algorithms was called with just t1c
+ args, kwargs = mock_standardize_single_inputs.call_args
+ input_keys = list(kwargs["inputs"].keys())
+ self.assertEqual(input_keys, ["t1c"])
+
+ self.assertEqual(mock_run_container.call_count, 1)
+ self.assertEqual(mock_process_batch_output.call_count, 1)
+
+ @patch("brats.core.brats_algorithm.BraTSAlgorithm._process_batch_output")
+ @patch("brats.core.brats_algorithm.run_container")
+ @patch(
+ "brats.core.segmentation_algorithms.MeningiomaSegmenter._standardize_single_inputs"
+ )
+ def test_meningioma_segmenter_infer_batch_2023(
+ self,
+ mock_standardize_single_inputs,
+ mock_run_container,
+ mock_process_batch_output,
+ ):
+ segmenter = MeningiomaSegmenter(algorithm=MeningiomaAlgorithms.BraTS23_1)
+
+ segmenter.infer_batch(
+ data_folder=self.data_folder,
+ output_folder=self.tmp_data_folder,
+ log_file=self.tmp_data_folder / "log.txt",
+ )
+
+ self.assertEqual(mock_standardize_single_inputs.call_count, 1)
+ # assert algorithms was called with all modalities
+ args, kwargs = mock_standardize_single_inputs.call_args
+ input_keys = list(kwargs["inputs"].keys())
+ self.assertEqual(input_keys, ["t1c", "t1n", "t2f", "t2w"])
+
+ self.assertEqual(mock_run_container.call_count, 1)
+ self.assertEqual(mock_process_batch_output.call_count, 1)
diff --git a/tests/utils/test_zenodo.py b/tests/utils/test_zenodo.py
index 30fa613..05497a6 100644
--- a/tests/utils/test_zenodo.py
+++ b/tests/utils/test_zenodo.py
@@ -44,13 +44,13 @@ def test_check_additional_files_path(
"http://test.url",
)
- # Test when local weights are up-to-date
+ # Test when local additional_files are up-to-date
result = check_additional_files_path(mock_record_id)
self.assertEqual(result, ADDITIONAL_FILES_FOLDER / f"{mock_record_id}_v1.0.0")
mock_rmtree.assert_not_called()
mock_download_additional_files.assert_not_called()
- # Test when new weights are available
+ # Test when new additional_files are available
mock_get_zenodo_metadata.return_value = (
{"version": "2.0.0"},
"http://test.url",