From 4c538177c5e565b6f24307aaaef2993af5937ed8 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Mon, 25 Nov 2024 11:58:19 -0500 Subject: [PATCH 01/63] fibertube density + endpoint scoring. Lacks demo update + tests --- scilpy/tractanalysis/fibertube_scoring.py | 290 ++++++++++----------- scripts/scil_fibertube_density.py | 119 +++++++++ scripts/scil_fibertube_score_tractogram.py | 46 ++-- 3 files changed, 280 insertions(+), 175 deletions(-) create mode 100755 scripts/scil_fibertube_density.py diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index 8a9f77bb6..f6378a56d 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -1,4 +1,5 @@ import numpy as np +import nibabel as nib from numba import objmode from math import sqrt, acos @@ -14,59 +15,118 @@ from scilpy.tractanalysis.todi import TrackOrientationDensityImaging -def mean_fibertube_density(sft): +def fibertube_density(sft, samples_per_voxel_axis, verbose=False): """ - Estimates the average per-voxel spatial density of a set of fibertubes. - This is obtained by dividing the volume of fibertube segments present - each voxel by the the total volume of the voxel. + Estimates the per-voxel spatial density of a set of fibertubes. In other + words, how much space is occupied by fibertubes and how much is emptiness. + + Works by building a binary mask segmenting voxels that contain at least + a single fibertube. Then, valid voxels are finely sampled and we count the + number of samples that landed within a fibertube. For each voxel, this + number is then divided by its total amount of samples. Parameters ---------- sft: StatefulTractogram Stateful Tractogram object containing the fibertubes. + samples_per_voxel_axis: int + Number of samples to be created along a single axis of a voxel. The + total number of samples in the voxel will be this number cubed. + verbose: bool + Whether the function and sub-functions should be verbose. Returns ------- - mean_density: float - Per-voxel spatial density, averaged for the whole tractogram. + density_3D: ndarray + Per-voxel spatial density of fibertubes as a 3D image. + density_flat: list + Per-voxel spatial density of fibertubes as a list, containing only + the voxels that were valid in the binary mask (i.e. that contained + fibertubes). This is useful for calculating measurements on the + various density values, like mean, median, etc. """ + if "diameters" not in sft.data_per_streamline: + raise ValueError('No diameter found as data_per_streamline ' + 'in the provided tractogram') diameters = np.reshape(sft.data_per_streamline['diameters'], len(sft.streamlines)) - mean_diameter = np.mean(diameters) - - mean_segment_lengths = [] - for streamline in sft.streamlines: - mean_segment_lengths.append( - np.mean(np.linalg.norm(streamline[1:] - streamline[:-1], axis=-1))) - mean_segment_length = np.mean(mean_segment_lengths) - # Computing mean tube density per voxel. + max_diameter = np.max(diameters) + + # Everything will be in vox for simplicity. sft.to_vox() + # Building a binary mask using TODI # Because compute_todi expects streamline points (in voxel coordinates) # to be in the range [0, size] rather than [-0.5, size - 0.5], we shift # the voxel origin to corner. sft.to_corner() - - # Computing TDI _, data_shape, _, _ = sft.space_attributes todi_obj = TrackOrientationDensityImaging(tuple(data_shape)) - todi_obj.compute_todi(sft.streamlines) - img = todi_obj.get_tdi() - img = todi_obj.reshape_to_3d(img) - - nb_voxels_nonzero = np.count_nonzero(img) - sum = np.sum(img, axis=-1) - sum = np.sum(sum, axis=-1) - sum = np.sum(sum, axis=-1) - - mean_seg_volume = np.pi * ((mean_diameter/2) ** 2) * mean_segment_length - - mean_seg_count = sum / nb_voxels_nonzero - mean_volume = mean_seg_count * mean_seg_volume - mean_density = mean_volume / (sft.voxel_sizes[0] * - sft.voxel_sizes[1] * - sft.voxel_sizes[2]) - - return mean_density + todi_obj.compute_todi(sft.streamlines, False) + mask = todi_obj.get_mask() + mask = todi_obj.reshape_to_3d(mask) + + sampling_density = np.array([samples_per_voxel_axis, + samples_per_voxel_axis, + samples_per_voxel_axis]) + + # Source: dipy.tracking.seeds_from_mask + # Grid of points between -.5 and .5, centered at 0, with given density + grid = np.mgrid[0: sampling_density[0], 0: sampling_density[1], + 0: sampling_density[2]] + grid = grid.T.reshape((-1, 3)) + grid = grid / sampling_density + grid += 0.5 / sampling_density - 0.5 + grid = grid.reshape(*sampling_density, 3) + + # Back to corner origin + grid += 0.5 + + # Add samples to each voxel in mask + samples = np.empty(mask.shape, dtype=object) + for i, j, k in np.ndindex(mask.shape): + if mask[i][j][k]: + samples[i][j][k] = [i, j, k] + grid + + # Building KDTree from fibertube segments + centers, indices, max_seg_length = streamlines_to_segments( + sft.streamlines, verbose) + tree = KDTree(centers) + + density_grid = np.zeros(mask.shape) + density_flat = [] + # For each voxel, get density + for i, j, k in np.ndindex(mask.shape): + if not mask[i][j][k]: + continue + + voxel_samples = np.reshape(samples[i][j][k], (-1, 3)) + + # Returns an list of lists of neighbor indexes. + # Ex: [[265, 45, 0, 1231], [12, 67]] + all_segments = tree.query_ball_point(voxel_samples, + max_seg_length/2+max_diameter/2, + workers=-1) + + nb_samples_in_fibertubes = 0 + for index, segments in enumerate(all_segments): + sample = voxel_samples[index] + for segi in segments: + fi = indices[segi][0] + pi = indices[segi][1] + centerline = sft.streamlines[fi] + radius = diameters[fi] / 2 + + dist, _, p = dist_point_segment(centerline[pi], + centerline[pi+1], + np.float32(sample)) + + if dist < radius: + nb_samples_in_fibertubes += 1 + + density_grid[i][j][k] = nb_samples_in_fibertubes / len(voxel_samples) + density_flat.append(density_grid[i][j][k]) + + return density_grid, density_flat def min_external_distance(sft, verbose): @@ -375,24 +435,25 @@ def mean_reconstruction_error(centerlines, centerlines_length, diameters, @njit -def endpoint_connectivity(step_size, blur_radius, centerlines, - centerlines_length, diameters, streamlines, - seeds_fiber): +def endpoint_connectivity(blur_radius, centerlines, centerlines_length, + diameters, streamlines, seeds_fiber): """ For every streamline, find whether or not it has reached the end segment - of its fiber. + of its fibertube. Each streamline is associated with an "Arrival fibertube + segment", which is the closest fibertube segment to its before-last + coordinate. + + VC: "Valid Connection": A streamline whose arrival fibertube segment is + the final segment of the fibertube in which is was originally seeded. - VC: "Valid Connection": Contains streamlines that ended in the final - segment of the fiber in which they have been seeded. - IC: "Invalid Connection": Contains streamlines that ended in the final - segment of another fiber. - NC: "No Connection": Contains streamlines that have not ended in the final - segment of any fiber. + IC: "Invalid Connection": A streamline whose arrival fibertube segment is + the start or final segment of a fibertube in which is was not seeded. + + NC: "No Connection": A streamline whose arrival fibertube segment is + not the start or final segment of any fibertube. Parameters ---------- - step_size: float - Step_size used during fibertube tracking. blur_radius: float Blur radius used during fibertube tracking. centerlines: ndarray @@ -414,20 +475,16 @@ def endpoint_connectivity(step_size, blur_radius, centerlines, Return ------ - truth_vc: list - Connections that are valid at ground-truth resolution. - truth_ic: list - Connections that are invalid at ground-truth resolution. - truth_nc: list - No-connections at ground-truth resolution. - resolution_vc: list - Connections that are valid at simulated resolution. - resolution_ic: list - Connections that are invalid at simulated resolution. - resolution_nc: list - No-connections at simulated resolution. + vc: list + List containing the indices of all streamlines that are valid + connections. + ic: list + List containing the indices of all streamlines that are invalid + connections. + nc: list + List containing the indices of all streamlines that are no + connections. """ - ratio = blur_radius / step_size max_diameter = np.max(diameters) # objmode allows the execution of non numba-compatible code within a numba @@ -437,105 +494,48 @@ def endpoint_connectivity(step_size, blur_radius, centerlines, centers, indices, max_seg_length = streamlines_to_segments( centerlines, False) - centerline_fixed_length = len(centerlines[0])-1 - - # Building KDTree with centerline segments - kdtree_centers = np.zeros((0, 3)) - for fi, fiber in enumerate(centerlines): - kdtree_centers = np.concatenate( - (kdtree_centers, centers[centerline_fixed_length * fi: - (centerline_fixed_length * fi + centerlines_length[fi] - 1)])) - tree = nbKDTree(kdtree_centers) + tree = nbKDTree(centers) - truth_vc = set() - truth_ic = set() - truth_nc = set() - res_vc = set() - res_ic = set() - res_nc = set() + vc = set() + ic = set() + nc = set() for si, streamline in enumerate(streamlines): - truth_connected = False - res_connected = False - seed_fi = seeds_fiber[si] + # streamline[1] is the last point with a valid direction neighbors = tree.query_radius( streamline[1], blur_radius + max_seg_length / 2 + max_diameter)[0] - # Checking VC and Res_VC first - for neighbor_segi in neighbors: - fi = indices[neighbor_segi][0] - if fi != seed_fi: - continue + min_dist = np.inf + min_seg = 0 - fiber = centerlines[fi] - fib_end_pt1 = fiber[centerlines_length[fi] - 2] - fib_end_pt2 = fiber[centerlines_length[fi] - 1] - radius = diameters[fi] / 2 - - # Connectivity - # Is in end segment of our fibertube - dist, _, _, _ = dist_segment_segment( - fib_end_pt1, fib_end_pt2, streamline[int(np.floor(ratio))], - streamline[int(np.ceil(ratio))+1]) - if dist < radius: - truth_connected = True - truth_vc.add(si) - - # Resolution-wise connectivity - # Passes by end segment of our fibertube - dist, _, _, _ = dist_segment_segment(fib_end_pt1, fib_end_pt2, - streamline[1], streamline[0]) - if dist < radius + blur_radius: - res_connected = True - res_vc.add(si) - - # If not VC we check IC/NC and if not Res_VC, we check Res_IC/Res_NC + # Finding closest segment + # There will always be a neighbor to override np.inf for neighbor_segi in neighbors: fi = indices[neighbor_segi][0] - if fi == seed_fi: - continue - - fiber = centerlines[fi] - fib_end_pt1 = fiber[centerlines_length[fi] - 2] - fib_end_pt2 = fiber[centerlines_length[fi] - 1] - radius = diameters[fi] / 2 + pi = indices[neighbor_segi][1] - is_vc = len(truth_vc.intersection({si})) != 0 - is_res_vc = len(res_vc.intersection({si})) != 0 + dist, _, _ = dist_point_segment(centerlines[fi][pi], + centerlines[fi][pi+1], + streamline[1]) - # Connectivity - # Is in start or end segment of a fibertube which is not ours - start_dist, _, _, _ = dist_segment_segment( - fiber[0], fiber[1], streamline[int(np.floor(ratio))], - streamline[int(np.ceil(ratio))+1]) + if dist < min_dist: + min_dist = dist + min_seg = neighbor_segi - end_dist, _, _, _ = dist_segment_segment( - fib_end_pt1, fib_end_pt2, streamline[int(np.floor(ratio))], - streamline[int(np.ceil(ratio))+1]) + fi = indices[min_seg][0] + pi = indices[min_seg][1] - if not is_vc and (start_dist < radius or end_dist < radius): - truth_connected = True - truth_ic.add(si) - - # Resolution-wise connectivity - # Passes by start or end segment of a fibertube which is not ours - start_dist, _, _, _ = dist_segment_segment( - fiber[0], fiber[1], streamline[1], streamline[0]) - - end_dist, _, _, _ = dist_segment_segment( - fib_end_pt1, fib_end_pt2, streamline[1], streamline[0]) - - if not is_res_vc and (start_dist < radius + blur_radius or - end_dist < radius + blur_radius): - res_connected = True - res_ic.add(si) - - if not truth_connected: - truth_nc.add(si) - if not res_connected: - res_nc.add(si) + # If the closest segment is the last of its centerlines + if pi == centerlines_length[fi]-1: + if fi == seed_fi: + vc.add(si) + else: + ic.add(si) + elif pi == 0: + ic.add(si) + else: + nc.add(si) - return (list(truth_vc), list(truth_ic), list(truth_nc), list(res_vc), - list(res_ic), list(res_nc)) + return (list(vc), list(ic), list(nc)) diff --git a/scripts/scil_fibertube_density.py b/scripts/scil_fibertube_density.py new file mode 100755 index 000000000..c4bf4f4be --- /dev/null +++ b/scripts/scil_fibertube_density.py @@ -0,0 +1,119 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Estimates the per-voxel spatial density of a set of fibertubes. In other +words, how much space is occupied by fibertubes and how much is emptiness. + +Works by building a binary mask segmenting voxels that contain at least +a single fibertube. Then, valid voxels are finely sampled and we count the +number of samples that landed within a fibertube. For each voxel, this +number is then divided by its total amount of samples. + +See also: + - docs/source/documentation/fibertube_tracking.rst +""" + +import os +import json +import nibabel as nib +import argparse +import logging +import numpy as np + +from scilpy.io.streamlines import load_tractogram_with_reference +from scilpy.tractanalysis.fibertube_scoring import fibertube_density +from scilpy.io.utils import (assert_inputs_exist, + assert_outputs_exist, + add_overwrite_arg, + add_verbose_arg, + add_json_args) + + +def _build_arg_parser(): + p = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + description=__doc__) + + p.add_argument('in_fibertubes', + help='Path to the tractogram (must be .trk) file \n' + 'containing fibertubes. They must be: \n' + '1- Void of any collision. \n' + '2- With their respective diameter saved \n' + 'as data_per_streamline. \n' + 'For both of these requirements, see \n' + 'scil_tractogram_filter_collisions.py.') + + p.add_argument('out_density', type=str, + help='Path of the output density image file') + + p.add_argument('--out_density_measures', default=None, type=str, + help='Path of the output file containing central \n' + 'tendency measures. (Must be .json)') + + add_overwrite_arg(p) + add_verbose_arg(p) + add_json_args(p) + + return p + + +def main(): + parser = _build_arg_parser() + args = parser.parse_args() + + logging.getLogger().setLevel(logging.getLevelName(args.verbose)) + logging.getLogger('numba').setLevel(logging.WARNING) + + if os.path.splitext(args.in_fibertubes)[1] != '.trk': + parser.error('Invalid input streamline file format (must be trk):' + + '{0}'.format(args.in_fibertubes)) + + if args.out_density_measures: + if os.path.splitext(args.out_density_measures)[1] != '.json': + parser.error('Invalid output file format (must be json): {0}' + .format(args.out_density_measures)) + + assert_inputs_exist(parser, args.in_fibertubes) + assert_outputs_exist(parser, args, [args.out_density], + [args.out_density_measures]) + + logging.debug('Loading tractogram & diameters') + sft = load_tractogram_with_reference(parser, args, args.in_fibertubes) + sft.to_voxmm() + sft.to_center() + + if "diameters" not in sft.data_per_streamline: + parser.error('No diameters found as data per streamline on ' + + args.in_fibertubes) + + logging.debug('Computing fibertube density') + density_3D, density_flat = fibertube_density(sft, 10, + args.verbose == 'WARNING') + + logging.debug('Saving density image') + header = nib.nifti1.Nifti1Header() + extra = { + 'affine': sft.affine, + 'dimensions': sft.dimensions, + 'voxel_size': sft.voxel_sizes[0], + 'voxel_order': "RAS" + } + density_img = nib.nifti1.Nifti1Image(density_3D, sft.affine, header, + extra) + nib.save(density_img, args.out_density) + + if args.out_density_measures: + density_measures = { + 'mean': np.mean(density_flat), + 'median': np.median(density_flat), + 'max': np.max(density_flat), + 'min': np.min(density_flat), + } + with open(args.out_density_measures, 'w') as file: + json.dump(density_measures, file, indent=args.indent, + sort_keys=args.sort_keys) + + +if __name__ == "__main__": + main() diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index 4c1cfec45..a44ab1d47 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -6,22 +6,18 @@ tracking, computes metrics about the quality of individual fiber reconstruction. -VC: "Valid Connection": A streamline that ended within the final segment - of the fibertube in which it was seeded. -IC: "Invalid Connection": A streamline that ended in the first or final - segment of another fibertube. -NC: "No Connection": A streamline that has not ended in the first or final - segment of any fibertube. - -Res_VC: "Resolution-wise Valid Connection": A streamline that passes closer - than [blur_darius] away from the last segment of the fibertube in which it - was seeded. -Res_IC: "Resolution-wise Invalid Connection": A streamline that passes closer - than [blur_darius] away from the first or last segment of another - fibertube. -Res_NC: "Resolution-wise No Connection": A streamlines that does not pass - closer than [blur_radius] away from the first or last segment of any - fibertube. +Each streamline is associated with an "Arrival fibertube segment", which is +the closest fibertube segment to its before-last coordinate. We then define +the following terms: + +VC: "Valid Connection": A streamline whose arrival fibertube segment is +the final segment of the fibertube in which is was originally seeded. + +IC: "Invalid Connection": A streamline whose arrival fibertube segment is +the start or final segment of a fibertube in which is was not seeded. + +NC: "No Connection": A streamline whose arrival fibertube segment is +not the start or final segment of any fibertube. The "absolute error" of a coordinate is the distance in mm between that coordinate and the closest point on its corresponding fibertube. The average @@ -35,12 +31,6 @@ Number of IC divided by the number of streamlines. - nc_ratio Number of NC divided by the number of streamlines. - - res_vc_ratio - Number of Res_VC divided by the number of streamlines. - - res_ic_ratio - Number of Res_IC divided by the number of streamlines. - - res_nc_ratio - Number of Res_NC divided by the number of streamlines. - mae_min Minimum MAE for the tractogram. - mae_max @@ -220,8 +210,7 @@ def main(): bbox_valid_check=False) logging.debug("Computing endpoint connectivity") - (truth_vc, truth_ic, truth_nc, - res_vc, res_ic, res_nc) = endpoint_connectivity( + vc, ic, nc = endpoint_connectivity( step_size, blur_radius, centerlines, centerlines_length, diameters, streamlines, @@ -233,12 +222,9 @@ def main(): streamlines_length, seeds_fiber, args.save_error_tractogram) metrics = { - 'vc_ratio': len(truth_vc)/len(streamlines), - 'ic_ratio': len(truth_ic)/len(streamlines), - 'nc_ratio': len(truth_nc)/len(streamlines), - 'res_vc_ratio': len(res_vc)/len(streamlines), - 'res_ic_ratio': len(res_ic)/len(streamlines), - 'res_nc_ratio': len(res_nc)/len(streamlines), + 'vc_ratio': len(vc)/len(streamlines), + 'ic_ratio': len(ic)/len(streamlines), + 'nc_ratio': len(nc)/len(streamlines), 'mae_min': np.min(mean_errors), 'mae_max': np.max(mean_errors), 'mae_mean': np.mean(mean_errors), From 8573b2d0fceabec01a78c10e0c228cfa252afe26 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 28 Nov 2024 07:41:49 -0500 Subject: [PATCH 02/63] doc fix --- scripts/scil_fibertube_tracking.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/scil_fibertube_tracking.py b/scripts/scil_fibertube_tracking.py index b4817a15b..f14df22ab 100644 --- a/scripts/scil_fibertube_tracking.py +++ b/scripts/scil_fibertube_tracking.py @@ -93,10 +93,9 @@ def _build_arg_parser(): '[%(default)s]') track_g.add_argument( '--theta', type=float, default=60., - help='Maximum angle between 2 steps. If the angle is ' - 'too big, streamline is \nstopped and the ' - 'following point is NOT included.\n' - '[%(default)s]') + help='Maximum angle between 2 steps. If the angle is \n' + 'too big, streamline is stopped and the \n' + 'following point is NOT included. [%(default)s]') track_g.add_argument( '--rk_order', metavar="K", type=int, default=1, choices=[1, 2, 4], From 650ebac7fba84c3b899ce0ae3211e0672355e433 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 28 Nov 2024 14:59:59 -0500 Subject: [PATCH 03/63] Fix volumetric density and add collision density --- scilpy/tractanalysis/fibertube_scoring.py | 47 ++++++++++++---- scripts/scil_fibertube_density.py | 68 ++++++++++++++++++----- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index bff5967ba..d2eca4fae 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -17,7 +17,7 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): """ - Estimates the per-voxel spatial density of a set of fibertubes. In other + Estimates the per-voxel volumetric density of a set of fibertubes. In other words, how much space is occupied by fibertubes and how much is emptiness. Works by building a binary mask segmenting voxels that contain at least @@ -37,10 +37,17 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): Returns ------- - density_3D: ndarray - Per-voxel spatial density of fibertubes as a 3D image. + density_grid: ndarray + Per-voxel volumetric density of fibertubes as a 3D image. density_flat: list - Per-voxel spatial density of fibertubes as a list, containing only + Per-voxel volumetric density of fibertubes as a list, containing only + the voxels that were valid in the binary mask (i.e. that contained + fibertubes). This is useful for calculating measurements on the + various density values, like mean, median, etc. + collision_grid: ndarray + Per-voxel density of fibertube collisions. + collision_flat: list + Per-voxel density of fibertubes collisions as a list, containing only the voxels that were valid in the binary mask (i.e. that contained fibertubes). This is useful for calculating measurements on the various density values, like mean, median, etc. @@ -94,8 +101,11 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): density_grid = np.zeros(mask.shape) density_flat = [] + collision_grid = np.zeros(mask.shape) + collision_flat = [] # For each voxel, get density - for i, j, k in np.ndindex(mask.shape): + for i, j, k in tqdm_if_verbose(np.ndindex(mask.shape), verbose, + total=len(np.ravel(mask))): if not mask[i][j][k]: continue @@ -103,30 +113,43 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): # Returns an list of lists of neighbor indexes. # Ex: [[265, 45, 0, 1231], [12, 67]] - all_segments = tree.query_ball_point(voxel_samples, + all_sample_neighbors = tree.query_ball_point(voxel_samples, max_seg_length/2+max_diameter/2, workers=-1) nb_samples_in_fibertubes = 0 - for index, segments in enumerate(all_segments): - sample = voxel_samples[index] - for segi in segments: + # Set containing sets of fibertube indexes + # This way, each pair of fibertube is only entered once. + # This is unless there is a trio. Then the same colliding fibertubes + # may be entered more than once. + collisions = set() + for index, sample in enumerate(voxel_samples): + neigbors = all_sample_neighbors[index] + fibertubes_touching_sample = set() + for segi in neigbors: fi = indices[segi][0] pi = indices[segi][1] centerline = sft.streamlines[fi] radius = diameters[fi] / 2 - dist, _, p = dist_point_segment(centerline[pi], + dist, _, _ = dist_point_segment(centerline[pi], centerline[pi+1], np.float32(sample)) if dist < radius: - nb_samples_in_fibertubes += 1 + fibertubes_touching_sample.add(fi) + + if len(fibertubes_touching_sample) > 0: + nb_samples_in_fibertubes += 1 + if len(fibertubes_touching_sample) > 1: + collisions.add(frozenset(fibertubes_touching_sample)) density_grid[i][j][k] = nb_samples_in_fibertubes / len(voxel_samples) density_flat.append(density_grid[i][j][k]) + collision_grid[i][j][k] = len(collisions) / len(voxel_samples) + collision_flat.append(collision_grid[i][j][k]) - return density_grid, density_flat + return density_grid, density_flat, collision_grid, collision_flat def min_external_distance(sft, verbose): diff --git a/scripts/scil_fibertube_density.py b/scripts/scil_fibertube_density.py index c4bf4f4be..59375b223 100755 --- a/scripts/scil_fibertube_density.py +++ b/scripts/scil_fibertube_density.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Estimates the per-voxel spatial density of a set of fibertubes. In other +Estimates the per-voxel volumetric density of a set of fibertubes. In other words, how much space is occupied by fibertubes and how much is emptiness. Works by building a binary mask segmenting voxels that contain at least @@ -21,7 +21,7 @@ import logging import numpy as np -from scilpy.io.streamlines import load_tractogram_with_reference +from scilpy.io.streamlines import load_tractogram from scilpy.tractanalysis.fibertube_scoring import fibertube_density from scilpy.io.utils import (assert_inputs_exist, assert_outputs_exist, @@ -44,13 +44,22 @@ def _build_arg_parser(): 'For both of these requirements, see \n' 'scil_tractogram_filter_collisions.py.') - p.add_argument('out_density', type=str, - help='Path of the output density image file') + p.add_argument('--out_density_map', default=None, type=str, + help='Path of the output volumetric density image file') p.add_argument('--out_density_measures', default=None, type=str, help='Path of the output file containing central \n' - 'tendency measures. (Must be .json)') + 'tendency measures about volumetric density. \n' + '(Must be .json)') + p.add_argument('--out_collision_map', default=None, type=str, + help='Path of the output collision density image file') + + p.add_argument('--out_collision_measures', default=None, type=str, + help='Path of the output file containing central \n' + 'tendency measures about collision density. \n' + '(Must be .json)') + add_overwrite_arg(p) add_verbose_arg(p) add_json_args(p) @@ -65,6 +74,10 @@ def main(): logging.getLogger().setLevel(logging.getLevelName(args.verbose)) logging.getLogger('numba').setLevel(logging.WARNING) + if not (args.out_density_map or args.out_density_measures or + args.out_collision_map or args.out_collision_measures): + parser.error('No output argument given') + if os.path.splitext(args.in_fibertubes)[1] != '.trk': parser.error('Invalid input streamline file format (must be trk):' + '{0}'.format(args.in_fibertubes)) @@ -74,12 +87,18 @@ def main(): parser.error('Invalid output file format (must be json): {0}' .format(args.out_density_measures)) + if args.out_collision_measures: + if os.path.splitext(args.out_collision_measures)[1] != '.json': + parser.error('Invalid output file format (must be json): {0}' + .format(args.out_collision_measures)) + assert_inputs_exist(parser, args.in_fibertubes) - assert_outputs_exist(parser, args, [args.out_density], - [args.out_density_measures]) + assert_outputs_exist(parser, args, [], + [args.out_density_map, args.out_density_measures, + args.out_collision_map, args.out_collision_measures]) logging.debug('Loading tractogram & diameters') - sft = load_tractogram_with_reference(parser, args, args.in_fibertubes) + sft = load_tractogram(args.in_fibertubes, 'same') sft.to_voxmm() sft.to_center() @@ -88,10 +107,13 @@ def main(): args.in_fibertubes) logging.debug('Computing fibertube density') - density_3D, density_flat = fibertube_density(sft, 10, - args.verbose == 'WARNING') + (density_grid, + density_flat, + collision_grid, + collision_flat) = fibertube_density(sft, 10, + args.verbose == 'WARNING') - logging.debug('Saving density image') + logging.debug('Saving output') header = nib.nifti1.Nifti1Header() extra = { 'affine': sft.affine, @@ -99,9 +121,11 @@ def main(): 'voxel_size': sft.voxel_sizes[0], 'voxel_order': "RAS" } - density_img = nib.nifti1.Nifti1Image(density_3D, sft.affine, header, - extra) - nib.save(density_img, args.out_density) + + if args.out_density_map: + density_img = nib.nifti1.Nifti1Image(density_grid, sft.affine, header, + extra) + nib.save(density_img, args.out_density_map) if args.out_density_measures: density_measures = { @@ -114,6 +138,22 @@ def main(): json.dump(density_measures, file, indent=args.indent, sort_keys=args.sort_keys) + if args.out_collision_map: + collision_img = nib.nifti1.Nifti1Image(collision_grid, sft.affine, header, + extra) + nib.save(collision_img, args.out_collision_map) + + if args.out_collision_measures: + collision_measures = { + 'mean': np.mean(collision_flat), + 'median': np.median(collision_flat), + 'max': np.max(collision_flat), + 'min': np.min(collision_flat), + } + with open(args.out_collision_measures, 'w') as file: + json.dump(collision_measures, file, indent=args.indent, + sort_keys=args.sort_keys) + if __name__ == "__main__": main() From 7c85e00d748e7b86acee66c71913d1072294da9c Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 28 Nov 2024 15:51:55 -0500 Subject: [PATCH 04/63] Density pep8 and test --- scilpy/tractanalysis/fibertube_scoring.py | 7 +-- ...y.py => scil_fibertube_compute_density.py} | 8 +-- .../tests/test_fibertube_compute_density.py | 57 +++++++++++++++++++ 3 files changed, 64 insertions(+), 8 deletions(-) rename scripts/{scil_fibertube_density.py => scil_fibertube_compute_density.py} (97%) create mode 100644 scripts/tests/test_fibertube_compute_density.py diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index d2eca4fae..898fa062e 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -111,11 +111,10 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): voxel_samples = np.reshape(samples[i][j][k], (-1, 3)) - # Returns an list of lists of neighbor indexes. + # Returns an list of lists of neighbor indexes for each sample # Ex: [[265, 45, 0, 1231], [12, 67]] - all_sample_neighbors = tree.query_ball_point(voxel_samples, - max_seg_length/2+max_diameter/2, - workers=-1) + all_sample_neighbors = tree.query_ball_point( + voxel_samples, max_seg_length/2+max_diameter/2, workers=-1) nb_samples_in_fibertubes = 0 # Set containing sets of fibertube indexes diff --git a/scripts/scil_fibertube_density.py b/scripts/scil_fibertube_compute_density.py similarity index 97% rename from scripts/scil_fibertube_density.py rename to scripts/scil_fibertube_compute_density.py index 59375b223..0ee95d3fa 100755 --- a/scripts/scil_fibertube_density.py +++ b/scripts/scil_fibertube_compute_density.py @@ -59,7 +59,7 @@ def _build_arg_parser(): help='Path of the output file containing central \n' 'tendency measures about collision density. \n' '(Must be .json)') - + add_overwrite_arg(p) add_verbose_arg(p) add_json_args(p) @@ -111,7 +111,7 @@ def main(): density_flat, collision_grid, collision_flat) = fibertube_density(sft, 10, - args.verbose == 'WARNING') + args.verbose == 'WARNING') logging.debug('Saving output') header = nib.nifti1.Nifti1Header() @@ -139,8 +139,8 @@ def main(): sort_keys=args.sort_keys) if args.out_collision_map: - collision_img = nib.nifti1.Nifti1Image(collision_grid, sft.affine, header, - extra) + collision_img = nib.nifti1.Nifti1Image(collision_grid, sft.affine, + header, extra) nib.save(collision_img, args.out_collision_map) if args.out_collision_measures: diff --git a/scripts/tests/test_fibertube_compute_density.py b/scripts/tests/test_fibertube_compute_density.py new file mode 100644 index 000000000..60f822951 --- /dev/null +++ b/scripts/tests/test_fibertube_compute_density.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import json +import tempfile +import numpy as np +import nibabel as nib + +from scilpy.io.streamlines import save_tractogram +from dipy.io.stateful_tractogram import StatefulTractogram, Space, Origin + +tmp_dir = tempfile.TemporaryDirectory() + + +def init_data(): + streamlines = [[[5., 1., 5.], [5., 5., 9.], [7., 9., 9.], [13., 11., 9.], + [5., 7., 7.]], [[7., 7., 7.], [9., 9., 9.]]] + + mask = np.ones((15, 15, 15)) + affine = np.eye(4) + header = nib.nifti1.Nifti1Header() + extra = { + 'affine': affine, + 'dimensions': (15, 15, 15), + 'voxel_size': 1., + 'voxel_order': "RAS" + } + mask_img = nib.nifti2.Nifti1Image(mask, affine, header, extra) + + sft_fibertubes = StatefulTractogram(streamlines, mask_img, Space.VOX, + Origin.NIFTI) + sft_fibertubes.data_per_streamline = { + "diameters": [0.2, 0.01] + } + + save_tractogram(sft_fibertubes, 'fibertubes.trk', True) + + +def test_help_option(script_runner): + ret = script_runner.run('scil_fibertube_compute_density.py', '--help') + assert ret.success + + +def test_execution(script_runner, monkeypatch): + monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) + init_data() + ret = script_runner.run('scil_fibertube_compute_density.py', + 'fibertubes.trk', + '--out_density_map', 'density_map.nii.gz', + '--out_density_measures', + 'density_measures.json', + '--out_collision_map', 'collision_map.nii.gz', + '--out_collision_measures', + 'collision_measures.json', + '-f') + assert ret.success From 29c2a0f59ea8790609fb9c02eadc658a7d826580 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Fri, 29 Nov 2024 14:17:18 -0500 Subject: [PATCH 05/63] minifix --- scripts/scil_fibertube_score_tractogram.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index 1e471c2ca..18fef9926 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -187,7 +187,6 @@ def main(): logging.debug("Loading config") with open(args.in_config, 'r') as f: config = json.load(f) - step_size = float(config['step_size']) blur_radius = float(config['blur_radius']) if len(seeds_fiber) != len(streamlines): From 133d8704672e127f752b1e2cf6ceb2f8d4de94df Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Fri, 29 Nov 2024 14:53:10 -0500 Subject: [PATCH 06/63] remove density metric from collision filtering --- scripts/scil_tractogram_filter_collisions.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/scil_tractogram_filter_collisions.py b/scripts/scil_tractogram_filter_collisions.py index 72134c67d..76bb389ca 100644 --- a/scripts/scil_tractogram_filter_collisions.py +++ b/scripts/scil_tractogram_filter_collisions.py @@ -45,10 +45,6 @@ If the --out_metrics parameter is given, several metrics about the data will be computed (all expressed in mm): - - fibertube_density - Estimate of the following ratio: volume of fibertubes / total volume - where the total volume is the combined volume of all voxels containing - at least one fibertube. - min_external_distance Smallest distance separating two streamlines, outside their diameter. - max_voxel_anisotropic @@ -86,8 +82,7 @@ from dipy.io.stateful_tractogram import StatefulTractogram from dipy.io.streamline import save_tractogram from scilpy.io.streamlines import load_tractogram_with_reference -from scilpy.tractanalysis.fibertube_scoring import (mean_fibertube_density, - min_external_distance, +from scilpy.tractanalysis.fibertube_scoring import (min_external_distance, max_voxels, max_voxel_rotated) from scilpy.io.utils import (assert_inputs_exist, @@ -277,12 +272,8 @@ def main(): max_voxel_ani, max_voxel_iso = max_voxels(min_ext_dist_vect) mvr_rot, mvr_edge = max_voxel_rotated(min_ext_dist_vect) - # Fibertube density comes last, because it changes space and origin. - mean_density = mean_fibertube_density(out_sft) - if args.out_metrics: metrics = { - 'mean_density': mean_density, 'min_external_distance': min_ext_dist.tolist(), 'max_voxel_anisotropic': max_voxel_ani.tolist(), 'max_voxel_isotropic': max_voxel_iso.tolist(), From b8df66014758da21316c7e36a50421da5040335b Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Fri, 29 Nov 2024 15:20:42 -0500 Subject: [PATCH 07/63] Remove fibertube density from demo --- docs/source/documentation/fibertube_tracking.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/source/documentation/fibertube_tracking.rst b/docs/source/documentation/fibertube_tracking.rst index 4dbcc9921..79d7c88d2 100644 --- a/docs/source/documentation/fibertube_tracking.rst +++ b/docs/source/documentation/fibertube_tracking.rst @@ -147,10 +147,6 @@ Fibertube metrics Before we get into tracking. Here is an overview of the metrics that we saved in ``metrics.json``. (Values expressed in mm): -- ``fibertube_density``: - Estimate of the following ratio: volume of fibertubes / total volume - where the total volume is the combined volume of all voxels containing - at least one fibertube. - ``min_external_distance``: Smallest distance separating two fibertubes, outside their diameter. - ``max_voxel_anisotropic``: Diagonal vector of the largest possible From d99245d36d7e34598cfe4644143ca6fac749df24 Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 12:50:21 -0500 Subject: [PATCH 08/63] add version string --- scilpy/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scilpy/version.py b/scilpy/version.py index 9ebf80e4d..cf5d2e873 100644 --- a/scilpy/version.py +++ b/scilpy/version.py @@ -17,6 +17,7 @@ _ver.append(_version_extra) __version__ = '.'.join(map(str, _ver)) +version_string = "\nScilpy version: " + __version__ CLASSIFIERS = ["Development Status :: 3 - Alpha", "Environment :: Console", From 0334e7d6213d23956243a1f104d0b05d50d76885 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Thu, 12 Dec 2024 13:42:44 -0500 Subject: [PATCH 09/63] BF - fixed voxel bundary conditions in uncompress streamlines --- scilpy/tractograms/uncompress.pyx | 42 ++++++++----------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/scilpy/tractograms/uncompress.pyx b/scilpy/tractograms/uncompress.pyx index ab58cbad8..df2344037 100644 --- a/scilpy/tractograms/uncompress.pyx +++ b/scilpy/tractograms/uncompress.pyx @@ -239,36 +239,16 @@ cdef cnp.npy_intp _uncompress( direction_norm = norm(direction[0], direction[1], direction[2]) - # Make sure that the next point is not exactly on a voxel - # intersection or on the face of the voxel, since the behavior - # is not easy to define in this case. - if fabs(next_pt[0] - floor(next_pt[0])) < 1e-8 or\ - fabs(next_pt[1] - floor(next_pt[1])) < 1e-8 or\ - fabs(next_pt[2] - floor(next_pt[2])) < 1e-8: - # TODO in future, jitter edge or add thickness to deal with - # case where on an edge / face and the corresponding - # component of the direction is 0 - next_pt[0] = next_pt[0] - 0.000001 * direction[0] - next_pt[1] = next_pt[1] - 0.000001 * direction[1] - next_pt[2] = next_pt[2] - 0.000001 * direction[2] - - # Make sure we don't "underflow" the grid - if next_pt[0] < 0.0 or next_pt[1] < 0.0 or next_pt[2] < 0.0: - next_pt[0] = next_pt[0] + 0.000002 * direction[0] - next_pt[1] = next_pt[1] + 0.000002 * direction[1] - next_pt[2] = next_pt[2] + 0.000002 * direction[2] - - # Keep it in mind to correctly set when going back in the loop - jittered_point[0] = next_pt[0] - jittered_point[1] = next_pt[1] - jittered_point[2] = next_pt[2] - - # Update those - direction[0] = next_pt[0] - current_pt[0] - direction[1] = next_pt[1] - current_pt[1] - direction[2] = next_pt[2] - current_pt[2] - - direction_norm = norm(direction[0], direction[1], direction[2]) + # Shift coordiates if the are too close to the voxel boundaries + for i in range(3): + if fabs(next_pt[i] - floor(next_pt[i])) < 1e-8: + if next_pt[i] > 0.000001: + next_pt[i] = next_pt[i] - 0.000001 + else: + next_pt[i] = next_pt[i] + 0.000001 + jittered_point[i] = next_pt[i] + direction[i] = next_pt[i] - current_pt[i] + direction_norm = norm(direction[0], direction[1], direction[2]) # Set the "remaining_distance" var to compute remaining length of # vector to process @@ -295,7 +275,7 @@ cdef cnp.npy_intp _uncompress( # Check if last point is already on an edge remaining_distance -= length_ratio * direction_norm - if remaining_distance < 0.: + if remaining_distance <= 0.: pointers.points_to_index_out[0] = nb_points_out - 1 pointers.points_to_index_out += 1 nb_points_out_pti += 1 From 96421e4ba1e526229f3d92d4ef1543ec383805c8 Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 14:04:38 -0500 Subject: [PATCH 10/63] add version --- scripts/scil_aodf_metrics.py | 43 ++++++++++--------- scripts/scil_bids_validate.py | 8 ++-- scripts/scil_bingham_metrics.py | 28 ++++++------ scripts/scil_btensor_metrics.py | 4 +- scripts/scil_bundle_alter_to_target_dice.py | 5 ++- scripts/scil_bundle_clean_qbx_clusters.py | 4 +- scripts/scil_bundle_compute_centroid.py | 7 +-- scripts/scil_bundle_compute_endpoints_map.py | 7 +-- scripts/scil_bundle_diameter.py | 7 ++- scripts/scil_bundle_filter_by_occurence.py | 6 ++- scripts/scil_bundle_fixel_analysis.py | 7 ++- scripts/scil_bundle_generate_priors.py | 20 ++++----- scripts/scil_bundle_label_map.py | 7 +-- scripts/scil_bundle_mean_fixel_afd.py | 25 ++++++----- .../scil_bundle_mean_fixel_afd_from_hdf5.py | 25 +++++------ .../scil_bundle_mean_fixel_bingham_metric.py | 5 ++- scripts/scil_bundle_mean_fixel_mrds_metric.py | 5 ++- scripts/scil_bundle_mean_std.py | 9 ++-- scripts/scil_bundle_pairwise_comparison.py | 8 ++-- scripts/scil_bundle_reject_outliers.py | 5 ++- ...undle_score_many_bundles_one_tractogram.py | 7 +-- ...le_score_same_bundle_many_segmentations.py | 5 ++- scripts/scil_bundle_shape_measures.py | 18 ++++---- scripts/scil_bundle_uniformize_endpoints.py | 8 ++-- scripts/scil_bundle_volume_per_label.py | 4 +- .../scil_connectivity_compare_populations.py | 25 ++++++----- scripts/scil_connectivity_compute_matrices.py | 8 ++-- scripts/scil_connectivity_compute_pca.py | 38 ++++++++-------- ...scil_connectivity_compute_simple_matrix.py | 7 ++- scripts/scil_connectivity_filter.py | 7 +-- scripts/scil_connectivity_graph_measures.py | 16 ++++--- ...l_connectivity_hdf5_average_density_map.py | 6 ++- scripts/scil_connectivity_math.py | 7 +-- scripts/scil_connectivity_normalize.py | 7 +-- .../scil_connectivity_pairwise_agreement.py | 7 +-- scripts/scil_connectivity_print_filenames.py | 6 ++- scripts/scil_connectivity_reorder_rois.py | 19 ++++---- scripts/scil_denoising_nlmeans.py | 6 ++- scripts/scil_dki_metrics.py | 8 ++-- scripts/scil_dti_convert_tensors.py | 4 +- scripts/scil_dti_metrics.py | 7 +-- scripts/scil_dwi_apply_bias_field.py | 11 ++--- scripts/scil_dwi_compute_snr.py | 6 ++- scripts/scil_dwi_concatenate.py | 7 +-- scripts/scil_dwi_convert_FDF.py | 7 +-- scripts/scil_dwi_detect_volume_outliers.py | 7 +-- scripts/scil_dwi_extract_b0.py | 4 +- scripts/scil_dwi_extract_shell.py | 7 +-- scripts/scil_dwi_powder_average.py | 7 +-- scripts/scil_dwi_prepare_eddy_command.py | 8 +++- scripts/scil_dwi_prepare_topup_command.py | 5 ++- scripts/scil_dwi_reorder_philips.py | 4 +- scripts/scil_dwi_split_by_indices.py | 7 +-- scripts/scil_dwi_to_sh.py | 6 ++- scripts/scil_fibertube_score_tractogram.py | 7 +-- scripts/scil_fibertube_tracking.py | 7 +-- scripts/scil_fodf_max_in_ventricles.py | 20 +++++---- scripts/scil_fodf_memsmt.py | 4 +- scripts/scil_fodf_metrics.py | 6 ++- scripts/scil_fodf_msmt.py | 5 ++- scripts/scil_fodf_ssst.py | 6 ++- scripts/scil_fodf_to_bingham.py | 28 ++++++------ scripts/scil_freewater_maps.py | 2 +- 63 files changed, 365 insertions(+), 261 deletions(-) diff --git a/scripts/scil_aodf_metrics.py b/scripts/scil_aodf_metrics.py index a0546e4bd..717c85a22 100755 --- a/scripts/scil_aodf_metrics.py +++ b/scripts/scil_aodf_metrics.py @@ -23,7 +23,25 @@ given as the ratio of the L2-norm of odd SH coefficients on the L2-norm of all SH coefficients. + Formerly: scil_compute_asym_odf_metrics.py +------------------------------------------------------------------------ + +References: +[1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: +Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; +2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 + +[2] S. Cetin Karayumak, E. Özarslan, and G. Unal, +"Asymmetric Orientation Distribution Functions (AODFs) revealing intravoxel +geometry in diffusion MRI," Magnetic Resonance Imaging, vol. 49, pp. 145-158, +Jun. 2018, doi: https://doi.org/10.1016/j.mri.2018.03.006. + +[3] C. Poirier, E. St-Onge, and M. Descoteaux, "Investigating the Occurence of +Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" +[Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, +Vancouver, BC, Abstract number 0865. +------------------------------------------------------------------------ """ @@ -44,29 +62,14 @@ assert_headers_compatible, assert_outputs_exist, add_overwrite_arg, parse_sh_basis_arg) from scilpy.io.image import get_data_as_mask - - -EPILOG = """ -References: -[1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: -Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; -2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 - -[2] S. Cetin Karayumak, E. Özarslan, and G. Unal, -"Asymmetric Orientation Distribution Functions (AODFs) revealing intravoxel -geometry in diffusion MRI," Magnetic Resonance Imaging, vol. 49, pp. 145-158, -Jun. 2018, doi: https://doi.org/10.1016/j.mri.2018.03.006. - -[3] C. Poirier, E. St-Onge, and M. Descoteaux, "Investigating the Occurence of -Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" -[Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, -Vancouver, BC, Abstract number 0865. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_sh', help='Input SH image.') p.add_argument('--mask', diff --git a/scripts/scil_bids_validate.py b/scripts/scil_bids_validate.py index 375ae4699..ea76428ee 100755 --- a/scripts/scil_bids_validate.py +++ b/scripts/scil_bids_validate.py @@ -34,7 +34,7 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) - +from scilpy.version import version_string conversion = {"i": "x", "i-": "x-", @@ -49,9 +49,9 @@ def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument("in_bids", help="Input BIDS folder.") diff --git a/scripts/scil_bingham_metrics.py b/scripts/scil_bingham_metrics.py index 68f938256..f0501fed1 100755 --- a/scripts/scil_bingham_metrics.py +++ b/scripts/scil_bingham_metrics.py @@ -17,6 +17,18 @@ with 1mm isotropic resolution. Other metrics take less than a second. Formerly: scil_compute_lobe_specific_fodf_metrics.py +------------------------------------------------------------------------------ +References: +[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond + fractional anisotropy: Extraction of bundle-specific structural metrics + from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, + doi: 10.1016/j.neuroimage.2014.06.015. + +[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility + Tracking: A method to evaluate anatomical connectivity and microstructural + properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. + 2014, doi: 10.1016/j.neuroimage.2014.01.002. +------------------------------------------------------------------------------ """ import nibabel as nib @@ -33,24 +45,14 @@ compute_fiber_spread, compute_fiber_fraction) - -EPILOG = """ -[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond - fractional anisotropy: Extraction of bundle-specific structural metrics - from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, - doi: 10.1016/j.neuroimage.2014.06.015. - -[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility - Tracking: A method to evaluate anatomical connectivity and microstructural - properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. - 2014, doi: 10.1016/j.neuroimage.2014.01.002. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) + p.add_argument('in_bingham', help='Input Bingham nifti image.') diff --git a/scripts/scil_btensor_metrics.py b/scripts/scil_btensor_metrics.py index 2bbf08823..92636dfd0 100755 --- a/scripts/scil_btensor_metrics.py +++ b/scripts/scil_btensor_metrics.py @@ -54,11 +54,13 @@ add_verbose_arg, add_skip_b0_check_arg, add_tolerance_arg, assert_headers_compatible) from scilpy.reconst.divide import fit_gamma, gamma_fit2metrics +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('--in_dwis', nargs='+', required=True, help='Path to the input diffusion volume for each ' diff --git a/scripts/scil_bundle_alter_to_target_dice.py b/scripts/scil_bundle_alter_to_target_dice.py index c8523d294..dc48819c1 100755 --- a/scripts/scil_bundle_alter_to_target_dice.py +++ b/scripts/scil_bundle_alter_to_target_dice.py @@ -52,11 +52,14 @@ from scilpy.tractograms.tractogram_operations import transform_streamlines_alter, \ trim_streamlines_alter, cut_streamlines_alter, subsample_streamlines_alter, \ replace_streamlines_alter, shuffle_streamlines, shuffle_streamlines_orientation +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input bundle filename (.trk, .tck).') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_clean_qbx_clusters.py b/scripts/scil_bundle_clean_qbx_clusters.py index e9bced44a..07127e280 100755 --- a/scripts/scil_bundle_clean_qbx_clusters.py +++ b/scripts/scil_bundle_clean_qbx_clusters.py @@ -34,11 +34,13 @@ assert_outputs_exist, assert_output_dirs_exist_and_empty, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='List of the clusters filename.') diff --git a/scripts/scil_bundle_compute_centroid.py b/scripts/scil_bundle_compute_centroid.py index d4b726efa..06eefcefd 100755 --- a/scripts/scil_bundle_compute_centroid.py +++ b/scripts/scil_bundle_compute_centroid.py @@ -20,12 +20,13 @@ add_verbose_arg, add_reference_arg) from scilpy.tractanalysis.bundle_operations import get_streamlines_centroid +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle file.') diff --git a/scripts/scil_bundle_compute_endpoints_map.py b/scripts/scil_bundle_compute_endpoints_map.py index c2ea551d2..ca393ad36 100755 --- a/scripts/scil_bundle_compute_endpoints_map.py +++ b/scripts/scil_bundle_compute_endpoints_map.py @@ -32,12 +32,13 @@ assert_outputs_exist) from scilpy.tractograms.streamline_and_mask_operations import \ get_head_tail_density_maps +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle filename.') diff --git a/scripts/scil_bundle_diameter.py b/scripts/scil_bundle_diameter.py index 65b0afdaf..0df1afe38 100755 --- a/scripts/scil_bundle_diameter.py +++ b/scripts/scil_bundle_diameter.py @@ -53,11 +53,14 @@ from scilpy.viz.backends.vtk import create_tube_with_radii from scilpy.viz.color import get_lookup_table from scilpy.viz.screenshot import compose_image +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='List of tractography files.') p.add_argument('in_labels', nargs='+', diff --git a/scripts/scil_bundle_filter_by_occurence.py b/scripts/scil_bundle_filter_by_occurence.py index deb54a351..8e0b910c2 100755 --- a/scripts/scil_bundle_filter_by_occurence.py +++ b/scripts/scil_bundle_filter_by_occurence.py @@ -31,11 +31,13 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractograms.tractogram_operations import ( intersection_robust, union_robust) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='Input bundles filename(s). All tractograms must have ' diff --git a/scripts/scil_bundle_fixel_analysis.py b/scripts/scil_bundle_fixel_analysis.py index a27613004..fb9bd0531 100755 --- a/scripts/scil_bundle_fixel_analysis.py +++ b/scripts/scil_bundle_fixel_analysis.py @@ -106,11 +106,14 @@ add_verbose_arg, assert_output_dirs_exist_and_empty) from scilpy.tractanalysis.fixel_density import (fixel_density, maps_to_masks) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_peaks', help='Path of the peaks. The peaks are expected to be ' 'given as unit directions. \nTo get these from fODF ' diff --git a/scripts/scil_bundle_generate_priors.py b/scripts/scil_bundle_generate_priors.py index 6af4cf4f0..f637eafdd 100755 --- a/scripts/scil_bundle_generate_priors.py +++ b/scripts/scil_bundle_generate_priors.py @@ -7,6 +7,11 @@ be used for bundle-specific tractography, but not for FOD metrics. Formerly: scil_generate_priors_from_bundle.py +----------------------------------------------------------------------------- +Reference: +[1] Rheault, Francois, et al. "Bundle-specific tractography with incorporated + anatomical and orientational priors." NeuroImage 186 (2019): 382-398 +----------------------------------------------------------------------------- """ import argparse @@ -30,19 +35,14 @@ assert_headers_compatible) from scilpy.reconst.utils import find_order_from_nb_coeff from scilpy.tractanalysis.todi import TrackOrientationDensityImaging - - -EPILOG = """ - References: - [1] Rheault, Francois, et al. "Bundle-specific tractography with - incorporated anatomical and orientational priors." - NeuroImage 186 (2019): 382-398 - """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input bundle filename.') diff --git a/scripts/scil_bundle_label_map.py b/scripts/scil_bundle_label_map.py index 9281a5c92..008534e12 100755 --- a/scripts/scil_bundle_label_map.py +++ b/scripts/scil_bundle_label_map.py @@ -44,12 +44,13 @@ from scilpy.tractograms.streamline_operations import \ resample_streamlines_num_points from scilpy.viz.color import get_lookup_table +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='Fiber bundle file.') diff --git a/scripts/scil_bundle_mean_fixel_afd.py b/scripts/scil_bundle_mean_fixel_afd.py index ae327b714..fa0139abd 100755 --- a/scripts/scil_bundle_mean_fixel_afd.py +++ b/scripts/scil_bundle_mean_fixel_afd.py @@ -11,6 +11,15 @@ Please use a bundle file rather than a whole tractogram. Formerly: scil_compute_fixel_afd_from_bundles.py + +----------------------------------------------------------------------------- +Reference: + [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., + Crozier, S., Salvado, O., & Connelly, A. (2012). + Apparent Fibre Density: a novel measure for the analysis of + diffusion-weighted magnetic resonance images. NeuroImage, 59(4), + 3976--3994. +----------------------------------------------------------------------------- """ import argparse @@ -26,20 +35,14 @@ parse_sh_basis_arg, assert_headers_compatible) from scilpy.tractanalysis.afd_along_streamlines \ import afd_map_along_streamlines - -EPILOG = """ -Reference: - [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., - Crozier, S., Salvado, O., & Connelly, A. (2012). - Apparent Fibre Density: a novel measure for the analysis of - diffusion-weighted magnetic resonance images. NeuroImage, 59(4), - 3976--3994. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_fodf', diff --git a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py index 012d88a96..8f2f2af77 100755 --- a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py +++ b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py @@ -11,6 +11,14 @@ Please use a hdf5 (.h5) file containing decomposed connections Formerly: scil_compute_fixel_afd_from_hdf5.py +---------------------------------------------------------------------------- + +Reference: +[1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R.,Crozier, + S., Salvado, O., & Connelly, A. (2012). Apparent Fibre Density: a novel + measure for the analysis of diffusion-weighted magnetic resonance images. + NeuroImage, 59(4), 3976--3994. +---------------------------------------------------------------------------- """ import argparse @@ -32,15 +40,7 @@ parse_sh_basis_arg, validate_nbr_processes) from scilpy.tractanalysis.afd_along_streamlines \ import afd_map_along_streamlines - -EPILOG = """ -Reference: - [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., - Crozier, S., Salvado, O., & Connelly, A. (2012). - Apparent Fibre Density: a novel measure for the analysis of - diffusion-weighted magnetic resonance images. NeuroImage, - 59(4), 3976--3994. -""" +from scilpy.version import version_string def _afd_rd_wrapper(args): @@ -65,10 +65,11 @@ def _afd_rd_wrapper(args): return key, afd_mean - def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_hdf5', help='HDF5 filename (.h5) containing decomposed ' 'connections.') diff --git a/scripts/scil_bundle_mean_fixel_bingham_metric.py b/scripts/scil_bundle_mean_fixel_bingham_metric.py index 880762728..a55bcb299 100755 --- a/scripts/scil_bundle_mean_fixel_bingham_metric.py +++ b/scripts/scil_bundle_mean_fixel_bingham_metric.py @@ -37,11 +37,14 @@ assert_outputs_exist, assert_headers_compatible) from scilpy.tractanalysis.bingham_metric_along_streamlines \ import bingham_metric_map_along_streamlines +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_bingham', diff --git a/scripts/scil_bundle_mean_fixel_mrds_metric.py b/scripts/scil_bundle_mean_fixel_mrds_metric.py index 8874a2e6b..5e9dd66a1 100755 --- a/scripts/scil_bundle_mean_fixel_mrds_metric.py +++ b/scripts/scil_bundle_mean_fixel_mrds_metric.py @@ -38,11 +38,14 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.tractanalysis.mrds_along_streamlines \ import mrds_metrics_along_streamlines +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_pdds', diff --git a/scripts/scil_bundle_mean_std.py b/scripts/scil_bundle_mean_std.py index d672cfdfb..61dace64b 100755 --- a/scripts/scil_bundle_mean_std.py +++ b/scripts/scil_bundle_mean_std.py @@ -35,12 +35,13 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.utils.metrics_tools import get_bundle_metrics_mean_std, \ get_bundle_metrics_mean_std_per_point +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle file to compute statistics on.') @@ -71,7 +72,7 @@ def _build_arg_parser(): p.add_argument('--out_json', help='Path of the output file. If not given, the output ' 'is simply printed on screen.') - + add_reference_arg(p) add_json_args(p) add_verbose_arg(p) diff --git a/scripts/scil_bundle_pairwise_comparison.py b/scripts/scil_bundle_pairwise_comparison.py index 8ab8b9c0f..892e2a6a2 100755 --- a/scripts/scil_bundle_pairwise_comparison.py +++ b/scripts/scil_bundle_pairwise_comparison.py @@ -52,12 +52,14 @@ from scilpy.tractograms.streamline_and_mask_operations import \ get_endpoints_density_map +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('out_json', diff --git a/scripts/scil_bundle_reject_outliers.py b/scripts/scil_bundle_reject_outliers.py index 1705b7fa1..c7b530115 100755 --- a/scripts/scil_bundle_reject_outliers.py +++ b/scripts/scil_bundle_reject_outliers.py @@ -23,11 +23,14 @@ assert_outputs_exist, check_tracts_same_format) from scilpy.tractanalysis.bundle_operations import remove_outliers_qb +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Fiber bundle file to remove outliers from.') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_score_many_bundles_one_tractogram.py b/scripts/scil_bundle_score_many_bundles_one_tractogram.py index 365c2c993..3d874be00 100755 --- a/scripts/scil_bundle_score_many_bundles_one_tractogram.py +++ b/scripts/scil_bundle_score_many_bundles_one_tractogram.py @@ -57,12 +57,13 @@ from scilpy.segment.tractogram_from_roi import compute_masks_from_bundles from scilpy.tractanalysis.scoring import compute_tractometry from scilpy.tractanalysis.scoring import __doc__ as tractometry_description +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__ + tractometry_description, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument("gt_config", help=".json dict configured as specified above.") diff --git a/scripts/scil_bundle_score_same_bundle_many_segmentations.py b/scripts/scil_bundle_score_same_bundle_many_segmentations.py index d8f1dfc7a..f413affb0 100755 --- a/scripts/scil_bundle_score_same_bundle_many_segmentations.py +++ b/scripts/scil_bundle_score_same_bundle_many_segmentations.py @@ -54,11 +54,14 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractanalysis.reproducibility_measures import binary_classification from scilpy.tractograms.tractogram_operations import intersection_robust +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('out_json', diff --git a/scripts/scil_bundle_shape_measures.py b/scripts/scil_bundle_shape_measures.py index 059aca521..797ba354d 100755 --- a/scripts/scil_bundle_shape_measures.py +++ b/scripts/scil_bundle_shape_measures.py @@ -33,6 +33,11 @@ Formerly: scil_compute_bundle_volume.py or scil_evaluate_bundles_individual_measures.py +------------------------------------------------------------------------------ +References: +[1] Fang-Cheng Yeh. 2020. + Shape analysis of the human association pathways. NeuroImage. +------------------------------------------------------------------------------ """ import argparse @@ -58,17 +63,14 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractograms.streamline_and_mask_operations import \ get_endpoints_density_map, get_head_tail_density_maps - -EPILOG = """ -References: -[1] Fang-Cheng Yeh. 2020. - Shape analysis of the human association pathways. NeuroImage. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('--out_json', diff --git a/scripts/scil_bundle_uniformize_endpoints.py b/scripts/scil_bundle_uniformize_endpoints.py index 1a9fe6b77..62adfa05e 100755 --- a/scripts/scil_bundle_uniformize_endpoints.py +++ b/scripts/scil_bundle_uniformize_endpoints.py @@ -34,12 +34,14 @@ assert_inputs_exist, assert_headers_compatible) from scilpy.tractanalysis.bundle_operations import \ uniformize_bundle_sft, uniformize_bundle_sft_using_mask +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input path of the tractography file.') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_volume_per_label.py b/scripts/scil_bundle_volume_per_label.py index 9b114525d..1ac0c93e3 100755 --- a/scripts/scil_bundle_volume_per_label.py +++ b/scripts/scil_bundle_volume_per_label.py @@ -28,11 +28,13 @@ add_verbose_arg, add_overwrite_arg, assert_inputs_exist) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('voxel_label_map', help='Fiber bundle file.') diff --git a/scripts/scil_connectivity_compare_populations.py b/scripts/scil_connectivity_compare_populations.py index 74a9bcced..6366167dc 100755 --- a/scripts/scil_connectivity_compare_populations.py +++ b/scripts/scil_connectivity_compare_populations.py @@ -18,6 +18,14 @@ statistical tests, useful when using --fdr or --bonferroni. Formerly: scil_compare_connectivity.py +---------------------------------------------------------------------------- +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +[2] Zalesky, Andrew, Alex Fornito, and Edward T. Bullmore. "Network-based + statistic: identifying differences in brain networks." Neuroimage 53.4 + (2010): 1197-1207. +---------------------------------------------------------------------------- """ import argparse @@ -32,22 +40,13 @@ load_matrix_in_any_format, save_matrix_in_any_format) from scilpy.stats.matrix_stats import ttest_two_matrices - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -[2] Zalesky, Andrew, Alex Fornito, and Edward T. Bullmore. "Network-based - statistic: identifying differences in brain networks." Neuroimage 53.4 - (2010): 1197-1207. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_pval_matrix', help='Output matrix (.npy) containing the edges p-value.') diff --git a/scripts/scil_connectivity_compute_matrices.py b/scripts/scil_connectivity_compute_matrices.py index f28a1730b..31a5f19f6 100755 --- a/scripts/scil_connectivity_compute_matrices.py +++ b/scripts/scil_connectivity_compute_matrices.py @@ -81,12 +81,14 @@ validate_nbr_processes, assert_inputs_dirs_exist, assert_headers_compatible, assert_output_dirs_exist_and_empty) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_hdf5', help='Input filename for the hdf5 container (.h5).') p.add_argument('in_labels', diff --git a/scripts/scil_connectivity_compute_pca.py b/scripts/scil_connectivity_compute_pca.py index 8a19d7a5f..c5020852c 100755 --- a/scripts/scil_connectivity_compute_pca.py +++ b/scripts/scil_connectivity_compute_pca.py @@ -39,6 +39,21 @@ EXAMPLE USAGE: scil_connectivity_compute_pca.py input_folder/ output_folder/ --metrics ad fa md rd [...] --list_ids list_ids.txt + +------------------------------------------------------------------------------- +References: +[1] Chamberland M, Raven EP, Genc S, Duffy K, Descoteaux M, Parker GD, Tax CMW, + Jones DK. Dimensionality reduction of diffusion MRI measures for improved + tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. + doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; + PMCID: PMC6711466. +[2] Gagnon A., Grenier G., Bocti C., Gillet V., Lepage J.-F., Baccarelli A. A., + Posner J., Descoteaux M., Takser L. (2022). White matter microstructural + variability linked to differential attentional skills and impulsive behavior + in a pediatric population. Cerebral Cortex. + https://doi.org/10.1093/cercor/bhac180 +[3] https://towardsdatascience.com/what-are-pca-loadings-and-biplots-9a7897f2e559 +------------------------------------------------------------------------------- """ # Import required libraries. @@ -56,28 +71,13 @@ add_verbose_arg, add_overwrite_arg, assert_output_dirs_exist_and_empty) +from scilpy.version import version_string -EPILOG = """ -[1] Chamberland M, Raven EP, Genc S, Duffy K, Descoteaux M, Parker GD, Tax CMW, - Jones DK. Dimensionality reduction of diffusion MRI measures for improved - tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. - doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; - PMCID: PMC6711466. -[2] Gagnon A., Grenier G., Bocti C., Gillet V., Lepage J.-F., Baccarelli A. A., - Posner J., Descoteaux M., Takser L. (2022). White matter microstructural - variability linked to differential attentional skills and impulsive behavior - in a pediatric population. Cerebral Cortex. - https://doi.org/10.1093/cercor/bhac180 -[3] https://towardsdatascience.com/what-are-pca-loadings-and-biplots-9a7897f2e559 - """ - - -# Build argument parser. def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_folder', help='Path to the input folder. See explanation above for ' diff --git a/scripts/scil_connectivity_compute_simple_matrix.py b/scripts/scil_connectivity_compute_simple_matrix.py index 2b954568b..c26ba7622 100644 --- a/scripts/scil_connectivity_compute_simple_matrix.py +++ b/scripts/scil_connectivity_compute_simple_matrix.py @@ -41,11 +41,14 @@ from scilpy.io.utils import assert_inputs_exist, assert_outputs_exist, \ add_verbose_arg, add_overwrite_arg, assert_headers_compatible, \ add_reference_arg +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_tractogram', help='Tractogram (trk or tck).') p.add_argument('in_labels', diff --git a/scripts/scil_connectivity_filter.py b/scripts/scil_connectivity_filter.py index 024501402..ded720fb1 100755 --- a/scripts/scil_connectivity_filter.py +++ b/scripts/scil_connectivity_filter.py @@ -45,12 +45,13 @@ assert_outputs_exist, load_matrix_in_any_format, save_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_matrix_mask', help='Output mask (matrix) resulting from the provided ' diff --git a/scripts/scil_connectivity_graph_measures.py b/scripts/scil_connectivity_graph_measures.py index 8c8a37e2d..8286e2ddd 100755 --- a/scripts/scil_connectivity_graph_measures.py +++ b/scripts/scil_connectivity_graph_measures.py @@ -29,6 +29,12 @@ https://www.gnu.org/licenses/gpl-3.0.en.html Formerly: scil_evaluate_connectivity_graph_measures.py +---------------------------------------------------------------------------- +Reference: +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +---------------------------------------------------------------------------- """ import argparse @@ -43,18 +49,14 @@ assert_inputs_exist, assert_outputs_exist, load_matrix_in_any_format) - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) + p.add_argument('in_conn_matrix', help='Input connectivity matrix (.npy).\n' 'Typically a streamline count weighted matrix.') diff --git a/scripts/scil_connectivity_hdf5_average_density_map.py b/scripts/scil_connectivity_hdf5_average_density_map.py index e79338a8c..6d3480ba6 100755 --- a/scripts/scil_connectivity_hdf5_average_density_map.py +++ b/scripts/scil_connectivity_hdf5_average_density_map.py @@ -36,11 +36,13 @@ assert_output_dirs_exist_and_empty, validate_nbr_processes) from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_hdf5', nargs='+', help='List of HDF5 filenames (.h5) from ' 'scil_tractogram_segment_connections_from_labels.py.') diff --git a/scripts/scil_connectivity_math.py b/scripts/scil_connectivity_math.py index f4830f34a..8d04594b2 100755 --- a/scripts/scil_connectivity_math.py +++ b/scripts/scil_connectivity_math.py @@ -24,6 +24,7 @@ load_matrix_in_any_format, save_matrix_in_any_format) from scilpy.utils import is_float +from scilpy.version import version_string OPERATIONS = get_array_ops() @@ -34,9 +35,9 @@ def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('operation', choices=OPERATIONS.keys(), diff --git a/scripts/scil_connectivity_normalize.py b/scripts/scil_connectivity_normalize.py index 8a3fd8a06..f922944e1 100755 --- a/scripts/scil_connectivity_normalize.py +++ b/scripts/scil_connectivity_normalize.py @@ -59,12 +59,13 @@ assert_outputs_exist, load_matrix_in_any_format, save_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrix', help='Input connectivity matrix. This is typically a ' diff --git a/scripts/scil_connectivity_pairwise_agreement.py b/scripts/scil_connectivity_pairwise_agreement.py index 41e62687d..4067a1c9a 100755 --- a/scripts/scil_connectivity_pairwise_agreement.py +++ b/scripts/scil_connectivity_pairwise_agreement.py @@ -24,12 +24,13 @@ assert_outputs_exist, load_matrix_in_any_format) from scilpy.tractanalysis.reproducibility_measures import compute_dice_voxel +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrices', nargs='+', help='Path of the input matricies.') p.add_argument('out_json', diff --git a/scripts/scil_connectivity_print_filenames.py b/scripts/scil_connectivity_print_filenames.py index a458f3d80..7ff42bd99 100755 --- a/scripts/scil_connectivity_print_filenames.py +++ b/scripts/scil_connectivity_print_filenames.py @@ -29,11 +29,13 @@ assert_inputs_exist, assert_outputs_exist, load_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrix', help='Binary matrix in numpy (.npy) format.\n' diff --git a/scripts/scil_connectivity_reorder_rois.py b/scripts/scil_connectivity_reorder_rois.py index 9ed78ab5d..d90268803 100755 --- a/scripts/scil_connectivity_reorder_rois.py +++ b/scripts/scil_connectivity_reorder_rois.py @@ -21,6 +21,12 @@ this option, we recommand an average streamline count or volume matrix. Formerly: scil_reorder_connectivity.py +----------------------------------------------------------------------------- +Reference: +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +----------------------------------------------------------------------------- """ import argparse @@ -38,18 +44,13 @@ assert_outputs_exist, add_verbose_arg, assert_output_dirs_exist_and_empty) - - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrices', nargs='+', help='Connectivity matrices in .npy or .txt format.') diff --git a/scripts/scil_denoising_nlmeans.py b/scripts/scil_denoising_nlmeans.py index 1256208a7..fbf281c09 100755 --- a/scripts/scil_denoising_nlmeans.py +++ b/scripts/scil_denoising_nlmeans.py @@ -65,11 +65,13 @@ assert_inputs_exist, assert_outputs_exist, assert_headers_compatible, ranged_type) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_image', help='Path of the image file to denoise (3D or 4D data)') diff --git a/scripts/scil_dki_metrics.py b/scripts/scil_dki_metrics.py index 5b56982fe..67767012c 100755 --- a/scripts/scil_dki_metrics.py +++ b/scripts/scil_dki_metrics.py @@ -65,12 +65,14 @@ is_normalized_bvecs, identify_shells, normalize_bvecs) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_dwi', help='Path of the input multi-shell DWI dataset.') p.add_argument('in_bval', diff --git a/scripts/scil_dti_convert_tensors.py b/scripts/scil_dti_convert_tensors.py index 95af331c8..27398d223 100755 --- a/scripts/scil_dti_convert_tensors.py +++ b/scripts/scil_dti_convert_tensors.py @@ -17,11 +17,13 @@ from scilpy.io.tensor import (supported_tensor_formats, tensor_format_description, convert_tensor_format) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__ + tensor_format_description, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_file', help='Input tensors filename.') diff --git a/scripts/scil_dti_metrics.py b/scripts/scil_dti_metrics.py index 27d3074c2..1efb1bd68 100755 --- a/scripts/scil_dti_metrics.py +++ b/scripts/scil_dti_metrics.py @@ -54,15 +54,16 @@ is_normalized_bvecs, normalize_bvecs) from scilpy.utils.filenames import add_filename_suffix, split_name_with_nii +from scilpy.version import version_string logger = logging.getLogger("DTI_Metrics") logger.setLevel(logging.INFO) def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_apply_bias_field.py b/scripts/scil_dwi_apply_bias_field.py index 150bc1394..6832d6b2e 100755 --- a/scripts/scil_dwi_apply_bias_field.py +++ b/scripts/scil_dwi_apply_bias_field.py @@ -20,12 +20,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI Nifti image.') p.add_argument('in_bias_field', @@ -38,10 +39,10 @@ def _build_arg_parser(): 'If this is not given, the bias field is still only ' 'applied only in non-background data \n(i.e. where ' 'the dwi is not 0).') - + add_verbose_arg(p) add_overwrite_arg(p) - + return p diff --git a/scripts/scil_dwi_compute_snr.py b/scripts/scil_dwi_compute_snr.py index 83c63659c..d57dc6f81 100755 --- a/scripts/scil_dwi_compute_snr.py +++ b/scripts/scil_dwi_compute_snr.py @@ -44,11 +44,13 @@ assert_inputs_exist) from scilpy.utils.filenames import split_name_with_nii from scilpy.image.volume_operations import compute_snr +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_dwi_concatenate.py b/scripts/scil_dwi_concatenate.py index 1ae54bc5c..6d6e26c99 100755 --- a/scripts/scil_dwi_concatenate.py +++ b/scripts/scil_dwi_concatenate.py @@ -20,12 +20,13 @@ add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_dwi', help='The name of the output DWI file.') diff --git a/scripts/scil_dwi_convert_FDF.py b/scripts/scil_dwi_convert_FDF.py index 88a19c0c5..b6812420d 100755 --- a/scripts/scil_dwi_convert_FDF.py +++ b/scripts/scil_dwi_convert_FDF.py @@ -20,12 +20,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_b0_path', help='Path to the b0 FDF file or folder to convert.') diff --git a/scripts/scil_dwi_detect_volume_outliers.py b/scripts/scil_dwi_detect_volume_outliers.py index 19b6256c9..61b30f398 100755 --- a/scripts/scil_dwi_detect_volume_outliers.py +++ b/scripts/scil_dwi_detect_volume_outliers.py @@ -26,12 +26,13 @@ add_verbose_arg, assert_inputs_exist, ) from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, normalize_bvecs) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DWI file (.nii) to concatenate.') diff --git a/scripts/scil_dwi_extract_b0.py b/scripts/scil_dwi_extract_b0.py index 754dd273d..a89428014 100755 --- a/scripts/scil_dwi_extract_b0.py +++ b/scripts/scil_dwi_extract_b0.py @@ -26,13 +26,15 @@ from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, B0ExtractionStrategy) from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string logger = logging.getLogger(__file__) def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI Nifti image.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_extract_shell.py b/scripts/scil_dwi_extract_shell.py index eb173a335..6b1be7a98 100755 --- a/scripts/scil_dwi_extract_shell.py +++ b/scripts/scil_dwi_extract_shell.py @@ -29,12 +29,13 @@ from scilpy.io.gradients import save_gradient_sampling_fsl from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DW image file to split.') diff --git a/scripts/scil_dwi_powder_average.py b/scripts/scil_dwi_powder_average.py index a0250e8b8..24cc67012 100755 --- a/scripts/scil_dwi_powder_average.py +++ b/scripts/scil_dwi_powder_average.py @@ -30,12 +30,13 @@ from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, assert_outputs_exist, add_verbose_arg, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_prepare_eddy_command.py b/scripts/scil_dwi_prepare_eddy_command.py index 0ccb32324..d28d05129 100755 --- a/scripts/scil_dwi_prepare_eddy_command.py +++ b/scripts/scil_dwi_prepare_eddy_command.py @@ -15,19 +15,23 @@ import os import subprocess -from dipy.io.gradients import read_bvals_bvecs import numpy as np + +from dipy.io.gradients import read_bvals_bvecs + from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_fsl_options_exist, assert_inputs_exist) from scilpy.preprocessing.distortion_correction import \ (create_acqparams, create_index, create_multi_topup_index, create_non_zero_norm_bvecs) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Input DWI Nifti image. If using multiple ' diff --git a/scripts/scil_dwi_prepare_topup_command.py b/scripts/scil_dwi_prepare_topup_command.py index b39a02776..68bd75c3d 100755 --- a/scripts/scil_dwi_prepare_topup_command.py +++ b/scripts/scil_dwi_prepare_topup_command.py @@ -15,15 +15,18 @@ import nibabel as nib import numpy as np + from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_fsl_options_exist) from scilpy.preprocessing.distortion_correction import create_acqparams +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_forward_b0', help='Input b0 Nifti image with forward phase encoding.') diff --git a/scripts/scil_dwi_reorder_philips.py b/scripts/scil_dwi_reorder_philips.py index c2eb65280..fe901f551 100755 --- a/scripts/scil_dwi_reorder_philips.py +++ b/scripts/scil_dwi_reorder_philips.py @@ -24,13 +24,15 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string SOFTWARE_VERSION_MIN = '5.6' def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Input dwi file.') diff --git a/scripts/scil_dwi_split_by_indices.py b/scripts/scil_dwi_split_by_indices.py index d9cff7b16..54871068f 100755 --- a/scripts/scil_dwi_split_by_indices.py +++ b/scripts/scil_dwi_split_by_indices.py @@ -23,12 +23,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DW image file to split.') diff --git a/scripts/scil_dwi_to_sh.py b/scripts/scil_dwi_to_sh.py index 938c1a45e..94eb6269d 100755 --- a/scripts/scil_dwi_to_sh.py +++ b/scripts/scil_dwi_to_sh.py @@ -23,11 +23,13 @@ assert_outputs_exist, parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.sh import compute_sh_coefficients +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the dwi volume.') p.add_argument('in_bval', diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index b8272c148..049361ee9 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -72,12 +72,13 @@ add_overwrite_arg, add_verbose_arg, add_json_args) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fibertubes', help='Path to the tractogram file (must be .trk) \n' diff --git a/scripts/scil_fibertube_tracking.py b/scripts/scil_fibertube_tracking.py index 6b5ca536e..d8a4f95d9 100644 --- a/scripts/scil_fibertube_tracking.py +++ b/scripts/scil_fibertube_tracking.py @@ -48,12 +48,13 @@ add_verbose_arg, add_json_args, add_overwrite_arg) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fibertubes', help='Path to the tractogram (must be .trk) file \n' diff --git a/scripts/scil_fodf_max_in_ventricles.py b/scripts/scil_fodf_max_in_ventricles.py index a40cf2973..eaf70723a 100755 --- a/scripts/scil_fodf_max_in_ventricles.py +++ b/scripts/scil_fodf_max_in_ventricles.py @@ -8,6 +8,13 @@ This allows to clip the noise of fODF using an absolute thresold. Formerly: scil_compute_fodf_max_in_ventricles.py +-------------------------------------------------------------------------- +Reference: +[1] Dell'Acqua, Flavio, et al. "Can spherical deconvolution provide more + information than fiber orientations? Hindrance modulated orientational + anisotropy, a true-tract specific index to characterize white matter + diffusion." Human brain mapping 34.10 (2013): 2464-2483. +-------------------------------------------------------------------------- """ import argparse @@ -21,18 +28,13 @@ assert_inputs_exist, assert_outputs_exist, parse_sh_basis_arg) from scilpy.reconst.fodf import get_ventricles_max_fodf - -EPILOG = """ -[1] Dell'Acqua, Flavio, et al. "Can spherical deconvolution provide more - information than fiber orientations? Hindrance modulated orientational - anisotropy, a true-tract specific index to characterize white matter - diffusion." Human brain mapping 34.10 (2013): 2464-2483. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fodfs', metavar='fODFs', help='Path of the fODF volume in spherical harmonics (SH).') diff --git a/scripts/scil_fodf_memsmt.py b/scripts/scil_fodf_memsmt.py index c70265242..86a6010bc 100755 --- a/scripts/scil_fodf_memsmt.py +++ b/scripts/scil_fodf_memsmt.py @@ -60,11 +60,13 @@ verify_failed_voxels_shm_coeff, verify_frf_files) from scilpy.reconst.sh import convert_sh_basis, verify_data_vs_sh_order +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_wm_frf', help='Text file of WM response function.') diff --git a/scripts/scil_fodf_metrics.py b/scripts/scil_fodf_metrics.py index 4112a1086..124284ef1 100755 --- a/scripts/scil_fodf_metrics.py +++ b/scripts/scil_fodf_metrics.py @@ -47,11 +47,13 @@ assert_inputs_exist, assert_outputs_exist, parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.sh import peaks_from_sh, maps_from_sh +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fODF', help='Path of the fODF volume in spherical harmonics (SH).') diff --git a/scripts/scil_fodf_msmt.py b/scripts/scil_fodf_msmt.py index 5e8db5829..c157b01ab 100755 --- a/scripts/scil_fodf_msmt.py +++ b/scripts/scil_fodf_msmt.py @@ -42,12 +42,13 @@ verify_failed_voxels_shm_coeff, verify_frf_files) from scilpy.reconst.sh import convert_sh_basis, verify_data_vs_sh_order +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_fodf_ssst.py b/scripts/scil_fodf_ssst.py index 4070f6816..c39933acc 100755 --- a/scripts/scil_fodf_ssst.py +++ b/scripts/scil_fodf_ssst.py @@ -30,11 +30,13 @@ parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.fodf import fit_from_model from scilpy.reconst.sh import convert_sh_basis +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_fodf_to_bingham.py b/scripts/scil_fodf_to_bingham.py index c71110ed9..025b8e158 100755 --- a/scripts/scil_fodf_to_bingham.py +++ b/scripts/scil_fodf_to_bingham.py @@ -13,6 +13,18 @@ 1mm isotropic resolution. Formerly: scil_fit_bingham_to_fodf.py +------------------------------------------------------------------------------- +References: +[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond + fractional anisotropy: Extraction of bundle-specific structural metrics + from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, + doi: 10.1016/j.neuroimage.2014.06.015. + +[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility + Tracking: A method to evaluate anatomical connectivity and microstructural + properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. + 2014, doi: 10.1016/j.neuroimage.2014.01.002. +------------------------------------------------------------------------------- """ import nibabel as nib @@ -26,25 +38,13 @@ assert_headers_compatible) from scilpy.io.image import get_data_as_mask from scilpy.reconst.bingham import (bingham_fit_sh) - - -EPILOG = """ -[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond - fractional anisotropy: Extraction of bundle-specific structural metrics - from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, - doi: 10.1016/j.neuroimage.2014.06.015. - -[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility - Tracking: A method to evaluate anatomical connectivity and microstructural - properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. - 2014, doi: 10.1016/j.neuroimage.2014.01.002. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) p.add_argument('in_sh', help='Input SH image.') diff --git a/scripts/scil_freewater_maps.py b/scripts/scil_freewater_maps.py index 854eb30ab..8ed9d1fbc 100755 --- a/scripts/scil_freewater_maps.py +++ b/scripts/scil_freewater_maps.py @@ -106,7 +106,7 @@ def main(): if args.verbose == "WARNING": f = io.StringIO() redirected_stdout = redirect_stdout(f) - redirect_stdout_c() + redirect_stdout_c() else: logging.getLogger().setLevel(logging.getLevelName(args.verbose)) redirected_stdout = redirect_stdout(sys.stdout) From c167952bfabbb2f0f4a1c3134e1af7e0bf59acaf Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 14:57:39 -0500 Subject: [PATCH 11/63] add version p3 --- scripts/scil_NODDI_maps.py | 24 ++++++------ scripts/scil_NODDI_priors.py | 23 ++++++----- scripts/scil_freewater_maps.py | 29 +++++++------- scripts/scil_frf_mean.py | 7 ++-- scripts/scil_frf_memsmt.py | 5 ++- scripts/scil_frf_msmt.py | 8 ++-- scripts/scil_frf_set_diffusivities.py | 6 ++- scripts/scil_frf_ssst.py | 12 ++++-- scripts/scil_gradients_apply_transform.py | 6 ++- scripts/scil_gradients_convert.py | 6 ++- scripts/scil_gradients_generate_sampling.py | 25 ++++++------ scripts/scil_gradients_modify_axes.py | 6 ++- scripts/scil_gradients_normalize_bvecs.py | 6 ++- scripts/scil_gradients_round_bvals.py | 7 ++-- scripts/scil_gradients_validate_correct.py | 21 +++++----- .../scil_gradients_validate_correct_eddy.py | 7 ++-- scripts/scil_header_print_info.py | 4 +- scripts/scil_header_validate_compatibility.py | 4 +- scripts/scil_json_convert_entries_to_xlsx.py | 6 ++- scripts/scil_json_harmonize_entries.py | 4 +- scripts/scil_json_merge_entries.py | 6 ++- scripts/scil_labels_combine.py | 21 +++++----- scripts/scil_labels_dilate.py | 19 ++++++---- scripts/scil_labels_from_mask.py | 5 ++- scripts/scil_labels_remove.py | 13 ++++++- scripts/scil_labels_split_volume_by_ids.py | 8 ++-- scripts/scil_labels_split_volume_from_lut.py | 4 +- scripts/scil_lesions_generate_nawm.py | 4 +- scripts/scil_lesions_info.py | 4 +- scripts/scil_mrds_metrics.py | 4 +- scripts/scil_mrds_select_number_of_tensors.py | 4 +- scripts/scil_mti_adjust_B1_header.py | 4 +- scripts/scil_mti_maps_MT.py | 19 ++++++---- scripts/scil_mti_maps_ihMT.py | 38 ++++++++++--------- 34 files changed, 219 insertions(+), 150 deletions(-) diff --git a/scripts/scil_NODDI_maps.py b/scripts/scil_NODDI_maps.py index 2d067c5ff..351ba8a9b 100755 --- a/scripts/scil_NODDI_maps.py +++ b/scripts/scil_NODDI_maps.py @@ -6,6 +6,14 @@ Multi-shell DWI necessary. Formerly: scil_compute_NODDI.py + +--------------------------------------------------------------- +Reference: +[1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. + NODDI: practical in vivo neurite orientation dispersion + and density imaging of the human brain. + NeuroImage. 2012 Jul 16;61:1000-16. +--------------------------------------------------------------- """ import argparse @@ -30,21 +38,13 @@ add_skip_b0_check_arg) from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, identify_shells) - - -EPILOG = """ -Reference: - [1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. - NODDI: practical in vivo neurite orientation dispersion - and density imaging of the human brain. - NeuroImage. 2012 Jul 16;61:1000-16. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI file acquired with a NODDI compatible protocol ' diff --git a/scripts/scil_NODDI_priors.py b/scripts/scil_NODDI_priors.py index 417f845f4..e027bb189 100755 --- a/scripts/scil_NODDI_priors.py +++ b/scripts/scil_NODDI_priors.py @@ -6,6 +6,14 @@ diffusivity priors for NODDI. Formerly: scil_compute_NODDI_priors.py + +--------------------------------------------------------------- +Reference: +[1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. + NODDI: practical in vivo neurite orientation dispersion + and density imaging of the human brain. + NeuroImage. 2012 Jul 16;61:1000-16. +--------------------------------------------------------------- """ import argparse @@ -19,19 +27,14 @@ assert_outputs_exist, add_overwrite_arg, add_verbose_arg) - -EPILOG = """ -Reference: - [1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. - NODDI: practical in vivo neurite orientation dispersion and density - imaging of the human brain. NeuroImage. 2012 Jul 16;61:1000-16. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_FA', help='Path to the FA volume.') p.add_argument('in_AD', diff --git a/scripts/scil_freewater_maps.py b/scripts/scil_freewater_maps.py index 854eb30ab..1b67dc9b4 100755 --- a/scripts/scil_freewater_maps.py +++ b/scripts/scil_freewater_maps.py @@ -6,6 +6,16 @@ This script supports both single and multi-shell data. Formerly: scil_compute_freewater.py + +---------------------------------------------------------- +References: + [1] Pasternak 0, Sochen N, Gur Y, Intrator N, Assaf Y. + Free water elimination and mapping from diffusion mri. + Magn Reson Med. 62 (3) (2009) 717-730. + [2] Daducci A, et al. Accelerated microstructure imaging + via convex optimization (AMICO) from diffusion MRI data. + Neuroimage 105 (2015) 32-44. +---------------------------------------------------------- """ import argparse @@ -28,24 +38,13 @@ assert_output_dirs_exist_and_empty, redirect_stdout_c) from scilpy.gradients.bvec_bval_tools import identify_shells - - -EPILOG = """ -Reference: - [1] Pasternak 0, Sochen N, Gur Y, Intrator N, Assaf Y. - Free water elimination and mapping from diffusion mri. - Magn Reson Med. 62 (3) (2009) 717-730. - [2] Daducci A, et al. Accelerated microstructure imaging - via convex optimization (AMICO) from diffusion MRI data. - Neuroimage 105 (2015) 32-44. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI file.') diff --git a/scripts/scil_frf_mean.py b/scripts/scil_frf_mean.py index 06838e40d..75affdf24 100755 --- a/scripts/scil_frf_mean.py +++ b/scripts/scil_frf_mean.py @@ -22,12 +22,13 @@ assert_inputs_exist, add_verbose_arg, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('frf_files', metavar='list', nargs='+', help='List of FRF filepaths.') diff --git a/scripts/scil_frf_memsmt.py b/scripts/scil_frf_memsmt.py index 9f1a0c764..14ba3f762 100755 --- a/scripts/scil_frf_memsmt.py +++ b/scripts/scil_frf_memsmt.py @@ -57,12 +57,13 @@ add_tolerance_arg, assert_headers_compatible) from scilpy.reconst.frf import compute_msmt_frf +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_wm_frf', help='Path to the output WM frf file, in .txt format.') diff --git a/scripts/scil_frf_msmt.py b/scripts/scil_frf_msmt.py index ce9bc3fcd..321eeac0c 100755 --- a/scripts/scil_frf_msmt.py +++ b/scripts/scil_frf_msmt.py @@ -42,13 +42,13 @@ assert_outputs_exist, assert_roi_radii_format, assert_headers_compatible) from scilpy.reconst.frf import compute_msmt_frf +from scilpy.version import version_string def _build_arg_parser(): - - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path to the input diffusion volume.') diff --git a/scripts/scil_frf_set_diffusivities.py b/scripts/scil_frf_set_diffusivities.py index 428cbce74..1603a9175 100755 --- a/scripts/scil_frf_set_diffusivities.py +++ b/scripts/scil_frf_set_diffusivities.py @@ -22,11 +22,13 @@ add_verbose_arg, assert_outputs_exist) from scilpy.reconst.frf import replace_frf +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('frf_file', metavar='input', help='Path of the FRF file.') diff --git a/scripts/scil_frf_ssst.py b/scripts/scil_frf_ssst.py index d1f12c3aa..74ad6ff68 100755 --- a/scripts/scil_frf_ssst.py +++ b/scripts/scil_frf_ssst.py @@ -8,6 +8,10 @@ found using a threshold on the FA. Formerly: scil_compute_ssst_frf.py +---------------------------------------------------------------------- +References: +[1] Tournier et al. NeuroImage 2007 +---------------------------------------------------------------------- """ import argparse @@ -26,13 +30,13 @@ assert_roi_radii_format, assert_headers_compatible) from scilpy.reconst.frf import compute_ssst_frf +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter, - epilog="References: [1] Tournier et al. NeuroImage 2007") + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_gradients_apply_transform.py b/scripts/scil_gradients_apply_transform.py index 36f519fc6..6e041871d 100755 --- a/scripts/scil_gradients_apply_transform.py +++ b/scripts/scil_gradients_apply_transform.py @@ -16,11 +16,13 @@ from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, assert_outputs_exist, add_verbose_arg, load_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bvecs', help='Path of the bvec file, in FSL format') diff --git a/scripts/scil_gradients_convert.py b/scripts/scil_gradients_convert.py index c2209a74d..78ecd089c 100755 --- a/scripts/scil_gradients_convert.py +++ b/scripts/scil_gradients_convert.py @@ -15,11 +15,13 @@ assert_inputs_exist, assert_outputs_exist, add_overwrite_arg, add_verbose_arg) from scilpy.io.gradients import fsl2mrtrix, mrtrix2fsl +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('gradients', nargs='+', metavar='GRADIENT_FILE(S)', help='Path(s) to the gradient file(s). Either FSL ' diff --git a/scripts/scil_gradients_generate_sampling.py b/scripts/scil_gradients_generate_sampling.py index 850188fca..b38c7a7c1 100755 --- a/scripts/scil_gradients_generate_sampling.py +++ b/scripts/scil_gradients_generate_sampling.py @@ -11,6 +11,14 @@ diffusion gradient amplitude over a few TR. Formerly: scil_generate_gradient_sampling.py + +------------------------------------------------------------------------------- +References: +[1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, + Rachid Deriche. Design of multishell gradient sampling with uniform coverage + in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, 69 (6), + pp. 1534-1540. +------------------------------------------------------------------------------- """ import argparse @@ -27,21 +35,14 @@ compute_min_duty_cycle_bruteforce, correct_b0s_philips, swap_sampling_eddy) from scilpy.io.gradients import ( save_gradient_sampling_fsl, save_gradient_sampling_mrtrix) - - -EPILOG = """ -References: [1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, -Rachid Deriche. Design of multishell gradient sampling with uniform coverage -in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, 69 (6), -pp. 1534-1540. - """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('nb_samples_per_shell', type=int, nargs='+', help='Number of samples on each non b0 shell. \n' 'If multishell, provide a number per shell.') diff --git a/scripts/scil_gradients_modify_axes.py b/scripts/scil_gradients_modify_axes.py index 760e63526..506c8d2ad 100755 --- a/scripts/scil_gradients_modify_axes.py +++ b/scripts/scil_gradients_modify_axes.py @@ -17,11 +17,13 @@ swap_gradient_axis) from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, assert_outputs_exist, add_verbose_arg) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_gradient_sampling_file', help='Path to gradient sampling file. (.bvec or .b)') diff --git a/scripts/scil_gradients_normalize_bvecs.py b/scripts/scil_gradients_normalize_bvecs.py index 422299bd1..e0dda7a77 100755 --- a/scripts/scil_gradients_normalize_bvecs.py +++ b/scripts/scil_gradients_normalize_bvecs.py @@ -13,11 +13,13 @@ from scilpy.io.utils import (assert_inputs_exist, assert_outputs_exist, add_overwrite_arg, add_verbose_arg) from scilpy.gradients.bvec_bval_tools import normalize_bvecs +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bvec', help='Path to the gradient file (.bvec).') diff --git a/scripts/scil_gradients_round_bvals.py b/scripts/scil_gradients_round_bvals.py index 848195f2c..ab630290d 100755 --- a/scripts/scil_gradients_round_bvals.py +++ b/scripts/scil_gradients_round_bvals.py @@ -25,12 +25,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) from scilpy.gradients.bvec_bval_tools import round_bvals_to_shell +from scilpy.version import version_string def _build_arg_parser(): - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) parser.add_argument('in_bval', help='The b-values in FSL format.') diff --git a/scripts/scil_gradients_validate_correct.py b/scripts/scil_gradients_validate_correct.py index a34c37f38..7721f133c 100755 --- a/scripts/scil_gradients_validate_correct.py +++ b/scripts/scil_gradients_validate_correct.py @@ -21,6 +21,13 @@ dimension. For example, peaks.nii.gz from scil_fodf_metrics.py could be used. Formerly: scil_validate_and_correct_bvecs.py +------------------------------------------------------------------------------ +Reference: +[1] Schilling KG, Yeh FC, Nath V, Hansen C, Williams O, Resnick S, Anderson AW, + Landman BA. A fiber coherence index for quality control of B-table orientation + in diffusion MRI scans. Magn Reson Imaging. 2019 May;58:82-89. + doi: 10.1016/j.mri.2019.01.018. +------------------------------------------------------------------------------ """ import argparse @@ -35,19 +42,13 @@ assert_headers_compatible) from scilpy.io.image import get_data_as_mask from scilpy.reconst.fiber_coherence import compute_coherence_table_for_transforms - - -EPILOG = """ -[1] Schilling KG, Yeh FC, Nath V, Hansen C, Williams O, Resnick S, Anderson AW, -Landman BA. A fiber coherence index for quality control of B-table orientation -in diffusion MRI scans. Magn Reson Imaging. 2019 May;58:82-89. -doi: 10.1016/j.mri.2019.01.018. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bvec', help='Path to bvec file.') diff --git a/scripts/scil_gradients_validate_correct_eddy.py b/scripts/scil_gradients_validate_correct_eddy.py index 121e68513..39c5c5647 100755 --- a/scripts/scil_gradients_validate_correct_eddy.py +++ b/scripts/scil_gradients_validate_correct_eddy.py @@ -16,12 +16,13 @@ from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, assert_outputs_exist, add_verbose_arg) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bvec', help='In bvec file.') p.add_argument('in_bval', diff --git a/scripts/scil_header_print_info.py b/scripts/scil_header_print_info.py index d5dfc5a0c..da3581c81 100755 --- a/scripts/scil_header_print_info.py +++ b/scripts/scil_header_print_info.py @@ -16,11 +16,13 @@ from scilpy.io.utils import assert_inputs_exist, add_verbose_arg from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_file', help='Input file (trk, nii and mgz).') diff --git a/scripts/scil_header_validate_compatibility.py b/scripts/scil_header_validate_compatibility.py index c1e319eb0..34a11a3c6 100755 --- a/scripts/scil_header_validate_compatibility.py +++ b/scripts/scil_header_validate_compatibility.py @@ -18,11 +18,13 @@ add_verbose_arg, assert_inputs_exist, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_files', nargs='+', help='List of file to compare (trk, tck and nii/nii.gz).') diff --git a/scripts/scil_json_convert_entries_to_xlsx.py b/scripts/scil_json_convert_entries_to_xlsx.py index bf5746278..5234b2591 100755 --- a/scripts/scil_json_convert_entries_to_xlsx.py +++ b/scripts/scil_json_convert_entries_to_xlsx.py @@ -17,6 +17,7 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string dps_dpp = ['data_per_streamline_keys', 'data_per_point_keys'] @@ -437,8 +438,9 @@ def _create_xlsx_from_json(json_path, xlsx_path, def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_json', help='File containing the json stats (.json).') diff --git a/scripts/scil_json_harmonize_entries.py b/scripts/scil_json_harmonize_entries.py index a2c601ba5..b89bfacce 100755 --- a/scripts/scil_json_harmonize_entries.py +++ b/scripts/scil_json_harmonize_entries.py @@ -28,11 +28,13 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.utils import recursive_print, recursive_update +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_file', help='Input file (json).') diff --git a/scripts/scil_json_merge_entries.py b/scripts/scil_json_merge_entries.py index 834ec6a38..a8812df03 100755 --- a/scripts/scil_json_merge_entries.py +++ b/scripts/scil_json_merge_entries.py @@ -37,11 +37,13 @@ assert_inputs_exist, add_verbose_arg, assert_outputs_exist) from scilpy.tractanalysis.json_utils import merge_dict, average_dict +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_json', nargs='+', help='List of json files to merge (.json).') diff --git a/scripts/scil_labels_combine.py b/scripts/scil_labels_combine.py index 161457ba4..1f70fb9b3 100755 --- a/scripts/scil_labels_combine.py +++ b/scripts/scil_labels_combine.py @@ -14,6 +14,13 @@ --volume_ids clean/s1__DKT.nii.gz 1028 2028 Formerly: scil_combine_labels.py. + +------------------------------------------------------------------------------ +Reference: +[1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., + Evans A.C. and Descoteaux M. OHBM 2019. + Surface integration for connectome analysis in age prediction. +------------------------------------------------------------------------------ """ @@ -27,19 +34,13 @@ from scilpy.image.labels import get_data_as_labels, combine_labels from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, add_verbose_arg, assert_outputs_exist) - - -EPILOG = """ - References: - [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., - Evans A.C. and Descoteaux M. OHBM 2019. - Surface integration for connectome analysis in age prediction. - """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('output', help='Combined labels volume output.') diff --git a/scripts/scil_labels_dilate.py b/scripts/scil_labels_dilate.py index d1f0194e2..a48734cd5 100755 --- a/scripts/scil_labels_dilate.py +++ b/scripts/scil_labels_dilate.py @@ -15,6 +15,13 @@ --label_not_to_dilate 4 43 10 11 12 49 50 51 Formerly: scil_dilate_labels.py + +----------------------------------------------------------------------- +References: + [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., + Evans A.C. and Descoteaux M. OHBM 2019. + Surface integration for connectome analysis in age prediction. +----------------------------------------------------------------------- """ import argparse @@ -29,17 +36,13 @@ assert_inputs_exist, add_verbose_arg, assert_outputs_exist, assert_headers_compatible) -EPILOG = """ - References: - [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., - Evans A.C. and Descoteaux M. OHBM 2019. - Surface integration for connectome analysis in age prediction. - """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_file', help='Path of the volume (nii or nii.gz).') diff --git a/scripts/scil_labels_from_mask.py b/scripts/scil_labels_from_mask.py index e91e60b62..60df0dd97 100755 --- a/scripts/scil_labels_from_mask.py +++ b/scripts/scil_labels_from_mask.py @@ -20,11 +20,14 @@ from scilpy.io.image import get_data_as_mask from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, add_verbose_arg, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_mask', type=str, help='Input mask file.') p.add_argument('out_labels', type=str, help='Output label file.') diff --git a/scripts/scil_labels_remove.py b/scripts/scil_labels_remove.py index d69b922a5..bd67c10ca 100755 --- a/scripts/scil_labels_remove.py +++ b/scripts/scil_labels_remove.py @@ -7,6 +7,13 @@ >>> scil_labels_remove.py DKT_labels.nii out_labels.nii.gz -i 5001 5002 Formerly: scil_remove_labels.py + +---------------------------------------------------------------------------- +References: +[1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., + Evans A.C. and Descoteaux M. OHBM 2019. + Surface integration for connectome analysis in age prediction. +---------------------------------------------------------------------------- """ @@ -24,11 +31,13 @@ Evans A.C. and Descoteaux M. OHBM 2019. Surface integration for connectome analysis in age prediction. """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_labels', help='Input labels volume.') diff --git a/scripts/scil_labels_split_volume_by_ids.py b/scripts/scil_labels_split_volume_by_ids.py index cc34a3acd..941a77622 100755 --- a/scripts/scil_labels_split_volume_by_ids.py +++ b/scripts/scil_labels_split_volume_by_ids.py @@ -22,12 +22,14 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_output_dirs_exist_and_empty) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_labels', help='Path of the input label file, ' 'in a format supported by Nibabel.') diff --git a/scripts/scil_labels_split_volume_from_lut.py b/scripts/scil_labels_split_volume_from_lut.py index a1b2b103f..a031aa4fd 100755 --- a/scripts/scil_labels_split_volume_from_lut.py +++ b/scripts/scil_labels_split_volume_from_lut.py @@ -23,13 +23,15 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_output_dirs_exist_and_empty) +from scilpy.version import version_string def _build_arg_parser(): luts = [os.path.splitext(f)[0] for f in os.listdir(get_lut_dir())] p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_label', help='Path of the input label file, in a format supported ' diff --git a/scripts/scil_lesions_generate_nawm.py b/scripts/scil_lesions_generate_nawm.py index 1407796a7..fb36e08f9 100755 --- a/scripts/scil_lesions_generate_nawm.py +++ b/scripts/scil_lesions_generate_nawm.py @@ -35,11 +35,13 @@ assert_output_dirs_exist_and_empty, add_verbose_arg) from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_image', help='Lesions file as mask OR labels (.nii.gz).\n' diff --git a/scripts/scil_lesions_info.py b/scripts/scil_lesions_info.py index 043420099..620c870ea 100755 --- a/scripts/scil_lesions_info.py +++ b/scripts/scil_lesions_info.py @@ -32,11 +32,13 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.utils.filenames import split_name_with_nii from scilpy.utils.metrics_tools import compute_lesion_stats +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_lesion', help='Lesions file as labels (.nii.gz).') diff --git a/scripts/scil_mrds_metrics.py b/scripts/scil_mrds_metrics.py index 5d6230cd3..2bbe80e8a 100755 --- a/scripts/scil_mrds_metrics.py +++ b/scripts/scil_mrds_metrics.py @@ -23,11 +23,13 @@ add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_evals', help='MRDS eigenvalues file (Shape: [X, Y, Z, 9]).\n' 'The last dimensions, values 1-3 are associated with ' diff --git a/scripts/scil_mrds_select_number_of_tensors.py b/scripts/scil_mrds_select_number_of_tensors.py index e2d9e98d7..4f39cd3d3 100755 --- a/scripts/scil_mrds_select_number_of_tensors.py +++ b/scripts/scil_mrds_select_number_of_tensors.py @@ -41,11 +41,13 @@ add_sh_basis_args, add_verbose_arg, assert_headers_compatible, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_prefix', help='Prefix used for all MRDS solutions.') p.add_argument('in_volume', diff --git a/scripts/scil_mti_adjust_B1_header.py b/scripts/scil_mti_adjust_B1_header.py index 2553946ae..f8b7ede2a 100755 --- a/scripts/scil_mti_adjust_B1_header.py +++ b/scripts/scil_mti_adjust_B1_header.py @@ -16,11 +16,13 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.reconst.mti import (adjust_B1_map_header) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_B1_map', help='Path to input B1 map file.') p.add_argument('out_B1_map', diff --git a/scripts/scil_mti_maps_MT.py b/scripts/scil_mti_maps_MT.py index d3f0337df..20941176b 100755 --- a/scripts/scil_mti_maps_MT.py +++ b/scripts/scil_mti_maps_MT.py @@ -74,6 +74,14 @@ By default, the script uses all the echoes available in the input folder. If you want to use a single echo, replace the * with the specific number of the echo. + +--------------------------------------------------------------------------------- +Reference: +[1] Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of + magnetization transfer with inherent correction for RF inhomogeneity + and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. + 2008;60(6):1396-407. +--------------------------------------------------------------------------------- """ import argparse @@ -93,18 +101,13 @@ compute_ratio_map, compute_saturation_map, threshold_map) - -EPILOG = """ -Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of -magnetization transfer with inherent correction for RF inhomogeneity -and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. -2008;60(6):1396-407. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_dir', help='Path to output folder.') p.add_argument('--out_prefix', diff --git a/scripts/scil_mti_maps_ihMT.py b/scripts/scil_mti_maps_ihMT.py index 6c811ff1f..2e6c13620 100755 --- a/scripts/scil_mti_maps_ihMT.py +++ b/scripts/scil_mti_maps_ihMT.py @@ -81,6 +81,23 @@ By default, the script uses all the echoes available in the input folder. If you want to use a single echo, replace the * with the specific number of the echo. + +-------------------------------------------------------------------------------------- +References: +[1] Varma G, Girard OM, Prevost VH, Grant AK, Duhamel G, Alsop DC. + Interpretation of magnetization transfer from inhomogeneously broadened lines + (ihMT) in tissues as a dipolar order effect within motion restricted molecules. + Journal of Magnetic Resonance. 1 nov 2015;260:67-76. + +[2] Manning AP, Chang KL, MacKay AL, Michal CA. The physical mechanism of + "inhomogeneous" magnetization transfer MRI. Journal of Magnetic Resonance. + 1 janv 2017;274:125-36. + +[3] Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of + magnetization transfer with inherent correction for RF inhomogeneity + and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. + 2008;60(6):1396-407. +-------------------------------------------------------------------------------------- """ import argparse @@ -99,27 +116,14 @@ compute_ratio_map, compute_saturation_map, threshold_map) - -EPILOG = """ -Varma G, Girard OM, Prevost VH, Grant AK, Duhamel G, Alsop DC. -Interpretation of magnetization transfer from inhomogeneously broadened lines -(ihMT) in tissues as a dipolar order effect within motion restricted molecules. -Journal of Magnetic Resonance. 1 nov 2015;260:67-76. - -Manning AP, Chang KL, MacKay AL, Michal CA. The physical mechanism of -"inhomogeneous" magnetization transfer MRI. Journal of Magnetic Resonance. -1 janv 2017;274:125-36. - -Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of -magnetization transfer with inherent correction for RF inhomogeneity -and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. -2008;60(6):1396-407. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('out_dir', help='Path to output folder.') p.add_argument('--out_prefix', From e6b4d611c8afb7214b8b9cf38525651f5e8a3291 Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 15:01:55 -0500 Subject: [PATCH 12/63] pep8 --- scripts/scil_gradients_round_bvals.py | 2 +- scripts/scil_gradients_validate_correct_eddy.py | 4 ++-- scripts/scil_labels_remove.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/scil_gradients_round_bvals.py b/scripts/scil_gradients_round_bvals.py index ab630290d..212fa9d6b 100755 --- a/scripts/scil_gradients_round_bvals.py +++ b/scripts/scil_gradients_round_bvals.py @@ -29,7 +29,7 @@ def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, epilog=version_string) diff --git a/scripts/scil_gradients_validate_correct_eddy.py b/scripts/scil_gradients_validate_correct_eddy.py index 39c5c5647..0acda8257 100755 --- a/scripts/scil_gradients_validate_correct_eddy.py +++ b/scripts/scil_gradients_validate_correct_eddy.py @@ -33,10 +33,10 @@ def _build_arg_parser(): help='Out bvec file.') p.add_argument('out_bval', help='Out bval file.') - + add_verbose_arg(p) add_overwrite_arg(p) - + return p diff --git a/scripts/scil_labels_remove.py b/scripts/scil_labels_remove.py index bd67c10ca..70ad20f15 100755 --- a/scripts/scil_labels_remove.py +++ b/scripts/scil_labels_remove.py @@ -48,10 +48,10 @@ def _build_arg_parser(): help='List of labels indices to remove.') p.add_argument('--background', type=int, default=0, help='Integer used for removed labels [%(default)s].') - + add_verbose_arg(p) add_overwrite_arg(p) - + return p From 79207cd384e4e3f555dfb425dfc386201e5c49c1 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Thu, 12 Dec 2024 15:16:53 -0500 Subject: [PATCH 13/63] Starting new script --- scilpy/gradients/gen_gradient_sampling.py | 49 +++++++++++++++ scripts/scil_gradients_validate_sampling.py | 70 +++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 scripts/scil_gradients_validate_sampling.py diff --git a/scilpy/gradients/gen_gradient_sampling.py b/scilpy/gradients/gen_gradient_sampling.py index 95965d9d9..3e44d9897 100644 --- a/scilpy/gradients/gen_gradient_sampling.py +++ b/scilpy/gradients/gen_gradient_sampling.py @@ -298,3 +298,52 @@ def _grad_electrostatic_repulsion_energy(bvecs, weight_matrix, alpha=1.0): (bvecs[i] + bvecs[indices]).T / sums).sum(1) grad = grad.reshape(nb_bvecs * 3) return grad + + +def compute_electrostatic_repulsion_energy(bvecs, nb_shells=1, alpha=1.0): + """ + Electrostatic-repulsion objective function. The alpha parameter controls + the power repulsion (energy varies as $1 / ralpha$). + + Parameters + --------- + bvecs : array-like shape (N * 3,) + Vectors, flattened. + nb_shells : int + Number of shells. + alpha : float + Controls the power of the repulsion. Default is 1.0 + + Returns + ------- + energy : float + sum of all interactions between any two vectors. + """ + # Groups of shells and relative coupling weights + nb_dir = bvecs.shape[0] + shell_groups = () + for i in range(nb_shells): + shell_groups += ([i],) + + shell_groups += (list(range(nb_shells)),) + alphas = list(len(shell_groups) * (1.0,)) + weights = _compute_weights(nb_shells, [nb_dir], + shell_groups, alphas) + nb_points_total = nb_dir + indices = np.cumsum(nb_dir).tolist() + indices.insert(0, 0) + weight_matrix = np.zeros((nb_points_total, nb_points_total)) + for s1 in range(nb_shells): + for s2 in range(nb_shells): + weight_matrix[indices[s1]:indices[s1 + 1], + indices[s2]:indices[s2 + 1]] = weights[s1, s2] + + epsilon = 1e-9 + energy = 0.0 + for i in range(nb_dir): + indices = (np.arange(nb_dir) > i) + diffs = ((bvecs[indices] - bvecs[i]) ** 2).sum(1) ** alpha + sums = ((bvecs[indices] + bvecs[i]) ** 2).sum(1) ** alpha + energy += (weight_matrix[i, indices] * + (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() + return energy \ No newline at end of file diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py new file mode 100644 index 000000000..c7cbc46b9 --- /dev/null +++ b/scripts/scil_gradients_validate_sampling.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" + +""" + +import argparse +import logging +import numpy as np + +from dipy.io.gradients import read_bvals_bvecs + +from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, + add_b0_thresh_arg) +from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, + compute_electrostatic_repulsion_energy) +from scilpy.viz.gradients import plot_proj_shell + + +def _build_arg_parser(): + p = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + description=__doc__) + p.add_argument('in_bvals', + help='') + p.add_argument('in_bvecs', + help='') + + add_b0_thresh_arg(p) + add_verbose_arg(p) + add_overwrite_arg(p) + + return p + + +def main(): + parser = _build_arg_parser() + args = parser.parse_args() + logging.getLogger().setLevel(logging.getLevelName(args.verbose)) + + bvals, bvecs = read_bvals_bvecs(args.in_bvals, args.in_bvecs) + + # ADD B0 CHECK + + no_b0_mask = bvals > args.b0_threshold + bvals = bvals[no_b0_mask] + bvecs = bvecs[no_b0_mask] + nb_dir = len(bvals) + + # Add warning when deplicates bvecs.!!!!!!!!!! + ubvecs, indices = np.unique(bvecs, return_index=True, axis=0) # This sorts the array! + # ubvals = bvals[indices] + + scipy_verbose = int(3 - logging.getLogger().getEffectiveLevel()//10) + opt_bvecs, _ = generate_gradient_sampling([nb_dir], verbose=scipy_verbose) + + # ADD OPTION FOR VISU!!!!!!! + # plot_proj_shell([ubvecs], use_sym=True) + # plot_proj_shell([opt_bvecs], use_sym=True) + + energy = compute_electrostatic_repulsion_energy(ubvecs) + opt_energy = compute_electrostatic_repulsion_energy(opt_bvecs) + + print("Input bvecs energy: ", np.round(energy, decimals=2)) + print("Optimal bvecs energy: ", np.round(opt_energy, decimals=2)) + + +if __name__ == "__main__": + main() From abdd932295a42dc14596cfc6d9adc121e727169d Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 15:17:03 -0500 Subject: [PATCH 14/63] pep8 --- scilpy/version.py | 1 + scripts/scil_dwi_apply_bias_field.py | 4 ++-- scripts/scil_freewater_maps.py | 2 +- scripts/scil_frf_ssst.py | 2 +- scripts/scil_gradients_round_bvals.py | 4 ++-- scripts/scil_labels_remove.py | 8 +------- scripts/scil_mti_maps_ihMT.py | 4 ++-- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/scilpy/version.py b/scilpy/version.py index 9ebf80e4d..cf5d2e873 100644 --- a/scilpy/version.py +++ b/scilpy/version.py @@ -17,6 +17,7 @@ _ver.append(_version_extra) __version__ = '.'.join(map(str, _ver)) +version_string = "\nScilpy version: " + __version__ CLASSIFIERS = ["Development Status :: 3 - Alpha", "Environment :: Console", diff --git a/scripts/scil_dwi_apply_bias_field.py b/scripts/scil_dwi_apply_bias_field.py index 150bc1394..94c02ebf1 100755 --- a/scripts/scil_dwi_apply_bias_field.py +++ b/scripts/scil_dwi_apply_bias_field.py @@ -38,10 +38,10 @@ def _build_arg_parser(): 'If this is not given, the bias field is still only ' 'applied only in non-background data \n(i.e. where ' 'the dwi is not 0).') - + add_verbose_arg(p) add_overwrite_arg(p) - + return p diff --git a/scripts/scil_freewater_maps.py b/scripts/scil_freewater_maps.py index 1b67dc9b4..25ea807ba 100755 --- a/scripts/scil_freewater_maps.py +++ b/scripts/scil_freewater_maps.py @@ -105,7 +105,7 @@ def main(): if args.verbose == "WARNING": f = io.StringIO() redirected_stdout = redirect_stdout(f) - redirect_stdout_c() + redirect_stdout_c() else: logging.getLogger().setLevel(logging.getLevelName(args.verbose)) redirected_stdout = redirect_stdout(sys.stdout) diff --git a/scripts/scil_frf_ssst.py b/scripts/scil_frf_ssst.py index 74ad6ff68..81f0c7d2f 100755 --- a/scripts/scil_frf_ssst.py +++ b/scripts/scil_frf_ssst.py @@ -9,7 +9,7 @@ Formerly: scil_compute_ssst_frf.py ---------------------------------------------------------------------- -References: +References: [1] Tournier et al. NeuroImage 2007 ---------------------------------------------------------------------- """ diff --git a/scripts/scil_gradients_round_bvals.py b/scripts/scil_gradients_round_bvals.py index 212fa9d6b..227619012 100755 --- a/scripts/scil_gradients_round_bvals.py +++ b/scripts/scil_gradients_round_bvals.py @@ -30,8 +30,8 @@ def _build_arg_parser(): parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter, - epilog=version_string) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) parser.add_argument('in_bval', help='The b-values in FSL format.') diff --git a/scripts/scil_labels_remove.py b/scripts/scil_labels_remove.py index 70ad20f15..fa5ec0e0a 100755 --- a/scripts/scil_labels_remove.py +++ b/scripts/scil_labels_remove.py @@ -25,12 +25,6 @@ from scilpy.image.labels import get_data_as_labels, remove_labels from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, add_verbose_arg, assert_outputs_exist) -EPILOG = """ - References: - [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., - Evans A.C. and Descoteaux M. OHBM 2019. - Surface integration for connectome analysis in age prediction. - """ from scilpy.version import version_string @@ -51,7 +45,7 @@ def _build_arg_parser(): add_verbose_arg(p) add_overwrite_arg(p) - + return p diff --git a/scripts/scil_mti_maps_ihMT.py b/scripts/scil_mti_maps_ihMT.py index 2e6c13620..d0fb1b0dc 100755 --- a/scripts/scil_mti_maps_ihMT.py +++ b/scripts/scil_mti_maps_ihMT.py @@ -82,7 +82,7 @@ If you want to use a single echo, replace the * with the specific number of the echo. --------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------- References: [1] Varma G, Girard OM, Prevost VH, Grant AK, Duhamel G, Alsop DC. Interpretation of magnetization transfer from inhomogeneously broadened lines @@ -97,7 +97,7 @@ magnetization transfer with inherent correction for RF inhomogeneity and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. 2008;60(6):1396-407. --------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------- """ import argparse From b2da519729359fe62067de9b2d3cbd4852110ada Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 15:32:37 -0500 Subject: [PATCH 15/63] pep8 --- scripts/scil_gradients_generate_sampling.py | 8 +++---- scripts/scil_gradients_round_bvals.py | 25 ++++++++++----------- scripts/scil_gradients_validate_correct.py | 4 ++-- scripts/scil_mti_maps_MT.py | 4 ++-- scripts/scil_mti_maps_ihMT.py | 10 ++++----- 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/scripts/scil_gradients_generate_sampling.py b/scripts/scil_gradients_generate_sampling.py index b38c7a7c1..7c3e792a5 100755 --- a/scripts/scil_gradients_generate_sampling.py +++ b/scripts/scil_gradients_generate_sampling.py @@ -13,11 +13,11 @@ Formerly: scil_generate_gradient_sampling.py ------------------------------------------------------------------------------- -References: +References: [1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, - Rachid Deriche. Design of multishell gradient sampling with uniform coverage - in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, 69 (6), - pp. 1534-1540. + Rachid Deriche. Design of multishell gradient sampling with uniform + coverage in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, + 69 (6), pp. 1534-1540. ------------------------------------------------------------------------------- """ diff --git a/scripts/scil_gradients_round_bvals.py b/scripts/scil_gradients_round_bvals.py index 227619012..959a85930 100755 --- a/scripts/scil_gradients_round_bvals.py +++ b/scripts/scil_gradients_round_bvals.py @@ -29,30 +29,29 @@ def _build_arg_parser(): - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter, - epilog=version_string) - - parser.add_argument('in_bval', - help='The b-values in FSL format.') - parser.add_argument( + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bval', + help='The b-values in FSL format.') + p.add_argument( 'shells', nargs='+', type=int, help='The list of expected shells. For example 0 1000 2000.\n' 'All b-values in the b_val file should correspond to one given ' 'shell (up to the tolerance).') - parser.add_argument('out_bval', - help='The name of the output b-values.') - parser.add_argument( + p.add_argument('out_bval', + help='The name of the output b-values.') + p.add_argument( 'tolerance', type=int, help='The tolerated gap between the b-values to extract and the \n' 'actual b-values. Expecting an integer value. Comparison is \n' 'strict: a b-value of 1010 with a tolerance of 10 is NOT \n' 'included in shell 1000. Suggestion: 20.') - add_verbose_arg(parser) - add_overwrite_arg(parser) + add_verbose_arg(p) + add_overwrite_arg(p) - return parser + return p def main(): diff --git a/scripts/scil_gradients_validate_correct.py b/scripts/scil_gradients_validate_correct.py index 7721f133c..15666df17 100755 --- a/scripts/scil_gradients_validate_correct.py +++ b/scripts/scil_gradients_validate_correct.py @@ -24,8 +24,8 @@ ------------------------------------------------------------------------------ Reference: [1] Schilling KG, Yeh FC, Nath V, Hansen C, Williams O, Resnick S, Anderson AW, - Landman BA. A fiber coherence index for quality control of B-table orientation - in diffusion MRI scans. Magn Reson Imaging. 2019 May;58:82-89. + Landman BA. A fiber coherence index for quality control of B-table + orientation in diffusion MRI scans. Magn Reson Imaging. 2019 May;58:82-89. doi: 10.1016/j.mri.2019.01.018. ------------------------------------------------------------------------------ """ diff --git a/scripts/scil_mti_maps_MT.py b/scripts/scil_mti_maps_MT.py index 20941176b..bb6218186 100755 --- a/scripts/scil_mti_maps_MT.py +++ b/scripts/scil_mti_maps_MT.py @@ -79,8 +79,8 @@ Reference: [1] Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of magnetization transfer with inherent correction for RF inhomogeneity - and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. - 2008;60(6):1396-407. + and T1 relaxation obtained from 3D FLASH MRI. + Magnetic Resonance in Medicine. 2008;60(6):1396-407. --------------------------------------------------------------------------------- """ diff --git a/scripts/scil_mti_maps_ihMT.py b/scripts/scil_mti_maps_ihMT.py index d0fb1b0dc..554fca5d4 100755 --- a/scripts/scil_mti_maps_ihMT.py +++ b/scripts/scil_mti_maps_ihMT.py @@ -85,9 +85,9 @@ ---------------------------------------------------------------------------------- References: [1] Varma G, Girard OM, Prevost VH, Grant AK, Duhamel G, Alsop DC. - Interpretation of magnetization transfer from inhomogeneously broadened lines - (ihMT) in tissues as a dipolar order effect within motion restricted molecules. - Journal of Magnetic Resonance. 1 nov 2015;260:67-76. + Interpretation of magnetization transfer from inhomogeneously broadened + lines (ihMT) in tissues as a dipolar order effect within motion + restricted molecules. Journal of Magnetic Resonance. 1 nov 2015;260:67-76. [2] Manning AP, Chang KL, MacKay AL, Michal CA. The physical mechanism of "inhomogeneous" magnetization transfer MRI. Journal of Magnetic Resonance. @@ -95,8 +95,8 @@ [3] Helms G, Dathe H, Kallenberg K, Dechent P. High-resolution maps of magnetization transfer with inherent correction for RF inhomogeneity - and T1 relaxation obtained from 3D FLASH MRI. Magnetic Resonance in Medicine. - 2008;60(6):1396-407. + and T1 relaxation obtained from 3D FLASH MRI. + Magnetic Resonance in Medicine. 2008;60(6):1396-407. --------------------------------------------------------------------------------- """ From 5ee96083ed25c12bff0be695bf8c13e22d11bb6d Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Fri, 13 Dec 2024 11:38:57 -0500 Subject: [PATCH 16/63] Working on ubvecs --- scilpy/gradients/gen_gradient_sampling.py | 15 +++-- scripts/scil_gradients_validate_sampling.py | 61 +++++++++++++++------ 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/scilpy/gradients/gen_gradient_sampling.py b/scilpy/gradients/gen_gradient_sampling.py index 3e44d9897..cc2b5d323 100644 --- a/scilpy/gradients/gen_gradient_sampling.py +++ b/scilpy/gradients/gen_gradient_sampling.py @@ -300,17 +300,20 @@ def _grad_electrostatic_repulsion_energy(bvecs, weight_matrix, alpha=1.0): return grad -def compute_electrostatic_repulsion_energy(bvecs, nb_shells=1, alpha=1.0): +def compute_electrostatic_repulsion_energy(bvecs, nb_shells, + nb_points_per_shell, alpha=1.0): """ Electrostatic-repulsion objective function. The alpha parameter controls the power repulsion (energy varies as $1 / ralpha$). Parameters --------- - bvecs : array-like shape (N * 3,) + bvecs : array-like shape (N, 3,) Vectors, flattened. nb_shells : int - Number of shells. + Number of shells + nb_points_per_shell : list of ints + Number of points per shell. alpha : float Controls the power of the repulsion. Default is 1.0 @@ -327,10 +330,10 @@ def compute_electrostatic_repulsion_energy(bvecs, nb_shells=1, alpha=1.0): shell_groups += (list(range(nb_shells)),) alphas = list(len(shell_groups) * (1.0,)) - weights = _compute_weights(nb_shells, [nb_dir], + weights = _compute_weights(nb_shells, nb_points_per_shell, shell_groups, alphas) - nb_points_total = nb_dir - indices = np.cumsum(nb_dir).tolist() + nb_points_total = np.sum(nb_points_per_shell) + indices = np.cumsum(nb_points_per_shell).tolist() indices.insert(0, 0) weight_matrix = np.zeros((nb_points_total, nb_points_total)) for s1 in range(nb_shells): diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index c7cbc46b9..023e3e5d1 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -9,10 +9,15 @@ import logging import numpy as np +from dipy.core.gradients import (unique_bvals_tolerance, get_bval_indices) from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, - add_b0_thresh_arg) + add_b0_thresh_arg, add_skip_b0_check_arg, + add_tolerance_arg) +from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, + is_normalized_bvecs, + normalize_bvecs) from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, compute_electrostatic_repulsion_energy) from scilpy.viz.gradients import plot_proj_shell @@ -28,8 +33,10 @@ def _build_arg_parser(): help='') add_b0_thresh_arg(p) - add_verbose_arg(p) add_overwrite_arg(p) + add_skip_b0_check_arg(p, will_overwrite_with_min=True) + add_tolerance_arg(p) + add_verbose_arg(p) return p @@ -39,28 +46,50 @@ def main(): args = parser.parse_args() logging.getLogger().setLevel(logging.getLevelName(args.verbose)) + # Load bvals and bvecs, remove b0s bvals, bvecs = read_bvals_bvecs(args.in_bvals, args.in_bvecs) - - # ADD B0 CHECK - - no_b0_mask = bvals > args.b0_threshold - bvals = bvals[no_b0_mask] - bvecs = bvecs[no_b0_mask] - nb_dir = len(bvals) - - # Add warning when deplicates bvecs.!!!!!!!!!! - ubvecs, indices = np.unique(bvecs, return_index=True, axis=0) # This sorts the array! - # ubvals = bvals[indices] + args.b0_threshold = check_b0_threshold(bvals.min(), + b0_thr=args.b0_threshold, + skip_b0_check=args.skip_b0_check) + bvecs = bvecs[bvals > args.b0_threshold] + bvals = bvals[bvals > args.b0_threshold] + + # Checking bvecs are normalized + if not is_normalized_bvecs(bvecs): + logging.warning('Your b-vectors do not seem normalized...') + bvecs = normalize_bvecs(bvecs) + + ubvals = unique_bvals_tolerance(bvals, tol=args.tolerance) + list_indices = [get_bval_indices(bvals, shell, tol=args.tolerance) + for shell in ubvals] + nb_shells = len(ubvals) + nb_dir_per_shell = [] + ubvecs = [] + for indices in list_indices: + shell_ubvecs = np.unique(bvecs[indices], axis=0) + if len(indices) != len(shell_ubvecs): + logging.warning('Some b-vectors have the same direction, which is ' + 'suboptimal. There is most likely a problem with ' + 'the gradient table. Proceeding to validation ' + 'while keeping only unique b-vectors.') + ubvecs.extend(shell_ubvecs) + nb_dir_per_shell.append(len(shell_ubvecs)) + ubvecs = np.array(ubvecs) scipy_verbose = int(3 - logging.getLogger().getEffectiveLevel()//10) - opt_bvecs, _ = generate_gradient_sampling([nb_dir], verbose=scipy_verbose) + opt_bvecs, _ = generate_gradient_sampling(nb_dir_per_shell, + verbose=scipy_verbose) # ADD OPTION FOR VISU!!!!!!! # plot_proj_shell([ubvecs], use_sym=True) # plot_proj_shell([opt_bvecs], use_sym=True) - energy = compute_electrostatic_repulsion_energy(ubvecs) - opt_energy = compute_electrostatic_repulsion_energy(opt_bvecs) + energy = compute_electrostatic_repulsion_energy(ubvecs, + nb_shells=nb_shells, + nb_points_per_shell=nb_dir_per_shell) + opt_energy = compute_electrostatic_repulsion_energy(opt_bvecs, + nb_shells=nb_shells, + nb_points_per_shell=nb_dir_per_shell) print("Input bvecs energy: ", np.round(energy, decimals=2)) print("Optimal bvecs energy: ", np.round(opt_energy, decimals=2)) From 999b86a8814fe3ef4faa72cbacc291539dd6fff8 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Fri, 13 Dec 2024 12:10:43 -0500 Subject: [PATCH 17/63] Working on cleaning the code --- scilpy/gradients/gen_gradient_sampling.py | 16 ++++++++++++---- scripts/scil_gradients_validate_sampling.py | 10 +++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/scilpy/gradients/gen_gradient_sampling.py b/scilpy/gradients/gen_gradient_sampling.py index cc2b5d323..6ac7883c8 100644 --- a/scilpy/gradients/gen_gradient_sampling.py +++ b/scilpy/gradients/gen_gradient_sampling.py @@ -300,8 +300,8 @@ def _grad_electrostatic_repulsion_energy(bvecs, weight_matrix, alpha=1.0): return grad -def compute_electrostatic_repulsion_energy(bvecs, nb_shells, - nb_points_per_shell, alpha=1.0): +def energy_comparison(bvecs, opt_bvecs, nb_shells, nb_points_per_shell, + alpha=1.0): """ Electrostatic-repulsion objective function. The alpha parameter controls the power repulsion (energy varies as $1 / ralpha$). @@ -309,7 +309,9 @@ def compute_electrostatic_repulsion_energy(bvecs, nb_shells, Parameters --------- bvecs : array-like shape (N, 3,) - Vectors, flattened. + Inputed b-vectors. + opt_bvecs : array-like shape (N, 3,) + Optimal b-vectors. nb_shells : int Number of shells nb_points_per_shell : list of ints @@ -343,10 +345,16 @@ def compute_electrostatic_repulsion_energy(bvecs, nb_shells, epsilon = 1e-9 energy = 0.0 + opt_energy = 0.0 for i in range(nb_dir): indices = (np.arange(nb_dir) > i) diffs = ((bvecs[indices] - bvecs[i]) ** 2).sum(1) ** alpha sums = ((bvecs[indices] + bvecs[i]) ** 2).sum(1) ** alpha energy += (weight_matrix[i, indices] * (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() - return energy \ No newline at end of file + diffs = ((opt_bvecs[indices] - opt_bvecs[i]) ** 2).sum(1) ** alpha + sums = ((opt_bvecs[indices] + opt_bvecs[i]) ** 2).sum(1) ** alpha + opt_energy += (weight_matrix[i, indices] * + (1.0 / (diffs + epsilon) + 1.0 / + (sums + epsilon))).sum() + return energy, opt_energy diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 023e3e5d1..d5792651f 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -19,7 +19,7 @@ is_normalized_bvecs, normalize_bvecs) from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, - compute_electrostatic_repulsion_energy) + energy_comparison) from scilpy.viz.gradients import plot_proj_shell @@ -84,12 +84,8 @@ def main(): # plot_proj_shell([ubvecs], use_sym=True) # plot_proj_shell([opt_bvecs], use_sym=True) - energy = compute_electrostatic_repulsion_energy(ubvecs, - nb_shells=nb_shells, - nb_points_per_shell=nb_dir_per_shell) - opt_energy = compute_electrostatic_repulsion_energy(opt_bvecs, - nb_shells=nb_shells, - nb_points_per_shell=nb_dir_per_shell) + energy, opt_energy = energy_comparison(ubvecs, opt_bvecs, nb_shells, + nb_dir_per_shell) print("Input bvecs energy: ", np.round(energy, decimals=2)) print("Optimal bvecs energy: ", np.round(opt_energy, decimals=2)) From 4513b792eb85b12ede29d212ad184d8dc802340b Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Fri, 13 Dec 2024 15:56:30 -0500 Subject: [PATCH 18/63] Adding plotting with title --- scilpy/viz/gradients.py | 28 +++++++-- scripts/scil_gradients_validate_sampling.py | 70 +++++++++++++++------ 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/scilpy/viz/gradients.py b/scilpy/viz/gradients.py index 11c014fba..871fe0fdb 100644 --- a/scilpy/viz/gradients.py +++ b/scilpy/viz/gradients.py @@ -14,7 +14,7 @@ def plot_each_shell(ms, centroids, plot_sym_vecs=True, use_sphere=True, same_color=False, rad=0.025, opacity=1.0, ofile=None, - ores=(300, 300)): + ores=(300, 300), titles=None): """ Plot each shell @@ -38,6 +38,8 @@ def plot_each_shell(ms, centroids, plot_sym_vecs=True, use_sphere=True, output filename ores: tuple resolution of the output png + titles: list of str + titles for the windows, one per shell """ _colors = generate_n_colors(len(ms)) @@ -67,7 +69,19 @@ def plot_each_shell(ms, centroids, plot_sym_vecs=True, use_sphere=True, if plot_sym_vecs: pts_actor = actor.point(-shell, _colors[i], point_radius=rad) scene.add(pts_actor) - window.show(scene) + if titles is not None: + if len(titles) == len(ms): + window.show(scene, title=titles[i]) + elif isinstance(titles, str): + window.show(scene, title=titles) + elif len(titles) == 1: + window.show(scene, title=titles[0]) + else: + logging.warning('No title could be added to the windows since ' + 'the given format is incorrect.') + window.show(scene) + else: + window.show(scene) if ofile: filename = ofile + '_shell_' + str(int(centroids[i])) + '.png' @@ -79,7 +93,8 @@ def plot_each_shell(ms, centroids, plot_sym_vecs=True, use_sphere=True, def plot_proj_shell(ms, use_sym=True, use_sphere=True, same_color=False, - rad=0.025, opacity=1.0, ofile=None, ores=(300, 300)): + rad=0.025, opacity=1.0, ofile=None, ores=(300, 300), + title=None): """ Plot each shell @@ -101,6 +116,8 @@ def plot_proj_shell(ms, use_sym=True, use_sphere=True, same_color=False, output filename ores: tuple resolution of the output png + title: str + title for the window """ _colors = generate_n_colors(len(ms)) @@ -129,7 +146,10 @@ def plot_proj_shell(ms, use_sym=True, use_sphere=True, same_color=False, if use_sym: pts_actor = actor.point(-shell, _colors[i], point_radius=rad) scene.add(pts_actor) - window.show(scene) + if title is not None: + window.show(scene, title=title) + else: + window.show(scene) if ofile: filename = ofile + '.png' # Legacy. When this snapshotting gets updated to align with the diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index d5792651f..680fd9238 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -8,29 +8,36 @@ import argparse import logging import numpy as np +import os -from dipy.core.gradients import (unique_bvals_tolerance, get_bval_indices) from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, add_b0_thresh_arg, add_skip_b0_check_arg, - add_tolerance_arg) + add_tolerance_arg, assert_inputs_exist, + assert_gradients_filenames_valid) from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, is_normalized_bvecs, - normalize_bvecs) + normalize_bvecs, + identify_shells) from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, energy_comparison) -from scilpy.viz.gradients import plot_proj_shell +from scilpy.viz.gradients import (plot_proj_shell, build_ms_from_shell_idx) def _build_arg_parser(): p = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, description=__doc__) - p.add_argument('in_bvals', - help='') - p.add_argument('in_bvecs', - help='') + p.add_argument( + 'in_gradients', nargs='+', + help='Path(s) to the gradient file(s). Either FSL ' + '(.bval, .bvec) or MRtrix (.b).') + + p.add_argument( + '--visualize', action='store_true', + help='If set, the inputed gradient scheme is displayed, and then the ' + 'optimal one.') add_b0_thresh_arg(p) add_overwrite_arg(p) @@ -46,8 +53,23 @@ def main(): args = parser.parse_args() logging.getLogger().setLevel(logging.getLevelName(args.verbose)) - # Load bvals and bvecs, remove b0s - bvals, bvecs = read_bvals_bvecs(args.in_bvals, args.in_bvecs) + # Perform checks on input files and load bvals and bvecs + assert_inputs_exist(parser, args.in_gradients) + if len(args.in_gradients) == 2: + assert_gradients_filenames_valid(parser, args.in_gradients, True) + in_gradients = args.in_gradients + in_gradients.sort() + bvals, bvecs = read_bvals_bvecs(in_gradients[0], + in_gradients[1]) + elif len(args.in_gradients) == 1: + assert_gradients_filenames_valid(parser, args.in_gradients, False) + bvecs = np.genfromtxt(args.in_gradients, delimiter=' ')[:, :3] + bvals = np.genfromtxt(args.in_gradients, delimiter=' ')[:, 3] + else: + parser.error('Depending on the gradient format, you should have ' + 'two files for FSL format and one file for MRtrix') + + # Check and remove b0s args.b0_threshold = check_b0_threshold(bvals.min(), b0_thr=args.b0_threshold, skip_b0_check=args.skip_b0_check) @@ -59,31 +81,41 @@ def main(): logging.warning('Your b-vectors do not seem normalized...') bvecs = normalize_bvecs(bvecs) - ubvals = unique_bvals_tolerance(bvals, tol=args.tolerance) - list_indices = [get_bval_indices(bvals, shell, tol=args.tolerance) - for shell in ubvals] + # Find shells and duplicate b-vectors + ubvals, shell_idx = identify_shells(bvals, tol=args.tolerance, sort=True) nb_shells = len(ubvals) nb_dir_per_shell = [] ubvecs = [] - for indices in list_indices: + ushell_idx = [] + for i in range(nb_shells): + indices = shell_idx == i shell_ubvecs = np.unique(bvecs[indices], axis=0) - if len(indices) != len(shell_ubvecs): + nb_ubvecs = len(shell_ubvecs) + if np.sum(indices) != nb_ubvecs: logging.warning('Some b-vectors have the same direction, which is ' 'suboptimal. There is most likely a problem with ' 'the gradient table. Proceeding to validation ' 'while keeping only unique b-vectors.') ubvecs.extend(shell_ubvecs) - nb_dir_per_shell.append(len(shell_ubvecs)) + ushell_idx.extend(np.repeat([i], nb_ubvecs)) + nb_dir_per_shell.append(nb_ubvecs) ubvecs = np.array(ubvecs) + ushell_idx = np.array(ushell_idx) + # Compute optimally distributed directions scipy_verbose = int(3 - logging.getLogger().getEffectiveLevel()//10) opt_bvecs, _ = generate_gradient_sampling(nb_dir_per_shell, verbose=scipy_verbose) - # ADD OPTION FOR VISU!!!!!!! - # plot_proj_shell([ubvecs], use_sym=True) - # plot_proj_shell([opt_bvecs], use_sym=True) + # Visualize the gradient schemes + if args.visualize: + viz_bvecs = build_ms_from_shell_idx(ubvecs, ushell_idx) + viz_opt_bvecs = build_ms_from_shell_idx(opt_bvecs, ushell_idx) + plot_proj_shell(viz_bvecs, use_sym=True, title="Inputed b-vectors") + plot_proj_shell(viz_opt_bvecs, use_sym=True, + title="Optimized b-vectors") + # Compute the energy for both the input bvecs and optimal bvecs. energy, opt_energy = energy_comparison(ubvecs, opt_bvecs, nb_shells, nb_dir_per_shell) From f95393c0f9fed141f3ff54423edece4e5832060a Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Fri, 13 Dec 2024 16:09:28 -0500 Subject: [PATCH 19/63] WIP --- scripts/scil_gradients_validate_sampling.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 680fd9238..dbd56330a 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -119,8 +119,11 @@ def main(): energy, opt_energy = energy_comparison(ubvecs, opt_bvecs, nb_shells, nb_dir_per_shell) - print("Input bvecs energy: ", np.round(energy, decimals=2)) - print("Optimal bvecs energy: ", np.round(opt_energy, decimals=2)) + perc_comp = np.round(opt_energy / energy * 100) + print('Compared to our reference optimal set of b-vectors, the inputed ' + 'b-vectors are {}% less well distributed.'.format(perc_comp)) + + # TODO: Find a better sentence, and add a warning if the bvecs are too shit if __name__ == "__main__": From 460427d26d953010e2d5574ce440b8cd5d8238d5 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Fri, 13 Dec 2024 16:15:23 -0500 Subject: [PATCH 20/63] WIP --- scripts/scil_gradients_validate_sampling.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index dbd56330a..1536b4385 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -8,7 +8,6 @@ import argparse import logging import numpy as np -import os from dipy.io.gradients import read_bvals_bvecs From fa68d873ec4572b189ca4b4edd01ae0961230aac Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 11:27:21 -0500 Subject: [PATCH 21/63] Fixing b0 check --- scilpy/viz/gradients.py | 4 +- scripts/scil_gradients_validate_sampling.py | 64 ++++++++++++--------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/scilpy/viz/gradients.py b/scilpy/viz/gradients.py index 871fe0fdb..ef315a4b4 100644 --- a/scilpy/viz/gradients.py +++ b/scilpy/viz/gradients.py @@ -60,7 +60,7 @@ def plot_each_shell(ms, centroids, plot_sym_vecs=True, use_sphere=True, scene = window.Scene() scene.SetBackground(1, 1, 1) if use_sphere: - sphere_actor = actor.odf_slicer(odfs, affine, sphere=sphere, + sphere_actor = actor.odf_slicer(odfs, affine=affine, sphere=sphere, colormap='winter', scale=1.0, opacity=opacity) scene.add(sphere_actor) @@ -132,7 +132,7 @@ def plot_proj_shell(ms, use_sym=True, use_sphere=True, same_color=False, odfs[:] = 1 odfs[..., 0] = 1 affine = np.eye(4) - sphere_actor = actor.odf_slicer(odfs, affine, sphere=sphere, + sphere_actor = actor.odf_slicer(odfs, affine=affine, sphere=sphere, colormap='winter', scale=1.0, opacity=opacity) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 1536b4385..639e95056 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -12,7 +12,7 @@ from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, - add_b0_thresh_arg, add_skip_b0_check_arg, + add_b0_thresh_arg, add_tolerance_arg, assert_inputs_exist, assert_gradients_filenames_valid) from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, @@ -40,7 +40,6 @@ def _build_arg_parser(): add_b0_thresh_arg(p) add_overwrite_arg(p) - add_skip_b0_check_arg(p, will_overwrite_with_min=True) add_tolerance_arg(p) add_verbose_arg(p) @@ -69,9 +68,25 @@ def main(): 'two files for FSL format and one file for MRtrix') # Check and remove b0s - args.b0_threshold = check_b0_threshold(bvals.min(), - b0_thr=args.b0_threshold, - skip_b0_check=args.skip_b0_check) + if args.b0_threshold > 20: + logging.warning( + 'Your defined b0 threshold is {}. This is suspicious. We ' + 'recommend using volumes with bvalues no higher than {} as b0s' + .format(args.b0_threshold, 20)) + + if bvals.min() < 0: + logging.warning( + 'Warning: Your dataset contains negative b-values (minimal bvalue ' + 'of {}). This is suspicious. We recommend you check your data.' + .format(bvals.min())) + + if bvals.min() > args.b0_threshold: + logging.warning( + 'Your minimal bvalue ({}) is above the threshold ({}). Please ' + 'check your data to ensure everything is correct.\n' + 'You may also increase the threshold with --b0_threshold. ' + 'The script will continue without b0s.' + .format(bvals.min(), args.b0_threshold)) bvecs = bvecs[bvals > args.b0_threshold] bvals = bvals[bvals > args.b0_threshold] @@ -83,23 +98,15 @@ def main(): # Find shells and duplicate b-vectors ubvals, shell_idx = identify_shells(bvals, tol=args.tolerance, sort=True) nb_shells = len(ubvals) - nb_dir_per_shell = [] - ubvecs = [] - ushell_idx = [] - for i in range(nb_shells): - indices = shell_idx == i - shell_ubvecs = np.unique(bvecs[indices], axis=0) - nb_ubvecs = len(shell_ubvecs) - if np.sum(indices) != nb_ubvecs: - logging.warning('Some b-vectors have the same direction, which is ' - 'suboptimal. There is most likely a problem with ' - 'the gradient table. Proceeding to validation ' - 'while keeping only unique b-vectors.') - ubvecs.extend(shell_ubvecs) - ushell_idx.extend(np.repeat([i], nb_ubvecs)) - nb_dir_per_shell.append(nb_ubvecs) - ubvecs = np.array(ubvecs) - ushell_idx = np.array(ushell_idx) + nb_dir_per_shell = [np.sum(shell_idx == idx) for idx in range(nb_shells)] + + ubvecs = np.unique(bvecs, axis=0) + nb_ubvecs = len(ubvecs) + if len(bvecs) != nb_ubvecs: + raise ValueError('{} b-vectors have the same direction as others, ' + 'which is suboptimal. There is most likely a problem ' + 'with the gradient table.' + .format(len(bvecs) - nb_ubvecs)) # Compute optimally distributed directions scipy_verbose = int(3 - logging.getLogger().getEffectiveLevel()//10) @@ -108,19 +115,24 @@ def main(): # Visualize the gradient schemes if args.visualize: - viz_bvecs = build_ms_from_shell_idx(ubvecs, ushell_idx) - viz_opt_bvecs = build_ms_from_shell_idx(opt_bvecs, ushell_idx) + viz_bvecs = build_ms_from_shell_idx(bvecs, shell_idx) + viz_opt_bvecs = build_ms_from_shell_idx(opt_bvecs, shell_idx) plot_proj_shell(viz_bvecs, use_sym=True, title="Inputed b-vectors") plot_proj_shell(viz_opt_bvecs, use_sym=True, title="Optimized b-vectors") # Compute the energy for both the input bvecs and optimal bvecs. - energy, opt_energy = energy_comparison(ubvecs, opt_bvecs, nb_shells, + energy, opt_energy = energy_comparison(bvecs, opt_bvecs, nb_shells, nb_dir_per_shell) perc_comp = np.round(opt_energy / energy * 100) print('Compared to our reference optimal set of b-vectors, the inputed ' - 'b-vectors are {}% less well distributed.'.format(perc_comp)) + 'b-vectors are {}% less optimally distributed.'.format(perc_comp)) + + logging.info('The calculated electrostatic-like repulsion energy for the ' + 'optimal b-vectors is: {}'.format(opt_energy)) + logging.info('The calculated electrostatic-like repulsion energy for the ' + 'inputed b-vectors is: {}'.format(energy)) # TODO: Find a better sentence, and add a warning if the bvecs are too shit From c86ab42ce7cf420677542ac7169308aa70e57ce6 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 11:51:24 -0500 Subject: [PATCH 22/63] Adding logging --- scripts/scil_gradients_validate_sampling.py | 41 ++++++++++++--------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 639e95056..184399e12 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -15,8 +15,7 @@ add_b0_thresh_arg, add_tolerance_arg, assert_inputs_exist, assert_gradients_filenames_valid) -from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, - is_normalized_bvecs, +from scilpy.gradients.bvec_bval_tools import (is_normalized_bvecs, normalize_bvecs, identify_shells) from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, @@ -28,10 +27,15 @@ def _build_arg_parser(): p = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, description=__doc__) + p.add_argument('in_gradients', nargs='+', + help='Path(s) to the gradient file(s). Either FSL ' + '(.bval, .bvec) or MRtrix (.b).') + p.add_argument( - 'in_gradients', nargs='+', - help='Path(s) to the gradient file(s). Either FSL ' - '(.bval, .bvec) or MRtrix (.b).') + '--max_ratio', default=1.1, type=float, + help='Maximum value for the ratio between the inputed b-vectors\' ' + 'energy \nand the optimal b-vectors\' energy. ' + '(input_energy/optimal_energy)[%(default)s]') p.add_argument( '--visualize', action='store_true', @@ -68,6 +72,7 @@ def main(): 'two files for FSL format and one file for MRtrix') # Check and remove b0s + # Note: this part will become check_b0_threshold once it is fixed if args.b0_threshold > 20: logging.warning( 'Your defined b0 threshold is {}. This is suspicious. We ' @@ -109,9 +114,8 @@ def main(): .format(len(bvecs) - nb_ubvecs)) # Compute optimally distributed directions - scipy_verbose = int(3 - logging.getLogger().getEffectiveLevel()//10) opt_bvecs, _ = generate_gradient_sampling(nb_dir_per_shell, - verbose=scipy_verbose) + verbose=0) # Visualize the gradient schemes if args.visualize: @@ -124,17 +128,20 @@ def main(): # Compute the energy for both the input bvecs and optimal bvecs. energy, opt_energy = energy_comparison(bvecs, opt_bvecs, nb_shells, nb_dir_per_shell) - - perc_comp = np.round(opt_energy / energy * 100) - print('Compared to our reference optimal set of b-vectors, the inputed ' - 'b-vectors are {}% less optimally distributed.'.format(perc_comp)) - logging.info('The calculated electrostatic-like repulsion energy for the ' - 'optimal b-vectors is: {}'.format(opt_energy)) - logging.info('The calculated electrostatic-like repulsion energy for the ' - 'inputed b-vectors is: {}'.format(energy)) - - # TODO: Find a better sentence, and add a warning if the bvecs are too shit + logging.info('\nThe quality of inputed b-vectors is assessed by computing ' + 'their electrostatic-like repulsion \nenergy and comparing ' + 'it with the energy of a reference optimal set of b-vectors.') + logging.info('\nEnergy for the optimal b-vectors: {}' + '\nEnergy for the inputed b-vectors: {}' + .format(np.round(opt_energy, decimals=3), + np.round(energy, decimals=3))) + e_ratio = energy / opt_energy + if e_ratio > args.max_ratio: + logging.warning('\nThe inputed b-vectors seem to be ill-distributed ' + 'on the sphere. \nTheir energy is {} times higher ' + 'than the optimal energy.' + .format(np.round(e_ratio, decimals=3))) if __name__ == "__main__": From 13ad3ab40b04feffd6c45a7a15f24fc35be84317 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 12:05:11 -0500 Subject: [PATCH 23/63] Add description --- scripts/scil_gradients_validate_sampling.py | 37 ++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 184399e12..d55a70a65 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -2,7 +2,25 @@ # -*- coding: utf-8 -*- """ - +Validate the sampling of a gradient table, in terms of how well distributed on +the sphere the b-vectors are. + +To do so, the script compares the electrostatic-like repulsion energy [1] of +the inputed b-vectors with the energy of optimally distributed b-vectors. The +same number of directions per shell (the script supports multi-shell) as the +input b-vectors are used to generate the optimal b-vectors as in [1]. It is +possible that the inputed b-vectors are better distributed than the optimal +ones. + +The script starts by looking for b0s, to remove them from the analysis. Then, +it looks for duplicate b-vectors. Finally, both energies are computed and +compared as the ratio between the inputed b-vectors' energy and the optimal +b-vectors' energy (input_energy/optimal_energy). Above a given maximum ratio +value, the script raises a warning. + +The user might want to use the -v verbose option to see the computed energies. +The --visualize option displays both the inputed and optimal b-vectors on a +single shell. """ import argparse @@ -23,10 +41,19 @@ from scilpy.viz.gradients import (plot_proj_shell, build_ms_from_shell_idx) +EPILOG = """ +References: [1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, +Rachid Deriche. Design of multishell gradient sampling with uniform coverage +in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, 69 (6), +pp. 1534-1540. + """ + + def _build_arg_parser(): p = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + description=__doc__, + epilog=EPILOG) p.add_argument('in_gradients', nargs='+', help='Path(s) to the gradient file(s). Either FSL ' '(.bval, .bvec) or MRtrix (.b).') @@ -34,8 +61,8 @@ def _build_arg_parser(): p.add_argument( '--max_ratio', default=1.1, type=float, help='Maximum value for the ratio between the inputed b-vectors\' ' - 'energy \nand the optimal b-vectors\' energy. ' - '(input_energy/optimal_energy)[%(default)s]') + 'energy \nand the optimal b-vectors\' energy ' + '(input_energy/optimal_energy).[%(default)s]') p.add_argument( '--visualize', action='store_true', @@ -43,9 +70,9 @@ def _build_arg_parser(): 'optimal one.') add_b0_thresh_arg(p) - add_overwrite_arg(p) add_tolerance_arg(p) add_verbose_arg(p) + add_overwrite_arg(p) return p From 2c332fea151c64896ec8187b6cf3e2b50a4205c4 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 14:04:40 -0500 Subject: [PATCH 24/63] Add doc to energy_comparison --- scilpy/gradients/gen_gradient_sampling.py | 41 +++++++++++---------- scripts/scil_gradients_validate_sampling.py | 4 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/scilpy/gradients/gen_gradient_sampling.py b/scilpy/gradients/gen_gradient_sampling.py index 6ac7883c8..09cf04aed 100644 --- a/scilpy/gradients/gen_gradient_sampling.py +++ b/scilpy/gradients/gen_gradient_sampling.py @@ -300,18 +300,18 @@ def _grad_electrostatic_repulsion_energy(bvecs, weight_matrix, alpha=1.0): return grad -def energy_comparison(bvecs, opt_bvecs, nb_shells, nb_points_per_shell, +def energy_comparison(bvecs1, bvecs2, nb_shells, nb_points_per_shell, alpha=1.0): """ - Electrostatic-repulsion objective function. The alpha parameter controls - the power repulsion (energy varies as $1 / ralpha$). + Compute the electrostatic-repulsion energy of two sets of b-vectors with + the same number of directions per shell. Parameters --------- - bvecs : array-like shape (N, 3,) - Inputed b-vectors. - opt_bvecs : array-like shape (N, 3,) - Optimal b-vectors. + bvecs1 : array-like shape (N, 3,) + First set of b-vectors. + bvecs2 : array-like shape (N, 3,) + Second set of b-vectors. nb_shells : int Number of shells nb_points_per_shell : list of ints @@ -321,11 +321,12 @@ def energy_comparison(bvecs, opt_bvecs, nb_shells, nb_points_per_shell, Returns ------- - energy : float - sum of all interactions between any two vectors. + energy1 : float + Electrostatic-repulsion energy of set bvecs1. + energy2 : float + Electrostatic-repulsion energy of set bvecs2. """ - # Groups of shells and relative coupling weights - nb_dir = bvecs.shape[0] + nb_dir = bvecs1.shape[0] shell_groups = () for i in range(nb_shells): shell_groups += ([i],) @@ -344,17 +345,17 @@ def energy_comparison(bvecs, opt_bvecs, nb_shells, nb_points_per_shell, indices[s2]:indices[s2 + 1]] = weights[s1, s2] epsilon = 1e-9 - energy = 0.0 - opt_energy = 0.0 + energy1 = 0.0 + energy2 = 0.0 for i in range(nb_dir): indices = (np.arange(nb_dir) > i) - diffs = ((bvecs[indices] - bvecs[i]) ** 2).sum(1) ** alpha - sums = ((bvecs[indices] + bvecs[i]) ** 2).sum(1) ** alpha - energy += (weight_matrix[i, indices] * + diffs = ((bvecs1[indices] - bvecs1[i]) ** 2).sum(1) ** alpha + sums = ((bvecs1[indices] + bvecs1[i]) ** 2).sum(1) ** alpha + energy1 += (weight_matrix[i, indices] * (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() - diffs = ((opt_bvecs[indices] - opt_bvecs[i]) ** 2).sum(1) ** alpha - sums = ((opt_bvecs[indices] + opt_bvecs[i]) ** 2).sum(1) ** alpha - opt_energy += (weight_matrix[i, indices] * + diffs = ((bvecs2[indices] - bvecs2[i]) ** 2).sum(1) ** alpha + sums = ((bvecs2[indices] + bvecs2[i]) ** 2).sum(1) ** alpha + energy2 += (weight_matrix[i, indices] * (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() - return energy, opt_energy + return energy1, energy2 diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index d55a70a65..aaf39d014 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -59,10 +59,10 @@ def _build_arg_parser(): '(.bval, .bvec) or MRtrix (.b).') p.add_argument( - '--max_ratio', default=1.1, type=float, + '--max_ratio', default=1.25, type=float, help='Maximum value for the ratio between the inputed b-vectors\' ' 'energy \nand the optimal b-vectors\' energy ' - '(input_energy/optimal_energy).[%(default)s]') + '(input_energy/optimal_energy). [%(default)s]') p.add_argument( '--visualize', action='store_true', From f2abe59772c6acdb4a07bf8ceab7ca6a6d30f698 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 14:32:23 -0500 Subject: [PATCH 25/63] Adding tests --- scilpy/gradients/gen_gradient_sampling.py | 4 +-- scripts/scil_gradients_validate_sampling.py | 14 ++++----- .../tests/test_gradients_validate_sampling.py | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 scripts/tests/test_gradients_validate_sampling.py diff --git a/scilpy/gradients/gen_gradient_sampling.py b/scilpy/gradients/gen_gradient_sampling.py index 09cf04aed..454e49fb6 100644 --- a/scilpy/gradients/gen_gradient_sampling.py +++ b/scilpy/gradients/gen_gradient_sampling.py @@ -352,10 +352,10 @@ def energy_comparison(bvecs1, bvecs2, nb_shells, nb_points_per_shell, diffs = ((bvecs1[indices] - bvecs1[i]) ** 2).sum(1) ** alpha sums = ((bvecs1[indices] + bvecs1[i]) ** 2).sum(1) ** alpha energy1 += (weight_matrix[i, indices] * - (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() + (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() diffs = ((bvecs2[indices] - bvecs2[i]) ** 2).sum(1) ** alpha sums = ((bvecs2[indices] + bvecs2[i]) ** 2).sum(1) ** alpha energy2 += (weight_matrix[i, indices] * - (1.0 / (diffs + epsilon) + 1.0 / + (1.0 / (diffs + epsilon) + 1.0 / (sums + epsilon))).sum() return energy1, energy2 diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index aaf39d014..6abc64dc5 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -113,12 +113,12 @@ def main(): .format(bvals.min())) if bvals.min() > args.b0_threshold: - logging.warning( - 'Your minimal bvalue ({}) is above the threshold ({}). Please ' - 'check your data to ensure everything is correct.\n' - 'You may also increase the threshold with --b0_threshold. ' - 'The script will continue without b0s.' - .format(bvals.min(), args.b0_threshold)) + logging.warning( + 'Your minimal bvalue ({}) is above the threshold ({}). Please ' + 'check your data to ensure everything is correct.\n' + 'You may also increase the threshold with --b0_threshold. ' + 'The script will continue without b0s.' + .format(bvals.min(), args.b0_threshold)) bvecs = bvecs[bvals > args.b0_threshold] bvals = bvals[bvals > args.b0_threshold] @@ -155,7 +155,7 @@ def main(): # Compute the energy for both the input bvecs and optimal bvecs. energy, opt_energy = energy_comparison(bvecs, opt_bvecs, nb_shells, nb_dir_per_shell) - + logging.info('\nThe quality of inputed b-vectors is assessed by computing ' 'their electrostatic-like repulsion \nenergy and comparing ' 'it with the energy of a reference optimal set of b-vectors.') diff --git a/scripts/tests/test_gradients_validate_sampling.py b/scripts/tests/test_gradients_validate_sampling.py new file mode 100644 index 000000000..23298d89d --- /dev/null +++ b/scripts/tests/test_gradients_validate_sampling.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import tempfile + +from scilpy import SCILPY_HOME +from scilpy.io.fetcher import fetch_data, get_testing_files_dict + +# If they already exist, this only takes 5 seconds (check md5sum) +fetch_data(get_testing_files_dict(), keys=['processing.zip']) +tmp_dir = tempfile.TemporaryDirectory() + + +def test_help_option(script_runner): + ret = script_runner.run('scil_gradients_validate_sampling.py', '--help') + assert ret.success + + +def test_execution_normal(script_runner, monkeypatch): + monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) + in_bval = os.path.join(SCILPY_HOME, 'processing', + '1000.bval') + in_bvec = os.path.join(SCILPY_HOME, 'processing', + '1000.bvec') + + ret = script_runner.run('scil_gradients_validate_sampling.py', in_bval, + in_bvec) + assert ret.success From 48e6e499bb85d80573d0315b63b8c8584badfac3 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Mon, 16 Dec 2024 15:09:54 -0500 Subject: [PATCH 26/63] Logging error instead of valueError --- scripts/scil_gradients_validate_sampling.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 6abc64dc5..006e8335d 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -135,10 +135,10 @@ def main(): ubvecs = np.unique(bvecs, axis=0) nb_ubvecs = len(ubvecs) if len(bvecs) != nb_ubvecs: - raise ValueError('{} b-vectors have the same direction as others, ' - 'which is suboptimal. There is most likely a problem ' - 'with the gradient table.' - .format(len(bvecs) - nb_ubvecs)) + logging.error('{} b-vectors have the same direction as others, ' + 'which is suboptimal. There is most likely a problem ' + 'with the gradient table.' + .format(len(bvecs) - nb_ubvecs)) # Compute optimally distributed directions opt_bvecs, _ = generate_gradient_sampling(nb_dir_per_shell, From ba80d07049730ce189a3f31ce9d52145ae761edd Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Tue, 17 Dec 2024 13:41:03 -0500 Subject: [PATCH 27/63] doc and test fix --- scilpy/tractanalysis/fibertube_scoring.py | 10 ++++++---- scripts/scil_fibertube_compute_density.py | 10 ++++++---- scripts/tests/test_fibertube_compute_density.py | 11 ++++++++++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index 53bc1b23b..3aade91c1 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -20,10 +20,12 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): Estimates the per-voxel volumetric density of a set of fibertubes. In other words, how much space is occupied by fibertubes and how much is emptiness. - Works by building a binary mask segmenting voxels that contain at least - a single fibertube. Then, valid voxels are finely sampled and we count the - number of samples that landed within a fibertube. For each voxel, this - number is then divided by its total amount of samples. + 1. Segments voxels that contain at least a single fibertube. + 2. Valid voxels are finely sampled and we count the number of samples that + landed within a fibertube. For each voxel, this number is then divided by + its total amount of samples. + 3. By doing the same steps for samples that landed within 2 or more + fibertubes, we can create a density map of the fibertube collisions. Parameters ---------- diff --git a/scripts/scil_fibertube_compute_density.py b/scripts/scil_fibertube_compute_density.py index 0ee95d3fa..5672ad8aa 100755 --- a/scripts/scil_fibertube_compute_density.py +++ b/scripts/scil_fibertube_compute_density.py @@ -5,10 +5,12 @@ Estimates the per-voxel volumetric density of a set of fibertubes. In other words, how much space is occupied by fibertubes and how much is emptiness. -Works by building a binary mask segmenting voxels that contain at least -a single fibertube. Then, valid voxels are finely sampled and we count the -number of samples that landed within a fibertube. For each voxel, this -number is then divided by its total amount of samples. +1. Segments voxels that contain at least a single fibertube. +2. Valid voxels are finely sampled and we count the number of samples that +landed within a fibertube. For each voxel, this number is then divided by +its total amount of samples. +3. By doing the same steps for samples that landed within 2 or more +fibertubes, we can create a density map of the fibertube collisions. See also: - docs/source/documentation/fibertube_tracking.rst diff --git a/scripts/tests/test_fibertube_compute_density.py b/scripts/tests/test_fibertube_compute_density.py index 60f822951..677b7daca 100644 --- a/scripts/tests/test_fibertube_compute_density.py +++ b/scripts/tests/test_fibertube_compute_density.py @@ -42,7 +42,7 @@ def test_help_option(script_runner): assert ret.success -def test_execution(script_runner, monkeypatch): +def test_execution_density(script_runner, monkeypatch): monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) init_data() ret = script_runner.run('scil_fibertube_compute_density.py', @@ -50,6 +50,15 @@ def test_execution(script_runner, monkeypatch): '--out_density_map', 'density_map.nii.gz', '--out_density_measures', 'density_measures.json', + '-f') + assert ret.success + + +def test_execution_collisions(script_runner, monkeypatch): + monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) + init_data() + ret = script_runner.run('scil_fibertube_compute_density.py', + 'fibertubes.trk', '--out_collision_map', 'collision_map.nii.gz', '--out_collision_measures', 'collision_measures.json', From ac0c4a1b5d855e4caed1faaf49c7bc37e4aad331 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Wed, 18 Dec 2024 11:50:37 -0500 Subject: [PATCH 28/63] Fix for PR --- scilpy/tractanalysis/fibertube_scoring.py | 18 +++++++----------- scripts/scil_fibertube_compute_density.py | 18 ++++++++---------- scripts/scil_fibertube_score_tractogram.py | 4 ++-- .../tests/test_fibertube_compute_density.py | 4 ++-- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index 3aade91c1..0ac6f3ea9 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -12,7 +12,7 @@ dist_segment_segment, dist_point_segment) from scilpy.tracking.utils import tqdm_if_verbose -from scilpy.tractanalysis.todi import TrackOrientationDensityImaging +from scilpy.tractograms.uncompress import uncompress def fibertube_density(sft, samples_per_voxel_axis, verbose=False): @@ -61,18 +61,14 @@ def fibertube_density(sft, samples_per_voxel_axis, verbose=False): len(sft.streamlines)) max_diameter = np.max(diameters) - # Everything will be in vox for simplicity. + # Everything will be in vox and corner for uncompress. sft.to_vox() - # Building a binary mask using TODI - # Because compute_todi expects streamline points (in voxel coordinates) - # to be in the range [0, size] rather than [-0.5, size - 0.5], we shift - # the voxel origin to corner. sft.to_corner() - _, data_shape, _, _ = sft.space_attributes - todi_obj = TrackOrientationDensityImaging(tuple(data_shape)) - todi_obj.compute_todi(sft.streamlines, False) - mask = todi_obj.get_mask() - mask = todi_obj.reshape_to_3d(mask) + vox_idx_for_streamline = uncompress(sft.streamlines) + mask_idx = np.concatenate(vox_idx_for_streamline) + mask = np.zeros((sft.dimensions), dtype=np.uint8) + # Numpy array indexing in 3D works like this + mask[mask_idx[:, 0], mask_idx[:, 1], mask_idx[:, 2]] = 1 sampling_density = np.array([samples_per_voxel_axis, samples_per_voxel_axis, diff --git a/scripts/scil_fibertube_compute_density.py b/scripts/scil_fibertube_compute_density.py index 5672ad8aa..e71bbbd9f 100755 --- a/scripts/scil_fibertube_compute_density.py +++ b/scripts/scil_fibertube_compute_density.py @@ -47,7 +47,7 @@ def _build_arg_parser(): 'scil_tractogram_filter_collisions.py.') p.add_argument('--out_density_map', default=None, type=str, - help='Path of the output volumetric density image file') + help='Path of the density Nifti image.') p.add_argument('--out_density_measures', default=None, type=str, help='Path of the output file containing central \n' @@ -55,7 +55,7 @@ def _build_arg_parser(): '(Must be .json)') p.add_argument('--out_collision_map', default=None, type=str, - help='Path of the output collision density image file') + help='Path of the collision Nifti image.') p.add_argument('--out_collision_measures', default=None, type=str, help='Path of the output file containing central \n' @@ -105,18 +105,17 @@ def main(): sft.to_center() if "diameters" not in sft.data_per_streamline: - parser.error('No diameters found as data per streamline on ' + + parser.error('No diameters found as data per streamline in ' + args.in_fibertubes) logging.debug('Computing fibertube density') (density_grid, density_flat, collision_grid, - collision_flat) = fibertube_density(sft, 10, - args.verbose == 'WARNING') + collision_flat) = fibertube_density(sft, 10, args.verbose == 'WARNING') logging.debug('Saving output') - header = nib.nifti1.Nifti1Header() + header = nib.Nifti1Header() extra = { 'affine': sft.affine, 'dimensions': sft.dimensions, @@ -125,8 +124,7 @@ def main(): } if args.out_density_map: - density_img = nib.nifti1.Nifti1Image(density_grid, sft.affine, header, - extra) + density_img = nib.Nifti1Image(density_grid, sft.affine, header, extra) nib.save(density_img, args.out_density_map) if args.out_density_measures: @@ -141,8 +139,8 @@ def main(): sort_keys=args.sort_keys) if args.out_collision_map: - collision_img = nib.nifti1.Nifti1Image(collision_grid, sft.affine, - header, extra) + collision_img = nib.Nifti1Image(collision_grid, sft.affine, header, + extra) nib.save(collision_img, args.out_collision_map) if args.out_collision_measures: diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index b8272c148..f45de7ff9 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -170,7 +170,7 @@ def main(): centerlines) if "diameters" not in truth_sft.data_per_streamline: - parser.error('No diameters found as data per streamline on ' + + parser.error('No diameters found as data per streamline in ' + args.in_fibertubes) diameters = np.reshape(truth_sft.data_per_streamline['diameters'], len(centerlines)) @@ -184,7 +184,7 @@ def main(): logging.debug("Loading seeds") if "seeds" not in in_sft.data_per_streamline: - parser.error('No seeds found as data per streamline on ' + + parser.error('No seeds found as data per streamline in ' + args.in_tracking) seeds = in_sft.data_per_streamline['seeds'] diff --git a/scripts/tests/test_fibertube_compute_density.py b/scripts/tests/test_fibertube_compute_density.py index 677b7daca..ca05c9b0b 100644 --- a/scripts/tests/test_fibertube_compute_density.py +++ b/scripts/tests/test_fibertube_compute_density.py @@ -19,14 +19,14 @@ def init_data(): mask = np.ones((15, 15, 15)) affine = np.eye(4) - header = nib.nifti1.Nifti1Header() + header = nib.Nifti1Header() extra = { 'affine': affine, 'dimensions': (15, 15, 15), 'voxel_size': 1., 'voxel_order': "RAS" } - mask_img = nib.nifti2.Nifti1Image(mask, affine, header, extra) + mask_img = nib.Nifti1Image(mask, affine, header, extra) sft_fibertubes = StatefulTractogram(streamlines, mask_img, Space.VOX, Origin.NIFTI) From 6fd2ebeacd4c2deb1793303dc2906bef5a45a3dc Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 19 Dec 2024 13:51:18 -0500 Subject: [PATCH 29/63] Allow importing 1 dps value, to be mapped to each streamline. --- scripts/scil_tractogram_dps_math.py | 38 ++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/scripts/scil_tractogram_dps_math.py b/scripts/scil_tractogram_dps_math.py index 02837e07d..1043562d9 100755 --- a/scripts/scil_tractogram_dps_math.py +++ b/scripts/scil_tractogram_dps_math.py @@ -61,9 +61,15 @@ def _build_arg_parser(): '"delete" operations.') import_args = p.add_argument_group('Operation "import" mandatory options') - import_args.add_argument('--in_dps_file', + import_excl = import_args.add_mutually_exclusive_group() + import_excl.add_argument('--in_dps_file', help='File containing the data to import to\n' - 'streamlines (.txt, .npy or .mat).') + 'streamlines (.txt, .npy or .mat). There\n' + 'must be the same amount of entries as\n' + 'there are streamlines.') + import_excl.add_argument('--in_dps_file_single_value', + help='File containing a single value to import\n' + 'to each streamlines (.txt, .npy or .mat).') export_args = p.add_argument_group('Operation "export" mandatory options') export_args.add_argument('--out_dps_file', @@ -98,9 +104,15 @@ def main(): sft = load_tractogram_with_reference(parser, args, args.in_tractogram) if args.operation == 'import': - if args.in_dps_file is None: - parser.error('The --in_dps_file option is required for ' + - 'the "import" operation.') + if args.in_dps_file: + dps_file = args.in_dps_file + else: + dps_file = args.in_dps_file_single_value + + if dps_file is None: + parser.error('One of --in_dps_file or ' + + '--in_dps_file_single_value is required for the ' + + '"import" operation.') if args.out_tractogram is None: parser.error('The --out_tractogram option is required for ' + @@ -113,13 +125,15 @@ def main(): ' overwriting.'.format(args.dps_key)) # Load data and remove extraneous dimensions - data = np.squeeze(load_matrix_in_any_format(args.in_dps_file)) - - # Quick check as the built-in error from sft is not too explicit - if len(sft) != data.shape[0]: - raise ValueError('Data must have as many entries ({}) as there are' - ' streamlines ({}).'.format(data.shape[0], - len(sft))) + data = np.squeeze(load_matrix_in_any_format(dps_file)) + + # Validate data shape + if args.in_dps_file and len(sft) != data.shape[0]: + raise ValueError( + 'Data must have as many entries ({}) as there are ' + 'streamlines ({}).'.format(data.shape[0], len(sft))) + if args.in_dps_file_single_value: + data = [data] * len(sft.streamlines) sft.data_per_streamline[args.dps_key] = data From cf1dced3ccb09bcc46676fb05bd0de6c201f039c Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 19 Dec 2024 13:54:58 -0500 Subject: [PATCH 30/63] Ad test for last commit feature --- scripts/tests/test_tractogram_dps_math.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/tests/test_tractogram_dps_math.py b/scripts/tests/test_tractogram_dps_math.py index 03b758de2..418590a94 100644 --- a/scripts/tests/test_tractogram_dps_math.py +++ b/scripts/tests/test_tractogram_dps_math.py @@ -37,6 +37,22 @@ def test_execution_dps_math_import(script_runner, monkeypatch): assert ret.success +def test_execution_dps_math_import_single_value(script_runner, + monkeypatch): + monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) + in_bundle = os.path.join(SCILPY_HOME, 'filtering', + 'bundle_4.trk') + filename = 'vals.npy' + outname = 'out.trk' + np.save(filename, [1.1]) + ret = script_runner.run('scil_tractogram_dps_math.py', + in_bundle, 'import', 'key', + '--in_dps_file_single_value', filename, + '--out_tractogram', outname, + '-f') + assert ret.success + + def test_execution_dps_math_import_with_missing_vals(script_runner, monkeypatch): monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) From c1ff5db82d00db0212e8037b521588a8108c9046 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Thu, 19 Dec 2024 14:02:20 -0500 Subject: [PATCH 31/63] Update doc to allow fibertubes with collisions. --- scripts/scil_fibertube_compute_density.py | 13 +++++++------ scripts/scil_fibertube_score_tractogram.py | 10 +++------- scripts/scil_fibertube_tracking.py | 13 +++++++------ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/scripts/scil_fibertube_compute_density.py b/scripts/scil_fibertube_compute_density.py index e71bbbd9f..93f8e2204 100755 --- a/scripts/scil_fibertube_compute_density.py +++ b/scripts/scil_fibertube_compute_density.py @@ -12,6 +12,11 @@ 3. By doing the same steps for samples that landed within 2 or more fibertubes, we can create a density map of the fibertube collisions. +To form fibertubes from a set of streamlines, you can use the scripts: +- scil_tractogram_filter_collisions.py to assign a diameter to each streamline + and remove all colliding fibertubes. +- scil_tractogram_dps_math.py to assign a diameter without filtering. + See also: - docs/source/documentation/fibertube_tracking.rst """ @@ -39,12 +44,8 @@ def _build_arg_parser(): p.add_argument('in_fibertubes', help='Path to the tractogram (must be .trk) file \n' - 'containing fibertubes. They must be: \n' - '1- Void of any collision. \n' - '2- With their respective diameter saved \n' - 'as data_per_streamline. \n' - 'For both of these requirements, see \n' - 'scil_tractogram_filter_collisions.py.') + 'containing fibertubes. They must have their \n' + 'respective diameter saved as data_per_streamline.') p.add_argument('--out_density_map', default=None, type=str, help='Path of the density Nifti image.') diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index f45de7ff9..9e49a11ae 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -80,13 +80,9 @@ def _build_arg_parser(): formatter_class=argparse.RawTextHelpFormatter) p.add_argument('in_fibertubes', - help='Path to the tractogram file (must be .trk) \n' - 'containing ground-truth fibertubes. They must be: \n' - '1- Void of any collision. \n' - '2- With their respective diameter saved \n' - 'as data_per_streamline. \n' - 'For both of these requirements, see \n' - 'scil_tractogram_filter_collisions.') + help='Path to the tractogram (must be .trk) file \n' + 'containing fibertubes. They must have their \n' + 'respective diameter saved as data_per_streamline.') p.add_argument('in_tracking', help='Path to the tractogram file (must be .trk) \n' diff --git a/scripts/scil_fibertube_tracking.py b/scripts/scil_fibertube_tracking.py index 2ef4b48a9..fdbb5b508 100644 --- a/scripts/scil_fibertube_tracking.py +++ b/scripts/scil_fibertube_tracking.py @@ -22,6 +22,11 @@ Seeding is done within the first segment of each fibertube. +To form fibertubes from a set of streamlines, you can use the scripts: +- scil_tractogram_filter_collisions.py to assign a diameter to each streamline + and remove all colliding fibertubes. +- scil_tractogram_dps_math.py to assign a diameter without filtering. + For a better understanding of Fibertube Tracking please see: - docs/source/documentation/fibertube_tracking.rst """ @@ -57,12 +62,8 @@ def _build_arg_parser(): p.add_argument('in_fibertubes', help='Path to the tractogram (must be .trk) file \n' - 'containing fibertubes. They must be: \n' - '1- Void of any collision. \n' - '2- With their respective diameter saved \n' - 'as data_per_streamline. \n' - 'For both of these requirements, see \n' - 'scil_tractogram_filter_collisions.py.') + 'containing fibertubes. They must have their \n' + 'respective diameter saved as data_per_streamline.') p.add_argument('out_tractogram', help='Tractogram output file (must be .trk or .tck).') From b45bd37867a3a7edce5bd494008501da10fcc3fa Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Thu, 12 Dec 2024 12:50:21 -0500 Subject: [PATCH 32/63] add version string --- scilpy/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scilpy/version.py b/scilpy/version.py index 9ebf80e4d..cf5d2e873 100644 --- a/scilpy/version.py +++ b/scilpy/version.py @@ -17,6 +17,7 @@ _ver.append(_version_extra) __version__ = '.'.join(map(str, _ver)) +version_string = "\nScilpy version: " + __version__ CLASSIFIERS = ["Development Status :: 3 - Alpha", "Environment :: Console", From 99c3d4313b106e1f1b514d01d1eb3f99454c6e14 Mon Sep 17 00:00:00 2001 From: elyz0801 Date: Sat, 11 Jan 2025 16:30:18 -0500 Subject: [PATCH 33/63] tractometry_description + solve conflicts --- scripts/scil_aodf_metrics.py | 43 ++++++++++--------- scripts/scil_bids_validate.py | 8 ++-- scripts/scil_bingham_metrics.py | 28 ++++++------ scripts/scil_btensor_metrics.py | 4 +- scripts/scil_bundle_alter_to_target_dice.py | 5 ++- scripts/scil_bundle_clean_qbx_clusters.py | 4 +- scripts/scil_bundle_compute_centroid.py | 7 +-- scripts/scil_bundle_compute_endpoints_map.py | 7 +-- scripts/scil_bundle_diameter.py | 7 ++- scripts/scil_bundle_filter_by_occurence.py | 6 ++- scripts/scil_bundle_fixel_analysis.py | 7 ++- scripts/scil_bundle_generate_priors.py | 20 ++++----- scripts/scil_bundle_label_map.py | 7 +-- scripts/scil_bundle_mean_fixel_afd.py | 25 ++++++----- .../scil_bundle_mean_fixel_afd_from_hdf5.py | 25 +++++------ .../scil_bundle_mean_fixel_bingham_metric.py | 5 ++- scripts/scil_bundle_mean_fixel_mrds_metric.py | 5 ++- scripts/scil_bundle_mean_std.py | 9 ++-- scripts/scil_bundle_pairwise_comparison.py | 8 ++-- scripts/scil_bundle_reject_outliers.py | 5 ++- ...undle_score_many_bundles_one_tractogram.py | 7 +-- ...le_score_same_bundle_many_segmentations.py | 5 ++- scripts/scil_bundle_shape_measures.py | 18 ++++---- scripts/scil_bundle_uniformize_endpoints.py | 8 ++-- scripts/scil_bundle_volume_per_label.py | 4 +- .../scil_connectivity_compare_populations.py | 25 ++++++----- scripts/scil_connectivity_compute_matrices.py | 8 ++-- scripts/scil_connectivity_compute_pca.py | 38 ++++++++-------- ...scil_connectivity_compute_simple_matrix.py | 7 ++- scripts/scil_connectivity_filter.py | 7 +-- scripts/scil_connectivity_graph_measures.py | 16 ++++--- ...l_connectivity_hdf5_average_density_map.py | 6 ++- scripts/scil_connectivity_math.py | 7 +-- scripts/scil_connectivity_normalize.py | 7 +-- .../scil_connectivity_pairwise_agreement.py | 7 +-- scripts/scil_connectivity_print_filenames.py | 6 ++- scripts/scil_connectivity_reorder_rois.py | 19 ++++---- scripts/scil_denoising_nlmeans.py | 6 ++- scripts/scil_dki_metrics.py | 8 ++-- scripts/scil_dti_convert_tensors.py | 4 +- scripts/scil_dti_metrics.py | 7 +-- scripts/scil_dwi_apply_bias_field.py | 11 ++--- scripts/scil_dwi_compute_snr.py | 6 ++- scripts/scil_dwi_concatenate.py | 7 +-- scripts/scil_dwi_convert_FDF.py | 7 +-- scripts/scil_dwi_detect_volume_outliers.py | 7 +-- scripts/scil_dwi_extract_b0.py | 4 +- scripts/scil_dwi_extract_shell.py | 7 +-- scripts/scil_dwi_powder_average.py | 7 +-- scripts/scil_dwi_prepare_eddy_command.py | 8 +++- scripts/scil_dwi_prepare_topup_command.py | 5 ++- scripts/scil_dwi_reorder_philips.py | 4 +- scripts/scil_dwi_split_by_indices.py | 7 +-- scripts/scil_dwi_to_sh.py | 6 ++- scripts/scil_fibertube_score_tractogram.py | 7 +-- scripts/scil_fibertube_tracking.py | 7 +-- scripts/scil_fodf_max_in_ventricles.py | 20 +++++---- scripts/scil_fodf_memsmt.py | 4 +- scripts/scil_fodf_metrics.py | 6 ++- scripts/scil_fodf_msmt.py | 5 ++- scripts/scil_fodf_ssst.py | 6 ++- scripts/scil_fodf_to_bingham.py | 28 ++++++------ scripts/scil_freewater_maps.py | 2 +- 63 files changed, 365 insertions(+), 261 deletions(-) diff --git a/scripts/scil_aodf_metrics.py b/scripts/scil_aodf_metrics.py index a0546e4bd..717c85a22 100755 --- a/scripts/scil_aodf_metrics.py +++ b/scripts/scil_aodf_metrics.py @@ -23,7 +23,25 @@ given as the ratio of the L2-norm of odd SH coefficients on the L2-norm of all SH coefficients. + Formerly: scil_compute_asym_odf_metrics.py +------------------------------------------------------------------------ + +References: +[1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: +Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; +2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 + +[2] S. Cetin Karayumak, E. Özarslan, and G. Unal, +"Asymmetric Orientation Distribution Functions (AODFs) revealing intravoxel +geometry in diffusion MRI," Magnetic Resonance Imaging, vol. 49, pp. 145-158, +Jun. 2018, doi: https://doi.org/10.1016/j.mri.2018.03.006. + +[3] C. Poirier, E. St-Onge, and M. Descoteaux, "Investigating the Occurence of +Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" +[Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, +Vancouver, BC, Abstract number 0865. +------------------------------------------------------------------------ """ @@ -44,29 +62,14 @@ assert_headers_compatible, assert_outputs_exist, add_overwrite_arg, parse_sh_basis_arg) from scilpy.io.image import get_data_as_mask - - -EPILOG = """ -References: -[1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: -Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; -2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 - -[2] S. Cetin Karayumak, E. Özarslan, and G. Unal, -"Asymmetric Orientation Distribution Functions (AODFs) revealing intravoxel -geometry in diffusion MRI," Magnetic Resonance Imaging, vol. 49, pp. 145-158, -Jun. 2018, doi: https://doi.org/10.1016/j.mri.2018.03.006. - -[3] C. Poirier, E. St-Onge, and M. Descoteaux, "Investigating the Occurence of -Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" -[Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, -Vancouver, BC, Abstract number 0865. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_sh', help='Input SH image.') p.add_argument('--mask', diff --git a/scripts/scil_bids_validate.py b/scripts/scil_bids_validate.py index 375ae4699..ea76428ee 100755 --- a/scripts/scil_bids_validate.py +++ b/scripts/scil_bids_validate.py @@ -34,7 +34,7 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) - +from scilpy.version import version_string conversion = {"i": "x", "i-": "x-", @@ -49,9 +49,9 @@ def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument("in_bids", help="Input BIDS folder.") diff --git a/scripts/scil_bingham_metrics.py b/scripts/scil_bingham_metrics.py index 68f938256..f0501fed1 100755 --- a/scripts/scil_bingham_metrics.py +++ b/scripts/scil_bingham_metrics.py @@ -17,6 +17,18 @@ with 1mm isotropic resolution. Other metrics take less than a second. Formerly: scil_compute_lobe_specific_fodf_metrics.py +------------------------------------------------------------------------------ +References: +[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond + fractional anisotropy: Extraction of bundle-specific structural metrics + from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, + doi: 10.1016/j.neuroimage.2014.06.015. + +[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility + Tracking: A method to evaluate anatomical connectivity and microstructural + properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. + 2014, doi: 10.1016/j.neuroimage.2014.01.002. +------------------------------------------------------------------------------ """ import nibabel as nib @@ -33,24 +45,14 @@ compute_fiber_spread, compute_fiber_fraction) - -EPILOG = """ -[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond - fractional anisotropy: Extraction of bundle-specific structural metrics - from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, - doi: 10.1016/j.neuroimage.2014.06.015. - -[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility - Tracking: A method to evaluate anatomical connectivity and microstructural - properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. - 2014, doi: 10.1016/j.neuroimage.2014.01.002. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) + p.add_argument('in_bingham', help='Input Bingham nifti image.') diff --git a/scripts/scil_btensor_metrics.py b/scripts/scil_btensor_metrics.py index 2bbf08823..92636dfd0 100755 --- a/scripts/scil_btensor_metrics.py +++ b/scripts/scil_btensor_metrics.py @@ -54,11 +54,13 @@ add_verbose_arg, add_skip_b0_check_arg, add_tolerance_arg, assert_headers_compatible) from scilpy.reconst.divide import fit_gamma, gamma_fit2metrics +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('--in_dwis', nargs='+', required=True, help='Path to the input diffusion volume for each ' diff --git a/scripts/scil_bundle_alter_to_target_dice.py b/scripts/scil_bundle_alter_to_target_dice.py index c8523d294..dc48819c1 100755 --- a/scripts/scil_bundle_alter_to_target_dice.py +++ b/scripts/scil_bundle_alter_to_target_dice.py @@ -52,11 +52,14 @@ from scilpy.tractograms.tractogram_operations import transform_streamlines_alter, \ trim_streamlines_alter, cut_streamlines_alter, subsample_streamlines_alter, \ replace_streamlines_alter, shuffle_streamlines, shuffle_streamlines_orientation +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input bundle filename (.trk, .tck).') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_clean_qbx_clusters.py b/scripts/scil_bundle_clean_qbx_clusters.py index e9bced44a..07127e280 100755 --- a/scripts/scil_bundle_clean_qbx_clusters.py +++ b/scripts/scil_bundle_clean_qbx_clusters.py @@ -34,11 +34,13 @@ assert_outputs_exist, assert_output_dirs_exist_and_empty, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='List of the clusters filename.') diff --git a/scripts/scil_bundle_compute_centroid.py b/scripts/scil_bundle_compute_centroid.py index d4b726efa..06eefcefd 100755 --- a/scripts/scil_bundle_compute_centroid.py +++ b/scripts/scil_bundle_compute_centroid.py @@ -20,12 +20,13 @@ add_verbose_arg, add_reference_arg) from scilpy.tractanalysis.bundle_operations import get_streamlines_centroid +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle file.') diff --git a/scripts/scil_bundle_compute_endpoints_map.py b/scripts/scil_bundle_compute_endpoints_map.py index 1b96a1967..9fe552bc1 100755 --- a/scripts/scil_bundle_compute_endpoints_map.py +++ b/scripts/scil_bundle_compute_endpoints_map.py @@ -32,12 +32,13 @@ assert_outputs_exist) from scilpy.tractograms.streamline_and_mask_operations import \ get_head_tail_density_maps +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle filename.') diff --git a/scripts/scil_bundle_diameter.py b/scripts/scil_bundle_diameter.py index 65b0afdaf..0df1afe38 100755 --- a/scripts/scil_bundle_diameter.py +++ b/scripts/scil_bundle_diameter.py @@ -53,11 +53,14 @@ from scilpy.viz.backends.vtk import create_tube_with_radii from scilpy.viz.color import get_lookup_table from scilpy.viz.screenshot import compose_image +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='List of tractography files.') p.add_argument('in_labels', nargs='+', diff --git a/scripts/scil_bundle_filter_by_occurence.py b/scripts/scil_bundle_filter_by_occurence.py index deb54a351..8e0b910c2 100755 --- a/scripts/scil_bundle_filter_by_occurence.py +++ b/scripts/scil_bundle_filter_by_occurence.py @@ -31,11 +31,13 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractograms.tractogram_operations import ( intersection_robust, union_robust) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='Input bundles filename(s). All tractograms must have ' diff --git a/scripts/scil_bundle_fixel_analysis.py b/scripts/scil_bundle_fixel_analysis.py index a27613004..fb9bd0531 100755 --- a/scripts/scil_bundle_fixel_analysis.py +++ b/scripts/scil_bundle_fixel_analysis.py @@ -106,11 +106,14 @@ add_verbose_arg, assert_output_dirs_exist_and_empty) from scilpy.tractanalysis.fixel_density import (fixel_density, maps_to_masks) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_peaks', help='Path of the peaks. The peaks are expected to be ' 'given as unit directions. \nTo get these from fODF ' diff --git a/scripts/scil_bundle_generate_priors.py b/scripts/scil_bundle_generate_priors.py index 6af4cf4f0..f637eafdd 100755 --- a/scripts/scil_bundle_generate_priors.py +++ b/scripts/scil_bundle_generate_priors.py @@ -7,6 +7,11 @@ be used for bundle-specific tractography, but not for FOD metrics. Formerly: scil_generate_priors_from_bundle.py +----------------------------------------------------------------------------- +Reference: +[1] Rheault, Francois, et al. "Bundle-specific tractography with incorporated + anatomical and orientational priors." NeuroImage 186 (2019): 382-398 +----------------------------------------------------------------------------- """ import argparse @@ -30,19 +35,14 @@ assert_headers_compatible) from scilpy.reconst.utils import find_order_from_nb_coeff from scilpy.tractanalysis.todi import TrackOrientationDensityImaging - - -EPILOG = """ - References: - [1] Rheault, Francois, et al. "Bundle-specific tractography with - incorporated anatomical and orientational priors." - NeuroImage 186 (2019): 382-398 - """ +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input bundle filename.') diff --git a/scripts/scil_bundle_label_map.py b/scripts/scil_bundle_label_map.py index 9281a5c92..008534e12 100755 --- a/scripts/scil_bundle_label_map.py +++ b/scripts/scil_bundle_label_map.py @@ -44,12 +44,13 @@ from scilpy.tractograms.streamline_operations import \ resample_streamlines_num_points from scilpy.viz.color import get_lookup_table +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundles', nargs='+', help='Fiber bundle file.') diff --git a/scripts/scil_bundle_mean_fixel_afd.py b/scripts/scil_bundle_mean_fixel_afd.py index ae327b714..fa0139abd 100755 --- a/scripts/scil_bundle_mean_fixel_afd.py +++ b/scripts/scil_bundle_mean_fixel_afd.py @@ -11,6 +11,15 @@ Please use a bundle file rather than a whole tractogram. Formerly: scil_compute_fixel_afd_from_bundles.py + +----------------------------------------------------------------------------- +Reference: + [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., + Crozier, S., Salvado, O., & Connelly, A. (2012). + Apparent Fibre Density: a novel measure for the analysis of + diffusion-weighted magnetic resonance images. NeuroImage, 59(4), + 3976--3994. +----------------------------------------------------------------------------- """ import argparse @@ -26,20 +35,14 @@ parse_sh_basis_arg, assert_headers_compatible) from scilpy.tractanalysis.afd_along_streamlines \ import afd_map_along_streamlines - -EPILOG = """ -Reference: - [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., - Crozier, S., Salvado, O., & Connelly, A. (2012). - Apparent Fibre Density: a novel measure for the analysis of - diffusion-weighted magnetic resonance images. NeuroImage, 59(4), - 3976--3994. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_fodf', diff --git a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py index 012d88a96..8f2f2af77 100755 --- a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py +++ b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py @@ -11,6 +11,14 @@ Please use a hdf5 (.h5) file containing decomposed connections Formerly: scil_compute_fixel_afd_from_hdf5.py +---------------------------------------------------------------------------- + +Reference: +[1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R.,Crozier, + S., Salvado, O., & Connelly, A. (2012). Apparent Fibre Density: a novel + measure for the analysis of diffusion-weighted magnetic resonance images. + NeuroImage, 59(4), 3976--3994. +---------------------------------------------------------------------------- """ import argparse @@ -32,15 +40,7 @@ parse_sh_basis_arg, validate_nbr_processes) from scilpy.tractanalysis.afd_along_streamlines \ import afd_map_along_streamlines - -EPILOG = """ -Reference: - [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., - Crozier, S., Salvado, O., & Connelly, A. (2012). - Apparent Fibre Density: a novel measure for the analysis of - diffusion-weighted magnetic resonance images. NeuroImage, - 59(4), 3976--3994. -""" +from scilpy.version import version_string def _afd_rd_wrapper(args): @@ -65,10 +65,11 @@ def _afd_rd_wrapper(args): return key, afd_mean - def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_hdf5', help='HDF5 filename (.h5) containing decomposed ' 'connections.') diff --git a/scripts/scil_bundle_mean_fixel_bingham_metric.py b/scripts/scil_bundle_mean_fixel_bingham_metric.py index 880762728..a55bcb299 100755 --- a/scripts/scil_bundle_mean_fixel_bingham_metric.py +++ b/scripts/scil_bundle_mean_fixel_bingham_metric.py @@ -37,11 +37,14 @@ assert_outputs_exist, assert_headers_compatible) from scilpy.tractanalysis.bingham_metric_along_streamlines \ import bingham_metric_map_along_streamlines +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_bingham', diff --git a/scripts/scil_bundle_mean_fixel_mrds_metric.py b/scripts/scil_bundle_mean_fixel_mrds_metric.py index 8874a2e6b..5e9dd66a1 100755 --- a/scripts/scil_bundle_mean_fixel_mrds_metric.py +++ b/scripts/scil_bundle_mean_fixel_mrds_metric.py @@ -38,11 +38,14 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.tractanalysis.mrds_along_streamlines \ import mrds_metrics_along_streamlines +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Path of the bundle file.') p.add_argument('in_pdds', diff --git a/scripts/scil_bundle_mean_std.py b/scripts/scil_bundle_mean_std.py index d672cfdfb..61dace64b 100755 --- a/scripts/scil_bundle_mean_std.py +++ b/scripts/scil_bundle_mean_std.py @@ -35,12 +35,13 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.utils.metrics_tools import get_bundle_metrics_mean_std, \ get_bundle_metrics_mean_std_per_point +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_bundle', help='Fiber bundle file to compute statistics on.') @@ -71,7 +72,7 @@ def _build_arg_parser(): p.add_argument('--out_json', help='Path of the output file. If not given, the output ' 'is simply printed on screen.') - + add_reference_arg(p) add_json_args(p) add_verbose_arg(p) diff --git a/scripts/scil_bundle_pairwise_comparison.py b/scripts/scil_bundle_pairwise_comparison.py index 8ab8b9c0f..892e2a6a2 100755 --- a/scripts/scil_bundle_pairwise_comparison.py +++ b/scripts/scil_bundle_pairwise_comparison.py @@ -52,12 +52,14 @@ from scilpy.tractograms.streamline_and_mask_operations import \ get_endpoints_density_map +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('out_json', diff --git a/scripts/scil_bundle_reject_outliers.py b/scripts/scil_bundle_reject_outliers.py index 1705b7fa1..c7b530115 100755 --- a/scripts/scil_bundle_reject_outliers.py +++ b/scripts/scil_bundle_reject_outliers.py @@ -23,11 +23,14 @@ assert_outputs_exist, check_tracts_same_format) from scilpy.tractanalysis.bundle_operations import remove_outliers_qb +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Fiber bundle file to remove outliers from.') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_score_many_bundles_one_tractogram.py b/scripts/scil_bundle_score_many_bundles_one_tractogram.py index 365c2c993..9c7e174f3 100755 --- a/scripts/scil_bundle_score_many_bundles_one_tractogram.py +++ b/scripts/scil_bundle_score_many_bundles_one_tractogram.py @@ -57,12 +57,13 @@ from scilpy.segment.tractogram_from_roi import compute_masks_from_bundles from scilpy.tractanalysis.scoring import compute_tractometry from scilpy.tractanalysis.scoring import __doc__ as tractometry_description +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__ + tractometry_description, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__ + tractometry_description, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument("gt_config", help=".json dict configured as specified above.") diff --git a/scripts/scil_bundle_score_same_bundle_many_segmentations.py b/scripts/scil_bundle_score_same_bundle_many_segmentations.py index d8f1dfc7a..f413affb0 100755 --- a/scripts/scil_bundle_score_same_bundle_many_segmentations.py +++ b/scripts/scil_bundle_score_same_bundle_many_segmentations.py @@ -54,11 +54,14 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractanalysis.reproducibility_measures import binary_classification from scilpy.tractograms.tractogram_operations import intersection_robust +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('out_json', diff --git a/scripts/scil_bundle_shape_measures.py b/scripts/scil_bundle_shape_measures.py index 059aca521..797ba354d 100755 --- a/scripts/scil_bundle_shape_measures.py +++ b/scripts/scil_bundle_shape_measures.py @@ -33,6 +33,11 @@ Formerly: scil_compute_bundle_volume.py or scil_evaluate_bundles_individual_measures.py +------------------------------------------------------------------------------ +References: +[1] Fang-Cheng Yeh. 2020. + Shape analysis of the human association pathways. NeuroImage. +------------------------------------------------------------------------------ """ import argparse @@ -58,17 +63,14 @@ from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map from scilpy.tractograms.streamline_and_mask_operations import \ get_endpoints_density_map, get_head_tail_density_maps - -EPILOG = """ -References: -[1] Fang-Cheng Yeh. 2020. - Shape analysis of the human association pathways. NeuroImage. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, epilog=EPILOG, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundles', nargs='+', help='Path of the input bundles.') p.add_argument('--out_json', diff --git a/scripts/scil_bundle_uniformize_endpoints.py b/scripts/scil_bundle_uniformize_endpoints.py index 1a9fe6b77..62adfa05e 100755 --- a/scripts/scil_bundle_uniformize_endpoints.py +++ b/scripts/scil_bundle_uniformize_endpoints.py @@ -34,12 +34,14 @@ assert_inputs_exist, assert_headers_compatible) from scilpy.tractanalysis.bundle_operations import \ uniformize_bundle_sft, uniformize_bundle_sft_using_mask +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_bundle', help='Input path of the tractography file.') p.add_argument('out_bundle', diff --git a/scripts/scil_bundle_volume_per_label.py b/scripts/scil_bundle_volume_per_label.py index 9b114525d..1ac0c93e3 100755 --- a/scripts/scil_bundle_volume_per_label.py +++ b/scripts/scil_bundle_volume_per_label.py @@ -28,11 +28,13 @@ add_verbose_arg, add_overwrite_arg, assert_inputs_exist) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('voxel_label_map', help='Fiber bundle file.') diff --git a/scripts/scil_connectivity_compare_populations.py b/scripts/scil_connectivity_compare_populations.py index 1a5b561ec..4f1cdfc2c 100755 --- a/scripts/scil_connectivity_compare_populations.py +++ b/scripts/scil_connectivity_compare_populations.py @@ -28,6 +28,14 @@ and --g2. Formerly: scil_compare_connectivity.py +---------------------------------------------------------------------------- +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +[2] Zalesky, Andrew, Alex Fornito, and Edward T. Bullmore. "Network-based + statistic: identifying differences in brain networks." Neuroimage 53.4 + (2010): 1197-1207. +---------------------------------------------------------------------------- """ import argparse @@ -42,22 +50,13 @@ load_matrix_in_any_format, save_matrix_in_any_format) from scilpy.stats.matrix_stats import ttest_two_matrices - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -[2] Zalesky, Andrew, Alex Fornito, and Edward T. Bullmore. "Network-based - statistic: identifying differences in brain networks." Neuroimage 53.4 - (2010): 1197-1207. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_pval_matrix', help='Output matrix (.npy) containing the edges p-value.') diff --git a/scripts/scil_connectivity_compute_matrices.py b/scripts/scil_connectivity_compute_matrices.py index bbd66644a..bb26ffb52 100755 --- a/scripts/scil_connectivity_compute_matrices.py +++ b/scripts/scil_connectivity_compute_matrices.py @@ -92,12 +92,14 @@ validate_nbr_processes, assert_inputs_dirs_exist, assert_headers_compatible, assert_output_dirs_exist_and_empty) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_hdf5', help='Input filename for the hdf5 container (.h5).') p.add_argument('in_labels', diff --git a/scripts/scil_connectivity_compute_pca.py b/scripts/scil_connectivity_compute_pca.py index 56b09541b..2cb90b5e0 100755 --- a/scripts/scil_connectivity_compute_pca.py +++ b/scripts/scil_connectivity_compute_pca.py @@ -45,6 +45,21 @@ EXAMPLE USAGE: scil_connectivity_compute_pca.py input_folder/ output_folder/ --metrics ad fa md rd [...] --list_ids list_ids.txt + +------------------------------------------------------------------------------- +References: +[1] Chamberland M, Raven EP, Genc S, Duffy K, Descoteaux M, Parker GD, Tax CMW, + Jones DK. Dimensionality reduction of diffusion MRI measures for improved + tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. + doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; + PMCID: PMC6711466. +[2] Gagnon A., Grenier G., Bocti C., Gillet V., Lepage J.-F., Baccarelli A. A., + Posner J., Descoteaux M., Takser L. (2022). White matter microstructural + variability linked to differential attentional skills and impulsive behavior + in a pediatric population. Cerebral Cortex. + https://doi.org/10.1093/cercor/bhac180 +[3] https://towardsdatascience.com/what-are-pca-loadings-and-biplots-9a7897f2e559 +------------------------------------------------------------------------------- """ import argparse @@ -63,28 +78,13 @@ add_overwrite_arg, assert_output_dirs_exist_and_empty, assert_inputs_dirs_exist, assert_inputs_exist) +from scilpy.version import version_string -EPILOG = """ -[1] Chamberland M, Raven EP, Genc S, Duffy K, Descoteaux M, Parker GD, Tax CMW, - Jones DK. Dimensionality reduction of diffusion MRI measures for improved - tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. - doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; - PMCID: PMC6711466. -[2] Gagnon A., Grenier G., Bocti C., Gillet V., Lepage J.-F., Baccarelli A. A., - Posner J., Descoteaux M., Takser L. (2022). White matter microstructural - variability linked to differential attentional skills and impulsive behavior - in a pediatric population. Cerebral Cortex. - https://doi.org/10.1093/cercor/bhac180 -[3] https://towardsdatascience.com/what-are-pca-loadings-and-biplots-9a7897f2e559 - """ - - -# Build argument parser. def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_folder', help='Path to the input folder. See explanation above for ' diff --git a/scripts/scil_connectivity_compute_simple_matrix.py b/scripts/scil_connectivity_compute_simple_matrix.py index 2b954568b..c26ba7622 100644 --- a/scripts/scil_connectivity_compute_simple_matrix.py +++ b/scripts/scil_connectivity_compute_simple_matrix.py @@ -41,11 +41,14 @@ from scilpy.io.utils import assert_inputs_exist, assert_outputs_exist, \ add_verbose_arg, add_overwrite_arg, assert_headers_compatible, \ add_reference_arg +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_tractogram', help='Tractogram (trk or tck).') p.add_argument('in_labels', diff --git a/scripts/scil_connectivity_filter.py b/scripts/scil_connectivity_filter.py index 024501402..ded720fb1 100755 --- a/scripts/scil_connectivity_filter.py +++ b/scripts/scil_connectivity_filter.py @@ -45,12 +45,13 @@ assert_outputs_exist, load_matrix_in_any_format, save_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_matrix_mask', help='Output mask (matrix) resulting from the provided ' diff --git a/scripts/scil_connectivity_graph_measures.py b/scripts/scil_connectivity_graph_measures.py index 8c8a37e2d..8286e2ddd 100755 --- a/scripts/scil_connectivity_graph_measures.py +++ b/scripts/scil_connectivity_graph_measures.py @@ -29,6 +29,12 @@ https://www.gnu.org/licenses/gpl-3.0.en.html Formerly: scil_evaluate_connectivity_graph_measures.py +---------------------------------------------------------------------------- +Reference: +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +---------------------------------------------------------------------------- """ import argparse @@ -43,18 +49,14 @@ assert_inputs_exist, assert_outputs_exist, load_matrix_in_any_format) - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) + p.add_argument('in_conn_matrix', help='Input connectivity matrix (.npy).\n' 'Typically a streamline count weighted matrix.') diff --git a/scripts/scil_connectivity_hdf5_average_density_map.py b/scripts/scil_connectivity_hdf5_average_density_map.py index e79338a8c..6d3480ba6 100755 --- a/scripts/scil_connectivity_hdf5_average_density_map.py +++ b/scripts/scil_connectivity_hdf5_average_density_map.py @@ -36,11 +36,13 @@ assert_output_dirs_exist_and_empty, validate_nbr_processes) from scilpy.tractanalysis.streamlines_metrics import compute_tract_counts_map +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_hdf5', nargs='+', help='List of HDF5 filenames (.h5) from ' 'scil_tractogram_segment_connections_from_labels.py.') diff --git a/scripts/scil_connectivity_math.py b/scripts/scil_connectivity_math.py index f4830f34a..8d04594b2 100755 --- a/scripts/scil_connectivity_math.py +++ b/scripts/scil_connectivity_math.py @@ -24,6 +24,7 @@ load_matrix_in_any_format, save_matrix_in_any_format) from scilpy.utils import is_float +from scilpy.version import version_string OPERATIONS = get_array_ops() @@ -34,9 +35,9 @@ def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('operation', choices=OPERATIONS.keys(), diff --git a/scripts/scil_connectivity_normalize.py b/scripts/scil_connectivity_normalize.py index c18ba9654..8a316f58b 100755 --- a/scripts/scil_connectivity_normalize.py +++ b/scripts/scil_connectivity_normalize.py @@ -61,12 +61,13 @@ assert_outputs_exist, load_matrix_in_any_format, save_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrix', help='Input connectivity matrix. This is typically a ' diff --git a/scripts/scil_connectivity_pairwise_agreement.py b/scripts/scil_connectivity_pairwise_agreement.py index 79eeb2023..aad2e66e7 100755 --- a/scripts/scil_connectivity_pairwise_agreement.py +++ b/scripts/scil_connectivity_pairwise_agreement.py @@ -32,12 +32,13 @@ load_matrix_in_any_format) from scilpy.stats.matrix_stats import pairwise_agreement from scilpy.tractanalysis.reproducibility_measures import compute_dice_voxel +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrices', nargs='+', help='Path of the input matricies.') p.add_argument('out_json', diff --git a/scripts/scil_connectivity_print_filenames.py b/scripts/scil_connectivity_print_filenames.py index a458f3d80..7ff42bd99 100755 --- a/scripts/scil_connectivity_print_filenames.py +++ b/scripts/scil_connectivity_print_filenames.py @@ -29,11 +29,13 @@ assert_inputs_exist, assert_outputs_exist, load_matrix_in_any_format) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrix', help='Binary matrix in numpy (.npy) format.\n' diff --git a/scripts/scil_connectivity_reorder_rois.py b/scripts/scil_connectivity_reorder_rois.py index 9d1d78339..a535b9a02 100755 --- a/scripts/scil_connectivity_reorder_rois.py +++ b/scripts/scil_connectivity_reorder_rois.py @@ -24,6 +24,12 @@ this option, we recommand an average streamline count or volume matrix. Formerly: scil_reorder_connectivity.py +----------------------------------------------------------------------------- +Reference: +[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain + connectivity: uses and interpretations." Neuroimage 52.3 (2010): + 1059-1069. +----------------------------------------------------------------------------- """ import argparse @@ -41,18 +47,13 @@ assert_outputs_exist, add_verbose_arg, assert_output_dirs_exist_and_empty) - - -EPILOG = """ -[1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain - connectivity: uses and interpretations." Neuroimage 52.3 (2010): - 1059-1069. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_matrices', nargs='+', help='Connectivity matrices in .npy or .txt format.') diff --git a/scripts/scil_denoising_nlmeans.py b/scripts/scil_denoising_nlmeans.py index 1256208a7..fbf281c09 100755 --- a/scripts/scil_denoising_nlmeans.py +++ b/scripts/scil_denoising_nlmeans.py @@ -65,11 +65,13 @@ assert_inputs_exist, assert_outputs_exist, assert_headers_compatible, ranged_type) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_image', help='Path of the image file to denoise (3D or 4D data)') diff --git a/scripts/scil_dki_metrics.py b/scripts/scil_dki_metrics.py index bbe834a8a..bb7ddac36 100755 --- a/scripts/scil_dki_metrics.py +++ b/scripts/scil_dki_metrics.py @@ -65,12 +65,14 @@ is_normalized_bvecs, identify_shells, normalize_bvecs) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) + p.add_argument('in_dwi', help='Path of the input multi-shell DWI dataset.') p.add_argument('in_bval', diff --git a/scripts/scil_dti_convert_tensors.py b/scripts/scil_dti_convert_tensors.py index 95af331c8..27398d223 100755 --- a/scripts/scil_dti_convert_tensors.py +++ b/scripts/scil_dti_convert_tensors.py @@ -17,11 +17,13 @@ from scilpy.io.tensor import (supported_tensor_formats, tensor_format_description, convert_tensor_format) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__ + tensor_format_description, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_file', help='Input tensors filename.') diff --git a/scripts/scil_dti_metrics.py b/scripts/scil_dti_metrics.py index f918681a9..67a62b3fa 100755 --- a/scripts/scil_dti_metrics.py +++ b/scripts/scil_dti_metrics.py @@ -54,15 +54,16 @@ normalize_bvecs) from scilpy.utils.filenames import add_filename_suffix, split_name_with_nii from scilpy.viz.plot import plot_residuals +from scilpy.version import version_string logger = logging.getLogger("DTI_Metrics") logger.setLevel(logging.INFO) def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_apply_bias_field.py b/scripts/scil_dwi_apply_bias_field.py index 150bc1394..6832d6b2e 100755 --- a/scripts/scil_dwi_apply_bias_field.py +++ b/scripts/scil_dwi_apply_bias_field.py @@ -20,12 +20,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI Nifti image.') p.add_argument('in_bias_field', @@ -38,10 +39,10 @@ def _build_arg_parser(): 'If this is not given, the bias field is still only ' 'applied only in non-background data \n(i.e. where ' 'the dwi is not 0).') - + add_verbose_arg(p) add_overwrite_arg(p) - + return p diff --git a/scripts/scil_dwi_compute_snr.py b/scripts/scil_dwi_compute_snr.py index 83c63659c..d57dc6f81 100755 --- a/scripts/scil_dwi_compute_snr.py +++ b/scripts/scil_dwi_compute_snr.py @@ -44,11 +44,13 @@ assert_inputs_exist) from scilpy.utils.filenames import split_name_with_nii from scilpy.image.volume_operations import compute_snr +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_dwi_concatenate.py b/scripts/scil_dwi_concatenate.py index 1ae54bc5c..6d6e26c99 100755 --- a/scripts/scil_dwi_concatenate.py +++ b/scripts/scil_dwi_concatenate.py @@ -20,12 +20,13 @@ add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('out_dwi', help='The name of the output DWI file.') diff --git a/scripts/scil_dwi_convert_FDF.py b/scripts/scil_dwi_convert_FDF.py index 88a19c0c5..b6812420d 100755 --- a/scripts/scil_dwi_convert_FDF.py +++ b/scripts/scil_dwi_convert_FDF.py @@ -20,12 +20,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter,) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_b0_path', help='Path to the b0 FDF file or folder to convert.') diff --git a/scripts/scil_dwi_detect_volume_outliers.py b/scripts/scil_dwi_detect_volume_outliers.py index 19b6256c9..61b30f398 100755 --- a/scripts/scil_dwi_detect_volume_outliers.py +++ b/scripts/scil_dwi_detect_volume_outliers.py @@ -26,12 +26,13 @@ add_verbose_arg, assert_inputs_exist, ) from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, normalize_bvecs) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DWI file (.nii) to concatenate.') diff --git a/scripts/scil_dwi_extract_b0.py b/scripts/scil_dwi_extract_b0.py index 754dd273d..a89428014 100755 --- a/scripts/scil_dwi_extract_b0.py +++ b/scripts/scil_dwi_extract_b0.py @@ -26,13 +26,15 @@ from scilpy.gradients.bvec_bval_tools import (check_b0_threshold, B0ExtractionStrategy) from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string logger = logging.getLogger(__file__) def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='DWI Nifti image.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_extract_shell.py b/scripts/scil_dwi_extract_shell.py index eb173a335..6b1be7a98 100755 --- a/scripts/scil_dwi_extract_shell.py +++ b/scripts/scil_dwi_extract_shell.py @@ -29,12 +29,13 @@ from scilpy.io.gradients import save_gradient_sampling_fsl from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DW image file to split.') diff --git a/scripts/scil_dwi_powder_average.py b/scripts/scil_dwi_powder_average.py index a0250e8b8..24cc67012 100755 --- a/scripts/scil_dwi_powder_average.py +++ b/scripts/scil_dwi_powder_average.py @@ -30,12 +30,13 @@ from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist, assert_outputs_exist, add_verbose_arg, assert_headers_compatible) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') p.add_argument('in_bval', diff --git a/scripts/scil_dwi_prepare_eddy_command.py b/scripts/scil_dwi_prepare_eddy_command.py index 0ccb32324..d28d05129 100755 --- a/scripts/scil_dwi_prepare_eddy_command.py +++ b/scripts/scil_dwi_prepare_eddy_command.py @@ -15,19 +15,23 @@ import os import subprocess -from dipy.io.gradients import read_bvals_bvecs import numpy as np + +from dipy.io.gradients import read_bvals_bvecs + from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_fsl_options_exist, assert_inputs_exist) from scilpy.preprocessing.distortion_correction import \ (create_acqparams, create_index, create_multi_topup_index, create_non_zero_norm_bvecs) +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Input DWI Nifti image. If using multiple ' diff --git a/scripts/scil_dwi_prepare_topup_command.py b/scripts/scil_dwi_prepare_topup_command.py index b39a02776..68bd75c3d 100755 --- a/scripts/scil_dwi_prepare_topup_command.py +++ b/scripts/scil_dwi_prepare_topup_command.py @@ -15,15 +15,18 @@ import nibabel as nib import numpy as np + from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist, assert_fsl_options_exist) from scilpy.preprocessing.distortion_correction import create_acqparams +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_forward_b0', help='Input b0 Nifti image with forward phase encoding.') diff --git a/scripts/scil_dwi_reorder_philips.py b/scripts/scil_dwi_reorder_philips.py index c2eb65280..fe901f551 100755 --- a/scripts/scil_dwi_reorder_philips.py +++ b/scripts/scil_dwi_reorder_philips.py @@ -24,13 +24,15 @@ assert_inputs_exist, assert_outputs_exist) from scilpy.utils.filenames import split_name_with_nii +from scilpy.version import version_string SOFTWARE_VERSION_MIN = '5.6' def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Input dwi file.') diff --git a/scripts/scil_dwi_split_by_indices.py b/scripts/scil_dwi_split_by_indices.py index d9cff7b16..54871068f 100755 --- a/scripts/scil_dwi_split_by_indices.py +++ b/scripts/scil_dwi_split_by_indices.py @@ -23,12 +23,13 @@ from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_outputs_exist) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='The DW image file to split.') diff --git a/scripts/scil_dwi_to_sh.py b/scripts/scil_dwi_to_sh.py index 938c1a45e..94eb6269d 100755 --- a/scripts/scil_dwi_to_sh.py +++ b/scripts/scil_dwi_to_sh.py @@ -23,11 +23,13 @@ assert_outputs_exist, parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.sh import compute_sh_coefficients +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the dwi volume.') p.add_argument('in_bval', diff --git a/scripts/scil_fibertube_score_tractogram.py b/scripts/scil_fibertube_score_tractogram.py index b8272c148..049361ee9 100644 --- a/scripts/scil_fibertube_score_tractogram.py +++ b/scripts/scil_fibertube_score_tractogram.py @@ -72,12 +72,13 @@ add_overwrite_arg, add_verbose_arg, add_json_args) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fibertubes', help='Path to the tractogram file (must be .trk) \n' diff --git a/scripts/scil_fibertube_tracking.py b/scripts/scil_fibertube_tracking.py index 6b5ca536e..d8a4f95d9 100644 --- a/scripts/scil_fibertube_tracking.py +++ b/scripts/scil_fibertube_tracking.py @@ -48,12 +48,13 @@ add_verbose_arg, add_json_args, add_overwrite_arg) +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, - description=__doc__) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fibertubes', help='Path to the tractogram (must be .trk) file \n' diff --git a/scripts/scil_fodf_max_in_ventricles.py b/scripts/scil_fodf_max_in_ventricles.py index a40cf2973..eaf70723a 100755 --- a/scripts/scil_fodf_max_in_ventricles.py +++ b/scripts/scil_fodf_max_in_ventricles.py @@ -8,6 +8,13 @@ This allows to clip the noise of fODF using an absolute thresold. Formerly: scil_compute_fodf_max_in_ventricles.py +-------------------------------------------------------------------------- +Reference: +[1] Dell'Acqua, Flavio, et al. "Can spherical deconvolution provide more + information than fiber orientations? Hindrance modulated orientational + anisotropy, a true-tract specific index to characterize white matter + diffusion." Human brain mapping 34.10 (2013): 2464-2483. +-------------------------------------------------------------------------- """ import argparse @@ -21,18 +28,13 @@ assert_inputs_exist, assert_outputs_exist, parse_sh_basis_arg) from scilpy.reconst.fodf import get_ventricles_max_fodf - -EPILOG = """ -[1] Dell'Acqua, Flavio, et al. "Can spherical deconvolution provide more - information than fiber orientations? Hindrance modulated orientational - anisotropy, a true-tract specific index to characterize white matter - diffusion." Human brain mapping 34.10 (2013): 2464-2483. -""" +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, - description=__doc__, epilog=EPILOG) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fodfs', metavar='fODFs', help='Path of the fODF volume in spherical harmonics (SH).') diff --git a/scripts/scil_fodf_memsmt.py b/scripts/scil_fodf_memsmt.py index c70265242..86a6010bc 100755 --- a/scripts/scil_fodf_memsmt.py +++ b/scripts/scil_fodf_memsmt.py @@ -60,11 +60,13 @@ verify_failed_voxels_shm_coeff, verify_frf_files) from scilpy.reconst.sh import convert_sh_basis, verify_data_vs_sh_order +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_wm_frf', help='Text file of WM response function.') diff --git a/scripts/scil_fodf_metrics.py b/scripts/scil_fodf_metrics.py index 4112a1086..124284ef1 100755 --- a/scripts/scil_fodf_metrics.py +++ b/scripts/scil_fodf_metrics.py @@ -47,11 +47,13 @@ assert_inputs_exist, assert_outputs_exist, parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.sh import peaks_from_sh, maps_from_sh +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_fODF', help='Path of the fODF volume in spherical harmonics (SH).') diff --git a/scripts/scil_fodf_msmt.py b/scripts/scil_fodf_msmt.py index 235cd8476..367567072 100755 --- a/scripts/scil_fodf_msmt.py +++ b/scripts/scil_fodf_msmt.py @@ -42,12 +42,13 @@ verify_failed_voxels_shm_coeff, verify_frf_files) from scilpy.reconst.sh import convert_sh_basis, verify_data_vs_sh_order +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_fodf_ssst.py b/scripts/scil_fodf_ssst.py index 4070f6816..c39933acc 100755 --- a/scripts/scil_fodf_ssst.py +++ b/scripts/scil_fodf_ssst.py @@ -30,11 +30,13 @@ parse_sh_basis_arg, assert_headers_compatible) from scilpy.reconst.fodf import fit_from_model from scilpy.reconst.sh import convert_sh_basis +from scilpy.version import version_string def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + epilog=version_string) p.add_argument('in_dwi', help='Path of the input diffusion volume.') diff --git a/scripts/scil_fodf_to_bingham.py b/scripts/scil_fodf_to_bingham.py index c71110ed9..025b8e158 100755 --- a/scripts/scil_fodf_to_bingham.py +++ b/scripts/scil_fodf_to_bingham.py @@ -13,6 +13,18 @@ 1mm isotropic resolution. Formerly: scil_fit_bingham_to_fodf.py +------------------------------------------------------------------------------- +References: +[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond + fractional anisotropy: Extraction of bundle-specific structural metrics + from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, + doi: 10.1016/j.neuroimage.2014.06.015. + +[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility + Tracking: A method to evaluate anatomical connectivity and microstructural + properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. + 2014, doi: 10.1016/j.neuroimage.2014.01.002. +------------------------------------------------------------------------------- """ import nibabel as nib @@ -26,25 +38,13 @@ assert_headers_compatible) from scilpy.io.image import get_data_as_mask from scilpy.reconst.bingham import (bingham_fit_sh) - - -EPILOG = """ -[1] T. W. Riffert, J. Schreiber, A. Anwander, and T. R. Knösche, “Beyond - fractional anisotropy: Extraction of bundle-specific structural metrics - from crossing fiber models,” NeuroImage, vol. 100, pp. 176-191, Oct. 2014, - doi: 10.1016/j.neuroimage.2014.06.015. - -[2] J. Schreiber, T. Riffert, A. Anwander, and T. R. Knösche, “Plausibility - Tracking: A method to evaluate anatomical connectivity and microstructural - properties along fiber pathways,” NeuroImage, vol. 90, pp. 163-178, Apr. - 2014, doi: 10.1016/j.neuroimage.2014.01.002. -""" +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, - epilog=EPILOG) + epilog=version_string) p.add_argument('in_sh', help='Input SH image.') diff --git a/scripts/scil_freewater_maps.py b/scripts/scil_freewater_maps.py index 854eb30ab..8ed9d1fbc 100755 --- a/scripts/scil_freewater_maps.py +++ b/scripts/scil_freewater_maps.py @@ -106,7 +106,7 @@ def main(): if args.verbose == "WARNING": f = io.StringIO() redirected_stdout = redirect_stdout(f) - redirect_stdout_c() + redirect_stdout_c() else: logging.getLogger().setLevel(logging.getLevelName(args.verbose)) redirected_stdout = redirect_stdout(sys.stdout) From c3a17b57a1f6499439ce0447386500556c2cbc9f Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Tue, 14 Jan 2025 15:38:21 -0500 Subject: [PATCH 34/63] Adding new check_b0_threshold function --- scripts/scil_gradients_validate_sampling.py | 26 ++++----------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 006e8335d..4c44d518e 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -30,10 +30,11 @@ from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, - add_b0_thresh_arg, + add_b0_thresh_arg, add_skip_b0_check_arg, add_tolerance_arg, assert_inputs_exist, assert_gradients_filenames_valid) from scilpy.gradients.bvec_bval_tools import (is_normalized_bvecs, + check_b0_threshold, normalize_bvecs, identify_shells) from scilpy.gradients.gen_gradient_sampling import (generate_gradient_sampling, @@ -70,6 +71,7 @@ def _build_arg_parser(): 'optimal one.') add_b0_thresh_arg(p) + add_skip_b0_check_arg(p, will_overwrite_with_min=False) add_tolerance_arg(p) add_verbose_arg(p) add_overwrite_arg(p) @@ -99,26 +101,8 @@ def main(): 'two files for FSL format and one file for MRtrix') # Check and remove b0s - # Note: this part will become check_b0_threshold once it is fixed - if args.b0_threshold > 20: - logging.warning( - 'Your defined b0 threshold is {}. This is suspicious. We ' - 'recommend using volumes with bvalues no higher than {} as b0s' - .format(args.b0_threshold, 20)) - - if bvals.min() < 0: - logging.warning( - 'Warning: Your dataset contains negative b-values (minimal bvalue ' - 'of {}). This is suspicious. We recommend you check your data.' - .format(bvals.min())) - - if bvals.min() > args.b0_threshold: - logging.warning( - 'Your minimal bvalue ({}) is above the threshold ({}). Please ' - 'check your data to ensure everything is correct.\n' - 'You may also increase the threshold with --b0_threshold. ' - 'The script will continue without b0s.' - .format(bvals.min(), args.b0_threshold)) + _ = check_b0_threshold(bvals.min(), args.b0_threshold, args.skip_b0_check, + overwrite_with_min=False) bvecs = bvecs[bvals > args.b0_threshold] bvals = bvals[bvals > args.b0_threshold] From 9c7b861fbeee6a6aa06bba09f14c01b3d38f6636 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Fri, 17 Jan 2025 11:19:38 -0500 Subject: [PATCH 35/63] Pass single_value as an argument instead of a file - tested + pep8 --- scripts/scil_tractogram_dps_math.py | 43 +++++++++++++---------- scripts/tests/test_tractogram_dps_math.py | 4 +-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/scripts/scil_tractogram_dps_math.py b/scripts/scil_tractogram_dps_math.py index 1043562d9..0130f6bd3 100755 --- a/scripts/scil_tractogram_dps_math.py +++ b/scripts/scil_tractogram_dps_math.py @@ -67,9 +67,11 @@ def _build_arg_parser(): 'streamlines (.txt, .npy or .mat). There\n' 'must be the same amount of entries as\n' 'there are streamlines.') - import_excl.add_argument('--in_dps_file_single_value', - help='File containing a single value to import\n' - 'to each streamlines (.txt, .npy or .mat).') + import_excl.add_argument('--in_dps_single_value', nargs='+', type=float, + help='Single value to import to each\n' + 'streamline. If the value is an array,\n' + 'enter each component with a space in\n' + 'between.') export_args = p.add_argument_group('Operation "export" mandatory options') export_args.add_argument('--out_dps_file', @@ -104,14 +106,9 @@ def main(): sft = load_tractogram_with_reference(parser, args, args.in_tractogram) if args.operation == 'import': - if args.in_dps_file: - dps_file = args.in_dps_file - else: - dps_file = args.in_dps_file_single_value - - if dps_file is None: + if args.in_dps_file is None and args.in_dps_single_value is None: parser.error('One of --in_dps_file or ' + - '--in_dps_file_single_value is required for the ' + + '--in_dps_single_value is required for the ' + '"import" operation.') if args.out_tractogram is None: @@ -124,15 +121,25 @@ def main(): parser.error('"{}" already in data per streamline. Use -f to force' ' overwriting.'.format(args.dps_key)) - # Load data and remove extraneous dimensions - data = np.squeeze(load_matrix_in_any_format(dps_file)) + if args.in_dps_file: + # Load data and remove extraneous dimensions + data = np.squeeze(load_matrix_in_any_format(args.in_dps_file)) + + # Validate data shape + if len(sft) != data.shape[0]: + raise ValueError( + 'Data must have as many entries ({}) as there are ' + 'streamlines ({}).'.format(data.shape[0], len(sft))) + elif args.in_dps_single_value: + data = np.array(args.in_dps_single_value) + if (np.mod(data, 1) == 0).all(): + data = data.astype(int) + + # Squeeze may remove axes of length 0, but still returns an + # ndarray. We would like a proper scalar type. + if len(data) == 1: + data = data[0] - # Validate data shape - if args.in_dps_file and len(sft) != data.shape[0]: - raise ValueError( - 'Data must have as many entries ({}) as there are ' - 'streamlines ({}).'.format(data.shape[0], len(sft))) - if args.in_dps_file_single_value: data = [data] * len(sft.streamlines) sft.data_per_streamline[args.dps_key] = data diff --git a/scripts/tests/test_tractogram_dps_math.py b/scripts/tests/test_tractogram_dps_math.py index 418590a94..d6aabbda0 100644 --- a/scripts/tests/test_tractogram_dps_math.py +++ b/scripts/tests/test_tractogram_dps_math.py @@ -42,12 +42,10 @@ def test_execution_dps_math_import_single_value(script_runner, monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) in_bundle = os.path.join(SCILPY_HOME, 'filtering', 'bundle_4.trk') - filename = 'vals.npy' outname = 'out.trk' - np.save(filename, [1.1]) ret = script_runner.run('scil_tractogram_dps_math.py', in_bundle, 'import', 'key', - '--in_dps_file_single_value', filename, + '--in_dps_single_value', '1', '1.1', '1.2', '--out_tractogram', outname, '-f') assert ret.success From a1fe3a802febd9c5c5c62c7e65a32e075094f332 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Fri, 17 Jan 2025 11:54:09 -0500 Subject: [PATCH 36/63] Clarify doc in fibertube_scoring --- scilpy/tractanalysis/fibertube_scoring.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scilpy/tractanalysis/fibertube_scoring.py b/scilpy/tractanalysis/fibertube_scoring.py index 0ac6f3ea9..7fe29527a 100644 --- a/scilpy/tractanalysis/fibertube_scoring.py +++ b/scilpy/tractanalysis/fibertube_scoring.py @@ -330,8 +330,9 @@ def get_external_vector_from_centerline_vector(vector, r1, r2): @njit def resolve_origin_seeding(seeds, centerlines, diameters): """ - Associates given seeds to segment 0 of the fibertube in which they have - been generated. This pairing only works with fiber origin seeding. + Given seeds generated in the first segment of fibertubes (origin seeding) + and a set of fibertubes, associates each seed with their corresponding + fibertube. Parameters ---------- @@ -344,8 +345,8 @@ def resolve_origin_seeding(seeds, centerlines, diameters): Return ------ seeds_fiber: ndarray - Array containing the fiber index of each seed. If the seed is not in a - fiber, its value will be -1. + Array containing the fibertube index of each seed. If the seed is + not in a fibertube, its value in the array will be -1. """ seeds_fiber = [-1] * len(seeds) From 0c13365dfb77c3059466be8c257cc4e0e8773783 Mon Sep 17 00:00:00 2001 From: EmmaRenauld Date: Tue, 21 Jan 2025 13:43:01 -0500 Subject: [PATCH 37/63] Support .tck for commit --- scripts/scil_tractogram_commit.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/scripts/scil_tractogram_commit.py b/scripts/scil_tractogram_commit.py index 291c5b519..b81b79b81 100755 --- a/scripts/scil_tractogram_commit.py +++ b/scripts/scil_tractogram_commit.py @@ -103,7 +103,8 @@ assert_inputs_exist, assert_output_dirs_exist_and_empty, redirect_stdout_c, add_tolerance_arg, - add_skip_b0_check_arg, assert_headers_compatible) + add_skip_b0_check_arg, assert_headers_compatible, + add_reference_arg) from scilpy.gradients.bvec_bval_tools import identify_shells, \ check_b0_threshold @@ -190,6 +191,7 @@ def _build_arg_parser(): g2.add_argument('--compute_only', action='store_true', help='Compute kernels only, --save_kernels must be used.') + add_reference_arg(p) add_tolerance_arg(p) add_skip_b0_check_arg(p, will_overwrite_with_min=True) add_processes_arg(p) @@ -236,7 +238,9 @@ def _save_results(args, tmp_dir, ext, in_hdf5_file, offsets_list, sub_dir, # Loading the tractogram (we never did yet! Only sent the filename to # commit). Reminder. If input was a hdf5, we have changed # args.in_tractogram to our tmp_tractogram saved in tmp_dir. - sft = load_tractogram(args.in_tractogram, 'same') + if ext == '.trk': + args.reference = 'same' + sft = load_tractogram(args.in_tractogram, args.reference) length_list = length(sft.streamlines) np.savetxt(os.path.join(commit_results_dir, 'streamlines_length.txt'), length_list) @@ -337,12 +341,17 @@ def main(): # === Verifications === assert_inputs_exist(parser, [args.in_tractogram, args.in_dwi, args.in_bval, args.in_bvec], - [args.in_peaks, args.in_tracking_mask]) + [args.in_peaks, args.in_tracking_mask, + args.reference]) assert_output_dirs_exist_and_empty(parser, args, args.out_dir, optional=args.save_kernels) _, ext = os.path.splitext(args.in_tractogram) - if ext == '.trk': - assert_headers_compatible(parser, [args.in_tractogram, args.in_dwi]) + if ext in ['.trk', '.tck']: + assert_headers_compatible(parser, [args.in_tractogram, args.in_dwi], + reference=args.reference) + + if ext == '.tck': + logging.warning("Commit only works with .trk format") if args.commit2: if os.path.splitext(args.in_tractogram)[1] != '.h5': @@ -414,13 +423,17 @@ def main(): shells_centroids)) with redirected_stdout: # Setting up the tractogram and nifti files + if ext == '.tck': + other_args = {'TCK_ref_image': args.reference} + else: + other_args = {} trk2dictionary.run(filename_tractogram=args.in_tractogram, filename_peaks=args.in_peaks, peaks_use_affine=False, filename_mask=args.in_tracking_mask, ndirs=args.nbr_dir, path_out=tmp_dir.name, - n_threads=args.nbr_processes) + n_threads=args.nbr_processes, **other_args) # Preparation for fitting commit.core.setup() From 1c250edaab62e1b449e0198be483212b92d48891 Mon Sep 17 00:00:00 2001 From: EmmaRenauld Date: Tue, 21 Jan 2025 14:00:48 -0500 Subject: [PATCH 38/63] Fix order of folder creation --- ...cil_tractogram_segment_connections_from_labels.py | 12 ++++++++++-- ...est_tractogram_segment_connections_from_labels.py | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/scil_tractogram_segment_connections_from_labels.py b/scripts/scil_tractogram_segment_connections_from_labels.py index 407d69323..20e1c6a3b 100755 --- a/scripts/scil_tractogram_segment_connections_from_labels.py +++ b/scripts/scil_tractogram_segment_connections_from_labels.py @@ -113,6 +113,7 @@ def _get_saving_options(args): def _create_required_output_dirs(args, out_paths): if not args.out_dir: return + os.mkdir(out_paths['final']) if args.save_raw_connections: @@ -196,7 +197,10 @@ def _build_arg_parser(): 'Includes loops, outliers and qb_loops.') s.add_argument('--save_final', action='store_true', help='If set, will save the final bundles (connections) ' - 'on disk (.trk) as well as in the hdf5.') + 'on disk (.trk) as well as in the hdf5.\n' + 'If this is not set, you can also get the final ' + 'bundles later, using:\n' + 'scil_tractogram_convert_hdf5_to_trk.py.') p.add_argument('--out_labels_list', metavar='OUT_FILE', help='Save the labels list as text file.\n' @@ -237,13 +241,17 @@ def main(): parser.error('To save outputs in the streamlines form, provide ' 'the output directory using --out_dir.') out_paths = _get_output_paths(args) - _create_required_output_dirs(args, out_paths) + elif args.out_dir: + logging.info("--out_dir will not be created, as there is nothing to " + "be saved.") + args.out_dir = None if args.out_dir: if os.path.abspath(args.out_dir) == os.getcwd(): parser.error('Do not use the current path as output directory.') assert_output_dirs_exist_and_empty(parser, args, args.out_dir, create_dir=True) + _create_required_output_dirs(args, out_paths) # Load everything img_labels = nib.load(args.in_labels) diff --git a/scripts/tests/test_tractogram_segment_connections_from_labels.py b/scripts/tests/test_tractogram_segment_connections_from_labels.py index 3c71b1516..e0ae13ae2 100644 --- a/scripts/tests/test_tractogram_segment_connections_from_labels.py +++ b/scripts/tests/test_tractogram_segment_connections_from_labels.py @@ -27,5 +27,6 @@ def test_execution_connectivity(script_runner, monkeypatch): 'scil_tractogram_segment_connections_from_labels.py', in_bundle, in_atlas, 'decompose.h5', '--min_length', '20', '--max_length', '200', '--outlier_threshold', '0.5', '--loop_max_angle', '330', - '--curv_qb_distance', '10', '--processes', '1', '-v', 'DEBUG') + '--curv_qb_distance', '10', '--processes', '1', '-v', 'DEBUG', + '--save_final', '--out_dir', os.path.join(tmp_dir.name, 'out_bundles')) assert ret.success From d1c9a7ce14b432933e652ddfb369bd092619bd01 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 14:51:45 -0500 Subject: [PATCH 39/63] NF - added input options for multiple ROI masks --- scripts/scil_volume_stats_in_ROI.py | 103 ++++++++++++++-------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 831373fdc..86b311a87 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -33,9 +33,9 @@ def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter) - p.add_argument('in_mask', - help='Mask volume filename.\nCan be a binary mask or a ' - 'weighted mask.') + p.add_argument('in_masks', nargs='+', + help='Mask volume filenames.\nCan be binary masks or ' + 'weighted masks.') g = p.add_argument_group('Metrics input options') gg = g.add_mutually_exclusive_group(required=True) @@ -74,59 +74,60 @@ def main(): if not os.path.exists(args.metrics_dir): parser.error("Metrics directory does not exist: {}" .format(args.metrics_dir)) - assert_inputs_exist(parser, args.in_mask) + assert_inputs_exist(parser, args.in_masks) tmp_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) args.metrics_file_list = [os.path.join(args.metrics_dir, f) for f in tmp_file_list] else: - assert_inputs_exist(parser, [args.in_mask] + args.metrics_file_list) - assert_headers_compatible(parser, - [args.in_mask] + args.metrics_file_list) - - # Loading - mask_data = nib.load(args.in_mask).get_fdata(dtype=np.float32) - if len(mask_data.shape) > 3: - parser.error('Mask should be a 3D image.') - if np.min(mask_data) < 0: - parser.error('Mask should not contain negative values.') - roi_name = split_name_with_nii(os.path.basename(args.in_mask))[0] - - # Discussion about the way the normalization is done. - # https://github.com/scilus/scilpy/pull/202#discussion_r411355609 - # Summary: - # 1) We don't want to normalize with data = (data-min) / (max-min) because - # it zeroes out the minimal values of the array. This is not a large error - # source, but not preferable. - # 2) data = data / max(data) or data = data / sum(data): in practice, when - # we use them in numpy using their weights argument, leads to the same - # result. - if args.normalize_weights: - mask_data /= np.max(mask_data) - elif args.bin: - mask_data[np.where(mask_data > 0.0)] = 1.0 - elif np.min(mask_data) < 0.0 or np.max(mask_data) > 1.0: - parser.error('Mask data should only contain values between 0 and 1. ' - 'Try --normalize_weights.') - - # Load and process all metrics files. - json_stats = {roi_name: {}} - for f in args.metrics_file_list: - metric_img = nib.load(f) - metric_name = split_name_with_nii(os.path.basename(f))[0] - if len(metric_img.shape) == 3: - data = metric_img.get_fdata(dtype=np.float64) - if np.any(np.isnan(data)): - logging.warning("Metric '{}' contains some NaN. Ignoring " - "voxels with NaN." - .format(os.path.basename(f))) - mean, std = weighted_mean_std(mask_data, data) - json_stats[roi_name][metric_name] = {'mean': mean, - 'std': std} - else: - parser.error( - 'Metric {} is not a 3D image ({}D shape).' - .format(f, len(metric_img.shape))) + assert_inputs_exist(parser, args.in_masks + args.metrics_file_list) + assert_headers_compatible(parser, args.in_masks + args.metrics_file_list) + + # Computing stats for all masks and metrics files + json_stats = {} + for mask_filename in args.in_masks: + mask_data = nib.load(mask_filename).get_fdata(dtype=np.float32) + if len(mask_data.shape) > 3: + parser.error('Mask should be a 3D image.') + if np.min(mask_data) < 0: + parser.error('Mask should not contain negative values.') + roi_name = split_name_with_nii(os.path.basename(mask_filename))[0] + + # Discussion about the way the normalization is done. + # https://github.com/scilus/scilpy/pull/202#discussion_r411355609 + # Summary: + # 1) We don't want to normalize with data = (data-min) / (max-min) because + # it zeroes out the minimal values of the array. This is not a large error + # source, but not preferable. + # 2) data = data / max(data) or data = data / sum(data): in practice, when + # we use them in numpy using their weights argument, leads to the same + # result. + if args.normalize_weights: + mask_data /= np.max(mask_data) + elif args.bin: + mask_data[np.where(mask_data > 0.0)] = 1.0 + elif np.min(mask_data) < 0.0 or np.max(mask_data) > 1.0: + parser.error('Mask data should only contain values between 0 and 1. ' + 'Try --normalize_weights.') + + # Load and process all metrics files. + json_stats[roi_name] = {} + for f in args.metrics_file_list: + metric_img = nib.load(f) + metric_name = split_name_with_nii(os.path.basename(f))[0] + if len(metric_img.shape) == 3: + data = metric_img.get_fdata(dtype=np.float64) + if np.any(np.isnan(data)): + logging.warning("Metric '{}' contains some NaN. Ignoring " + "voxels with NaN." + .format(os.path.basename(f))) + mean, std = weighted_mean_std(mask_data, data) + json_stats[roi_name][metric_name] = {'mean': mean, + 'std': std} + else: + parser.error( + 'Metric {} is not a 3D image ({}D shape).' + .format(f, len(metric_img.shape))) # Print results print(json.dumps(json_stats, indent=args.indent, sort_keys=args.sort_keys)) From 5255e87f7e091c42248279e0e38ca814a5f8e7a4 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 15:44:53 -0500 Subject: [PATCH 40/63] RF - removed the roi dict if a single ROI is used --- scripts/scil_volume_stats_in_ROI.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 86b311a87..da5257a2b 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -129,6 +129,9 @@ def main(): 'Metric {} is not a 3D image ({}D shape).' .format(f, len(metric_img.shape))) + + if len(args.in_masks) == 1: + json_stats = json_stats[roi_name] # Print results print(json.dumps(json_stats, indent=args.indent, sort_keys=args.sort_keys)) From 0a298a9ad9a0e69e6b545de4eef6325d102cc216 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 15:45:24 -0500 Subject: [PATCH 41/63] NF - allow for multiple metrics input --- scripts/scil_volume_stats_in_labels.py | 54 ++++++++++++++++++++------ 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/scripts/scil_volume_stats_in_labels.py b/scripts/scil_volume_stats_in_labels.py index 07fc290b7..57211b170 100755 --- a/scripts/scil_volume_stats_in_labels.py +++ b/scripts/scil_volume_stats_in_labels.py @@ -11,8 +11,10 @@ """ import argparse +import glob import json import logging +import os import nibabel as nib import numpy as np @@ -20,6 +22,7 @@ from scilpy.image.labels import get_data_as_labels, get_stats_in_label from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_headers_compatible) +from scilpy.utils.filenames import split_name_with_nii def _build_arg_parser(): @@ -30,8 +33,16 @@ def _build_arg_parser(): p.add_argument('in_labels_lut', help='Path of the LUT file corresponding to labels,' 'used to name the regions of interest.') - p.add_argument('in_map', - help='Path of the input map file. Expecting a 3D file.') + + g = p.add_argument_group('Metrics input options') + gg = g.add_mutually_exclusive_group(required=True) + gg.add_argument('--metrics_dir', metavar='dir', + help='Name of the directory containing metrics files: ' + 'we will \nload all nifti files.') + gg.add_argument('--metrics', dest='metrics_file_list', nargs='+', + metavar='file', + help='Metrics nifti filename. List of the names of the ' + 'metrics file, \nin nifti format.') add_verbose_arg(p) add_overwrite_arg(p) @@ -45,21 +56,42 @@ def main(): logging.getLogger().setLevel(logging.getLevelName(args.verbose)) # Verifications - assert_inputs_exist(parser, - [args.in_labels, args.in_map, args.in_labels_lut]) - assert_headers_compatible(parser, [args.in_labels, args.in_map]) + + # Get input list. Either all files in dir or given list. + if args.metrics_dir: + if not os.path.exists(args.metrics_dir): + parser.error("Metrics directory does not exist: {}" + .format(args.metrics_dir)) + assert_inputs_exist(parser, [args.in_labels, args.in_labels_lut]) + + tmp_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) + args.metrics_file_list = [os.path.join(args.metrics_dir, f) + for f in tmp_file_list] + else: + assert_inputs_exist(parser, [args.in_labels] + args.metrics_file_list) + assert_headers_compatible(parser, [args.in_labels] + args.metrics_file_list) + # Loading label_data = get_data_as_labels(nib.load(args.in_labels)) with open(args.in_labels_lut) as f: label_dict = json.load(f) - map_data = nib.load(args.in_map).get_fdata(dtype=np.float32) - if len(map_data.shape) > 3: - parser.error('Mask should be a 3D image.') - # Process - out_dict = get_stats_in_label(map_data, label_data, label_dict) - print(json.dumps(out_dict)) + # Computing stats for all metrics files + json_stats = {} + for metric_filename in args.metrics_file_list: + metric_data = nib.load(metric_filename).get_fdata(dtype=np.float32) + metric_name = split_name_with_nii(os.path.basename(metric_filename))[0] + if len(metric_data.shape) > 3: + parser.error('Mask should be a 3D image.') + + # Process + out_dict = get_stats_in_label(metric_data, label_data, label_dict) + json_stats[metric_name] = out_dict + + if len(args.metrics_file_list) == 1: + json_stats = json_stats[metric_name] + print(json.dumps(json_stats)) if __name__ == "__main__": From 69a7b1fbca50d29d6b6ad48fed3fa5561abb8d1d Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 15:55:27 -0500 Subject: [PATCH 42/63] RF - change variable names from mask to roi --- scripts/scil_volume_stats_in_ROI.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index da5257a2b..e6ce2363c 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -33,7 +33,7 @@ def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter) - p.add_argument('in_masks', nargs='+', + p.add_argument('in_rois', nargs='+', help='Mask volume filenames.\nCan be binary masks or ' 'weighted masks.') @@ -74,24 +74,24 @@ def main(): if not os.path.exists(args.metrics_dir): parser.error("Metrics directory does not exist: {}" .format(args.metrics_dir)) - assert_inputs_exist(parser, args.in_masks) + assert_inputs_exist(parser, args.in_rois) tmp_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) args.metrics_file_list = [os.path.join(args.metrics_dir, f) for f in tmp_file_list] else: - assert_inputs_exist(parser, args.in_masks + args.metrics_file_list) - assert_headers_compatible(parser, args.in_masks + args.metrics_file_list) + assert_inputs_exist(parser, args.in_rois + args.metrics_file_list) + assert_headers_compatible(parser, args.in_rois + args.metrics_file_list) # Computing stats for all masks and metrics files json_stats = {} - for mask_filename in args.in_masks: - mask_data = nib.load(mask_filename).get_fdata(dtype=np.float32) - if len(mask_data.shape) > 3: + for roi_filename in args.in_rois: + roi_data = nib.load(roi_filename).get_fdata(dtype=np.float32) + if len(roi_data.shape) > 3: parser.error('Mask should be a 3D image.') - if np.min(mask_data) < 0: + if np.min(roi_data) < 0: parser.error('Mask should not contain negative values.') - roi_name = split_name_with_nii(os.path.basename(mask_filename))[0] + roi_name = split_name_with_nii(os.path.basename(roi_filename))[0] # Discussion about the way the normalization is done. # https://github.com/scilus/scilpy/pull/202#discussion_r411355609 @@ -103,10 +103,10 @@ def main(): # we use them in numpy using their weights argument, leads to the same # result. if args.normalize_weights: - mask_data /= np.max(mask_data) + roi_data /= np.max(roi_data) elif args.bin: - mask_data[np.where(mask_data > 0.0)] = 1.0 - elif np.min(mask_data) < 0.0 or np.max(mask_data) > 1.0: + roi_data[np.where(roi_data > 0.0)] = 1.0 + elif np.min(roi_data) < 0.0 or np.max(roi_data) > 1.0: parser.error('Mask data should only contain values between 0 and 1. ' 'Try --normalize_weights.') @@ -121,7 +121,7 @@ def main(): logging.warning("Metric '{}' contains some NaN. Ignoring " "voxels with NaN." .format(os.path.basename(f))) - mean, std = weighted_mean_std(mask_data, data) + mean, std = weighted_mean_std(roi_data, data) json_stats[roi_name][metric_name] = {'mean': mean, 'std': std} else: @@ -130,7 +130,7 @@ def main(): .format(f, len(metric_img.shape))) - if len(args.in_masks) == 1: + if len(args.in_rois) == 1: json_stats = json_stats[roi_name] # Print results print(json.dumps(json_stats, indent=args.indent, sort_keys=args.sort_keys)) From 92946b03f65b9ce59f7987600216144a3576c805 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 16:01:13 -0500 Subject: [PATCH 43/63] DOC - updated script documentation --- scripts/scil_volume_stats_in_ROI.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index e6ce2363c..6f726277e 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -3,11 +3,11 @@ """ Compute the statistics (mean, std) of scalar maps, which can represent -diffusion metrics, in a ROI. Prints the results. +diffusion metrics, in ROIs. Prints the results. -The mask can either be a binary mask, or a weighting mask. If the mask is -a weighting mask it should either contain floats between 0 and 1 or should be -normalized with --normalize_weights. IMPORTANT: if the mask contains weights +The ROIs can either be a binary masks, or a weighting masks. If the ROIs are + weighting masks it should either contain floats between 0 and 1 or should be +normalized with --normalize_weights. IMPORTANT: if the ROIs contain weights (and not 0 and 1 exclusively), the standard deviation will also be weighted. """ @@ -34,7 +34,7 @@ def _build_arg_parser(): formatter_class=argparse.RawTextHelpFormatter) p.add_argument('in_rois', nargs='+', - help='Mask volume filenames.\nCan be binary masks or ' + help='ROIs volume filenames.\nCan be binary masks or ' 'weighted masks.') g = p.add_argument_group('Metrics input options') @@ -83,14 +83,15 @@ def main(): assert_inputs_exist(parser, args.in_rois + args.metrics_file_list) assert_headers_compatible(parser, args.in_rois + args.metrics_file_list) - # Computing stats for all masks and metrics files + # Computing stats for all ROIs and metrics files json_stats = {} for roi_filename in args.in_rois: roi_data = nib.load(roi_filename).get_fdata(dtype=np.float32) if len(roi_data.shape) > 3: - parser.error('Mask should be a 3D image.') + parser.error('ROI {} should be a 3D image.'.format(roi_filename)) if np.min(roi_data) < 0: - parser.error('Mask should not contain negative values.') + parser.error('ROI {} should not contain negative values.' + .format(roi_filename)) roi_name = split_name_with_nii(os.path.basename(roi_filename))[0] # Discussion about the way the normalization is done. @@ -107,8 +108,9 @@ def main(): elif args.bin: roi_data[np.where(roi_data > 0.0)] = 1.0 elif np.min(roi_data) < 0.0 or np.max(roi_data) > 1.0: - parser.error('Mask data should only contain values between 0 and 1. ' - 'Try --normalize_weights.') + parser.error('ROI {} data should only contain values between 0 and 1. ' + 'Try --normalize_weights.' + .format(roi_filename)) # Load and process all metrics files. json_stats[roi_name] = {} @@ -129,9 +131,9 @@ def main(): 'Metric {} is not a 3D image ({}D shape).' .format(f, len(metric_img.shape))) - if len(args.in_rois) == 1: json_stats = json_stats[roi_name] + # Print results print(json.dumps(json_stats, indent=args.indent, sort_keys=args.sort_keys)) From 9b6a921ebb5396e8eb2256a4474c2f38398e0ee3 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 16:05:30 -0500 Subject: [PATCH 44/63] TST - updated tests, pep8 --- scripts/scil_volume_stats_in_ROI.py | 2 +- scripts/scil_volume_stats_in_labels.py | 3 +-- scripts/tests/test_volume_stats_in_ROI.py | 4 ++-- scripts/tests/test_volume_stats_in_labels.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 6f726277e..9fc6002bf 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -125,7 +125,7 @@ def main(): .format(os.path.basename(f))) mean, std = weighted_mean_std(roi_data, data) json_stats[roi_name][metric_name] = {'mean': mean, - 'std': std} + 'std': std} else: parser.error( 'Metric {} is not a 3D image ({}D shape).' diff --git a/scripts/scil_volume_stats_in_labels.py b/scripts/scil_volume_stats_in_labels.py index 57211b170..35d5c6c16 100755 --- a/scripts/scil_volume_stats_in_labels.py +++ b/scripts/scil_volume_stats_in_labels.py @@ -71,7 +71,6 @@ def main(): assert_inputs_exist(parser, [args.in_labels] + args.metrics_file_list) assert_headers_compatible(parser, [args.in_labels] + args.metrics_file_list) - # Loading label_data = get_data_as_labels(nib.load(args.in_labels)) with open(args.in_labels_lut) as f: @@ -88,7 +87,7 @@ def main(): # Process out_dict = get_stats_in_label(metric_data, label_data, label_dict) json_stats[metric_name] = out_dict - + if len(args.metrics_file_list) == 1: json_stats = json_stats[metric_name] print(json.dumps(json_stats)) diff --git a/scripts/tests/test_volume_stats_in_ROI.py b/scripts/tests/test_volume_stats_in_ROI.py index af3dd29ec..71eb2bc8d 100644 --- a/scripts/tests/test_volume_stats_in_ROI.py +++ b/scripts/tests/test_volume_stats_in_ROI.py @@ -19,10 +19,10 @@ def test_help_option(script_runner): def test_execution_tractometry(script_runner, monkeypatch): monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) - in_mask = os.path.join(SCILPY_HOME, 'tractometry', + in_roi = os.path.join(SCILPY_HOME, 'tractometry', 'IFGWM.nii.gz') in_ref = os.path.join(SCILPY_HOME, 'tractometry', 'mni_masked.nii.gz') ret = script_runner.run('scil_volume_stats_in_ROI.py', - in_mask, '--metrics', in_ref) + in_roi, '--metrics', in_ref) assert ret.success diff --git a/scripts/tests/test_volume_stats_in_labels.py b/scripts/tests/test_volume_stats_in_labels.py index 83bd07b8f..df1ea8064 100644 --- a/scripts/tests/test_volume_stats_in_labels.py +++ b/scripts/tests/test_volume_stats_in_labels.py @@ -21,5 +21,5 @@ def test_execution(script_runner, monkeypatch): in_atlas = os.path.join(SCILPY_HOME, 'plot', 'atlas_brainnetome.nii.gz') atlas_lut = os.path.join(SCILPY_HOME, 'plot', 'atlas_brainnetome.json') ret = script_runner.run('scil_volume_stats_in_labels.py', - in_atlas, atlas_lut, in_map) + in_atlas, atlas_lut, "--metrics", in_map) assert ret.success From ba480242bcba1cd339eb6b1445f9fab93f157be4 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Tue, 3 Dec 2024 16:15:53 -0500 Subject: [PATCH 45/63] NF - added add_json_args to the arguments, format the output json --- scripts/scil_volume_stats_in_ROI.py | 16 ++++++++-------- scripts/scil_volume_stats_in_labels.py | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 9fc6002bf..4382a4a9c 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -97,19 +97,19 @@ def main(): # Discussion about the way the normalization is done. # https://github.com/scilus/scilpy/pull/202#discussion_r411355609 # Summary: - # 1) We don't want to normalize with data = (data-min) / (max-min) because - # it zeroes out the minimal values of the array. This is not a large error - # source, but not preferable. - # 2) data = data / max(data) or data = data / sum(data): in practice, when - # we use them in numpy using their weights argument, leads to the same - # result. + # 1) We don't want to normalize with data = (data-min) / (max-min) + # because it zeroes out the minimal values of the array. This is + # not a large error source, but not preferable. + # 2) data = data / max(data) or data = data / sum(data): in practice, + # when we use them in numpy using their weights argument, leads to the + # same result. if args.normalize_weights: roi_data /= np.max(roi_data) elif args.bin: roi_data[np.where(roi_data > 0.0)] = 1.0 elif np.min(roi_data) < 0.0 or np.max(roi_data) > 1.0: - parser.error('ROI {} data should only contain values between 0 and 1. ' - 'Try --normalize_weights.' + parser.error('ROI {} data should only contain values between 0 ' + 'and 1. Try --normalize_weights.' .format(roi_filename)) # Load and process all metrics files. diff --git a/scripts/scil_volume_stats_in_labels.py b/scripts/scil_volume_stats_in_labels.py index 35d5c6c16..6a311459f 100755 --- a/scripts/scil_volume_stats_in_labels.py +++ b/scripts/scil_volume_stats_in_labels.py @@ -20,7 +20,7 @@ import numpy as np from scilpy.image.labels import get_data_as_labels, get_stats_in_label -from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, +from scilpy.io.utils import (add_json_args, add_overwrite_arg, add_verbose_arg, assert_inputs_exist, assert_headers_compatible) from scilpy.utils.filenames import split_name_with_nii @@ -43,7 +43,7 @@ def _build_arg_parser(): metavar='file', help='Metrics nifti filename. List of the names of the ' 'metrics file, \nin nifti format.') - + add_json_args(p) add_verbose_arg(p) add_overwrite_arg(p) @@ -69,7 +69,8 @@ def main(): for f in tmp_file_list] else: assert_inputs_exist(parser, [args.in_labels] + args.metrics_file_list) - assert_headers_compatible(parser, [args.in_labels] + args.metrics_file_list) + assert_headers_compatible(parser, + [args.in_labels] + args.metrics_file_list) # Loading label_data = get_data_as_labels(nib.load(args.in_labels)) @@ -90,7 +91,7 @@ def main(): if len(args.metrics_file_list) == 1: json_stats = json_stats[metric_name] - print(json.dumps(json_stats)) + print(json.dumps(json_stats, indent=args.indent, sort_keys=args.sort_keys)) if __name__ == "__main__": From d95a6c9cc2eac73208658cf3bbb85d27915828f5 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Thu, 19 Dec 2024 15:50:41 -0500 Subject: [PATCH 46/63] TST - Scripts inputs --- scripts/tests/test_volume_stats_in_ROI.py | 26 +++++++++++++++++++- scripts/tests/test_volume_stats_in_labels.py | 19 ++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/scripts/tests/test_volume_stats_in_ROI.py b/scripts/tests/test_volume_stats_in_ROI.py index 71eb2bc8d..c03593858 100644 --- a/scripts/tests/test_volume_stats_in_ROI.py +++ b/scripts/tests/test_volume_stats_in_ROI.py @@ -20,9 +20,33 @@ def test_help_option(script_runner): def test_execution_tractometry(script_runner, monkeypatch): monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) in_roi = os.path.join(SCILPY_HOME, 'tractometry', - 'IFGWM.nii.gz') + 'IFGWM.nii.gz') in_ref = os.path.join(SCILPY_HOME, 'tractometry', 'mni_masked.nii.gz') + + # Test with a single ROI input ret = script_runner.run('scil_volume_stats_in_ROI.py', in_roi, '--metrics', in_ref) assert ret.success + + # Test with multiple ROIs input + ret = script_runner.run('scil_volume_stats_in_ROI.py', + in_roi, in_roi, in_roi, '--metrics', in_ref) + assert ret.success + + # Test with multiple metric input + ret = script_runner.run('scil_volume_stats_in_ROI.py', + in_roi, '--metrics', in_ref, in_ref, in_ref) + assert ret.success + + # Test with multiple metric and ROIs input + ret = script_runner.run('scil_volume_stats_in_ROI.py', + in_roi, in_roi, '--metrics', in_ref, in_ref) + assert ret.success + + # Test with a metric folder + metrics_dir = os.path.join(SCILPY_HOME, 'plot') + in_roi = os.path.join(SCILPY_HOME, 'plot', 'mask_wm.nii.gz') + ret = script_runner.run('scil_volume_stats_in_ROI.py', + in_roi, '--metrics_dir', metrics_dir) + assert ret.success diff --git a/scripts/tests/test_volume_stats_in_labels.py b/scripts/tests/test_volume_stats_in_labels.py index df1ea8064..4a4471384 100644 --- a/scripts/tests/test_volume_stats_in_labels.py +++ b/scripts/tests/test_volume_stats_in_labels.py @@ -17,9 +17,24 @@ def test_help_option(script_runner): def test_execution(script_runner, monkeypatch): monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) - in_map = os.path.join(SCILPY_HOME, 'plot', 'fa.nii.gz') + in_metric = os.path.join(SCILPY_HOME, 'plot', 'fa.nii.gz') in_atlas = os.path.join(SCILPY_HOME, 'plot', 'atlas_brainnetome.nii.gz') atlas_lut = os.path.join(SCILPY_HOME, 'plot', 'atlas_brainnetome.json') + + # Test with a single metric + ret = script_runner.run('scil_volume_stats_in_labels.py', + in_atlas, atlas_lut, "--metrics", in_metric) + assert ret.success + + # Test with multiple metrics + ret = script_runner.run('scil_volume_stats_in_labels.py', + in_atlas, atlas_lut, "--metrics", + in_metric, in_metric, in_metric) + assert ret.success + + # Test with a metric folder + metrics_dir = os.path.join(SCILPY_HOME, 'plot') ret = script_runner.run('scil_volume_stats_in_labels.py', - in_atlas, atlas_lut, "--metrics", in_map) + in_atlas, atlas_lut, "--metrics_dir", + metrics_dir) assert ret.success From fa86a0ab08dd817e011da7220e002ebf6cff0466 Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Wed, 8 Jan 2025 11:24:59 -0500 Subject: [PATCH 47/63] BF - fixed errenous path when using metrics_dir --- scripts/scil_volume_stats_in_ROI.py | 4 +--- scripts/scil_volume_stats_in_labels.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 4382a4a9c..88916becf 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -76,9 +76,7 @@ def main(): .format(args.metrics_dir)) assert_inputs_exist(parser, args.in_rois) - tmp_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) - args.metrics_file_list = [os.path.join(args.metrics_dir, f) - for f in tmp_file_list] + args.metrics_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) else: assert_inputs_exist(parser, args.in_rois + args.metrics_file_list) assert_headers_compatible(parser, args.in_rois + args.metrics_file_list) diff --git a/scripts/scil_volume_stats_in_labels.py b/scripts/scil_volume_stats_in_labels.py index 6a311459f..05963e5af 100755 --- a/scripts/scil_volume_stats_in_labels.py +++ b/scripts/scil_volume_stats_in_labels.py @@ -64,9 +64,7 @@ def main(): .format(args.metrics_dir)) assert_inputs_exist(parser, [args.in_labels, args.in_labels_lut]) - tmp_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) - args.metrics_file_list = [os.path.join(args.metrics_dir, f) - for f in tmp_file_list] + args.metrics_file_list = glob.glob(os.path.join(args.metrics_dir, '*nii.gz')) else: assert_inputs_exist(parser, [args.in_labels] + args.metrics_file_list) assert_headers_compatible(parser, From 47448d5f6e8803a56e215f9d835cc0946ce01c1e Mon Sep 17 00:00:00 2001 From: Gabriel Girard Date: Thu, 23 Jan 2025 11:26:40 -0500 Subject: [PATCH 48/63] DOC --- scripts/scil_volume_stats_in_ROI.py | 4 ++-- scripts/scil_volume_stats_in_labels.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/scil_volume_stats_in_ROI.py b/scripts/scil_volume_stats_in_ROI.py index 88916becf..e31cb29f9 100755 --- a/scripts/scil_volume_stats_in_ROI.py +++ b/scripts/scil_volume_stats_in_ROI.py @@ -5,8 +5,8 @@ Compute the statistics (mean, std) of scalar maps, which can represent diffusion metrics, in ROIs. Prints the results. -The ROIs can either be a binary masks, or a weighting masks. If the ROIs are - weighting masks it should either contain floats between 0 and 1 or should be +The ROIs can either be binary masks, or weighting masks. If the ROIs are + weighting masks, they should either contain floats between 0 and 1 or should be normalized with --normalize_weights. IMPORTANT: if the ROIs contain weights (and not 0 and 1 exclusively), the standard deviation will also be weighted. """ diff --git a/scripts/scil_volume_stats_in_labels.py b/scripts/scil_volume_stats_in_labels.py index 05963e5af..5ff5c6f7c 100755 --- a/scripts/scil_volume_stats_in_labels.py +++ b/scripts/scil_volume_stats_in_labels.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Computes the information from the input map for each cortical region -(corresponding to an atlas). +Computes the information from the input metrics for each cortical region +(corresponding to an atlas). If more than one metric are provided, statistics are +computed separately for each. Hint: For instance, this script could be useful if you have a seed map from a specific bundle, to know from which regions it originated. @@ -81,7 +82,7 @@ def main(): metric_data = nib.load(metric_filename).get_fdata(dtype=np.float32) metric_name = split_name_with_nii(os.path.basename(metric_filename))[0] if len(metric_data.shape) > 3: - parser.error('Mask should be a 3D image.') + parser.error('Input metrics should be 3D images.') # Process out_dict = get_stats_in_label(metric_data, label_data, label_dict) From 71c21e6ce957f4406d08f17f9bda484f77a946ae Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Thu, 23 Jan 2025 16:13:07 -0500 Subject: [PATCH 49/63] Adding test for colinearity --- scripts/scil_gradients_validate_sampling.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 4c44d518e..6c01284f2 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -116,13 +116,17 @@ def main(): nb_shells = len(ubvals) nb_dir_per_shell = [np.sum(shell_idx == idx) for idx in range(nb_shells)] - ubvecs = np.unique(bvecs, axis=0) - nb_ubvecs = len(ubvecs) - if len(bvecs) != nb_ubvecs: - logging.error('{} b-vectors have the same direction as others, ' + # Count colinear vectors (either same or rotated by 180 degrees) + colinear_vectors = 0 + for bvec in bvecs: + cross_matrix = np.cross(bvec, bvecs) + if len(np.argwhere(np.sum(cross_matrix, axis=-1) == 0)) > 1: + colinear_vectors += 1 + if colinear_vectors != 0: + logging.error('{} b-vectors are colinear, ' 'which is suboptimal. There is most likely a problem ' 'with the gradient table.' - .format(len(bvecs) - nb_ubvecs)) + .format(colinear_vectors)) # Compute optimally distributed directions opt_bvecs, _ = generate_gradient_sampling(nb_dir_per_shell, From c43bca3dad02acb2b92dae4764a619fdb8a8a099 Mon Sep 17 00:00:00 2001 From: elyz081 Date: Fri, 24 Jan 2025 13:48:54 -0500 Subject: [PATCH 50/63] fix_1_comment --- scripts/scil_bundle_mean_fixel_afd.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/scil_bundle_mean_fixel_afd.py b/scripts/scil_bundle_mean_fixel_afd.py index fa0139abd..f081a81c2 100755 --- a/scripts/scil_bundle_mean_fixel_afd.py +++ b/scripts/scil_bundle_mean_fixel_afd.py @@ -11,7 +11,6 @@ Please use a bundle file rather than a whole tractogram. Formerly: scil_compute_fixel_afd_from_bundles.py - ----------------------------------------------------------------------------- Reference: [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., From 49b41b5c4e8ec8a75d68228fe909564939a20937 Mon Sep 17 00:00:00 2001 From: elyz081 Date: Fri, 24 Jan 2025 14:13:45 -0500 Subject: [PATCH 51/63] answer_PK_comments --- scripts/scil_aodf_metrics.py | 2 -- scripts/scil_bundle_mean_fixel_afd.py | 10 +++++----- scripts/scil_bundle_mean_fixel_afd_from_hdf5.py | 1 - scripts/scil_bundle_shape_measures.py | 2 +- scripts/scil_connectivity_compare_populations.py | 1 + scripts/scil_connectivity_compute_pca.py | 16 ++++++++-------- scripts/scil_dwi_prepare_eddy_command.py | 1 - 7 files changed, 15 insertions(+), 18 deletions(-) diff --git a/scripts/scil_aodf_metrics.py b/scripts/scil_aodf_metrics.py index 717c85a22..89f4586f8 100755 --- a/scripts/scil_aodf_metrics.py +++ b/scripts/scil_aodf_metrics.py @@ -23,10 +23,8 @@ given as the ratio of the L2-norm of odd SH coefficients on the L2-norm of all SH coefficients. - Formerly: scil_compute_asym_odf_metrics.py ------------------------------------------------------------------------ - References: [1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; diff --git a/scripts/scil_bundle_mean_fixel_afd.py b/scripts/scil_bundle_mean_fixel_afd.py index f081a81c2..ade771254 100755 --- a/scripts/scil_bundle_mean_fixel_afd.py +++ b/scripts/scil_bundle_mean_fixel_afd.py @@ -13,11 +13,11 @@ Formerly: scil_compute_fixel_afd_from_bundles.py ----------------------------------------------------------------------------- Reference: - [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., - Crozier, S., Salvado, O., & Connelly, A. (2012). - Apparent Fibre Density: a novel measure for the analysis of - diffusion-weighted magnetic resonance images. NeuroImage, 59(4), - 3976--3994. +[1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R., + Crozier, S., Salvado, O., & Connelly, A. (2012). + Apparent Fibre Density: a novel measure for the analysis of + diffusion-weighted magnetic resonance images. NeuroImage, 59(4), + 3976--3994. ----------------------------------------------------------------------------- """ diff --git a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py index 8f2f2af77..f3383d811 100755 --- a/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py +++ b/scripts/scil_bundle_mean_fixel_afd_from_hdf5.py @@ -12,7 +12,6 @@ Formerly: scil_compute_fixel_afd_from_hdf5.py ---------------------------------------------------------------------------- - Reference: [1] Raffelt, D., Tournier, JD., Rose, S., Ridgway, GR., Henderson, R.,Crozier, S., Salvado, O., & Connelly, A. (2012). Apparent Fibre Density: a novel diff --git a/scripts/scil_bundle_shape_measures.py b/scripts/scil_bundle_shape_measures.py index 797ba354d..709a8c015 100755 --- a/scripts/scil_bundle_shape_measures.py +++ b/scripts/scil_bundle_shape_measures.py @@ -34,7 +34,7 @@ Formerly: scil_compute_bundle_volume.py or scil_evaluate_bundles_individual_measures.py ------------------------------------------------------------------------------ -References: +Reference: [1] Fang-Cheng Yeh. 2020. Shape analysis of the human association pathways. NeuroImage. ------------------------------------------------------------------------------ diff --git a/scripts/scil_connectivity_compare_populations.py b/scripts/scil_connectivity_compare_populations.py index 4f1cdfc2c..99417aada 100755 --- a/scripts/scil_connectivity_compare_populations.py +++ b/scripts/scil_connectivity_compare_populations.py @@ -29,6 +29,7 @@ Formerly: scil_compare_connectivity.py ---------------------------------------------------------------------------- +References: [1] Rubinov, Mikail, and Olaf Sporns. "Complex network measures of brain connectivity: uses and interpretations." Neuroimage 52.3 (2010): 1059-1069. diff --git a/scripts/scil_connectivity_compute_pca.py b/scripts/scil_connectivity_compute_pca.py index 2cb90b5e0..d5a49438e 100755 --- a/scripts/scil_connectivity_compute_pca.py +++ b/scripts/scil_connectivity_compute_pca.py @@ -49,15 +49,15 @@ ------------------------------------------------------------------------------- References: [1] Chamberland M, Raven EP, Genc S, Duffy K, Descoteaux M, Parker GD, Tax CMW, - Jones DK. Dimensionality reduction of diffusion MRI measures for improved - tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. - doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; - PMCID: PMC6711466. + Jones DK. Dimensionality reduction of diffusion MRI measures for improved + tractometry of the human brain. Neuroimage. 2019 Oct 15;200:89-100. + doi: 10.1016/j.neuroimage.2019.06.020. Epub 2019 Jun 20. PMID: 31228638; + PMCID: PMC6711466. [2] Gagnon A., Grenier G., Bocti C., Gillet V., Lepage J.-F., Baccarelli A. A., - Posner J., Descoteaux M., Takser L. (2022). White matter microstructural - variability linked to differential attentional skills and impulsive behavior - in a pediatric population. Cerebral Cortex. - https://doi.org/10.1093/cercor/bhac180 + Posner J., Descoteaux M., Takser L. (2022). White matter microstructural + variability linked to differential attentional skills and impulsive behavior + in a pediatric population. Cerebral Cortex. + https://doi.org/10.1093/cercor/bhac180 [3] https://towardsdatascience.com/what-are-pca-loadings-and-biplots-9a7897f2e559 ------------------------------------------------------------------------------- """ diff --git a/scripts/scil_dwi_prepare_eddy_command.py b/scripts/scil_dwi_prepare_eddy_command.py index 0e9aac6f6..5508177db 100755 --- a/scripts/scil_dwi_prepare_eddy_command.py +++ b/scripts/scil_dwi_prepare_eddy_command.py @@ -16,7 +16,6 @@ import subprocess import numpy as np - from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, From 026b8691ed89685af71947ec4979451722d26a35 Mon Sep 17 00:00:00 2001 From: elyz081 Date: Fri, 24 Jan 2025 14:26:09 -0500 Subject: [PATCH 52/63] answering PK comments --- scripts/scil_NODDI_maps.py | 1 - scripts/scil_NODDI_priors.py | 1 - scripts/scil_freewater_maps.py | 13 ++++++------- scripts/scil_frf_ssst.py | 2 +- scripts/scil_gradients_generate_sampling.py | 1 - scripts/scil_labels_combine.py | 3 +-- scripts/scil_labels_dilate.py | 9 ++++----- scripts/scil_labels_remove.py | 3 +-- 8 files changed, 13 insertions(+), 20 deletions(-) diff --git a/scripts/scil_NODDI_maps.py b/scripts/scil_NODDI_maps.py index 351ba8a9b..01647dfd2 100755 --- a/scripts/scil_NODDI_maps.py +++ b/scripts/scil_NODDI_maps.py @@ -6,7 +6,6 @@ Multi-shell DWI necessary. Formerly: scil_compute_NODDI.py - --------------------------------------------------------------- Reference: [1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. diff --git a/scripts/scil_NODDI_priors.py b/scripts/scil_NODDI_priors.py index e027bb189..67bed2523 100755 --- a/scripts/scil_NODDI_priors.py +++ b/scripts/scil_NODDI_priors.py @@ -6,7 +6,6 @@ diffusivity priors for NODDI. Formerly: scil_compute_NODDI_priors.py - --------------------------------------------------------------- Reference: [1] Zhang H, Schneider T, Wheeler-Kingshott CA, Alexander DC. diff --git a/scripts/scil_freewater_maps.py b/scripts/scil_freewater_maps.py index 25ea807ba..3065d18eb 100755 --- a/scripts/scil_freewater_maps.py +++ b/scripts/scil_freewater_maps.py @@ -6,15 +6,14 @@ This script supports both single and multi-shell data. Formerly: scil_compute_freewater.py - ---------------------------------------------------------- References: - [1] Pasternak 0, Sochen N, Gur Y, Intrator N, Assaf Y. - Free water elimination and mapping from diffusion mri. - Magn Reson Med. 62 (3) (2009) 717-730. - [2] Daducci A, et al. Accelerated microstructure imaging - via convex optimization (AMICO) from diffusion MRI data. - Neuroimage 105 (2015) 32-44. +[1] Pasternak 0, Sochen N, Gur Y, Intrator N, Assaf Y. + Free water elimination and mapping from diffusion mri. + Magn Reson Med. 62 (3) (2009) 717-730. +[2] Daducci A, et al. Accelerated microstructure imaging + via convex optimization (AMICO) from diffusion MRI data. + Neuroimage 105 (2015) 32-44. ---------------------------------------------------------- """ diff --git a/scripts/scil_frf_ssst.py b/scripts/scil_frf_ssst.py index 81f0c7d2f..116704198 100755 --- a/scripts/scil_frf_ssst.py +++ b/scripts/scil_frf_ssst.py @@ -9,7 +9,7 @@ Formerly: scil_compute_ssst_frf.py ---------------------------------------------------------------------- -References: +Reference: [1] Tournier et al. NeuroImage 2007 ---------------------------------------------------------------------- """ diff --git a/scripts/scil_gradients_generate_sampling.py b/scripts/scil_gradients_generate_sampling.py index 7c3e792a5..b1751c9c5 100755 --- a/scripts/scil_gradients_generate_sampling.py +++ b/scripts/scil_gradients_generate_sampling.py @@ -11,7 +11,6 @@ diffusion gradient amplitude over a few TR. Formerly: scil_generate_gradient_sampling.py - ------------------------------------------------------------------------------- References: [1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, diff --git a/scripts/scil_labels_combine.py b/scripts/scil_labels_combine.py index 1f70fb9b3..2c69216d6 100755 --- a/scripts/scil_labels_combine.py +++ b/scripts/scil_labels_combine.py @@ -13,8 +13,7 @@ --volume_ids a2009s_aseg.nii.gz all --volume_ids clean/s1__DKT.nii.gz 1028 2028 -Formerly: scil_combine_labels.py. - +Formerly: scil_combine_labels.py ------------------------------------------------------------------------------ Reference: [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., diff --git a/scripts/scil_labels_dilate.py b/scripts/scil_labels_dilate.py index a48734cd5..45aa3f8de 100755 --- a/scripts/scil_labels_dilate.py +++ b/scripts/scil_labels_dilate.py @@ -15,12 +15,11 @@ --label_not_to_dilate 4 43 10 11 12 49 50 51 Formerly: scil_dilate_labels.py - ----------------------------------------------------------------------- -References: - [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., - Evans A.C. and Descoteaux M. OHBM 2019. - Surface integration for connectome analysis in age prediction. +Reference: +[1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., + Evans A.C. and Descoteaux M. OHBM 2019. + Surface integration for connectome analysis in age prediction. ----------------------------------------------------------------------- """ diff --git a/scripts/scil_labels_remove.py b/scripts/scil_labels_remove.py index fa5ec0e0a..026d952be 100755 --- a/scripts/scil_labels_remove.py +++ b/scripts/scil_labels_remove.py @@ -7,9 +7,8 @@ >>> scil_labels_remove.py DKT_labels.nii out_labels.nii.gz -i 5001 5002 Formerly: scil_remove_labels.py - ---------------------------------------------------------------------------- -References: +Reference: [1] Al-Sharif N.B., St-Onge E., Vogel J.W., Theaud G., Evans A.C. and Descoteaux M. OHBM 2019. Surface integration for connectome analysis in age prediction. From a13f7a2e1eb66b27a0efc365b9c2e6d16ae789fa Mon Sep 17 00:00:00 2001 From: EmmaRenauld Date: Mon, 27 Jan 2025 08:53:46 -0500 Subject: [PATCH 53/63] Remove obsolete warning --- scripts/scil_tractogram_commit.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/scil_tractogram_commit.py b/scripts/scil_tractogram_commit.py index b81b79b81..d51efb523 100755 --- a/scripts/scil_tractogram_commit.py +++ b/scripts/scil_tractogram_commit.py @@ -350,9 +350,6 @@ def main(): assert_headers_compatible(parser, [args.in_tractogram, args.in_dwi], reference=args.reference) - if ext == '.tck': - logging.warning("Commit only works with .trk format") - if args.commit2: if os.path.splitext(args.in_tractogram)[1] != '.h5': parser.error('COMMIT2 requires .h5 file for connectomics.') From 3161a8a4e1259531a710bc467fc390321eaf1155 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 27 Jan 2025 10:36:59 -0500 Subject: [PATCH 54/63] Adding save option, fixing deprecated issue, and adding positive message --- scilpy/viz/gradients.py | 2 +- scripts/scil_gradients_validate_sampling.py | 41 ++++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/scilpy/viz/gradients.py b/scilpy/viz/gradients.py index ef315a4b4..f9dc07f40 100644 --- a/scilpy/viz/gradients.py +++ b/scilpy/viz/gradients.py @@ -125,7 +125,7 @@ def plot_proj_shell(ms, use_sym=True, use_sphere=True, same_color=False, scene = window.Scene() scene.SetBackground(1, 1, 1) if use_sphere: - sphere = get_sphere('symmetric724') + sphere = get_sphere(name='symmetric724') shape = (1, 1, 1, sphere.vertices.shape[0]) _, fname = mkstemp(suffix='_odf_slicer.mmap') odfs = np.memmap(fname, dtype=np.float64, mode='w+', shape=shape) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 6c01284f2..8b5cd58dc 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -19,20 +19,25 @@ value, the script raises a warning. The user might want to use the -v verbose option to see the computed energies. -The --visualize option displays both the inputed and optimal b-vectors on a -single shell. +The --viz option displays both the inputed and optimal b-vectors on a +single shell. The --viz_and_save option first displays both the inputed and +optimal b-vectors on a single shell and then saves them as png. Use one or the +other, not both. For more options on visualization, please use +scil_viz_gradients_screenshot.py. """ import argparse import logging import numpy as np +from pathlib import Path from dipy.io.gradients import read_bvals_bvecs from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, add_b0_thresh_arg, add_skip_b0_check_arg, add_tolerance_arg, assert_inputs_exist, - assert_gradients_filenames_valid) + assert_gradients_filenames_valid, + assert_outputs_exist) from scilpy.gradients.bvec_bval_tools import (is_normalized_bvecs, check_b0_threshold, normalize_bvecs, @@ -65,10 +70,13 @@ def _build_arg_parser(): 'energy \nand the optimal b-vectors\' energy ' '(input_energy/optimal_energy). [%(default)s]') - p.add_argument( - '--visualize', action='store_true', - help='If set, the inputed gradient scheme is displayed, and then the ' - 'optimal one.') + p2 = p.add_mutually_exclusive_group() + p2.add_argument('--viz', action='store_true', + help='Visualize the inputed gradient scheme, then the ' + 'optimal one.') + p2.add_argument('--viz_and_save', metavar='OUT_FOLDER', + help='Save the inputed and optimal gradient schemes in ' + 'the specified folder.') add_b0_thresh_arg(p) add_skip_b0_check_arg(p, will_overwrite_with_min=False) @@ -100,6 +108,15 @@ def main(): parser.error('Depending on the gradient format, you should have ' 'two files for FSL format and one file for MRtrix') + # Check output files + out_files = [None, None] + if args.viz_and_save: + out_path = Path(args.viz_and_save) + out_files = [str(out_path / "inputed_gradient_scheme"), + str(out_path / "optimized_gradient_scheme")] + assert_outputs_exist(parser, args, out_files) + print(out_files) + # Check and remove b0s _ = check_b0_threshold(bvals.min(), args.b0_threshold, args.skip_b0_check, overwrite_with_min=False) @@ -133,12 +150,13 @@ def main(): verbose=0) # Visualize the gradient schemes - if args.visualize: + if args.viz or args.viz_and_save: viz_bvecs = build_ms_from_shell_idx(bvecs, shell_idx) viz_opt_bvecs = build_ms_from_shell_idx(opt_bvecs, shell_idx) - plot_proj_shell(viz_bvecs, use_sym=True, title="Inputed b-vectors") + plot_proj_shell(viz_bvecs, use_sym=True, title="Inputed b-vectors", + ofile=out_files[0]) plot_proj_shell(viz_opt_bvecs, use_sym=True, - title="Optimized b-vectors") + title="Optimized b-vectors", ofile=out_files[1]) # Compute the energy for both the input bvecs and optimal bvecs. energy, opt_energy = energy_comparison(bvecs, opt_bvecs, nb_shells, @@ -157,6 +175,9 @@ def main(): 'on the sphere. \nTheir energy is {} times higher ' 'than the optimal energy.' .format(np.round(e_ratio, decimals=3))) + else: + logging.warning('Everything looks fine with the inputed gradient ' + 'scheme.') if __name__ == "__main__": From a94b32c804ca2e6f164a354412a8ef84e814bbb5 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 27 Jan 2025 10:58:34 -0500 Subject: [PATCH 55/63] Fixing assert_outputs_exist and removing Path --- scripts/scil_gradients_validate_sampling.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 8b5cd58dc..b1a1e77c0 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -29,7 +29,7 @@ import argparse import logging import numpy as np -from pathlib import Path +import os from dipy.io.gradients import read_bvals_bvecs @@ -111,11 +111,14 @@ def main(): # Check output files out_files = [None, None] if args.viz_and_save: - out_path = Path(args.viz_and_save) - out_files = [str(out_path / "inputed_gradient_scheme"), - str(out_path / "optimized_gradient_scheme")] - assert_outputs_exist(parser, args, out_files) - print(out_files) + out_path = args.viz_and_save + out_files = [os.path.join(out_path, "inputed_gradient_scheme"), + os.path.join(out_path, "optimized_gradient_scheme")] + real_out_files = [os.path.join(out_path, + "inputed_gradient_scheme.png"), + os.path.join(out_path, + "optimized_gradient_scheme.png")] + assert_outputs_exist(parser, args, [], optional=real_out_files) # Check and remove b0s _ = check_b0_threshold(bvals.min(), args.b0_threshold, args.skip_b0_check, From 19935d176ccd2db0559fafac8eba3e2c3413d707 Mon Sep 17 00:00:00 2001 From: VincentBeaud Date: Mon, 27 Jan 2025 12:45:30 -0500 Subject: [PATCH 56/63] Add new test for dps_math: Import single value with non-array value. --- scripts/tests/test_tractogram_dps_math.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/tests/test_tractogram_dps_math.py b/scripts/tests/test_tractogram_dps_math.py index d6aabbda0..8a133633e 100644 --- a/scripts/tests/test_tractogram_dps_math.py +++ b/scripts/tests/test_tractogram_dps_math.py @@ -43,6 +43,20 @@ def test_execution_dps_math_import_single_value(script_runner, in_bundle = os.path.join(SCILPY_HOME, 'filtering', 'bundle_4.trk') outname = 'out.trk' + ret = script_runner.run('scil_tractogram_dps_math.py', + in_bundle, 'import', 'key', + '--in_dps_single_value', '42', + '--out_tractogram', outname, + '-f') + assert ret.success + + +def test_execution_dps_math_import_single_value_array(script_runner, + monkeypatch): + monkeypatch.chdir(os.path.expanduser(tmp_dir.name)) + in_bundle = os.path.join(SCILPY_HOME, 'filtering', + 'bundle_4.trk') + outname = 'out.trk' ret = script_runner.run('scil_tractogram_dps_math.py', in_bundle, 'import', 'key', '--in_dps_single_value', '1', '1.1', '1.2', From 14e39eae31c3c739806c6d4ed77454fe2c3746c3 Mon Sep 17 00:00:00 2001 From: elyz081 Date: Mon, 27 Jan 2025 13:37:31 -0500 Subject: [PATCH 57/63] answer PK comment --- scripts/scil_gradients_generate_sampling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/scil_gradients_generate_sampling.py b/scripts/scil_gradients_generate_sampling.py index b1751c9c5..6961dd70e 100755 --- a/scripts/scil_gradients_generate_sampling.py +++ b/scripts/scil_gradients_generate_sampling.py @@ -12,7 +12,7 @@ Formerly: scil_generate_gradient_sampling.py ------------------------------------------------------------------------------- -References: +Reference: [1] Emmanuel Caruyer, Christophe Lenglet, Guillermo Sapiro, Rachid Deriche. Design of multishell gradient sampling with uniform coverage in diffusion MRI. Magnetic Resonance in Medicine, Wiley, 2013, From 5a4db32ee624cc888f4259187f9c9543f983ce0a Mon Sep 17 00:00:00 2001 From: elyz081 Date: Mon, 27 Jan 2025 13:52:35 -0500 Subject: [PATCH 58/63] add missing import --- scripts/scil_connectivity_filter.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/scil_connectivity_filter.py b/scripts/scil_connectivity_filter.py index 5f02e0540..81b8b9ab0 100755 --- a/scripts/scil_connectivity_filter.py +++ b/scripts/scil_connectivity_filter.py @@ -33,11 +33,13 @@ If the user wants to manually handle the requirements, --keep_condition_count can be used and manually binarized using scil_connectivity_math.py -[1] Sidhu, J. (2022). Inter-lobar Connectivity of the Frontal Lobe Association -Tracts Consistently Observed in 105 Healthy Adults -(Doctoral dissertation, Université de Sherbrooke). - Formerly: scil_filter_connectivity.py +---------------------------------------------------------------------------- +Reference: +[1] Sidhu, J. (2022). Inter-lobar Connectivity of the Frontal Lobe Association + Tracts Consistently Observed in 105 Healthy Adults + (Doctoral dissertation, Université de Sherbrooke). +---------------------------------------------------------------------------- """ import argparse @@ -50,7 +52,7 @@ assert_outputs_exist, load_matrix_in_any_format, save_matrix_in_any_format, assert_inputs_exist) - +from scilpy.version import version_string def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, From d81aa3fdac6c7f6aad3c8fb6b88508c1aa0c7563 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 27 Jan 2025 15:10:34 -0500 Subject: [PATCH 59/63] Optimize code --- scripts/scil_gradients_validate_sampling.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index b1a1e77c0..7d8f71369 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -114,11 +114,8 @@ def main(): out_path = args.viz_and_save out_files = [os.path.join(out_path, "inputed_gradient_scheme"), os.path.join(out_path, "optimized_gradient_scheme")] - real_out_files = [os.path.join(out_path, - "inputed_gradient_scheme.png"), - os.path.join(out_path, - "optimized_gradient_scheme.png")] - assert_outputs_exist(parser, args, [], optional=real_out_files) + assert_outputs_exist(parser, args, [], + optional=[file + '.png' for file in out_files]) # Check and remove b0s _ = check_b0_threshold(bvals.min(), args.b0_threshold, args.skip_b0_check, From a1a85ee45b77594d863e494dda80584798b1f176 Mon Sep 17 00:00:00 2001 From: Arnaud Bore Date: Mon, 27 Jan 2025 15:45:46 -0500 Subject: [PATCH 60/63] Update scripts/scil_gradients_validate_sampling.py --- scripts/scil_gradients_validate_sampling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/scil_gradients_validate_sampling.py b/scripts/scil_gradients_validate_sampling.py index 7d8f71369..000f03775 100644 --- a/scripts/scil_gradients_validate_sampling.py +++ b/scripts/scil_gradients_validate_sampling.py @@ -115,7 +115,7 @@ def main(): out_files = [os.path.join(out_path, "inputed_gradient_scheme"), os.path.join(out_path, "optimized_gradient_scheme")] assert_outputs_exist(parser, args, [], - optional=[file + '.png' for file in out_files]) + optional=[f + '.png' for f in out_files]) # Check and remove b0s _ = check_b0_threshold(bvals.min(), args.b0_threshold, args.skip_b0_check, From 77c83ba9b308748e705dd75b3911e07dbac64e36 Mon Sep 17 00:00:00 2001 From: elyz081 Date: Mon, 27 Jan 2025 16:01:08 -0500 Subject: [PATCH 61/63] add tabs to references --- scripts/scil_aodf_metrics.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/scil_aodf_metrics.py b/scripts/scil_aodf_metrics.py index 89f4586f8..6679a9e3e 100755 --- a/scripts/scil_aodf_metrics.py +++ b/scripts/scil_aodf_metrics.py @@ -24,22 +24,22 @@ SH coefficients. Formerly: scil_compute_asym_odf_metrics.py ------------------------------------------------------------------------- +-------------------------------------------------------------------------------- References: [1] C. Poirier and M. Descoteaux, "Filtering Methods for Asymmetric ODFs: -Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; -2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 + Where and How Asymmetry Occurs in the White Matter." bioRxiv. 2022 Jan 1; + 2022.12.18.520881. doi: https://doi.org/10.1101/2022.12.18.520881 -[2] S. Cetin Karayumak, E. Özarslan, and G. Unal, -"Asymmetric Orientation Distribution Functions (AODFs) revealing intravoxel -geometry in diffusion MRI," Magnetic Resonance Imaging, vol. 49, pp. 145-158, -Jun. 2018, doi: https://doi.org/10.1016/j.mri.2018.03.006. +[2] S. Cetin Karayumak, E. Özarslan, and G. Unal,"Asymmetric Orientation + Distribution Functions (AODFs) revealing intravoxel geometry in diffusion MRI" + Magnetic Resonance Imaging, vol. 49, pp. 145-158, Jun. 2018, + doi: https://doi.org/10.1016/j.mri.2018.03.006. [3] C. Poirier, E. St-Onge, and M. Descoteaux, "Investigating the Occurence of -Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" -[Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, -Vancouver, BC, Abstract number 0865. ------------------------------------------------------------------------- + Asymmetric Patterns in White Matter Fiber Orientation Distribution Functions" + [Abstract], In: Proc. Intl. Soc. Mag. Reson. Med. 29 (2021), 2021 May 15-20, + Vancouver, BC, Abstract number 0865. +--------------------------------------------------------------------------------- """ @@ -67,7 +67,7 @@ def _build_arg_parser(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter, epilog=version_string) - + p.add_argument('in_sh', help='Input SH image.') p.add_argument('--mask', From b173dbf4a1053cf67dca3c36292b4e452b52805a Mon Sep 17 00:00:00 2001 From: elyz081 Date: Mon, 27 Jan 2025 17:03:39 -0500 Subject: [PATCH 62/63] revert change --- scripts/scil_dwi_prepare_eddy_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/scil_dwi_prepare_eddy_command.py b/scripts/scil_dwi_prepare_eddy_command.py index 5508177db..9e41a0dcb 100755 --- a/scripts/scil_dwi_prepare_eddy_command.py +++ b/scripts/scil_dwi_prepare_eddy_command.py @@ -15,8 +15,8 @@ import os import subprocess -import numpy as np from dipy.io.gradients import read_bvals_bvecs +import numpy as np from scilpy.io.utils import (add_overwrite_arg, add_verbose_arg, assert_fsl_options_exist, From 0d6ec120a1790469407c6aec3b05a47a54cc80e9 Mon Sep 17 00:00:00 2001 From: EmmaRenauld Date: Tue, 28 Jan 2025 14:13:45 -0500 Subject: [PATCH 63/63] Delete script fix_trk --- scripts/legacy/scil_fix_dsi_studio_trk.py | 21 -- scripts/scil_tractogram_fix_trk.py | 258 ---------------------- scripts/tests/test_tractogram_fix_trk.py | 7 - 3 files changed, 286 deletions(-) delete mode 100755 scripts/legacy/scil_fix_dsi_studio_trk.py delete mode 100755 scripts/scil_tractogram_fix_trk.py delete mode 100644 scripts/tests/test_tractogram_fix_trk.py diff --git a/scripts/legacy/scil_fix_dsi_studio_trk.py b/scripts/legacy/scil_fix_dsi_studio_trk.py deleted file mode 100755 index 2bb16f191..000000000 --- a/scripts/legacy/scil_fix_dsi_studio_trk.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from scilpy.io.deprecator import deprecate_script -from scripts.scil_tractogram_fix_trk import main as new_main - - -DEPRECATION_MSG = """ -This script has been renamed scil_tractogram_fix_trk.py. - -Please change your existing pipelines accordingly. -""" - - -@deprecate_script("scil_fix_dsi_studio_trk.py", DEPRECATION_MSG, '2.0.0') -def main(): - new_main() - - -if __name__ == "__main__": - main() diff --git a/scripts/scil_tractogram_fix_trk.py b/scripts/scil_tractogram_fix_trk.py deleted file mode 100755 index e7923348d..000000000 --- a/scripts/scil_tractogram_fix_trk.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -This script is made to fix DSI-Studio or Startrack TRK file -(unknown space/convention) to make it compatible with TrackVis, -MI-Brain, Dipy Horizon (Stateful Tractogram). - -DSI-Studio -========== - -The script either make it match with an anatomy from DSI-Studio (AC-PC aligned, -sometimes flipped) or if --in_native_fa is provided it moves it back to native -DWI space (this involved registration). - -Since DSI-Studio sometimes leaves some skull around the brain, the --auto_crop -aims to stabilize registration. If this option fails, manually BET both FA. -Registration is more robust at resolution above 2mm (iso), be careful. - -If you are fixing bundles, use this script once with --save_transfo and verify -results. Once satisfied, call the scripts on bundles using a bash for loop with ---load_transfo to save computation. - -We recommand the --cut_invalid to remove invalid points of streamlines rather -removing entire streamlines. - -This script was tested on various datasets and worked on all of them. However, -always verify the results and if a specific case does not work. Open an issue -on the Scilpy GitHub repository. - -Startrack -========== - -The script will create a new stateful tractogram using the reference in -order to fix the missing information in the header of the trk. - - -WARNING: This script is still experimental, DSI-Studio and Startrack -evolve quickly and results may vary depending on the data itself -as well as DSI-studio/Startrack version. - -Formerly: scil_fix_dsi_studio_trk.py -""" - -import argparse -import logging - -from dipy.align.imaffine import (transform_centers_of_mass, - MutualInformationMetric, - AffineRegistration) -from dipy.align.transforms import RigidTransform3D -from dipy.io.stateful_tractogram import StatefulTractogram, Space -from dipy.io.utils import get_reference_info -from dipy.io.streamline import save_tractogram, load_tractogram -import nibabel as nib -import numpy as np - -from scilpy.image.volume_operations import mask_data_with_default_cube -from scilpy.io.utils import (add_bbox_arg, - add_verbose_arg, - add_overwrite_arg, - assert_inputs_exist, - assert_outputs_exist) -from scilpy.tractograms.tractogram_operations import (flip_sft, - transform_warp_sft, - get_axis_flip_vector) -from scilpy.tractograms.streamline_operations import cut_invalid_streamlines - -softwares = ['dsi_studio', 'startrack'] - - -def _build_arg_parser(): - p = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) - - p.add_argument('in_tractogram', - help='Path of the input tractogram file from DSI studio ' - '(.trk).') - p.add_argument('out_tractogram', - help='Path of the output tractogram file.') - p.add_argument('--software', metavar='string', default='None', - choices=softwares, - help='Software used to create in_tractogram.\n' - 'Choices: {}'.format(softwares)) - - invalid = p.add_mutually_exclusive_group() - invalid.add_argument('--cut_invalid', action='store_true', - help='Cut invalid streamlines rather than removing ' - 'them.\nKeep the longest segment only.') - invalid.add_argument('--remove_invalid', action='store_true', - help='Remove the streamlines landing out of the ' - 'bounding box.') - - g1 = p.add_argument_group(title='DSI options') - g1.add_argument('--in_dsi_fa', - help='Path of the input FA from DSI Studio (.nii.gz).') - - g1.add_argument('--in_native_fa', - help='Path of the input FA from Dipy/MRtrix (.nii.gz).\n' - 'Move the tractogram back to a "proper" space, ' - 'include registration.') - g1.add_argument('--auto_crop', action='store_true', - help='If both FA are not already BET, ' - 'perform registration \n' - 'using a centered-cube crop to ignore the skull.\n' - 'A good BET for both is more robust.') - transfo = g1.add_mutually_exclusive_group() - transfo.add_argument('--save_transfo', metavar='FILE', - help='Save estimated transformation to avoid ' - 'recomputing (.txt).') - transfo.add_argument('--load_transfo', metavar='FILE', - help='Load estimated transformation to apply ' - 'to other files (.txt).') - - g2 = p.add_argument_group(title='StarTrack options') - g2.add_argument('--reference', - help='Reference anatomy (.nii or .nii.gz).') - - add_bbox_arg(p) - add_verbose_arg(p) - add_overwrite_arg(p) - - return p - - -def main(): - parser = _build_arg_parser() - args = parser.parse_args() - logging.getLogger().setLevel(logging.getLevelName(args.verbose)) - - warning_msg = """ - # This script is still experimental, DSI-Studio and Startrack - # evolve quickly and results may vary depending on the data itself - # as well as DSI-studio/Startrack version. - """ - - logging.warning(warning_msg) - - assert_outputs_exist(parser, args, args.out_tractogram) - - if args.software == 'startrack': - assert_inputs_exist(parser, [args.in_tractogram, args.reference]) - sft = load_tractogram(args.in_tractogram, 'same', - bbox_valid_check=args.bbox_check, - trk_header_check=False) - new_sft = StatefulTractogram(sft.streamlines, args.reference, - Space.VOX) - - # Startrack flips the TRK - flip_axis = ['x'] - new_sft.to_vox() - new_sft.streamlines._data -= get_axis_flip_vector(flip_axis) - new_sft = flip_sft(new_sft, flip_axis) - new_sft.to_rasmm() - - else: - if args.load_transfo and args.in_native_fa is None: - parser.error('When loading a transformation, the final ' - 'reference is needed, use --in_native_fa.') - - assert_inputs_exist(parser, [args.in_tractogram, args.in_dsi_fa], - optional=args.in_native_fa) - - sft = load_tractogram(args.in_tractogram, 'same', - bbox_valid_check=args.bbox_check) - # LPS -> RAS convention in voxel space - sft.to_vox() - flip_axis = ['x', 'y'] - sft_fix = StatefulTractogram(sft.streamlines, args.in_dsi_fa, - Space.VOXMM) - sft_fix.to_vox() - sft_fix.streamlines._data -= get_axis_flip_vector(flip_axis) - - sft_flip = flip_sft(sft_fix, flip_axis) - - sft_flip.to_rasmm() - sft_flip.streamlines._data -= [0.5, 0.5, -0.5] - - if not args.in_native_fa: - if args.cut_invalid: - sft_flip, _ = cut_invalid_streamlines(sft_flip) - elif args.remove_invalid: - sft_flip.remove_invalid_streamlines() - save_tractogram(sft_flip, args.out_tractogram, - bbox_valid_check=args.bbox_check) - else: - static_img = nib.load(args.in_native_fa) - static_data = static_img.get_fdata() - moving_img = nib.load(args.in_dsi_fa) - moving_data = moving_img.get_fdata() - - # DSI-Studio flips the volume without changing the affine (I think) - # So this has to be reversed (not the same problem as above) - vox_order = get_reference_info(moving_img)[3] - flip_axis = [] - if vox_order[0] == 'L': - moving_data = moving_data[::-1, :, :] - flip_axis.append('x') - if vox_order[1] == 'P': - moving_data = moving_data[:, ::-1, :] - flip_axis.append('y') - if vox_order[2] == 'I': - moving_data = moving_data[:, :, ::-1] - flip_axis.append('z') - sft_flip_back = flip_sft(sft_flip, flip_axis) - - if args.load_transfo: - transfo = np.loadtxt(args.load_transfo) - else: - # Sometimes DSI studio has quite a lot of skull left - # Dipy Median Otsu does not work with FA/GFA - if args.auto_crop: - moving_data = mask_data_with_default_cube(moving_data) - static_data = mask_data_with_default_cube(static_data) - - # Since DSI Studio register to AC/PC and does not save the - # transformation We must estimate the transformation, - # since it's rigid it is 'easy' - c_of_mass = transform_centers_of_mass(static_data, - static_img.affine, - moving_data, - moving_img.affine) - - nbins = 32 - sampling_prop = None - level_iters = [1000, 100, 10] - sigmas = [3.0, 2.0, 1.0] - factors = [3, 2, 1] - metric = MutualInformationMetric(nbins, sampling_prop) - affreg = AffineRegistration(metric=metric, - level_iters=level_iters, - sigmas=sigmas, - factors=factors) - transform = RigidTransform3D() - rigid = affreg.optimize(static_data, moving_data, transform, - None, static_img.affine, - moving_img.affine, - starting_affine=c_of_mass.affine) - transfo = rigid.affine - if args.save_transfo: - np.savetxt(args.save_transfo, transfo) - - new_sft = transform_warp_sft(sft_flip_back, transfo, - static_img, inverse=True, - remove_invalid=args.remove_invalid, - cut_invalid=args.cut_invalid) - - if args.cut_invalid: - new_sft, _ = cut_invalid_streamlines(new_sft) - elif args.remove_invalid: - new_sft.remove_invalid_streamlines() - - save_tractogram(new_sft, args.out_tractogram, - bbox_valid_check=args.bbox_check) - - -if __name__ == "__main__": - main() diff --git a/scripts/tests/test_tractogram_fix_trk.py b/scripts/tests/test_tractogram_fix_trk.py deleted file mode 100644 index c6f340e06..000000000 --- a/scripts/tests/test_tractogram_fix_trk.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - - -def test_help_option(script_runner): - ret = script_runner.run('scil_tractogram_fix_trk.py', '--help') - assert ret.success