From a5c76824d10cd65f176909a653af83855da011a4 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 30 Jan 2023 14:58:47 -0500 Subject: [PATCH 1/7] Adding variance to script --- scilpy/viz/scene_utils.py | 47 +++++++++++++++++++++++++++++----- scripts/scil_visualize_fodf.py | 39 ++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/scilpy/viz/scene_utils.py b/scilpy/viz/scene_utils.py index 799880d71..ab9d9df01 100644 --- a/scilpy/viz/scene_utils.py +++ b/scilpy/viz/scene_utils.py @@ -122,7 +122,8 @@ def set_display_extent(slicer_actor, orientation, volume_shape, slice_index): def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, nb_subdivide, sh_order, sh_basis, full_basis, - scale, radial_scale, norm, colormap): + scale, radial_scale, norm, colormap, sh_variance=None, + variance_color=(255, 255, 255)): """ Create a ODF slicer actor displaying a fODF slice. The input volume is a 3-dimensional grid containing the SH coefficients of the fODF for each @@ -158,6 +159,10 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, If True, enables normalization of ODF slicer. colormap : str Colormap for the ODF slicer. If None, a RGB colormap is used. + sh_variance : np.ndarray, optional + Spherical harmonics of the variance fODF data. + variance_color : tupple, optional + Color of the variance fODF data, in RGB. Returns ------- @@ -172,14 +177,42 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, B_mat = sh_to_sf_matrix(sphere, sh_order, sh_basis, full_basis, return_inv=False) - odf_actor = actor.odf_slicer(sh_fodf, mask=mask, norm=norm, - radial_scale=radial_scale, - sphere=sphere, - colormap=colormap, - scale=scale, B_matrix=B_mat) + if sh_variance is not None: + fodf = sh_to_sf(sh_fodf, sphere, sh_order, sh_basis, + full_basis=full_basis) + fodf_var = sh_to_sf(sh_variance, sphere, sh_order, sh_basis, + full_basis=full_basis) + # normalise fodf and variance + if norm: + maximums = np.abs(np.append(fodf, fodf_var, axis=-1))\ + .max(axis=-1) + fodf[maximums > 0] /= maximums[maximums > 0][..., None] + fodf_var[maximums > 0] /= maximums[maximums > 0][..., None] + + odf_actor = odf_slicer(fodf, mask=mask, norm=False, + radial_scale=radial_scale, + sphere=sphere, scale=scale, + colormap=colormap) + + var_actor = odf_slicer(fodf_var, mask=mask, norm=False, + radial_scale=radial_scale, + sphere=sphere, scale=scale, + colormap=variance_color) + var_actor.GetProperty().SetDiffuse(0.0) + var_actor.GetProperty().SetAmbient(1.0) + var_actor.GetProperty().SetFrontfaceCulling(True) + else: + odf_actor = actor.odf_slicer(sh_fodf, mask=mask, norm=norm, + radial_scale=radial_scale, + sphere=sphere, + colormap=colormap, + scale=scale, B_matrix=B_mat) set_display_extent(odf_actor, orientation, sh_fodf.shape[:3], slice_index) + if var_actor is not None: + set_display_extent(var_actor, orientation, + fodf_var.shape[:3], slice_index) - return odf_actor + return odf_actor, var_actor def _get_affine_for_texture(orientation, offset): diff --git a/scripts/scil_visualize_fodf.py b/scripts/scil_visualize_fodf.py index 2314932e6..ab7b43032 100755 --- a/scripts/scil_visualize_fodf.py +++ b/scripts/scil_visualize_fodf.py @@ -146,6 +146,12 @@ def _build_arg_parser(): help='Length of the peaks segments. ' '[%(default)s]') + # fODF variance options + p.add_argument('--variance', + help='FODF variance (mean + k * variance) file.') + p.add_argument('--var_color', nargs=3, type=int, default=(255, 255, 255), + help='Color of variance outline. [%(default)s]') + return p @@ -222,6 +228,16 @@ def _get_data_from_inputs(args): peak_vals =\ nib.nifti1.load(args.peaks_values).get_fdata(dtype=np.float32) data['peaks_values'] = peak_vals + if args.variance: + variance = nib.load(args.variance).get_fdata(dtype=np.float32) + if len(variance.shape) == 3: + variance = np.reshape(variance, variance.shape + (1,)) + if variance.shape != fodf.shape: + raise ValueError('Dimensions mismatch between fODF {0} and ' + 'variance {1}.' + .format(fodf.shape, variance.shape)) + data['variance'] = _crop_along_axis(variance, args.slice_index, + args.axis_name) return data @@ -246,17 +262,24 @@ def main(): else: color_rgb = None + variance = data['variance'] if args.variance else None # Instantiate the ODF slicer actor - odf_actor = create_odf_slicer(data['fodf'], args.axis_name, - args.slice_index, mask, sph, - args.sph_subdivide, sh_order, - args.sh_basis, full_basis, - args.scale, - not args.radial_scale_off, - not args.norm_off, - args.colormap or color_rgb) + odf_actor, var_actor = create_odf_slicer(data['fodf'], args.axis_name, + args.slice_index, mask, sph, + args.sph_subdivide, sh_order, + args.sh_basis, full_basis, + args.scale, + not args.radial_scale_off, + not args.norm_off, + args.colormap or color_rgb, + sh_variance=variance, + variance_color=args.var_color) actors.append(odf_actor) + # Instantiate a variance slicer actor if a variance image is supplied + if 'variance' in data: + actors.append(var_actor) + # Instantiate a texture slicer actor if a background image is supplied if 'bg' in data: bg_actor = create_texture_slicer(data['bg'], From c88bdf74676005640698e82fdf39757c1b774bab Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 30 Jan 2023 15:20:51 -0500 Subject: [PATCH 2/7] Small fixes. --- scilpy/viz/scene_utils.py | 20 +++++++++++--------- scripts/scil_visualize_fodf.py | 14 ++++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/scilpy/viz/scene_utils.py b/scilpy/viz/scene_utils.py index ab9d9df01..5fbccabb3 100644 --- a/scilpy/viz/scene_utils.py +++ b/scilpy/viz/scene_utils.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt import vtk -from dipy.reconst.shm import sh_to_sf_matrix +from dipy.reconst.shm import sh_to_sf_matrix, sh_to_sf from fury import window, actor from fury.colormap import distinguishable_colormap from PIL import Image @@ -176,6 +176,8 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, # SH coefficients to SF coefficients matrix B_mat = sh_to_sf_matrix(sphere, sh_order, sh_basis, full_basis, return_inv=False) + + var_actor = None if sh_variance is not None: fodf = sh_to_sf(sh_fodf, sphere, sh_order, sh_basis, @@ -189,15 +191,15 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, fodf[maximums > 0] /= maximums[maximums > 0][..., None] fodf_var[maximums > 0] /= maximums[maximums > 0][..., None] - odf_actor = odf_slicer(fodf, mask=mask, norm=False, - radial_scale=radial_scale, - sphere=sphere, scale=scale, - colormap=colormap) + odf_actor = actor.odf_slicer(fodf, mask=mask, norm=False, + radial_scale=radial_scale, + sphere=sphere, scale=scale, + colormap=colormap) - var_actor = odf_slicer(fodf_var, mask=mask, norm=False, - radial_scale=radial_scale, - sphere=sphere, scale=scale, - colormap=variance_color) + var_actor = actor.odf_slicer(fodf_var, mask=mask, norm=False, + radial_scale=radial_scale, + sphere=sphere, scale=scale, + colormap=variance_color) var_actor.GetProperty().SetDiffuse(0.0) var_actor.GetProperty().SetAmbient(1.0) var_actor.GetProperty().SetFrontfaceCulling(True) diff --git a/scripts/scil_visualize_fodf.py b/scripts/scil_visualize_fodf.py index ab7b43032..1bbc698d9 100755 --- a/scripts/scil_visualize_fodf.py +++ b/scripts/scil_visualize_fodf.py @@ -149,8 +149,9 @@ def _build_arg_parser(): # fODF variance options p.add_argument('--variance', help='FODF variance (mean + k * variance) file.') - p.add_argument('--var_color', nargs=3, type=int, default=(255, 255, 255), - help='Color of variance outline. [%(default)s]') + p.add_argument('--var_color', nargs=3, type=float, default=(1, 1, 1), + help='Color of variance outline. Must be RGB values scaled ' + 'between 0 and 1. [%(default)s]') return p @@ -229,15 +230,15 @@ def _get_data_from_inputs(args): nib.nifti1.load(args.peaks_values).get_fdata(dtype=np.float32) data['peaks_values'] = peak_vals if args.variance: - variance = nib.load(args.variance).get_fdata(dtype=np.float32) + assert_same_resolution([args.variance, args.in_fodf]) + variance = nib.nifti1.load(args.variance).get_fdata(dtype=np.float32) if len(variance.shape) == 3: variance = np.reshape(variance, variance.shape + (1,)) if variance.shape != fodf.shape: raise ValueError('Dimensions mismatch between fODF {0} and ' 'variance {1}.' .format(fodf.shape, variance.shape)) - data['variance'] = _crop_along_axis(variance, args.slice_index, - args.axis_name) + data['variance'] = variance return data @@ -263,6 +264,7 @@ def main(): color_rgb = None variance = data['variance'] if args.variance else None + var_color = np.asarray(args.var_color) * 255 # Instantiate the ODF slicer actor odf_actor, var_actor = create_odf_slicer(data['fodf'], args.axis_name, args.slice_index, mask, sph, @@ -273,7 +275,7 @@ def main(): not args.norm_off, args.colormap or color_rgb, sh_variance=variance, - variance_color=args.var_color) + variance_color=var_color) actors.append(odf_actor) # Instantiate a variance slicer actor if a variance image is supplied From 975d1a0171cc3f04ba403ebdadd9a327f140e733 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 30 Jan 2023 15:22:35 -0500 Subject: [PATCH 3/7] Fixing pep8 --- scilpy/viz/scene_utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scilpy/viz/scene_utils.py b/scilpy/viz/scene_utils.py index 5fbccabb3..a8a124b49 100644 --- a/scilpy/viz/scene_utils.py +++ b/scilpy/viz/scene_utils.py @@ -176,7 +176,7 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, # SH coefficients to SF coefficients matrix B_mat = sh_to_sf_matrix(sphere, sh_order, sh_basis, full_basis, return_inv=False) - + var_actor = None if sh_variance is not None: @@ -205,10 +205,10 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, var_actor.GetProperty().SetFrontfaceCulling(True) else: odf_actor = actor.odf_slicer(sh_fodf, mask=mask, norm=norm, - radial_scale=radial_scale, - sphere=sphere, - colormap=colormap, - scale=scale, B_matrix=B_mat) + radial_scale=radial_scale, + sphere=sphere, + colormap=colormap, + scale=scale, B_matrix=B_mat) set_display_extent(odf_actor, orientation, sh_fodf.shape[:3], slice_index) if var_actor is not None: set_display_extent(var_actor, orientation, From 52408ae31761f5331165d1fe02ab3223f1bdb190 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Wed, 1 Feb 2023 13:34:23 -0500 Subject: [PATCH 4/7] Fixing typo --- scilpy/viz/scene_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scilpy/viz/scene_utils.py b/scilpy/viz/scene_utils.py index a8a124b49..4ab8caa10 100644 --- a/scilpy/viz/scene_utils.py +++ b/scilpy/viz/scene_utils.py @@ -161,7 +161,7 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, Colormap for the ODF slicer. If None, a RGB colormap is used. sh_variance : np.ndarray, optional Spherical harmonics of the variance fODF data. - variance_color : tupple, optional + variance_color : tuple, optional Color of the variance fODF data, in RGB. Returns From 65e6e51dd385ba531db680b219cee5a7b63aced0 Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 6 Mar 2023 14:30:10 -0500 Subject: [PATCH 5/7] Adding doc --- scripts/scil_visualize_fodf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/scil_visualize_fodf.py b/scripts/scil_visualize_fodf.py index 1bbc698d9..6a67b1c89 100755 --- a/scripts/scil_visualize_fodf.py +++ b/scripts/scil_visualize_fodf.py @@ -148,7 +148,10 @@ def _build_arg_parser(): # fODF variance options p.add_argument('--variance', - help='FODF variance (mean + k * variance) file.') + help='FODF variance file. In order to get visualized ' + 'properly, this input must correspond to the input ' + 'fODF (in_fodf) plus a certain factor (k, for example ' + '2) of the variance (mean + k * variance).') p.add_argument('--var_color', nargs=3, type=float, default=(1, 1, 1), help='Color of variance outline. Must be RGB values scaled ' 'between 0 and 1. [%(default)s]') From 7cddbdcfaf4c9227df94150a6c989b07bb21d91a Mon Sep 17 00:00:00 2001 From: Philippe Karan Date: Mon, 6 Mar 2023 16:02:59 -0500 Subject: [PATCH 6/7] Changing the way we input the variance --- scilpy/viz/scene_utils.py | 14 +++++++++----- scripts/scil_visualize_fodf.py | 13 +++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/scilpy/viz/scene_utils.py b/scilpy/viz/scene_utils.py index 995a10b7a..3a1984218 100644 --- a/scilpy/viz/scene_utils.py +++ b/scilpy/viz/scene_utils.py @@ -123,7 +123,7 @@ def set_display_extent(slicer_actor, orientation, volume_shape, slice_index): def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, nb_subdivide, sh_order, sh_basis, full_basis, scale, radial_scale, norm, colormap, sh_variance=None, - variance_color=(255, 255, 255)): + variance_k=1, variance_color=(255, 255, 255)): """ Create a ODF slicer actor displaying a fODF slice. The input volume is a 3-dimensional grid containing the SH coefficients of the fODF for each @@ -161,6 +161,8 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, Colormap for the ODF slicer. If None, a RGB colormap is used. sh_variance : np.ndarray, optional Spherical harmonics of the variance fODF data. + variance_k : float, optional + Factor that multiplies sqrt(variance). variance_color : tuple, optional Color of the variance fODF data, in RGB. @@ -184,19 +186,21 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, full_basis=full_basis) fodf_var = sh_to_sf(sh_variance, sphere, sh_order, sh_basis, full_basis=full_basis) + fodf_uncertainty = fodf + variance_k * np.sqrt(np.clip(fodf_var, 0, + None)) # normalise fodf and variance if norm: - maximums = np.abs(np.append(fodf, fodf_var, axis=-1))\ + maximums = np.abs(np.append(fodf, fodf_uncertainty, axis=-1))\ .max(axis=-1) fodf[maximums > 0] /= maximums[maximums > 0][..., None] - fodf_var[maximums > 0] /= maximums[maximums > 0][..., None] + fodf_uncertainty[maximums > 0] /= maximums[maximums > 0][..., None] odf_actor = actor.odf_slicer(fodf, mask=mask, norm=False, radial_scale=radial_scale, sphere=sphere, scale=scale, colormap=colormap) - var_actor = actor.odf_slicer(fodf_var, mask=mask, norm=False, + var_actor = actor.odf_slicer(fodf_uncertainty, mask=mask, norm=False, radial_scale=radial_scale, sphere=sphere, scale=scale, colormap=variance_color) @@ -212,7 +216,7 @@ def create_odf_slicer(sh_fodf, orientation, slice_index, mask, sphere, set_display_extent(odf_actor, orientation, sh_fodf.shape[:3], slice_index) if var_actor is not None: set_display_extent(var_actor, orientation, - fodf_var.shape[:3], slice_index) + fodf_uncertainty.shape[:3], slice_index) return odf_actor, var_actor diff --git a/scripts/scil_visualize_fodf.py b/scripts/scil_visualize_fodf.py index 6a67b1c89..ae43a7a5a 100755 --- a/scripts/scil_visualize_fodf.py +++ b/scripts/scil_visualize_fodf.py @@ -148,10 +148,14 @@ def _build_arg_parser(): # fODF variance options p.add_argument('--variance', - help='FODF variance file. In order to get visualized ' - 'properly, this input must correspond to the input ' - 'fODF (in_fodf) plus a certain factor (k, for example ' - '2) of the variance (mean + k * variance).') + help='FODF variance file. For the visualization of fodf ' + 'uncertainty, this variance will be used as follow: ' + 'mean + k * sqrt(variance), where mean is the input ' + 'fodf (in_fodf) and k is the scaling factor ' + '(variance_k).') + p.add_argument('--variance_k', default=1, type=float, + help='Scaling factor (k) for the computation of the fodf ' + 'uncertainty. [%(default)s]') p.add_argument('--var_color', nargs=3, type=float, default=(1, 1, 1), help='Color of variance outline. Must be RGB values scaled ' 'between 0 and 1. [%(default)s]') @@ -278,6 +282,7 @@ def main(): not args.norm_off, args.colormap or color_rgb, sh_variance=variance, + variance_k=args.variance_k, variance_color=var_color) actors.append(odf_actor) From 7f50d71827cfa2197873fe96bc0bf7cf00a7aaf6 Mon Sep 17 00:00:00 2001 From: karp2601 Date: Tue, 7 Mar 2023 11:08:36 -0500 Subject: [PATCH 7/7] Working on parser --- scripts/scil_visualize_fodf.py | 42 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/scripts/scil_visualize_fodf.py b/scripts/scil_visualize_fodf.py index ae43a7a5a..78f9f9870 100755 --- a/scripts/scil_visualize_fodf.py +++ b/scripts/scil_visualize_fodf.py @@ -100,45 +100,50 @@ def _build_arg_parser(): help='Disable normalization of ODF slicer.') # Background image options - p.add_argument('--background', + bg = p.add_argument_group('Background arguments') + bg.add_argument('--background', help='Background image file. If RGB, values must ' 'be between 0 and 255.') - p.add_argument('--bg_range', nargs=2, metavar=('MIN', 'MAX'), type=float, + bg.add_argument('--bg_range', nargs=2, metavar=('MIN', 'MAX'), type=float, help='The range of values mapped to range [0, 1] ' 'for background image. [(bg.min(), bg.max())]') - p.add_argument('--bg_opacity', type=float, default=1.0, + bg.add_argument('--bg_opacity', type=float, default=1.0, help='The opacity of the background image. Opacity of 0.0 ' 'means transparent and 1.0 is completely visible. ' '[%(default)s]') - p.add_argument('--bg_offset', type=float, default=0.5, + bg.add_argument('--bg_offset', type=float, default=0.5, help='The offset of the background image. [%(default)s]') - p.add_argument('--bg_interpolation', + bg.add_argument('--bg_interpolation', default='nearest', choices={'linear', 'nearest'}, help='Interpolation mode for the background image. ' '[%(default)s]') - p.add_argument('--bg_color', nargs=3, type=float, default=(0, 0, 0), + bg.add_argument('--bg_color', nargs=3, type=float, default=(0, 0, 0), help='The color of the overall background, behind ' 'everything. Must be RGB values scaled between 0 and ' '1. [%(default)s]') # Peaks input file options - p.add_argument('--peaks', + peaks = p.add_argument_group('Peaks arguments') + peaks.add_argument('--peaks', help='Peaks image file.') - p.add_argument('--peaks_color', nargs=3, type=float, + peaks.add_argument('--peaks_color', nargs=3, type=float, help='Color used for peaks, as RGB values scaled between 0 ' 'and 1. If None, then a RGB colormap is used. ' '[%(default)s]') - p.add_argument('--peaks_width', default=1.0, type=float, + peaks.add_argument('--peaks_width', default=1.0, type=float, help='Width of peaks segments. [%(default)s]') - peaks_scale_group = p.add_mutually_exclusive_group() + peaks_scale = p.add_argument_group('Peaks scaling arguments', 'Choose ' + 'between peaks values and arbitrary ' + 'length.') + peaks_scale_group = peaks_scale.add_mutually_exclusive_group() peaks_scale_group.add_argument('--peaks_values', help='Peaks values file.') @@ -147,16 +152,17 @@ def _build_arg_parser(): '[%(default)s]') # fODF variance options - p.add_argument('--variance', - help='FODF variance file. For the visualization of fodf ' - 'uncertainty, this variance will be used as follow: ' - 'mean + k * sqrt(variance), where mean is the input ' - 'fodf (in_fodf) and k is the scaling factor ' - '(variance_k).') - p.add_argument('--variance_k', default=1, type=float, + var = p.add_argument_group('Variance arguments', 'For the visualization ' + 'of fodf uncertainty, the variance is used ' + 'as follow: mean + k * sqrt(variance), where ' + 'mean is the input fodf (in_fodf) and k is the ' + 'scaling factor (variance_k).') + var.add_argument('--variance', + help='FODF variance file.') + var.add_argument('--variance_k', default=1, type=float, help='Scaling factor (k) for the computation of the fodf ' 'uncertainty. [%(default)s]') - p.add_argument('--var_color', nargs=3, type=float, default=(1, 1, 1), + var.add_argument('--var_color', nargs=3, type=float, default=(1, 1, 1), help='Color of variance outline. Must be RGB values scaled ' 'between 0 and 1. [%(default)s]')