Skip to content

Commit

Permalink
Merge pull request #160 from ThoumyreStanislas/stat_tractometry
Browse files Browse the repository at this point in the history
[Module] Stats tractometry module.
  • Loading branch information
AlexVCaron committed Jun 13, 2024
2 parents c3df6d2 + 8aa5461 commit f402276
Show file tree
Hide file tree
Showing 8 changed files with 714 additions and 0 deletions.
7 changes: 7 additions & 0 deletions modules/nf-scil/bundle/stats/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: "bundle_stats"
channels:
- Docker
- Apptainer
dependencies:
- "scilpy"
186 changes: 186 additions & 0 deletions modules/nf-scil/bundle/stats/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
process BUNDLE_STATS {
tag "$meta.id"
label 'process_single'

container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
'https://scil.usherbrooke.ca/containers/scilus_2.0.2.sif':
'scilus/scilus:2.0.2' }"

input:
tuple val(meta), path(bundles), path(labels_map), path(metrics)

output:
tuple val(meta), path("*_length_stats.json") , emit: length, optional: true
tuple val(meta), path("*_endpoints_map_raw.json") , emit: endpoints_raw, optional: true
tuple val(meta), path("*_endpoints_metric_stats.json") , emit: endpoints_metric_stats, optional: true
tuple val(meta), path("*_mean_std.json") , emit: mean_std, optional: true
tuple val(meta), path("*_volume.json") , emit: volume, optional: true
tuple val(meta), path("*_streamline_count.json") , emit: streamline_count, optional: true
tuple val(meta), path("*_volume_per_label.json") , emit: volume_per_labels, optional: true
tuple val(meta), path("*_mean_std_per_point.json") , emit: mean_std_per_point, optional: true
tuple val(meta), path("*_endpoints_map_head.nii.gz") , emit: endpoints_head, optional: true
tuple val(meta), path("*_endpoints_map_tail.nii.gz") , emit: endpoints_tail, optional: true
path "versions.yml" , emit: versions

when:
task.ext.when == null || task.ext.when

script:
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"
def density_weighting = task.ext.density_weighting ? "--density_weighting" : ""
def normalize_weights = task.ext.normalize_weights ? "--normalize_weights" : "--bin"
def length_stats = task.ext.length_stats ?: ""
def endpoints = task.ext.endpoints ?: ""
def mean_std = task.ext.mean_std ?: ""
def volume = task.ext.volume ?: ""
def streamline_count = task.ext.streamline_count ?: ""
def volume_per_labels = task.ext.volume_per_labels ?: ""
def mean_std_per_point = task.ext.mean_std_per_point ?: ""

