Skip to content

Commit

Permalink
Merge branch '1.9.0.8-HOTFIX' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
wasciutto committed Nov 16, 2023
2 parents 96ab8c8 + 793f4be commit 66442e9
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .lmod/module_file_template.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ whatis("URL: https://github.com/cohenlabUNC/clpipe")
unload("python")

-- load in clpipe dependencies
load("fsl/6.0.3")
load("freesurfer/6.0.0")
load("fsl/6.0.3")
load("afni/20.3.00")
load("r/4.2.1")
load("dcm2niix/1.0.20211006")
Expand Down
7 changes: 3 additions & 4 deletions clpipe/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
from .config.package import VERSION

DEFAULT_HELP_PRIORITY = 5

CONTEXT_SETTINGS = dict(help_option_names=["-help"])
CONTEXT_SETTINGS = dict(help_option_names=["-help"], show_default=True)

# Click path validation types
CLICK_FILE_TYPE = click.Path(dir_okay=False, file_okay=True)
Expand Down Expand Up @@ -915,7 +914,7 @@ def fmri_roi_extraction_cli(
custom_atlas,
custom_label,
custom_type,
radius,
sphere_radius,
submit,
single,
overlap_ok,
Expand All @@ -937,7 +936,7 @@ def fmri_roi_extraction_cli(
custom_atlas=custom_atlas,
custom_label=custom_label,
custom_type=custom_type,
radius=radius,
sphere_radius=sphere_radius,
submit=submit,
single=single,
overlap_ok=overlap_ok,
Expand Down
4 changes: 2 additions & 2 deletions clpipe/config/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class SourceOptions(Option):

@validates("source_url")
def validate_(self, value):
if not value.startswith("fw://"):
if not value.startswith("fw://") and value != "":
raise ValidationError("source_url must start with prefix: fw://")


Expand Down Expand Up @@ -164,7 +164,7 @@ class Convert2BIDSOptions(Option):
@validates("conversion_config")
def validate_conversion_config(self, value):
suffix = Path(value).suffix
if (suffix != ".py") and (suffix != ".json"):
if (suffix != ".py") and (suffix != ".json") and (suffix != ""):
raise ValidationError("Must be type '.py' or '.json'")

def populate_project_paths(
Expand Down
66 changes: 34 additions & 32 deletions clpipe/roi_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def fmri_roi_extraction(
custom_atlas=None,
custom_label=None,
custom_type=None,
radius="5",
sphere_radius="5",
submit=False,
single=False,
overlap_ok=None,
Expand Down Expand Up @@ -80,22 +80,24 @@ def fmri_roi_extraction(

atlas_names = [atlas["atlas_name"] for atlas in atlas_library["Atlases"]]
logger.debug(atlas_names)
custom_radius = radius
custom_radius = sphere_radius
submission_string = (
"""fmri_roi_extraction -config_file={config} -atlas_name={atlas} -single"""
"""clpipe roi extract -config_file={config} -atlas_name={atlas} -single"""
)
submission_string_custom = (
"clpipe roi extract -config_file={config} "
"-atlas_name={atlas} -custom_atlas={custom_atlas} -custom_label={custom_labels} "
"-custom_type={custom_type} -single"
)
submission_string_custom = """fmri_roi_extraction -config_file={config}
-atlas_name={atlas} -custom_atlas={custom_atlas} -custom_label={custom_labels}
-custom_type={custom_type} -single"""

batch_manager = BatchManager(config.batch_config_path, log_output_dir)
batch_manager = BatchManager(config.batch_config_path, config.roi_extraction.log_directory)
batch_manager.update_mem_usage(config.roi_extraction.memory_usage)
batch_manager.update_time(config.roi_extraction.time_usage)
batch_manager.update_nthreads(config.roi_extraction.n_threads)
batch_manager.update_email(config.email_address)
batch_manager.createsubmissionhead()
for subject in sublist:
logger.info(f"Starting ROI extraction for suject: {subject}")
logger.debug(f"Setting up ROI extraction for subject {subject}")
for cur_atlas in atlas_list:
custom_flag = False
sphere_flag = False
Expand Down Expand Up @@ -162,7 +164,7 @@ def fmri_roi_extraction(
atlas=atlas_name,
)
if sphere_flag:
sub_string_temp = sub_string_temp + " -radius=" + custom_radius
sub_string_temp = sub_string_temp + " -sphere_radius=" + custom_radius
if task is not None:
sub_string_temp = sub_string_temp + " -task=" + task
if overlap_ok or config.roi_extraction.overlap_ok:
Expand Down Expand Up @@ -204,7 +206,7 @@ def _fmri_roi_extract_subject(
atlas_filename,
atlas_label,
atlas_type,
radius,
sphere_radius,
custom_flag,
config: ProjectOptions,
overlap_ok,
Expand Down Expand Up @@ -257,7 +259,7 @@ def _fmri_roi_extract_subject(
atlas_name,
atlas_path,
atlas_type,
radius,
sphere_radius,
overlap_ok,
overwrite,
logger,
Expand All @@ -270,7 +272,7 @@ def fmri_roi_extract_image(
atlas_name,
atlas_path,
atlas_type,
radius,
sphere_radius,
overlap_ok,
overwrite,
logger,
Expand All @@ -293,8 +295,8 @@ def fmri_roi_extract_image(
return

try:
# First, try to find this image's mask.
mask_file = _mask_finder(file, config, logger)
# First, try to find this image's mask from fMRIPrep.
mask_file = fmriprep_mask_finder(file, config, logger)
except MaskFileNotFoundError:
if config.roi_extraction.require_mask:
# If a mask is required, return here due to missing mask.
Expand All @@ -306,7 +308,7 @@ def fmri_roi_extract_image(
"Unable to find a mask for this image. Extracting ROIs without using brain mask."
)
ROI_ts = _fmri_roi_extract_image(
file, atlas_path, atlas_type, radius, overlap_ok, logger
file, atlas_path, atlas_type, sphere_radius, overlap_ok, logger
)
else:
try:
Expand All @@ -316,7 +318,7 @@ def fmri_roi_extract_image(
file,
atlas_path,
atlas_type,
radius,
sphere_radius,
overlap_ok,
logger,
mask=mask_file,
Expand All @@ -326,12 +328,12 @@ def fmri_roi_extract_image(
logger.warning(ve.__str__() + ". Extracting ROIs without using brain mask.")
logger.info("Starting non-masked ROI extraction...")
ROI_ts = _fmri_roi_extract_image(
file, atlas_path, atlas_type, radius, overlap_ok, logger
file, atlas_path, atlas_type, sphere_radius, overlap_ok, logger
)

temp_mask = concat_imgs([mask_file, mask_file])
mask_ROIs = _fmri_roi_extract_image(
temp_mask, atlas_path, atlas_type, radius, overlap_ok, logger
temp_mask, atlas_path, atlas_type, sphere_radius, overlap_ok, logger
)
mask_ROIs = np.nan_to_num(mask_ROIs)
logger.debug(mask_ROIs[0])
Expand Down Expand Up @@ -370,7 +372,7 @@ def fmri_roi_extract_image(


def _fmri_roi_extract_image(
data, atlas_path, atlas_type, radius, overlap_ok, logger, mask=None
data, atlas_path, atlas_type, sphere_radius, overlap_ok, logger, mask=None
):
if "label" in atlas_type:
logger.info("Extract type: label")
Expand All @@ -379,9 +381,9 @@ def _fmri_roi_extract_image(
if "sphere" in atlas_type:
atlas_path = np.loadtxt(atlas_path)
logger.info("Extract type: sphere")
logger.info(f"Sphere radius: {radius}mm")
logger.info(f"Sphere radius: {sphere_radius}mm")
spheres_masker = NiftiSpheresMasker(
atlas_path, float(radius), mask_img=mask, allow_overlap=overlap_ok
atlas_path, float(sphere_radius), mask_img=mask, allow_overlap=overlap_ok
)
timeseries = spheres_masker.fit_transform(data)
if "maps" in atlas_type:
Expand All @@ -406,22 +408,22 @@ def get_available_atlases():
print("")


def _mask_finder(data, config: ProjectOptions, logger):
def fmriprep_mask_finder(image_path, config: ProjectOptions, logger) -> os.PathLike:
"""Search for a mask in the fmriprep output directory matching
the name of the image targeted for roi_extraction"""

_, _, _, front_matter, type, path = _file_folder_generator(
os.path.basename(data),
os.path.basename(image_path),
"func",
target_suffix=config.roi_extraction.target_suffix,
)
logger.debug(f"Target suffix: {config.roi_extraction.target_suffix}")
logger.debug(
f"Image components (front_matter, type, path): {front_matter, type, path}"
)
logger.debug(f'Target suffix: {config.roi_extraction.target_suffix}')
logger.debug(f"Image components (front_matter, type, path): {front_matter, type, path}")

fmriprep_dir = resolve_fmriprep_dir(config.roi_extraction.target_directory)
logger.debug(f"fMRIPrep dir: {fmriprep_dir}")
fmriprep_dir = resolve_fmriprep_dir(
config.postprocessing.target_directory
)
logger.debug(f"Searching for mask in fMRIPrep dir: {fmriprep_dir}")

target_mask = os.path.join(
fmriprep_dir, path + "_" + type + "_desc-brain_mask.nii.gz"
Expand All @@ -439,9 +441,9 @@ def _mask_finder(data, config: ProjectOptions, logger):


def setup_dirs(config: ProjectOptions):
os.makedirs(
Path(config.roi_extraction.output_directory) / "postproc_default", exist_ok=True
)
"""Setup the directories necessary for ROI extraction's output."""

os.makedirs(config.roi_extraction.output_directory, exist_ok=True)
os.makedirs(config.roi_extraction.log_directory, exist_ok=True)


Expand Down
27 changes: 27 additions & 0 deletions tests/test_roi_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from clpipe.roi_extractor import (
fmri_roi_extraction,
fmri_roi_extract_image,
fmriprep_mask_finder,
STEP_NAME,
)
from clpipe.utils import get_logger
Expand Down Expand Up @@ -68,3 +69,29 @@ def test_fmri_roi_extract_image(clpipe_postproc_dir, artifact_dir, request, help
True,
logger,
)

def test_fmriprep_mask_finder(clpipe_postproc_dir):
"""Ensure that this function finds the correct fMRIPrep mask given
a specific postprocessing image."""

logger = get_logger(STEP_NAME, debug=True, log_dir=clpipe_postproc_dir / "logs")

image_name = "sub-1_task-gonogo_space-MNI152NLin2009cAsym_desc-postproc_bold.nii.gz"
image_path = (
clpipe_postproc_dir
/ "data_postproc/default/sub-1/func" / image_name
)
config_file_path = clpipe_postproc_dir / "clpipe_config.json"

config: ProjectOptions = ProjectOptions.load(config_file_path)

matching_mask = fmriprep_mask_finder(
image_path=image_path,
config=config,
logger=logger
)

assert Path(matching_mask).name == "sub-1_task-gonogo_space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"



0 comments on commit 66442e9

Please sign in to comment.