Skip to content

Commit

Permalink
Merge pull request #28 from clementpoiret/feat/subsOverride
Browse files Browse the repository at this point in the history
Avoid re-segmenting already processed subjects
  • Loading branch information
clementpoiret authored Mar 29, 2024
2 parents bede808 + eca176d commit 3153901
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 23 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
runs-on: ${{ matrix.operating-system }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down
8 changes: 2 additions & 6 deletions LAST_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

## Software

* Release of the HSF finetuning pipeline,
* Bug fixes and optimizations,
* Updated dependencies.
* Option to override already segmented mris.

## Models

* Models trained on hippocampal subfields from Clark et al. (2023) dataset (https://doi.org/10.1038/s41597-023-02449-9),
* Models are now hosted on HuggingFace,
* Bug fixes and optimizations.
* No changelog.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ Changelogs
HSF
---

**Version 1.2.1**

* Option to override already segmented mris.

**Version 1.2.0**

* Released finetuning scripts,
Expand Down
4 changes: 4 additions & 0 deletions docs/about/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Current maintainers:

## HSF

### Version 1.2.1 (2024-03-29)

* Added an option to override already segmented mris.

### Version 1.2.0 (2024-02-06)

* Released finetuning scripts,
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ I/O are managed through the `files.*` arguments. Default parameters are defined
- `files.path` and `files.pattern` are mandatory arguments and respectively define where to search for MRIs, and how to find them through a `glob()` pattern.
- `files.mask_pattern` defines how to find brain extraction masks for registration purposes (see [ROILoc documentation](user-guide/roiloc.md)).
- `files.output_dir` defines where to store temporary files in a relative subject directory.
- `files.overwrite` defines whether to overwrite existing segmentations.

The following example will recursively search all `*T2w.nii.gz` files in the `~Datasets/MRI/` folder, for search a `*T2w_bet_mask.nii.gz` located next to each T2w images:

Expand Down
2 changes: 1 addition & 1 deletion hsf/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.2.0'
__version__ = '1.2.1'
1 change: 1 addition & 0 deletions hsf/conf/files/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ path: ???
pattern: ???
mask_pattern: "*mask.nii.gz"
output_dir: "hsf_outputs"
overwrite: false
48 changes: 47 additions & 1 deletion hsf/factory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from pathlib import Path, PosixPath
from typing import Generator, Optional
from typing import Generator, List, Optional

import ants
import hydra
Expand Down Expand Up @@ -144,6 +144,45 @@ def save(mri: PosixPath, hippocampus: PosixPath, hard_pred: torch.Tensor,
log.info(f"Saved segmentation in native space to {str(output_path)}")


def filter_mris(mris: List[PosixPath], overwrite: bool) -> List[PosixPath]:
"""
Filter mris.
Args:
mris (List[PosixPath]): List of MRI paths.
overwrite (bool): Overwrite existing segmentations.
Returns:
List[PosixPath]: List of filtered MRI paths.
"""

def _get_segmentations(mri: PosixPath) -> List[PosixPath]:
extensions = "".join(mri.suffixes)
stem = mri.name.replace(extensions, "")
segmentations = list(mri.parent.glob(f"{stem}*_hippocampus_seg.nii.gz"))

if len(segmentations) > 2:
log.warning(
f"Found {len(segmentations)} segmentations for {mri}. "
"As HSF produces only two files per MRI, this might indicate a misconfiguration."
)

if len(segmentations) > 0:
log.info(
f"Found {len(segmentations)} segmentations for {mri}. "
"Skipping segmentation. If you want to overwrite, set overwrite=True."
)

return segmentations

mris = [mri for mri in mris if not mri.name.endswith("_seg.nii.gz")]
if overwrite:
return mris

mris = [mri for mri in mris if len(_get_segmentations(mri)) == 0]
return mris


@hydra.main(config_path="conf", config_name="config", version_base="1.1")
def main(cfg: DictConfig) -> None:
fetch_models(cfg.segmentation.models_path, cfg.segmentation.models)
Expand All @@ -158,6 +197,13 @@ def main(cfg: DictConfig) -> None:
) % bs == 0, "test_time_num_aug+1 must be a multiple of batch_size for deepsparse"

mris = load_from_config(cfg.files.path, cfg.files.pattern)
_n = len(mris)

mris = filter_mris(mris, cfg.files.overwrite)

if len(mris) == 0 and _n > 0:
log.info("No new MRI found. Skipping segmentation.")
return

log.warning(
"Please be aware that the segmentation is highly dependant on ROILoc to locate both hippocampi.\nROILoc will be run using the following configuration.\nIf the segmentation is of bad quality, please tune your ROILoc settings (e.g. ``margin``)."
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "HSF"
version = "1.2.0"
version = "1.2.1"
description = "A simple yet exhaustive segmentation tool of the Hippocampal Subfields in T1w and T2w MRIs."
authors = ["Clément POIRET <[email protected]>"]
license = "MIT"
Expand Down
File renamed without changes.
Binary file added tests/mri/sub1_tse.nii.gz
Binary file not shown.
Binary file added tests/mri/sub1_tse_left_hippocampus_seg.nii.gz
Binary file not shown.
Binary file added tests/mri/sub1_tse_right_hippocampus_seg.nii.gz
Binary file not shown.
26 changes: 14 additions & 12 deletions tests/test_hsf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
from pathlib import Path

import ants
import pytest
import torch
from omegaconf import DictConfig

import hsf.engines
import hsf.factory
import hsf.fetch_models
import hsf.multispectrality
import hsf.roiloc_wrapper
import hsf.segment
import hsf.uncertainty
import pytest
import torch
from hsf import __version__
from omegaconf import DictConfig


def test_version():
assert __version__ == '1.2.0'
assert __version__ == '1.2.1'


# SETUP FIXTURES
Expand All @@ -30,7 +31,7 @@ def models_path(tmpdir_factory):
tmpdir_path = Path(tmpdir_path)

# Copy sample mri
shutil.copy("tests/mri/tse.nii.gz", tmpdir_path / "tse.nii.gz")
shutil.copy("tests/mri/sub0_tse.nii.gz", tmpdir_path / "sub0_tse.nii.gz")
shutil.copy("tests/mri/mask.nii.gz", tmpdir_path / "mask.nii.gz")

# Download model
Expand All @@ -47,9 +48,10 @@ def config(models_path):
configuration = {
"files": {
"path": str(models_path),
"pattern": "tse.nii.gz",
"pattern": "sub*_tse.nii.gz",
"mask_pattern": None,
"output_dir": "hsf_outputs",
"overwrite": False
},
"hardware": {
"engine": "onnxruntime",
Expand Down Expand Up @@ -140,7 +142,7 @@ def test_main_compute_uncertainty(models_path):
soft_pred = torch.randn(5, 6, 448, 30, 448)
soft_pred = torch.softmax(soft_pred, dim=1)

hsf.factory.compute_uncertainty(models_path / "tse.nii.gz", soft_pred)
hsf.factory.compute_uncertainty(models_path / "sub0_tse.nii.gz", soft_pred)


# fetch_models
Expand All @@ -160,7 +162,7 @@ def test_fetch_models(models_path, config):
# # ROILoc
def test_roiloc(models_path):
"""Tests that we can locate and save hippocampi."""
mris = hsf.roiloc_wrapper.load_from_config(models_path, "tse.nii.gz")
mris = hsf.roiloc_wrapper.load_from_config(models_path, "sub0_tse.nii.gz")
assert mris

mri, mask = hsf.roiloc_wrapper.get_mri(mris[0], mask_pattern="mask.nii.gz")
Expand All @@ -186,7 +188,7 @@ def test_roiloc(models_path):
# Segmentation
def test_segment(models_path, config, deepsparse_inference_engines):
"""Tests that we can segment and save a hippocampus."""
mri = models_path / "tse_right_hippocampus.nii.gz"
mri = models_path / "sub0_tse_right_hippocampus.nii.gz"
sub = hsf.segment.mri_to_subject(mri)
sub = [sub, sub]

Expand All @@ -210,17 +212,17 @@ def test_multispectrality(models_path):
"output_dir": str(models_path)
},
"multispectrality": {
"pattern": "tse.nii.gz",
"pattern": "sub0_tse.nii.gz",
"same_space": False,
"registration": {
"type_of_transform": "AffineFast"
}
}
})

mri = hsf.roiloc_wrapper.load_from_config(models_path, "tse.nii.gz")[0]
mri = hsf.roiloc_wrapper.load_from_config(models_path, "sub0_tse.nii.gz")[0]
second_contrast = hsf.multispectrality.get_second_contrast(
mri, "tse.nii.gz")
mri, "sub0_tse.nii.gz")

registered = hsf.multispectrality.register(
mri, second_contrast,
Expand Down

0 comments on commit 3153901

Please sign in to comment.