"""
bundles=( ${bundles.join(" ")} )
label_map=( ${labels_map.join(" ")} )
for index in \${!bundles[@]};
do\
bname=\$(basename \${bundles[index]} .trk);
b_metrics="$metrics";
if [[ "$length_stats" ]];
then
scil_tractogram_print_info.py \${bundles[index]} > \${bname}_length.json
fi
if [[ "$endpoints" ]];
then
scil_bundle_compute_endpoints_map.py \${bundles[index]} \
\${bname}_endpoints_map_head.nii.gz \
\${bname}_endpoints_map_tail.nii.gz >\
${prefix}__\${bname}_endpoints_raw.json;
scil_volume_stats_in_ROI.py \${bname}_endpoints_map_head.nii.gz $normalize_weights\
--metrics \${b_metrics} > \${bname}_head.json
scil_volume_stats_in_ROI.py \${bname}_endpoints_map_tail.nii.gz $normalize_weights\
--metrics \${b_metrics} > \${bname}_tail.json;
fi
if [[ "$mean_std" ]];
then
scil_bundle_mean_std.py $density_weighting \${bundles[index]} \${b_metrics} >\
\${bname}_std.json
fi
if [[ "$volume" ]];
then
scil_bundle_shape_measures.py \${bundles[index]} > \${bname}_volume_stat.json
elif [[ "$streamline_count" ]];
then
scil_tractogram_count_streamlines.py \${bundles[index]} > \${bname}_streamlines.json
fi
if [[ "$volume_per_labels" ]];
then
scil_bundle_volume_per_label.py \${label_map[index]} \$bname --sort_keys >\
\${bname}_volume_label.json
fi
if [[ "$mean_std_per_point" ]];
then
scil_bundle_mean_std.py \${bundles[index]} \${b_metrics}\
--per_point \${label_map[index]} --sort_keys $density_weighting > \${bname}_std_per_point.json
fi;
done
#Bundle_Length_Stats
if [[ "$length_stats" ]];
then
scil_json_merge_entries.py *_length.json ${prefix}_length_stats.json --add_parent_key ${prefix} \
--keep_separate
fi
#Bundle_Endpoints_Map
if [[ "$endpoints" ]];
then
scil_json_merge_entries.py *_endpoints_raw.json ${prefix}_endpoints_map_raw.json \
--no_list --add_parent_key ${prefix}
#Bundle_Metrics_Stats_In_Endpoints
scil_json_merge_entries.py *_tail.json *_head.json ${prefix}_endpoints_metric_stats.json \
--no_list --add_parent_key ${prefix}
fi
#Bundle_Mean_Std
if [[ "$mean_std" ]];
then
scil_json_merge_entries.py *_std.json ${prefix}_mean_std.json --no_list --add_parent_key ${prefix}
fi
#Bundle_Volume
if [[ "$volume" ]];
then
scil_json_merge_entries.py *_volume_stat.json ${prefix}_volume.json --no_list --add_parent_key ${prefix}
#Bundle_Streamline_Count
elif [[ "$streamline_count" ]];
then
scil_json_merge_entries.py *_streamlines.json ${prefix}_streamline_count.json --no_list \
--add_parent_key ${prefix}
fi
#Bundle_Volume_Per_Label
if [[ "$volume_per_labels" ]];
then
scil_json_merge_entries.py *_volume_label.json ${prefix}_volume_per_label.json --no_list \
--add_parent_key ${prefix}
fi
#Bundle_Mean_Std_Per_Point
if [[ "$mean_std_per_point" ]];
then
scil_json_merge_entries.py *_std_per_point.json ${prefix}_mean_std_per_point.json --no_list \
--add_parent_key ${prefix}
fi
cat <<-END_VERSIONS > versions.yml
"${task.process}":
scilpy: 2.0.2
END_VERSIONS
"""

stub:
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"

"""
scil_tractogram_print_info.py -h
scil_bundle_compute_endpoints_map.py -h
scil_volume_stats_in_ROI.py -h
scil_bundle_mean_std.py -h
scil_bundle_shape_measures.py -h
scil_tractogram_count_streamlines.py -h
scil_bundle_volume_per_label.py -h
scil_bundle_mean_std.py -h
scil_json_merge_entries.py -h
touch ${prefix}__length_stats.json
touch ${prefix}__endpoints_map_raw.json
touch ${prefix}__endpoints_metric_stats.json
touch ${prefix}__mean_std.json
touch ${prefix}__volume.json
touch ${prefix}__streamline_count.json
touch ${prefix}__volume_per_label.json
touch ${prefix}__mean_std_per_point.json
touch ${prefix}_endpoints_map_head.nii.gz
touch ${prefix}_endpoints_map_tail.nii.gz
cat <<-END_VERSIONS > versions.yml
"${task.process}":
scilpy: 2.0.2
END_VERSIONS
"""
}
144 changes: 144 additions & 0 deletions modules/nf-scil/bundle/stats/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
name: "bundle_stats"
description: |
Compile statistics on bundle profiles. Uses the data after segmenting the tractogram into different bundles, which in turn are segmented into different sections.
This module allows you to perform statistical analysis on bundles using metrics maps. You can choose from several types of statistics.
----------- Available statistics -----------
volume:
- volume_info: volume, volume_endpoints
- streamlines_info: streamlines_count, avg_length (in mm or in number of point), average step size, min_length, max_length.
- shape_info: span, curl, diameter, elongation, surface area, irregularity, end surface area, radius, end surface irregularity, mean_curvature, fractal dimension.
** The diameter, here, is a simple estimation using volume / length.
length:
number of streamlines, and mean / min / max / std of :
- length in number of points
- length in mm
- step size.
endpoints:
Computes the endpoint map of a bundle. The endpoint map is simply a count of the number of streamlines that start or end in each voxel.
Then, Compute the statistics (mean, std) of scalar maps, which can represent diffusion metrics, in endpoint map.
mean std:
- mean and std for each metric.
streamline count:
- number of streamlines in a tractogram. (as this information is given by the volume stat you can choose to want only the streamlines count by deactivating volume and activating streamlines count.)
volume per labels:
- bundle volume per label in mm3. This script supports anisotropic voxels resolution.
Volume is estimated by counting the number of voxel occupied by each label and multiplying it by the volume of a single voxel.
mean std per labels:
- mean and std for each metric along the bundle for each point(labels).
**To create label_map and distance_map, see scil_bundle_label_map.py
keywords:
- Bundle
- Labels
- Statistic
- Volume
- Length
- Endpoint
- Mean std
- Streamlines count
- json

tools:
- "scilpy":
description: "The Sherbrooke Connectivity Imaging Lab (SCIL) Python dMRI processing toolbox."
homepage: "https://github.com/scilus/scilpy.git"

input:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- bundles:
type: file
description: Fiber bundle file to compute statistics on.
pattern: "*.trk"

- labels_map:
type: file
description:
label map of the corresponding fiber bundle. this file must have the same dimension than bundle file and have datatype in int.
If you have multiple bundes, it must have the same numbers of sections.
pattern: "*.nii.gz"

- metrics:
type: file
description: Nifti file to compute statistics on. Probably some tractometry measure(s) such as FA, MD, RD, ...
The metrics has to follow a specific naming convention.
pattern: "*_{bundle_name}_{metric_name}.nii.gz"

output:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- length:
type: file
description: Information on a tractogram, number of streamlines, mean / min / max / std of length in number of points, length in mm and step size.
pattern: "*__length_stats.json"

- endpoints_raw:
type: file
description: Estimation of the cortical area affected by the bundle (assuming streamlines start/end in the cortex).
pattern: "*__endpoints_map_raw.json"

- endpoints_metric_stats:
type: file
description: Compute the statistics of metrics at the bundle endpoint map.
pattern: "*__endpoints_metric_stats.json"

- mean_std:
type: file
description: Average the metric values of all voxels occupied by the bundle.
pattern: "*__mean_std.json"

- volume:
type: file
description: Evaluate basic measurements of bundle(s).
pattern: "*__volume.json"

- streamline_count:
type: file
description: Return the number of streamlines in a tractogram.
pattern: "*__streamline_count.json"

- volume_per_labels:
type: file
description: Compute bundle volume per label in mm3. This script supports anisotropic voxels resolution. Volume is estimated by counting the number of voxel occupied by each label and multiplying it by the volume of a single voxel.
pattern: "*__volume_per_label.json"

- mean_std_per_point:
type: file
description: Average the metric values of all voxels occupied by the bundle per label.
pattern: "*__mean_std_per_point.json"

- endpoints_head:
type: file
description: Endpoint head map of bundle. The endpoint head map is simply a count of the number of streamlines that start in each voxel.
pattern: "*.nii.gz"

- endpoints_tail:
type: file
description: Endpoint tail map of bundle. The endpoint tail map is simply a count of the number of streamlines that end in each voxel.
pattern: "*.nii.gz"

- versions:
type: file
description: File containing software versions
pattern: "versions.yml"

authors:
- "@ThoumyreStanislas"
maintainers:
- "@ThoumyreStanislas"
Loading

0 comments on commit f402276

Please sign in to comment.