Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compare tractograms (TODI, TDI, ACC) #767

Merged
merged 35 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8a200c4
Transfer script
Oct 23, 2023
abf57c3
Pep8 and docstring
Oct 23, 2023
06799e6
Skip
frheault Nov 3, 2023
b90cecd
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Nov 27, 2023
7b6762a
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
Dec 14, 2023
54db51a
rename script
Dec 14, 2023
bd4ecc5
Rename script and add test
Dec 14, 2023
d358207
Merge branch 'compare_tractogram_big' of github.com:frheault/scilpy i…
frheault Dec 18, 2023
b75b8ea
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Dec 18, 2023
4908fe2
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Jan 15, 2024
3181ea8
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Jan 29, 2024
9199845
Converting the main to a wrapper function
frheault Jan 29, 2024
602948c
Add _ to wrapper
frheault Jan 29, 2024
569aa3d
First pass emmanuel comments
Feb 2, 2024
824561b
Fix typo/errors
Feb 2, 2024
aa78ad2
test start
frheault Feb 12, 2024
d674e17
Added test
frheault Feb 12, 2024
9fe6f9e
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Feb 19, 2024
04e5b21
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Feb 20, 2024
122db3d
Address Emmanuel comments
frheault Feb 21, 2024
493399d
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Feb 21, 2024
b7e0d4a
Reorganize functions and tests
frheault Feb 21, 2024
b996640
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Feb 22, 2024
9359b19
Fix conflcit
frheault Feb 27, 2024
33ce72f
Fix errors
frheault Feb 27, 2024
8593c92
Fix kdtree type error
frheault Feb 28, 2024
813f3e7
Fix kdtree type error
frheault Feb 28, 2024
695528e
Merge branch 'master' of github.com:scilus/scilpy into compare_tracto…
frheault Mar 1, 2024
6075810
Remove data from moved test
frheault Mar 1, 2024
b2b938c
Fix conflict
Mar 4, 2024
4403d23
Fix conflict
Mar 4, 2024
85f48cf
Fix Conflict
Mar 18, 2024
e5272e5
Passing test
Mar 18, 2024
3d52b37
Missing import
frheault Mar 18, 2024
b67a138
Fix conflicts
frheault Mar 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion scilpy/image/tests/test_volume_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
import nibabel as nib
import numpy as np
from dipy.io.gradients import read_bvals_bvecs
from numpy.testing import assert_equal
from numpy.testing import assert_equal, assert_almost_equal

from scilpy import SCILPY_HOME
from scilpy.image.volume_operations import (apply_transform,
compute_snr,
crop_volume,
flip_volume,
merge_metrics,
normalize_metric,
resample_volume)
from scilpy.io.fetcher import fetch_data, get_testing_files_dict
from scilpy.utils.util import compute_nifti_bounding_box
Expand Down Expand Up @@ -127,3 +129,35 @@ def test_resample_volume():
resampled_img = resample_volume(moving3d_img, res=(2, 2, 2), interp='nn')

assert_equal(resampled_img.get_fdata(), ref3d)


def test_normalize_metric_basic():
metric = np.array([1, 2, 3, 4, 5])
expected_output = np.array([0., 0.25, 0.5, 0.75, 1.])
normalized_metric = normalize_metric(metric)
assert_almost_equal(normalized_metric, expected_output)


def test_normalize_metric_nan_handling():
metric = np.array([1, np.nan, 3, np.nan, 5])
expected_output = np.array([0., np.nan, 0.5, np.nan, 1.])
normalized_metric = normalize_metric(metric)

assert_almost_equal(normalized_metric, expected_output)


def test_merge_metrics_basic():
arrays = [np.array([1, 2, 3]), np.array([4, 5, 6])]
# Geometric mean boosted by beta=1
expected_output = np.array([2.0, 3.162278, 4.242641])
merged_metric = merge_metrics(*arrays)

assert_almost_equal(merged_metric, expected_output, decimal=6)


def test_merge_metrics_nan_propagation():
arrays = [np.array([1, np.nan, 3]), np.array([4, 5, 6])]
expected_output = np.array([2., np.nan, 4.242641]) # NaN replaced with -2
merged_metric = merge_metrics(*arrays)

assert_almost_equal(merged_metric, expected_output, decimal=6)
67 changes: 67 additions & 0 deletions scilpy/image/volume_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from dipy.segment.mask import crop, median_otsu
import nibabel as nib
import numpy as np
from numpy import ma
from scipy.ndimage import binary_dilation, gaussian_filter

from scilpy.image.reslice import reslice # Don't use Dipy's reslice. Buggy.
Expand Down Expand Up @@ -528,3 +529,69 @@ def crop_data_with_default_cube(data):
roi_mask = _mask_from_roi(shape, roi_center, roi_radii)

return data * roi_mask


def normalize_metric(metric, reverse=False):
arnaudbore marked this conversation as resolved.
Show resolved Hide resolved
"""
Normalize a metric array to a range between 0 and 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could actually work with an array of any dimension. It does not have to be a volume. Ex, it is probably useful everytime we want to change the range of a colormap. Should we put it in utils? @arnaudbore

optionally reversing the normalization.

Parameters
----------
metric : ndarray
The input metric array to be normalized.
reverse : bool, optional
If True, reverse the normalization (i.e., 1 - normalized value).
Default is False.

Returns
-------
ndarray
The normalized (and possibly reversed) metric array.
NaN values in the input are retained.
"""
mask = np.isnan(metric)
masked_metric = ma.masked_array(metric, mask)

min_val, max_val = masked_metric.min(), masked_metric.max()
normalized_metric = (masked_metric - min_val) / (max_val - min_val)

if reverse:
normalized_metric = 1 - normalized_metric

return ma.filled(normalized_metric, fill_value=np.nan)


def merge_metrics(*arrays, beta=1.0):
"""
Merge an arbitrary number of metrics into a single heatmap using a weighted
geometric mean, ignoring NaN values. Each input array contributes equally
to the geometric mean, and the result is boosted by a specified factor.

Parameters
----------
*arrays : ndarray
An arbitrary number of input arrays (ndarrays).
All arrays must have the same shape.
beta : float, optional
Boosting factor for the geometric mean. The default is 1.0.

Returns
-------
ndarray
Boosted geometric mean of the inputs (same shape as the input arrays)
NaN values in any input array are propagated to the output.
"""

# Create a mask for NaN values in any of the arrays
mask = np.any([np.isnan(arr) for arr in arrays], axis=0)
masked_arrays = [ma.masked_array(arr, mask) for arr in arrays]

# Calculate the product of the arrays for the geometric mean
array_product = np.prod(masked_arrays, axis=0)

# Calculate the geometric mean for valid data
geometric_mean = np.power(array_product, 1 / len(arrays))
boosted_mean = geometric_mean ** beta

return ma.filled(boosted_mean, fill_value=np.nan)
Loading