diff --git a/NiChart_DLMUSE/__init__.py b/NiChart_DLMUSE/__init__.py
index 7f41d576..7612d577 100644
--- a/NiChart_DLMUSE/__init__.py
+++ b/NiChart_DLMUSE/__init__.py
@@ -1,4 +1 @@
-from .dlmuse_pipeline import run_pipeline, run_dlicv, run_dlmuse
-
-if __name__ == "__main__":
- pass
+from .dlmuse_pipeline import run_dlicv, run_dlmuse, run_pipeline
diff --git a/NiChart_DLMUSE/__main__.py b/NiChart_DLMUSE/__main__.py
index 026353a0..f879750e 100644
--- a/NiChart_DLMUSE/__main__.py
+++ b/NiChart_DLMUSE/__main__.py
@@ -87,7 +87,7 @@ def main() -> None:
"--cores",
type=str,
help="Number of cores",
- default=4,
+ default=1,
required=False,
)
@@ -131,6 +131,14 @@ def main() -> None:
help="Pass additional args to be sent to DLICV (ex. '-nps 1 -npp 1'). It is recommended to surround these args in a set of double quotes. See the DLICV documentation for details.",
)
+ parser.add_argument(
+ "--refaced-data",
+ action="store_true",
+ required=False,
+ default=False,
+ help="If set, refine DLICV mask by keeping only the largest connected component (for refaced data).",
+ )
+
# HELP argument
help = "Show this message and exit"
parser.add_argument("-h", "--help", action="store_true", help=help)
@@ -142,6 +150,7 @@ def main() -> None:
device = args.device
dlicv_extra_args = args.dlicv_args
dlmuse_extra_args = args.dlmuse_args
+ refaced_data = args.refaced_data
print()
print("Arguments:")
@@ -185,6 +194,7 @@ def main() -> None:
device,
dlmuse_extra_args,
dlicv_extra_args,
+ refaced_data,
i,
),
)
@@ -198,7 +208,14 @@ def main() -> None:
remove_subfolders("raw_temp_T1")
remove_subfolders(out_dir)
else: # No core parallelization
- run_pipeline(in_dir, out_dir, device, dlmuse_extra_args, dlicv_extra_args)
+ run_pipeline(
+ in_dir,
+ out_dir,
+ device,
+ dlmuse_extra_args,
+ dlicv_extra_args,
+ refaced_data,
+ )
else: # Non-BIDS
if int(args.cores) > 1:
@@ -216,6 +233,7 @@ def main() -> None:
device,
dlmuse_extra_args,
dlicv_extra_args,
+ refaced_data,
i,
),
)
@@ -229,7 +247,14 @@ def main() -> None:
remove_subfolders(in_dir)
remove_subfolders(out_dir)
else: # No core parallelization
- run_pipeline(in_dir, out_dir, device, dlmuse_extra_args, dlicv_extra_args)
+ run_pipeline(
+ in_dir,
+ out_dir,
+ device,
+ dlmuse_extra_args,
+ dlicv_extra_args,
+ refaced_data,
+ )
if __name__ == "__main__":
diff --git a/NiChart_DLMUSE/dlmuse_pipeline.py b/NiChart_DLMUSE/dlmuse_pipeline.py
index 8268fd79..c6642d89 100644
--- a/NiChart_DLMUSE/dlmuse_pipeline.py
+++ b/NiChart_DLMUSE/dlmuse_pipeline.py
@@ -1,5 +1,6 @@
import logging
import os
+import sys
import pkg_resources # type: ignore
@@ -32,7 +33,9 @@
)
logger = logging.getLogger(__name__)
-logging.basicConfig(filename="pipeline.log", encoding="utf-8", level=logging.DEBUG)
+logging.basicConfig(encoding="utf-8", level=logging.DEBUG,
+ handlers=[logging.FileHandler("pipeline.log"),
+ logging.StreamHandler(sys.stdout)])
def run_pipeline(
@@ -41,6 +44,7 @@ def run_pipeline(
device: str,
dlmuse_extra_args: str = '',
dlicv_extra_args: str = '',
+ refaced_data: bool = False,
sub_fldr: int = 1,
progress_bar = None,
) -> None:
@@ -108,6 +112,24 @@ def run_pipeline(
progress_bar.set_description("Running DLICV")
run_dlicv(in_dir, in_suff, out_dir, out_suff, device, dlicv_extra_args)
+ # If refaced data is specified, refine the masks used in the next step (s3_masked)
+ if refaced_data:
+ import SimpleITK as sitk
+
+ for _, tmp_row in df_img.iterrows():
+ img_prefix = tmp_row.img_prefix
+ fpath = os.path.join(out_dir, img_prefix + SUFF_DLICV)
+ if os.path.exists(fpath):
+ s2_dlicv_output = sitk.ReadImage(fpath)
+ # Keep only the largest connected component
+ mask_component = sitk.ConnectedComponent(s2_dlicv_output)
+ mask_sorted_component = sitk.RelabelComponent(
+ mask_component, sortByObjectSize=True
+ )
+ final_mask = sitk.Equal(mask_sorted_component, 1)
+ # Write refined mask back in-place within s2_dlicv
+ sitk.WriteImage(final_mask, fpath)
+
logging.info(f"Applying DLICV for batch [{sub_fldr}] done")
logging.info(f"Applying mask for batch [{sub_fldr}]...")
diff --git a/NiChart_DLMUSE/utils.py b/NiChart_DLMUSE/utils.py
index f4c4e310..60966f66 100644
--- a/NiChart_DLMUSE/utils.py
+++ b/NiChart_DLMUSE/utils.py
@@ -65,7 +65,9 @@ def remove_common_suffix(list_files: list) -> list:
bnames = list_files
if len(list_files) == 1:
- if list_files[0].endswith('_T1'): # If there is a single image with suffix _T1, remove it
+ if list_files[0].endswith(
+ "_T1"
+ ): # If there is a single image with suffix _T1, remove it
bnames = [x[0:-3] for x in bnames]
return bnames
diff --git a/README.rst b/README.rst
index d7b23f9f..b458232f 100644
--- a/README.rst
+++ b/README.rst
@@ -53,11 +53,11 @@ Or from out latest stable PyPI wheel: ::
(If needed for your system) Install PyTorch with compatible CUDA.
You only need to run this step if you experience errors with CUDA while running NiChart_DLMUSE.
Run "pip uninstall torch torchaudio torchvision".
-Then follow the `PyTorch installation instructions `_ for your CUDA version.
-Note that we highly recommend matching the torch version you install to the version used by NiChart_DLMUSE. For example, after installing NiChart_DLMUSE: ::
+Then follow the `PyTorch installation instructions `_ for your CUDA version.
+Specific versions are needed for full compatibility. On Linux, download Torch 2.3.1, on Windows install 2.5.1. For example, after installing NiChart_DLMUSE: ::
$ pip uninstall torch torchvision torchaudio
- pip install torch==2.2.1 --index-url https://download.pytorch.org/whl/cu121
+ pip install torch==2.3.1 --index-url https://download.pytorch.org/whl/cu121
******************
Run NiChart_DLMUSE
@@ -77,7 +77,7 @@ Docker build
************
The package comes already pre-built as a `docker container `_, for convenience. Please see `Usage <#usage>`_ for more information on how to use it. Alternatively, you can build the docker image
-locally, like so: ::
+locally from the source repo, like so: ::
$ docker build -t cbica/nichart_dlmuse .
diff --git a/docs/installation.rst b/docs/installation.rst
index 82b9555b..da6f4a1e 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -30,7 +30,9 @@ You can build the package by running the following command: ::
$ docker build -t cbica/nichart_dlmuse .
-.. _`Singularity/Apptainer build`
+***************************
+Singularity/Apptainer build
+***************************
Singularity and Apptainer images can be built for NiChart_DLMUSE, allowing for frozen versions of the pipeline and easier
installation for end-users. Note that the Singularity project recently underwent a rename to "Apptainer", with a commercial
@@ -43,7 +45,9 @@ After installing the container engine, run: ::
This will take some time, but will build a containerized version of your current repo. Be aware that this includes any local changes!
The nichart_dlmuse.sif file can be distributed via direct download, or pushed to a container registry that accepts SIF images.
-.. _`Manual installation`
+*******************
+Manual installation
+*******************
You can manually build the package from source by running: ::
@@ -51,5 +55,6 @@ You can manually build the package from source by running: ::
$ cd NiChart_DLMUSE && python3 -m pip install -e .
-We **do not** recomment installing the package directly from source as the repository above is under heavy development and can cause
-crashes and bugs.
+.. note::
+ We **do not** recommend installing the package directly from source as the repository above is under heavy development and can cause
+ crashes and bugs.
diff --git a/requirements.txt b/requirements.txt
index 79414b1a..2428040f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ DLICV
DLMUSE
nibabel>=5.2
scipy
+SimpleITK
# Developer tools
pytest
diff --git a/scripts/wrapper.py b/scripts/wrapper.py
new file mode 100644
index 00000000..254175bc
--- /dev/null
+++ b/scripts/wrapper.py
@@ -0,0 +1,59 @@
+import argparse
+import os
+import shutil
+import tempfile
+from pathlib import Path
+
+# This wrapper script just adapts NiChart_DLMUSE to take two separate output args. Everything else is passed transparently
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Wrapper", allow_abbrev=False)
+ parser.add_argument("-i", "--in_dir", required=True, help="Input directory")
+ parser.add_argument(
+ "-o1",
+ "--out_segs",
+ required=True,
+ help="Output directory for segmentation files",
+ )
+ parser.add_argument(
+ "-o2", "--out_csvs", required=True, help="Output directory for CSV files"
+ )
+
+ # Parse known args; leave the rest for original app
+ args, extra_args = parser.parse_known_args()
+
+ input_dir = args.in_dir
+ seg_dir = Path(args.out_segs)
+ csv_dir = Path(args.out_csvs)
+
+ seg_dir.mkdir(parents=True, exist_ok=True)
+ csv_dir.mkdir(parents=True, exist_ok=True)
+
+ with tempfile.TemporaryDirectory() as tmp_output:
+ tmp_output_path = Path(tmp_output)
+
+ # Build command to run original application
+ cmd = [
+ "NiChart_DLMUSE",
+ "-i",
+ input_dir,
+ "-o",
+ str(tmp_output_path),
+ ] + extra_args
+ command = " ".join(cmd)
+ os.system(command)
+
+ # Copy output files
+ for item in tmp_output_path.rglob("*"):
+ if item.is_file():
+ if item.suffix.lower() == ".csv":
+ shutil.copy2(item, csv_dir / item.name)
+ else:
+ dest_path = seg_dir / item.relative_to(tmp_output_path)
+ dest_path.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(item, dest_path)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/setup.py b/setup.py
index b353a48a..9698ec15 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
setup(
name="NiChart_DLMUSE",
- version="1.0.8",
+ version="1.0.9",
description="Run NiChart_DLMUSE on your data (currently only structural pipeline is supported).",
long_description=long_description,
long_description_content_type="text/markdown",