Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions NiChart_DLMUSE/__init__.py
Original file line number Diff line number Diff line change
@@ -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
31 changes: 28 additions & 3 deletions NiChart_DLMUSE/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def main() -> None:
"--cores",
type=str,
help="Number of cores",
default=4,
default=1,
required=False,
)

Expand Down Expand Up @@ -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)
Expand All @@ -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:")
Expand Down Expand Up @@ -185,6 +194,7 @@ def main() -> None:
device,
dlmuse_extra_args,
dlicv_extra_args,
refaced_data,
i,
),
)
Expand All @@ -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:
Expand All @@ -216,6 +233,7 @@ def main() -> None:
device,
dlmuse_extra_args,
dlicv_extra_args,
refaced_data,
i,
),
)
Expand All @@ -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__":
Expand Down
24 changes: 23 additions & 1 deletion NiChart_DLMUSE/dlmuse_pipeline.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import os
import sys

import pkg_resources # type: ignore

Expand Down Expand Up @@ -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(
Expand All @@ -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:
Expand Down Expand Up @@ -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}]...")
Expand Down
4 changes: 3 additions & 1 deletion NiChart_DLMUSE/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://pytorch.org/get-started/locally/>`_ 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 <https://pytorch.org/get-started/locally/>`_ 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
Expand All @@ -77,7 +77,7 @@ Docker build
************

The package comes already pre-built as a `docker container <https://hub.docker.com/repository/docker/cbica/nichart_dlmuse/general>`_, 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 .

Expand Down
13 changes: 9 additions & 4 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,13 +45,16 @@ 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: ::

$ git clone https://github.com/CBICA/NiChart_DLMUSE

$ 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.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ DLICV
DLMUSE
nibabel>=5.2
scipy
SimpleITK

# Developer tools
pytest
Expand Down
59 changes: 59 additions & 0 deletions scripts/wrapper.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading