From e49b73d6993447279e7adf77b647686665080cc8 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 23 Jun 2025 16:34:34 -0700 Subject: [PATCH 001/116] [WIP/ENH] Retooling Multishell pipeline --- AFQ/api/utils.py | 5 +- AFQ/definitions/image.py | 72 +++++- AFQ/models/asym_filtering.py | 393 +++++++++++++++++++++++++++++ AFQ/models/msmt.py | 272 ++++++++++++++++++++ AFQ/tasks/data.py | 417 ++++++++++++++++++++++++++++++- AFQ/tasks/tractography.py | 60 +++-- AFQ/tractography/tractography.py | 51 ++-- setup.cfg | 1 + 8 files changed, 1217 insertions(+), 54 deletions(-) create mode 100644 AFQ/models/asym_filtering.py create mode 100644 AFQ/models/msmt.py diff --git a/AFQ/api/utils.py b/AFQ/api/utils.py index 56258264e..4264233d5 100644 --- a/AFQ/api/utils.py +++ b/AFQ/api/utils.py @@ -142,7 +142,10 @@ def export_all_helper(api_afq_object, xforms, indiv, viz): api_afq_object.export("median_bundle_lengths") api_afq_object.export("profiles") api_afq_object.export("seed_thresh") - api_afq_object.export("stop_thresh") + try: + api_afq_object.export("stop_thresh") + except ValueError: + pass if viz: try: diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index 06ffd908d..704603d6c 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -13,7 +13,7 @@ __all__ = [ "ImageFile", "FullImage", "RoiImage", "B0Image", "LabelledImageFile", "ThresholdedImageFile", "ScalarImage", "ThresholdedScalarImage", - "TemplateImage", "GQImage"] + "TemplateImage", "GQImage", "DKI3TImage", "DkiGmWmInterfaceImage"] logger = logging.getLogger('AFQ') @@ -681,7 +681,8 @@ def __init__(self, scalar, lower_bound=None, upper_bound=None, class PFTImage(ImageDefinition): """ Define an image for use in PFT tractography. Only use - if tracker set to 'pft' in tractography. + if tracker set to 'pft' in tractography. Used to provide + custom segmentations. Parameters ---------- @@ -727,6 +728,73 @@ def get_image_getter(self, task_name): for probseg in self.probsegs] +class DKI3TImage(PFTImage): + """ + Define an image for use in PFT tractography. Only use + if tracker set to 'pft' in tractography. Use to generate + WM/GM/CSF probsegs from DKI data. + + Examples + -------- + api.GroupAFQ(tracking_params={ + "stop_image": DKI3TImage(), + "stop_threshold": "CMC", + "tracker": "pft"}) + """ + + def __init__(self): + self.probsegs = ( + ScalarImage("dki_wm"), + ScalarImage("dki_gm"), + ScalarImage("dki_csf")) + + def get_name(self): + return "dki3t" + + +class DkiGmWmInterfaceImage(ImageDefinition): + """ + Define an image of the WM/GM interface. + This is based on WM/GM/CSF probsegs from DKI data. + Typically used for seeding. + + Examples + -------- + api.GroupAFQ(tracking_params={ + "seed_image": DkiGmWmInterfaceImage()}) + """ + + def __init__(self): + pass + + def get_name(self): + return "DkiGmWmInterface" + + def get_image_getter(self, task_name): + if task_name == "data": + raise ValueError(( + "ScalarImage cannot be used in this context, as they" + "require later derivatives to be calculated")) + # Note: in the future, we may want to weight seeding, + # using something like this: + # data[data > 0.5] = 1 - data[data > 0.5] + # Or, weight by gradient like in MRTrix + # (more seeds in places where transition is sharper) + + def image_getter(data_imap): + wm_img = nib.load(data_imap["dki_wm"]) + gm_img = nib.load(data_imap["dki_gm"]) + gmwmi = wm_img.get_fdata() + gmwmi[gmwmi == 1] = 0 # exclude pure WM + gmwmi[gm_img.get_fdata() == 0] = 0 # exclude CSF interface + gmwmi = (gmwmi > 0).astype(np.float32) # convert to binary + + return nib.Nifti1Image(gmwmi, wm_img.affine), dict( + FromWM=data_imap["dki_wm"], + FromGM=data_imap["dki_gm"]) + return image_getter + + class TemplateImage(ImageDefinition): """ Define a scalar based on a template. diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py new file mode 100644 index 000000000..bc94d55f9 --- /dev/null +++ b/AFQ/models/asym_filtering.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# Original source: github.com/scilus/scilpy +# Copyright (c) 2012-- Sherbrooke Connectivity Imaging Lab [SCIL], Université de Sherbrooke. +# Licensed under the MIT License (https://opensource.org/licenses/MIT). +# Modified by John Kruper for pyAFQ +# OpenCL and cosine filtering removed + +import numpy as np +import logging +from dipy.reconst.shm import sh_to_sf_matrix +from dipy.data import get_sphere + + +__all__ = ["unified_filtering"] + + +def _get_sh_order_and_fullness(ncoeffs): + """ + Get the order of the SH basis from the number of SH coefficients + as well as a boolean indicating if the basis is full. + """ + # the two curves (sym and full) intersect at ncoeffs = 1, in what + # case both bases correspond to order 1. + sym_order = (-3.0 + np.sqrt(1.0 + 8.0 * ncoeffs)) / 2.0 + if sym_order.is_integer(): + return sym_order, False + full_order = np.sqrt(ncoeffs) - 1.0 + if full_order.is_integer(): + return full_order, True + raise ValueError('Invalid number of coefficients for SH basis.') + + +def unified_filtering(sh_data, sphere, + sh_basis='descoteaux07', is_legacy=False, + sigma_spatial=1.0, sigma_align=0.8, + sigma_angle=None, rel_sigma_range=0.2, + win_hwidth=None, exclude_center=False): + """ + Unified asymmetric filtering as described in [1]. + + Parameters + ---------- + sh_data: ndarray + SH coefficients image. + sphere: str or DIPY sphere + Name of the DIPY sphere to use for SH to SF projection. + sh_basis: str + SH basis definition used for input and output SH image. + One of 'descoteaux07' or 'tournier07'. + Default: 'descoteaux07'. + is_legacy: bool + Whether the legacy SH basis definition should be used. + Default: False. + sigma_spatial: float or None + Standard deviation of spatial filter. Can be None to replace + by mean filter, in what case win_hwidth must be given. + sigma_align: float or None + Standard deviation of alignment filter. `None` disables + alignment filtering. + sigma_angle: float or None + Standard deviation of the angle filter. `None` disables + angle filtering. + rel_sigma_range: float or None + Standard deviation of the range filter, relative to the + range of SF amplitudes. `None` disables range filtering. + disable_spatial: bool, optional + Replace gaussian filter by a mean filter for spatial filter. + The value from `sigma_spatial` is still used for setting the + size of the filtering window. + win_hwidth: int, optional + Half-width of the filtering window. When None, the + filtering window half-width is given by (6*sigma_spatial + 1). + exclude_center: bool, optional + Assign a weight of 0 to the center voxel of the filter. + + References + ---------- + [1] Poirier and Descoteaux, 2024, "A Unified Filtering Method for + Estimating Asymmetric Orientation Distribution Functions", + Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 + """ + if sigma_spatial is None and win_hwidth is None: + raise ValueError('sigma_spatial and win_hwidth cannot both be None') + + if isinstance(sphere, str): + sphere = get_sphere(name=sphere) + + if sigma_spatial is not None: + if sigma_spatial <= 0.0: + raise ValueError('sigma_spatial cannot be <= 0.') + # calculate half-width from sigma_spatial + half_width = int(round(3 * sigma_spatial)) + if sigma_align is not None: + if sigma_align <= 0.0: + raise ValueError('sigma_align cannot be <= 0.') + if sigma_angle is not None: + if sigma_angle <= 0.0: + raise ValueError('sigma_align cannot be <= 0.') + + sh_order, full_basis = _get_sh_order_and_fullness(sh_data.shape[-1]) + + # overwrite half-width if win_hwidth is supplied + if win_hwidth is not None: + half_width = win_hwidth + + # filter shape computed from half_width + filter_shape = (half_width * 2 + 1, half_width * 2 + 1, half_width * 2 + 1) + + # build filters + uv_filter = _unified_filter_build_uv(sigma_angle, sphere) + nx_filter = _unified_filter_build_nx(filter_shape, sigma_spatial, + sigma_align, sphere, exclude_center) + + B = sh_to_sf_matrix(sphere, sh_order, sh_basis, full_basis, + legacy=is_legacy, return_inv=False) + _, B_inv = sh_to_sf_matrix(sphere, sh_order, sh_basis, True, + legacy=is_legacy, return_inv=True) + + # compute "real" sigma_range scaled by sf amplitudes + # if rel_sigma_range is supplied + sigma_range = None + if rel_sigma_range is not None: + if rel_sigma_range <= 0.0: + raise ValueError('sigma_rangel cannot be <= 0.') + sigma_range = rel_sigma_range * _get_sf_range(sh_data, B) + + return _unified_filter_call_python(sh_data, nx_filter, uv_filter, + sigma_range, B, B_inv, sphere) + + +def _unified_filter_build_uv(sigma_angle, sphere): + """ + Build the angle filter, weighted on angle between current direction u + and neighbour direction v. + + Parameters + ---------- + sigma_angle: float + Standard deviation of filter. Values at distances greater than + sigma_angle are clipped to 0 to reduce computation time. + sphere: DIPY sphere + Sphere used for sampling the SF. + + Returns + ------- + weights: ndarray + Angle filter of shape (N_dirs, N_dirs). + """ + directions = sphere.vertices + if sigma_angle is not None: + dot = directions.dot(directions.T) + x = np.arccos(np.clip(dot, -1.0, 1.0)) + weights = _evaluate_gaussian_distribution(x, sigma_angle) + mask = x > (3.0 * sigma_angle) + weights[mask] = 0.0 + weights /= np.sum(weights, axis=-1) + else: + weights = np.eye(len(directions)) + return weights + + +def _unified_filter_build_nx(filter_shape, sigma_spatial, sigma_align, + sphere, exclude_center): + """ + Build the combined spatial and alignment filter. + + Parameters + ---------- + filter_shape: tuple + Dimensions of filtering window. + sigma_spatial: float or None + Standard deviation of spatial filter. None disables Gaussian + weighting for spatial filtering. + sigma_align: float or None + Standard deviation of the alignment filter. None disables Gaussian + weighting for alignment filtering. + sphere: DIPY sphere + Sphere for SH to SF projection. + exclude_center: bool + Whether the center voxel is included in the neighbourhood. + + Returns + ------- + weights: ndarray + Combined spatial + alignment filter of shape (W, H, D, N) where + N is the number of sphere directions. + """ + directions = sphere.vertices.astype(np.float32) + + grid_directions = _get_window_directions(filter_shape).astype(np.float32) + distances = np.linalg.norm(grid_directions, axis=-1) + grid_directions[distances > 0] = grid_directions[distances > 0] /\ + distances[distances > 0][..., None] + + if sigma_spatial is None: + w_spatial = np.ones(filter_shape) + else: + w_spatial = _evaluate_gaussian_distribution(distances, sigma_spatial) + + if sigma_align is None: + w_align = np.ones(np.append(filter_shape, (len(directions),))) + else: + cos_theta = np.clip(grid_directions.dot(directions.T), -1.0, 1.0) + theta = np.arccos(cos_theta) + theta[filter_shape[0] // 2, + filter_shape[1] // 2, + filter_shape[2] // 2] = 0.0 + w_align = _evaluate_gaussian_distribution(theta, sigma_align) + + # resulting filter + w = w_spatial[..., None] * w_align + + if exclude_center: + w[filter_shape[0] // 2, + filter_shape[1] // 2, + filter_shape[2] // 2] = 0.0 + + # normalize and return + w /= np.sum(w, axis=(0, 1, 2), keepdims=True) + return w + + +def _get_sf_range(sh_data, B_mat): + """ + Get the range of SF amplitudes for input `sh_data`. + + Parameters + ---------- + sh_data: ndarray + Spherical harmonics coefficients image. + B_mat: ndarray + SH to SF projection matrix. + + Returns + ------- + sf_range: float + Range of SF amplitudes. + """ + sf = np.array([np.dot(i, B_mat) for i in sh_data], + dtype=sh_data.dtype) + sf[sf < 0.0] = 0.0 + sf_max = np.max(sf) + sf_min = np.min(sf) + return sf_max - sf_min + + +def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, + B_mat, B_inv, sphere): + """ + Run filtering using pure python implementation. + + Parameters + ---------- + sh_data: ndarray + Input SH data. + nx_filter: ndarray + Combined spatial and alignment filter. + uv_filter: ndarray + Angle filter. + sigma_range: float or None + Standard deviation of range filter. None disables range filtering. + B_mat: ndarray + SH to SF projection matrix. + B_inv: ndarray + SF to SH projection matrix. + sphere: DIPY sphere + Sphere for SH to SF projection. + + Returns + ------- + out_sh: ndarray + Filtered output as SH coefficients. + """ + nb_sf = len(sphere.vertices) + mean_sf = np.zeros(sh_data.shape[:-1] + (nb_sf,)) + + # Apply filter to each sphere vertice + for u_sph_id in range(nb_sf): + if u_sph_id % 20 == 0: + logging.info('Processing direction: {}/{}' + .format(u_sph_id, nb_sf)) + mean_sf[..., u_sph_id] = _correlate(sh_data, nx_filter, uv_filter, + sigma_range, u_sph_id, B_mat) + + out_sh = np.array([np.dot(i, B_inv) for i in mean_sf], + dtype=sh_data.dtype) + return out_sh + + +def _correlate(sh_data, nx_filter, uv_filter, sigma_range, u_index, B_mat): + """ + Apply the filters to the SH image for the sphere direction + described by `u_index`. + + Parameters + ---------- + sh_data: ndarray + Input SH coefficients. + nx_filter: ndarray + Combined spatial and alignment filter. + uv_filter: ndarray + Angle filter. + sigma_range: float or None + Standard deviation of range filter. None disables range filtering. + u_index: int + Index of the current sphere direction to process. + B_mat: ndarray + SH to SF projection matrix. + + Returns + ------- + out_sf: ndarray + Output SF amplitudes along the direction described by `u_index`. + """ + v_indices = np.flatnonzero(uv_filter[u_index]) + nx_filter = nx_filter[..., u_index] + h_w, h_h, h_d = nx_filter.shape[:3] + half_w, half_h, half_d = h_w // 2, h_h // 2, h_d // 2 + out_sf = np.zeros(sh_data.shape[:3]) + sh_data = np.pad(sh_data, ((half_w, half_w), + (half_h, half_h), + (half_d, half_d), + (0, 0))) + + sf_u = np.dot(sh_data, B_mat[:, u_index]) + sf_v = np.dot(sh_data, B_mat[:, v_indices]) + uv_filter = uv_filter[u_index, v_indices] + + _get_range = _evaluate_gaussian_distribution\ + if sigma_range is not None else lambda x, _: np.ones_like(x) + + for ii in range(out_sf.shape[0]): + for jj in range(out_sf.shape[1]): + for kk in range(out_sf.shape[2]): + a = sf_v[ii:ii + h_w, jj:jj + h_h, kk:kk + h_d] + b = sf_u[ii + half_w, jj + half_h, kk + half_d] + x_range = a - b + range_filter = _get_range(x_range, sigma_range) + + # the resulting filter for the current voxel and v_index + res_filter = range_filter * nx_filter[..., None] + res_filter =\ + res_filter * np.reshape(uv_filter, + (1, 1, 1, len(uv_filter))) + out_sf[ii, jj, kk] = np.sum( + sf_v[ii:ii + h_w, jj:jj + h_h, kk:kk + h_d] * res_filter) + out_sf[ii, jj, kk] /= np.sum(res_filter) + + return out_sf + + +def _evaluate_gaussian_distribution(x, sigma): + """ + 1-dimensional 0-centered Gaussian distribution + with standard deviation sigma. + + Parameters + ---------- + x: ndarray or float + Points where the distribution is evaluated. + sigma: float + Standard deviation. + + Returns + ------- + out: ndarray or float + Values at x. + """ + assert sigma > 0.0, "Sigma must be greater than 0." + cnorm = 1.0 / sigma / np.sqrt(2.0 * np.pi) + return cnorm * np.exp(-x**2 / 2 / sigma**2) + + +def _get_window_directions(shape): + """ + Get directions from center voxel to all neighbours + for a window of given shape. + + Parameters + ---------- + shape: tuple + Dimensions of the window. + + Returns + ------- + grid: ndarray + Grid containing the direction from the center voxel to + the current position for all positions inside the window. + """ + grid = np.indices(shape) + grid = np.moveaxis(grid, 0, -1) + grid = grid - np.asarray(shape) // 2 + return grid diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py new file mode 100644 index 000000000..3e7340319 --- /dev/null +++ b/AFQ/models/msmt.py @@ -0,0 +1,272 @@ +import numpy as np +from scipy.optimize import minimize +from numba import njit, prange, config, set_num_threads +from tqdm import tqdm + +from dipy.reconst.mcsd import MSDeconvFit + + +config.THREADING_LAYER = 'workqueue' + + +__all__ = ["fit"] + + +# CPU implementation of primal-dual Interior Point method +# for convex quadratic constrained optimization (QP) +# Specifically, ||Rx-d||_2 where Ax>=b +# parallelized to solve 10s-100s of thousands of QPs +# ultimately used to fit MSMT CSD +@njit(fastmath=True) +def solve_qp(Rt, R_pinv, G, A, b, x0, + d, max_iter, tol): + ''' + Solves 1/2*x^t*G*x+(Rt*d)^t*x given Ax>=b + In MSMT, G, R, A, b are the same across voxels, + but there are different d for different voxels. + This fact is not used currently. So this is a more general + batched CLS QP solver. + + Let: + c=(Rt*d)^t + L = diag(l) + Y = diag(y) + mu as centering parameter that tends to 0 with iteration number. + + Set up with interior points, this is reformulated to: + | G 0 -A.T | |dx| |0 | | G*x-A.T*l+c | + | A -I 0 | |dy| = |0 | - | A*x-y-b | + | 0 L Y | |dl| |mu| | YL | + + This reduces to: + |G -A.T| |dx| = | -G*x-A.T*l+c | + |A Y\L | |dl| | -(A*x-y-b) + (-y+mu/l)| + with dy = A*dx+(A*x-y-b) + + Solving for dx, dl: + (G+A.T*(Y\L)*A)*dx = -G*x-A.T*l+c + dl = (Y\L)*(-(A*x-y-b) + (-y+mu/l) - A*dx) + + So, the tricky part is solving for dx. + However, note that G+A.T*(Y\L)*A is hermitian positive semidefinite. + So, it can be solved using conjugate gradients. + This is done every iteration and is the longest part of the calculation. + ''' + + n = A.shape[1] + m = A.shape[0] + + # First check if naive solution satisfies constraints + x = R_pinv @ d + if np.all(A @ x - b >= -tol): + return True, x + + c = Rt @ d + + x = x0.copy() + y = np.ones(m) + l = np.ones(m) + dx = np.zeros(n) + dy = np.zeros(m) + dl = np.zeros(m) + + tau = 0.9 + shur_regularization = 1e-6 + for _ in range(max_iter): + mu = (y @ l) / m + + # Predictor step + Z = safe_divide_vec(l, y) + dy = A @ x - y - b + rhs2 = -dy - y + rhs1l = -(G @ x - A.T @ l + c) + rhs1 = rhs1l + A.T @ (Z * rhs2) + + schur = G + A.T @ np.diag(Z) @ A + schur += np.eye(schur.shape[0]) * shur_regularization + schur_diag = np.diag(schur) + + # dx = cholesky_solve(schur, rhs1) + dx = conjugate_gradient_precond( + schur, rhs1, dx, + schur_diag, 5, 1e-4 * tol) + dl_aff = Z * (rhs2 - A @ dx) + dy_aff = dy + A @ dx + + alpha_aff_pri = 1.0 + alpha_aff_dual = 1.0 + for ii in range(m): + if dy_aff[ii] < 0: + alpha_aff_pri = min(alpha_aff_pri, -y[ii] / dy_aff[ii]) + if dl_aff[ii] < 0: + alpha_aff_dual = min(alpha_aff_dual, -l[ii] / dl_aff[ii]) + alpha_aff = min(tau * alpha_aff_pri, tau * alpha_aff_dual) + + mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m + sigma = (mu_aff / mu) ** 3 + mu = sigma * mu + + # Main corrector step + rhs2 = -dy + (-y + safe_divide_vec(mu, l)) + rhs1 = rhs1l + A.T @ (Z * rhs2) + + # dx = cholesky_solve(schur, rhs1) + dx = conjugate_gradient_precond( + schur, rhs1, dx, + schur_diag, max_iter, max(tol, 1e-2 * mu)) + dl = Z * (rhs2 - A @ dx) + dy += A @ dx + + beta = 1.0 + sigma = 1.0 + for ii in range(m): + if dy[ii] < 0 and dy[ii] * sigma < -y[ii]: + sigma = -y[ii] / dy[ii] + if dl[ii] < 0 and dl[ii] * beta < -l[ii]: + beta = -l[ii] / dl[ii] + beta *= tau + sigma *= tau + alpha = min(beta, sigma) + + x += alpha * dx + y += alpha * dy + l += alpha * dl + + if (alpha * np.sum(np.abs(dx)) < n * tol + and alpha * np.sum(np.abs(dy)) < m * tol + and alpha * np.sum(np.abs(dl)) < m * tol): + if np.all(A @ x - b >= -tol): + return True, x + else: + return False, x0 + + return False, x0 + + +@njit(fastmath=True) +def conjugate_gradient_precond(A, b, x, diag, max_iter, tol): + """ + Solves A x = b with Jacobi preconditioner (diag) using Conjugate Gradient. + + Args: + A : 2D np.ndarray, symmetric positive-definite matrix + b : 1D np.ndarray, RHS vector + x : 1D np.ndarray, initial guess (will be modified in-place) + diag : 1D np.ndarray, diagonal of preconditioner (Jacobi) + max_iter : int, maximum number of iterations + tol : float, stopping threshold + + Returns: + x : Updated solution + """ + r = b - A @ x + r /= diag + p = r.copy() + rs_old = np.dot(r * r, diag) + + for _ in range(max_iter): + Ap = A @ p + alpha = rs_old / np.dot(p, Ap) + x += alpha * p + r -= alpha * Ap / diag + rs_new = np.dot(r * r, diag) + if rs_new < tol: + break + beta = rs_new / rs_old + p = r + beta * p + rs_old = rs_new + + return x + + +@njit(fastmath=True) +def safe_divide_vec(numer, denom): + safe_divisor = 1e-6 # Used to avoid division by zero + return numer / np.where( + np.abs(denom) < safe_divisor, + np.sign(denom) * safe_divisor, denom) + + +def find_analytic_center(A, b, x0): + """Find the analytic center using scipy.optimize.minimize""" + cons = {'type': 'ineq', 'fun': lambda x: A @ x - b} + result = minimize( + lambda x: 1e-3 * np.linalg.norm(x)**2 - np.sum(np.log(A @ x - b)), + x0, + constraints=cons, + method='SLSQP' + ) + return result.x + + +@njit(parallel=True, fastmath=True) +def _process_slice(slice_data, slice_mask, slice_results, + Rt, R_pinv, + G, A, b, x0, + max_iter, tol): + for j in prange(slice_data.shape[0]): + x_prev = x0.copy() + for k in range(slice_data.shape[1]): + if slice_mask[j, k]: + # previous values warm start next solver + # More complicated warm starts are slower + x_prev = np.clip(x_prev, -1.0, 1.0) + success, x_prev = solve_qp( + Rt, R_pinv, G, A, b, + x_prev, + slice_data[j, k], + max_iter, tol) + + if success: + slice_results[j, k] = x_prev + else: + slice_results[j, k] = np.zeros(A.shape[1]) + else: + slice_results[j, k] = np.zeros(A.shape[1]) + + +def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, n_threads=None): + if n_threads is not None: + set_num_threads(n_threads) + + m, n = self.fitter._reg.shape + coeff = np.zeros((*data.shape[:3], n), dtype=np.float64) + if mask is None: + mask = np.ones(data.shape[:3], dtype=bool) + + R = np.ascontiguousarray(self.fitter._X, dtype=np.float64) + A = np.ascontiguousarray(self.fitter._reg, dtype=np.float64) + b = np.zeros(A.shape[0], dtype=np.float64) + + # Normalize constraints + for i in range(A.shape[0]): + A[i] /= np.linalg.norm(A[i]) + + Q = R.T @ R + # Q += 1e-1 * np.eye(n) # Strong Regularization # TODO + x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) + x0 = find_analytic_center(A, b, x0) + + Rt = -R.T + R_pinv = np.linalg.pinv(R).astype(np.float64) + data = np.ascontiguousarray(data, dtype=np.float64) + + Rt = np.ascontiguousarray(Rt, dtype=np.float64) + R_pinv = np.ascontiguousarray(R_pinv, dtype=np.float64) + Q = np.ascontiguousarray(Q, dtype=np.float64) + A = np.ascontiguousarray(A, dtype=np.float64) + b = np.ascontiguousarray(b, dtype=np.float64) + x0 = np.ascontiguousarray(x0, dtype=np.float64) + + for ii in tqdm(range(data.shape[0])): + _process_slice( + data[ii], mask[ii], coeff[ii], + Rt, + R_pinv, + Q, + A, + b, + x0, + max_iter, tol) + + return MSDeconvFit(self, coeff, None) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 8ffae6112..084189e71 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -1,10 +1,15 @@ import nibabel as nib import numpy as np import logging +from tqdm import tqdm from dipy.io.gradients import read_bvals_bvecs import dipy.core.gradients as dpg -from dipy.data import default_sphere +from dipy.data import default_sphere, get_sphere + +from scipy.signal import find_peaks, peak_widths +from scipy.ndimage import gaussian_filter1d +from scipy.special import erf import immlib @@ -16,6 +21,13 @@ from dipy.reconst.rumba import RumbaSDModel, RumbaFit from dipy.reconst import shm from dipy.reconst.dki_micro import axonal_water_fraction +from dipy.segment.tissue import compute_directional_average +from dipy.reconst.mcsd import ( + MultiShellDeconvModel, + mask_for_response_msmt, + multi_shell_fiber_response, + response_from_mask_msmt) +from dipy.core.gradients import unique_bvals_tolerance from AFQ.tasks.decorators import as_file, as_img, as_fit_deriv from AFQ.tasks.utils import get_fname, with_name, str_to_desc @@ -33,8 +45,10 @@ from AFQ.models.dki import _fit as dki_fit_model from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model +from AFQ.models.msmt import fit as msmt_fit from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) +from AFQ.models.asym_filtering import unified_filtering logger = logging.getLogger('AFQ') @@ -89,18 +103,19 @@ def get_data_gtab(dwi_data_file, bval_file, bvec_file, min_bval=-np.inf, @immlib.calc("b0") @as_file('_b0ref.nii.gz') +@as_img def b0(dwi, gtab): """ full path to a nifti file containing the mean b0 """ mean_b0 = np.mean(dwi.get_fdata()[..., gtab.b0s_mask], -1) - mean_b0_img = nib.Nifti1Image(mean_b0, dwi.affine) meta = dict(b0_threshold=gtab.b0_threshold) - return mean_b0_img, meta + return mean_b0, meta @immlib.calc("masked_b0") @as_file('_desc-masked_b0ref.nii.gz') +@as_img def b0_mask(b0, brain_mask): """ full path to a nifti file containing the @@ -112,11 +127,259 @@ def b0_mask(b0, brain_mask): masked_data = img.get_fdata() masked_data[~brain_mask] = 0 - masked_b0_img = nib.Nifti1Image(masked_data, img.affine) meta = dict( source=b0, masked=True) - return masked_b0_img, meta + return masked_data, meta + + +@pimms.calc("dam_params") +@as_file(suffix='_model-dam_param-slopeintercept_dwimap.nii.gz', + subfolder="models") +@as_img +def dam_fit(data, gtab, masked_b0, + dam_low_signal_thresh=50): + """ + direction-averaged signal map (DAM) [1] slope and intercept + + Parameters + ---------- + dam_low_signal_thresh : float, optional + The threshold below which a voxel is considered to have low signal. + Default: 50 + + References + ---------- + .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., + & Garyfallidis, E. (2020). Segmentation of the brain using + direction-averaged signal of DWI images. + Magnetic Resonance Imaging, 69, 1-7. Elsevier. + https://doi.org/10.1016/j.mri.2020.02.010 + """ + # Precompute unique b-values, masks + unique_bvals = np.unique(gtab.bvals) + if len(unique_bvals) <= 2: + raise ValueError(("Insufficient unique b-values for fitting DAM. " + "Note, DAM requires multi-shell data")) + masks = gtab.bvals[:, np.newaxis] == unique_bvals[np.newaxis, 1:] + + b0_data = nib.load(masked_b0).get_fdata() + + # If the mean signal for b=0 is too low, + # set those voxels to 0 for both P and V + valid_voxels = b0_data >= dam_low_signal_thresh + + params_map = np.zeros((*data.shape[:-1], 2)) + logger.info("Fitting directional average map (DAM)...") + for idx in tqdm(range(data.shape[0] * data.shape[1] * data.shape[2])): + i, j, k = np.unravel_index(idx, data.shape[:-1]) + if valid_voxels[i, j, k]: + aa, bb = compute_directional_average( + data[i, j, k, :], + gtab.bvals, + masks=masks, + b0_mask=gtab.b0s_mask, + s0_map=b0_data[i, j, k], + low_signal_threshold=dam_low_signal_thresh, + ) + if aa > 0.01 and bb < 0: + params_map[i, j, k, 0] = aa + params_map[i, j, k, 1] = -bb + + return params_map, dict(low_signal_thresh=dam_low_signal_thresh) + + +@pimms.calc("dam_csf") +@as_file(suffix='_model-dam_param-csf_probseg.nii.gz', + subfolder="models") +@as_img +def dam_csf(dam_params): + """ + CSF probability map from DAM intercept + """ + dam_intercept_data = nib.load(dam_params).get_fdata()[..., 1] + beta_values = dam_intercept_data.flatten() + beta_values = beta_values[beta_values != 0] + + # Make a smoothed histogram + hist, bin_edges = np.histogram(beta_values, bins=200, density=True) + bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 + smoothed_hist = gaussian_filter1d(hist, sigma=2) + + # Find the main peak + peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) + if len(peaks) == 0: + raise ValueError(( + "DAM intercept: No peaks found in the histogram, " + "required for DAM CSF estimate")) + + main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] + main_peak_val = bin_centers[main_peak_idx] + + # Find the threshold symmetric to 0 with respect to the peak center + threshold = 2 * main_peak_val + + return dam_intercept_data > threshold, dict( + DAMParamsFile=dam_params, + threshold=threshold) + + +@pimms.calc("dam_pseudot1") +@as_file(suffix='_model-dam_param-pseudot1_dwimap.nii.gz', + subfolder="models") +@as_img +def dam_pseudot1(dam_params, dam_csf): + """ + Pseudo T1 map from DAM fit + """ + dam_slope_data = nib.load(dam_params).get_fdata()[..., 0] + dam_csf_data = nib.load(dam_csf).get_fdata() + + dam_slope_min = np.percentile( + dam_slope_data[dam_slope_data != 0], 0.5) + dam_slope_data[dam_slope_data < dam_slope_min] = 0 + + dam_slope_max = np.percentile( + dam_slope_data[dam_slope_data != 0], 99.5) + dam_slope_data[dam_slope_data > dam_slope_max] = 0 + + dam_slope_data[dam_slope_data != 0] = dam_slope_max - \ + dam_slope_data[dam_slope_data != 0] + pseudo_t1 = (1.0 - dam_csf_data) * dam_slope_data + + return pseudo_t1, dict(source=dam_params) + + +@pimms.calc("dki_csf") +@as_file(suffix='_model-dki_param-csf_probseg.nii.gz', + subfolder="models") +@as_img +def dki_csf(dki_md): + """ + CSF probability map from DKI MD inspired by [1] + + References + ---------- + .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., + & Garyfallidis, E. (2020). Segmentation of the brain using + direction-averaged signal of DWI images. + Magnetic Resonance Imaging, 69, 1-7. Elsevier. + https://doi.org/10.1016/j.mri.2020.02.010 + """ + dki_md_data = nib.load(dki_md).get_fdata() + beta_values = dki_md_data.flatten() + beta_values = beta_values[beta_values != 0] + + # Make a smoothed histogram + hist, bin_edges = np.histogram(beta_values, bins=200, density=True) + bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 + smoothed_hist = gaussian_filter1d(hist, sigma=2) + + # Find the main peak + peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) + if len(peaks) == 0: + raise ValueError(( + "DKI MD: No peaks found in the histogram, " + "required for DKI CSF estimate")) + + main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] + main_peak_val = bin_centers[main_peak_idx] + + # Estimate uncertainty in peak center location + # The FWHM of a Gaussian is 2.355*sigma, so sigma ≈ FWHM/2.355 + width_results = peak_widths( + smoothed_hist, [main_peak_idx], rel_height=0.5) + peak_width_md = abs( + bin_centers[1] - bin_centers[0]) * width_results[0][0] + peak_sigma = peak_width_md / 2.355 + + # Find the threshold symmetric to 0 with respect to the peak center + main_peak_val = 2 * main_peak_val + peak_sigma = 2 * peak_sigma + + dki_md_data[dki_md_data != 0] = \ + 0.5 * (1 + erf((dki_md_data[dki_md_data != 0] - main_peak_val) + / (peak_sigma * np.sqrt(2)))) + + return dki_md_data, dict( + DKI_MD_source=dki_md, + main_peak_val=main_peak_val, + peak_sigma=peak_sigma) + + +@pimms.calc("dki_wm") +@as_file(suffix='_model-dki_param-wm_probseg.nii.gz', + subfolder="models") +@as_img +def dki_wm(dki_fa, dki_wm_ll=0.1, dki_gm_ul=0.3): + """ + WM probability map from DKI FA + + Parameters + ---------- + dki_wm_ll : float, optional + Lower limit of FA in white matter to calculate probability mask. + Default: 0.1 + dki_gm_ul : float, optional + Upper limit of FA in gray matter to calculate probability mask. + Default: 0.3 + """ + dki_fa_data = nib.load(dki_fa).get_fdata() + + uncertain_slice = np.logical_and( + dki_fa_data > dki_wm_ll, + dki_fa_data <= dki_gm_ul) + wm_data = dki_fa_data.copy() + wm_data[dki_fa_data >= dki_gm_ul] = 1.0 + wm_data[uncertain_slice] = (dki_fa_data[uncertain_slice] - dki_wm_ll) /\ + (dki_gm_ul - dki_wm_ll) + wm_data[dki_fa_data < dki_wm_ll] = 0.0 + + return wm_data, dict( + DKI_FA_source=dki_fa, + dki_wm_ll=dki_wm_ll, + dki_gm_ul=dki_gm_ul) + + +@pimms.calc("dki_gm") +@as_file(suffix='_model-dki_param-gm_probseg.nii.gz', + subfolder="models") +@as_img +def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): + """ + GM probability map from DKI FA + + Parameters + ---------- + dki_wm_ll : float, optional + Lower limit of FA in white matter to calculate probability mask. + Default: 0.1 + dki_gm_ul : float, optional + Upper limit of FA in gray matter to calculate probability mask. + Default: 0.3 + """ + dki_fa_data = nib.load(dki_fa).get_fdata() + dki_csf_data = nib.load(dki_csf).get_fdata() + + uncertain_slice = np.logical_and( + dki_fa_data > dki_wm_ll, + dki_fa_data <= dki_gm_ul) + gm_data = dki_fa_data.copy() + gm_data[dki_fa_data >= dki_gm_ul] = 0.0 + gm_data[uncertain_slice] = (dki_gm_ul - dki_fa_data[uncertain_slice]) /\ + (dki_gm_ul - dki_wm_ll) + gm_data[np.logical_and( + dki_fa_data < dki_wm_ll, + dki_fa_data != 0)] = 1.0 + + gm_data = gm_data - dki_csf_data # Remove CSF contribution + gm_data[gm_data < 0.0] = 0.0 + + return gm_data, dict( + DKI_FA_source=dki_fa, + DKI_CSF_source=dki_csf, + dki_wm_ll=dki_wm_ll, + dki_gm_ul=dki_gm_ul) @immlib.calc("dti_tf") @@ -299,6 +562,110 @@ def msdki_msk(msdki_tf): return msdki_tf.msk +@immlib.calc("msmtcsd_params") +@as_file(suffix='_model-msmtcsd_param-fod_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_params(brain_mask, gtab, data, + dki_wm, dki_gm, dki_csf, + msmt_sh_order=8, + msmt_fa_thr=0.7): + """ + full path to a nifti file containing + parameters for the MSMT CSD fit + + Parameters + ---------- + msmt_sh_order : int, optional. + Spherical harmonic order to use for the MSMT CSD fit. + Default: 8 + msmt_fa_thr : float, optional. + The threshold on the FA used to calculate the multi shell auto + response. Can be useful to reduce for baby subjects. + Default: 0.7 + + References + ---------- + .. [1] B. Jeurissen, J.-D. Tournier, T. Dhollander, A. Connelly, + and J. Sijbers. Multi-tissue constrained spherical + deconvolution for improved analysis of multi-shell diffusion + MRI data. NeuroImage, 103 (2014), pp. 411–426 + """ + mask =\ + nib.load(brain_mask).get_fdata() + + mask_wm, mask_gm, mask_csf = mask_for_response_msmt( + gtab, + data, + roi_radii=10, + wm_fa_thr=msmt_fa_thr, + gm_fa_thr=0.3, + csf_fa_thr=0.15, + gm_md_thr=0.001, + csf_md_thr=0.0032) + mask_wm *= nib.load(dki_wm).get_fdata() > 0.5 + mask_gm *= nib.load(dki_gm).get_fdata() > 0.5 + mask_csf *= nib.load(dki_csf).get_fdata() > 0.5 + response_wm, response_gm, response_csf = response_from_mask_msmt( + gtab, data, mask_wm, mask_gm, mask_csf) + ubvals = unique_bvals_tolerance(gtab.bvals) + response_mcsd = multi_shell_fiber_response( + msmt_sh_order, + ubvals, + response_wm, + response_gm, + response_csf) + + mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) + logger.info("Fitting Multi-Shell CSD model...") + mcsd_fit = msmt_fit(mcsd_model, data, mask) + + meta = dict( + SphericalHarmonicDegree=msmt_sh_order, + SphericalHarmonicBasis="DESCOTEAUX") + return mcsd_fit.shm_coeff, meta + + +@immlib.calc("msmt_apm") +@as_file(suffix='_model-msmtcsd_param-apm_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_apm(msmtcsd_params): + """ + full path to a nifti file containing + the anisotropic power map + """ + sh_coeff = nib.load(msmtcsd_params).get_fdata() + pmap = anisotropic_power(sh_coeff) + return pmap, dict(MSMTCSDParamsFile=msmtcsd_params) + + +@immlib.calc("msmt_aodf") +@as_file(suffix='_model-msmtcsd_param-aodf_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_aodf(msmtcsd_params): + """ + full path to a nifti file containing + MSMT CSD ODFs filtered by unified filtering [1] + + References + ---------- + [1] Poirier and Descoteaux, 2024, "A Unified Filtering Method for + Estimating Asymmetric Orientation Distribution Functions", + Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 + """ + sh_coeff = nib.load(msmtcsd_params).get_fdata() + + aodf = unified_filtering( + sh_coeff, + get_sphere(name="repulsion724")) + + return aodf, dict( + MSMTCSDParamsFile=msmtcsd_params, + Sphere="repulsion724") + + @immlib.calc("csd_params") @as_file(suffix='_model-csd_param-fod_dwimap.nii.gz', subfolder="models") @@ -1011,6 +1378,42 @@ def dki_kfa(dki_tf): return dki_tf.kfa +@immlib.calc("dki_cl") +@as_file('_model-dki_param-cl_dwimap.nii.gz', + subfolder="models") +@as_fit_deriv('DKI') +def dki_cl(dki_tf): + """ + full path to a nifti file containing + the DKI linearity file + """ + return dki_tf.linearity + + +@immlib.calc("dki_cp") +@as_file('_model-dki_param-cp_dwimap.nii.gz', + subfolder="models") +@as_fit_deriv('DKI') +def dki_cp(dki_tf): + """ + full path to a nifti file containing + the DKI planarity file + """ + return dki_tf.planarity + + +@immlib.calc("dki_cs") +@as_file('_model-dki_param-cs_dwimap.nii.gz', + subfolder="models") +@as_fit_deriv('DKI') +def dki_cs(dki_tf): + """ + full path to a nifti file containing + the DKI sphericity file + """ + return dki_tf.sphericity + + @immlib.calc("dki_ga") @as_file(suffix='_model-dki_param-ga_dwimap.nii.gz', subfolder="models") @@ -1186,8 +1589,10 @@ def get_data_plan(kwargs): data_tasks = with_name([ get_data_gtab, b0, b0_mask, brain_mask, + dam_fit, dam_csf, dam_pseudot1, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, csd_anisotropic_index, + msmt_params, msmt_apm, msmt_aodf, dti_fa, dti_lt, dti_cfa, dti_pdd, dti_md, dki_kt, dki_lt, dki_fa, gq, gq_pmap, gq_ai, opdt_params, opdt_pmap, opdt_ai, csa_params, csa_pmap, csa_ai, @@ -1196,6 +1601,8 @@ def get_data_plan(kwargs): dki_md, dki_awf, dki_mk, dki_kfa, dki_ga, dki_rd, dti_ga, dti_rd, dti_ad, dki_ad, dki_rk, dki_ak, dti_params, dki_params, fwdti_params, + dki_cl, dki_cp, dki_cs, + dki_csf, dki_wm, dki_gm, rumba_fit, rumba_params, rumba_model, rumba_f_csf, rumba_f_gm, rumba_f_wm, csd_params, get_bundle_dict]) diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 5d1f483b0..7b5f6bd31 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -13,7 +13,7 @@ from AFQ.definitions.utils import Definition import AFQ.tractography.tractography as aft from AFQ.tasks.utils import get_default_args -from AFQ.definitions.image import ScalarImage +from AFQ.definitions.image import ScalarImage, DKI3TImage, DkiGmWmInterfaceImage from AFQ.tractography.utils import gen_seeds, get_percentile_threshold from trx.trx_file_memmap import TrxFile @@ -133,12 +133,16 @@ def export_stop_mask_thresholded(data_imap, stop, tracking_params): full path to a nifti file containing the tractography stop mask thresholded """ - thresh = tracking_params['stop_threshold'] - threshed_data = nib.load(stop).get_fdata() > thresh - stop_mask_desc = dict(source=stop, thresh=thresh) - return nib.Nifti1Image( - threshed_data.astype(np.float32), - data_imap["dwi_affine"]), stop_mask_desc + if isinstance(tracking_params['stop_threshold'], str): + raise ValueError("Cannot generate thresholded " + "stop mask for CMC or ACT") + else: + thresh = tracking_params['stop_threshold'] + threshed_data = nib.load(stop).get_fdata() > thresh + stop_mask_desc = dict(source=stop, thresh=thresh) + return nib.Nifti1Image( + threshed_data.astype(np.float32), + data_imap["dwi_affine"]), stop_mask_desc @immlib.calc("stop") @@ -445,25 +449,41 @@ def get_tractography_plan(kwargs): kwargs["tracking_params"]["odf_model"] =\ kwargs["tracking_params"]["odf_model"].upper() if kwargs["tracking_params"]["seed_mask"] is None: - kwargs["tracking_params"]["seed_mask"] = ScalarImage( - kwargs["best_scalar"]) - kwargs["tracking_params"]["seed_threshold"] = 0.2 - logger.info(( - "No seed mask given, using FA (or first scalar if none are FA)" - "thresholded to 0.2")) + if kwargs["tracking_params"]["tracker"] == "pft": + kwargs["tracking_params"]["seed_mask"] = DkiGmWmInterfaceImage() + kwargs["tracking_params"]["seed_threshold"] = 0.5 + logger.info(( + "No seed mask given, using GM-WM interface " + "from 3T prob maps esimated from DKI")) + else: + kwargs["tracking_params"]["seed_mask"] = ScalarImage( + kwargs["best_scalar"]) + kwargs["tracking_params"]["seed_threshold"] = 0.2 + logger.info(( + "No seed mask given, using FA " + "(or first scalar if none are FA) " + "thresholded to 0.2")) if kwargs["tracking_params"]["stop_mask"] is None: - kwargs["tracking_params"]["stop_mask"] = ScalarImage( - kwargs["best_scalar"]) - kwargs["tracking_params"]["stop_threshold"] = 0.2 - logger.info(( - "No stop mask given, using FA (or first scalar if none are FA)" - "thresholded to 0.2")) + if kwargs["tracking_params"]["tracker"] == "pft": + kwargs["tracking_params"]["stop_threshold"] = "CMC" + kwargs["tracking_params"]["stop_mask"] = DKI3TImage() + logger.info(( + "No stop mask given, using CMC " + "and 3T prob maps esimated from DKI")) + else: + kwargs["tracking_params"]["stop_mask"] = ScalarImage( + kwargs["best_scalar"]) + kwargs["tracking_params"]["stop_threshold"] = 0.2 + logger.info(( + "No stop mask given, using FA " + "(or first scalar if none are FA) " + "thresholded to 0.2")) stop_mask = kwargs["tracking_params"]['stop_mask'] seed_mask = kwargs["tracking_params"]['seed_mask'] odf_model = kwargs["tracking_params"]['odf_model'] - if kwargs["tracking_params"]["tracker"] == "pft": + if isinstance(kwargs["tracking_params"]["stop_threshold"], str): probseg_funcs = stop_mask.get_image_getter("tractography") tractography_tasks["wm_res"] = immlib.calc("pve_wm")(as_file( '_desc-wm_probseg.nii.gz', subfolder="tractography")( diff --git a/AFQ/tractography/tractography.py b/AFQ/tractography/tractography.py index 7dfeb7739..aef299c26 100644 --- a/AFQ/tractography/tractography.py +++ b/AFQ/tractography/tractography.py @@ -178,38 +178,18 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, dg = dg.from_shcoeff(model_params, max_angle=max_angle, sphere=sphere, basis_type=basis_type, legacy=legacy) - if tracker == "local": - if stop_mask is None: - stop_mask = np.ones(params_img.shape[:3]) - - if len(np.unique(stop_mask)) <= 2: - stopping_criterion = ThresholdStoppingCriterion(stop_mask, - 0.5) - else: - if thresholds_as_percentages: - stop_threshold = get_percentile_threshold( - stop_mask, stop_threshold) - stop_mask_copy = np.copy(stop_mask) - stop_thresh_copy = np.copy(stop_threshold) - stopping_criterion = ThresholdStoppingCriterion(stop_mask_copy, - stop_thresh_copy) + if stop_mask is None: + stop_mask = np.ones(params_img.shape[:3]) - my_tracker = LocalTracking - - elif tracker == "pft": - if not isinstance(stop_threshold, str): - raise RuntimeError( - "You are using PFT tracking, but did not provide a string ", - "'stop_threshold' input. ", - "Possible inputs are: 'CMC' or 'ACT'") + if isinstance(stop_threshold, str): if not (isinstance(stop_mask, Iterable) and len(stop_mask) == 3): raise RuntimeError( - "You are using PFT tracking, but did not provide a length " + "You are using CMC/ACT stropping, but did not provide a length " "3 iterable for `stop_mask`. " "Expected a (pve_wm, pve_gm, pve_csf) tuple.") pves = [] pve_imgs = [] - for ii, pve in enumerate(stop_mask): + for pve in stop_mask: if isinstance(pve, str): img = nib.load(pve) else: @@ -232,7 +212,6 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, moving_affine=pve_csf_img.affine, static_affine=params_img.affine).get_fdata() - my_tracker = ParticleFilteringTracking if stop_threshold == "CMC": stopping_criterion = CmcStoppingCriterion.from_pve( pve_wm_data, @@ -246,6 +225,26 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, pve_wm_data, pve_gm_data, pve_csf_data) + else: + if len(np.unique(stop_mask)) <= 2: + stopping_criterion = ThresholdStoppingCriterion(stop_mask, + 0.5) + else: + if thresholds_as_percentages: + stop_threshold = get_percentile_threshold( + stop_mask, stop_threshold) + stop_mask_copy = np.copy(stop_mask) + stop_thresh_copy = np.copy(stop_threshold) + stopping_criterion = ThresholdStoppingCriterion(stop_mask_copy, + stop_thresh_copy) + + if tracker == "local": + my_tracker = LocalTracking + elif tracker == "pft": + my_tracker = ParticleFilteringTracking + else: + raise ValueError(f"Unrecognized tracker '{tracker}'. Must be one of " + "{'local', 'pft'}.") logger.info( f"Tracking with {len(seeds)} seeds, 2 directions per seed...") diff --git a/setup.cfg b/setup.cfg index b641c66fd..24f57886c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ install_requires = pybids>=0.16.2 templateflow>=0.8 immlib + numba pydra trx-python ray From 9e267dd08a0a3d76749d3de7114e686bfa54fa0d Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 23 Jun 2025 17:53:42 -0700 Subject: [PATCH 002/116] add cholesky option --- AFQ/models/msmt.py | 53 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 3e7340319..70f4c3423 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -19,7 +19,7 @@ # ultimately used to fit MSMT CSD @njit(fastmath=True) def solve_qp(Rt, R_pinv, G, A, b, x0, - d, max_iter, tol): + d, max_iter, tol, use_chol): ''' Solves 1/2*x^t*G*x+(Rt*d)^t*x given Ax>=b In MSMT, G, R, A, b are the same across voxels, @@ -84,12 +84,14 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, schur = G + A.T @ np.diag(Z) @ A schur += np.eye(schur.shape[0]) * shur_regularization - schur_diag = np.diag(schur) - - # dx = cholesky_solve(schur, rhs1) - dx = conjugate_gradient_precond( - schur, rhs1, dx, - schur_diag, 5, 1e-4 * tol) + if use_chol: + L = np.linalg.cholesky(schur) + dx = cholesky_solve(L, rhs1) + else: + schur_diag = np.diag(schur) + dx = conjugate_gradient_precond( + schur, rhs1, dx, + schur_diag, 5, 1e-4 * tol) dl_aff = Z * (rhs2 - A @ dx) dy_aff = dy + A @ dx @@ -110,10 +112,12 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, rhs2 = -dy + (-y + safe_divide_vec(mu, l)) rhs1 = rhs1l + A.T @ (Z * rhs2) - # dx = cholesky_solve(schur, rhs1) - dx = conjugate_gradient_precond( - schur, rhs1, dx, - schur_diag, max_iter, max(tol, 1e-2 * mu)) + if use_chol: + dx = cholesky_solve(L, rhs1) + else: + dx = conjugate_gradient_precond( + schur, rhs1, dx, + schur_diag, max_iter, max(tol, 1e-2 * mu)) dl = Z * (rhs2 - A @ dx) dy += A @ dx @@ -143,6 +147,23 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, return False, x0 +@njit(fastmath=True) +def cholesky_solve(L, b): + n = L.shape[0] + + # Forward substitution: L * y = b + y = np.zeros_like(b) + for i in range(n): + y[i] = (b[i] - np.dot(L[i, :i], y[:i])) / L[i, i] + + # Backward substitution: L^T * x = y + x = np.zeros_like(b) + for i in range(n - 1, -1, -1): + x[i] = (y[i] - np.dot(L[i + 1:, i], x[i + 1:])) / L[i, i] + + return x + + @njit(fastmath=True) def conjugate_gradient_precond(A, b, x, diag, max_iter, tol): """ @@ -203,7 +224,7 @@ def find_analytic_center(A, b, x0): def _process_slice(slice_data, slice_mask, slice_results, Rt, R_pinv, G, A, b, x0, - max_iter, tol): + max_iter, tol, use_chol): for j in prange(slice_data.shape[0]): x_prev = x0.copy() for k in range(slice_data.shape[1]): @@ -215,7 +236,7 @@ def _process_slice(slice_data, slice_mask, slice_results, Rt, R_pinv, G, A, b, x_prev, slice_data[j, k], - max_iter, tol) + max_iter, tol, use_chol) if success: slice_results[j, k] = x_prev @@ -225,7 +246,9 @@ def _process_slice(slice_data, slice_mask, slice_results, slice_results[j, k] = np.zeros(A.shape[1]) -def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, n_threads=None): +def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, + n_threads=None, use_chol=False): + # Note cholesky is ~50% slower but more robust if n_threads is not None: set_num_threads(n_threads) @@ -267,6 +290,6 @@ def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, n_threads=None): A, b, x0, - max_iter, tol) + max_iter, tol, use_chol) return MSDeconvFit(self, coeff, None) From 605d64a8788cf6bdad0c406c490940089b21d5df Mon Sep 17 00:00:00 2001 From: 36000 Date: Sat, 28 Jun 2025 22:21:21 -0700 Subject: [PATCH 003/116] Implement AODF in numba; add isolation forest for cleaning callosal and VOF --- AFQ/api/bundle_dict.py | 31 +++-- AFQ/models/asym_filtering.py | 223 ++++++++++++++++--------------- AFQ/recognition/cleaning.py | 64 ++++++++- AFQ/recognition/criteria.py | 39 +++++- AFQ/tasks/data.py | 3 +- AFQ/tractography/tractography.py | 11 ++ setup.cfg | 1 + 7 files changed, 242 insertions(+), 130 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index cc3b6cf78..d4d421897 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -243,7 +243,6 @@ def default18_bd(): 'space': 'template', 'start': templates['VOF_L_start'], 'end': templates['VOF_L_end'], - 'inc_addtol': [4, 0], 'Left Arcuate': { 'node_thresh': 20}, 'Left Posterior Arcuate': { @@ -251,13 +250,14 @@ def default18_bd(): 'core': 'Anterior'}, 'Left Inferior Longitudinal': { 'core': 'Right'}, + 'isolation_forest': { + 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', 'primary_axis_percentage': 40}, 'Right Vertical Occipital': {'cross_midline': False, 'space': 'template', 'start': templates['VOF_R_start'], 'end': templates['VOF_R_end'], - 'inc_addtol': [4, 0], 'Right Arcuate': { 'node_thresh': 20}, 'Right Posterior Arcuate': { @@ -265,6 +265,8 @@ def default18_bd(): 'core': 'Anterior'}, 'Right Inferior Longitudinal': { 'core': 'Left'}, + 'isolation_forest': { + 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', 'primary_axis_percentage': 40}}) @@ -514,6 +516,7 @@ def baby_bd(): def callosal_bd(): + templates = afd.read_templates(as_img=False) callosal_templates =\ afd.read_callosum_templates(as_img=False) return BundleDict({ @@ -522,56 +525,64 @@ def callosal_bd(): 'include': [callosal_templates['R_AntFrontal'], callosal_templates['Callosum_midsag'], callosal_templates['L_AntFrontal']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Motor': { 'cross_midline': True, 'include': [callosal_templates['R_Motor'], callosal_templates['Callosum_midsag'], callosal_templates['L_Motor']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Occipital': { 'cross_midline': True, 'include': [callosal_templates['R_Occipital'], callosal_templates['Callosum_midsag'], callosal_templates['L_Occipital']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Orbital': { 'cross_midline': True, 'include': [callosal_templates['R_Orbital'], callosal_templates['Callosum_midsag'], callosal_templates['L_Orbital']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Posterior Parietal': { 'cross_midline': True, 'include': [callosal_templates['R_PostParietal'], callosal_templates['Callosum_midsag'], callosal_templates['L_PostParietal']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Superior Frontal': { 'cross_midline': True, 'include': [callosal_templates['R_SupFrontal'], callosal_templates['Callosum_midsag'], callosal_templates['L_SupFrontal']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Superior Parietal': { 'cross_midline': True, 'include': [callosal_templates['R_SupParietal'], callosal_templates['Callosum_midsag'], callosal_templates['L_SupParietal']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}, 'Callosum Temporal': { 'cross_midline': True, 'include': [callosal_templates['R_Temporal'], callosal_templates['Callosum_midsag'], callosal_templates['L_Temporal']], - 'exclude': [], + 'isolation_forest': {'percent_outlier_thresh': 20}, + 'exclude': [templates['CST_roi1_L'], templates['CST_roi1_R']], 'space': 'template'}}) diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index bc94d55f9..f9f8970d0 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -4,13 +4,18 @@ # Licensed under the MIT License (https://opensource.org/licenses/MIT). # Modified by John Kruper for pyAFQ # OpenCL and cosine filtering removed +# Replaced with numba import numpy as np -import logging +from numba import njit, prange, config +from tqdm import tqdm from dipy.reconst.shm import sh_to_sf_matrix from dipy.data import get_sphere +config.THREADING_LAYER = 'workqueue' + + __all__ = ["unified_filtering"] @@ -102,15 +107,14 @@ def unified_filtering(sh_data, sphere, # overwrite half-width if win_hwidth is supplied if win_hwidth is not None: half_width = win_hwidth - - # filter shape computed from half_width filter_shape = (half_width * 2 + 1, half_width * 2 + 1, half_width * 2 + 1) # build filters - uv_filter = _unified_filter_build_uv(sigma_angle, sphere) - nx_filter = _unified_filter_build_nx(filter_shape, sigma_spatial, - sigma_align, sphere, exclude_center) - + uv_filter = _unified_filter_build_uv(sigma_angle, + sphere.vertices.astype(np.float64)) + nx_filter = _unified_filter_build_nx(sphere.vertices.astype(np.float64), + sigma_spatial, sigma_align, + False, False) B = sh_to_sf_matrix(sphere, sh_order, sh_basis, full_basis, legacy=is_legacy, return_inv=False) _, B_inv = sh_to_sf_matrix(sphere, sh_order, sh_basis, True, @@ -124,11 +128,13 @@ def unified_filtering(sh_data, sphere, raise ValueError('sigma_rangel cannot be <= 0.') sigma_range = rel_sigma_range * _get_sf_range(sh_data, B) - return _unified_filter_call_python(sh_data, nx_filter, uv_filter, - sigma_range, B, B_inv, sphere) + return _unified_filter_call_python( + sh_data, nx_filter, uv_filter, + sigma_range, B, B_inv, sphere) -def _unified_filter_build_uv(sigma_angle, sphere): +@njit(fastmath=True, cache=True) +def _unified_filter_build_uv(sigma_angle, directions): """ Build the angle filter, weighted on angle between current direction u and neighbour direction v. @@ -138,15 +144,14 @@ def _unified_filter_build_uv(sigma_angle, sphere): sigma_angle: float Standard deviation of filter. Values at distances greater than sigma_angle are clipped to 0 to reduce computation time. - sphere: DIPY sphere - Sphere used for sampling the SF. + directions: DIPY sphere directions. + Vertices from DIPY sphere for sampling the SF. Returns ------- weights: ndarray Angle filter of shape (N_dirs, N_dirs). """ - directions = sphere.vertices if sigma_angle is not None: dot = directions.dot(directions.T) x = np.arccos(np.clip(dot, -1.0, 1.0)) @@ -159,65 +164,58 @@ def _unified_filter_build_uv(sigma_angle, sphere): return weights -def _unified_filter_build_nx(filter_shape, sigma_spatial, sigma_align, - sphere, exclude_center): +@njit(fastmath=True, cache=True) +def _unified_filter_build_nx(directions, sigma_spatial, sigma_align, + disable_spatial, + disable_align, j_invariance=False): """ - Build the combined spatial and alignment filter. - - Parameters - ---------- - filter_shape: tuple - Dimensions of filtering window. - sigma_spatial: float or None - Standard deviation of spatial filter. None disables Gaussian - weighting for spatial filtering. - sigma_align: float or None - Standard deviation of the alignment filter. None disables Gaussian - weighting for alignment filtering. - sphere: DIPY sphere - Sphere for SH to SF projection. - exclude_center: bool - Whether the center voxel is included in the neighbourhood. - - Returns - ------- - weights: ndarray - Combined spatial + alignment filter of shape (W, H, D, N) where - N is the number of sphere directions. + Original source: github.com/CHrlS98/aodf-toolkit + Copyright (c) 2023 Charles Poirier + Licensed under the MIT License (https://opensource.org/licenses/MIT). """ - directions = sphere.vertices.astype(np.float32) - - grid_directions = _get_window_directions(filter_shape).astype(np.float32) - distances = np.linalg.norm(grid_directions, axis=-1) - grid_directions[distances > 0] = grid_directions[distances > 0] /\ - distances[distances > 0][..., None] - - if sigma_spatial is None: - w_spatial = np.ones(filter_shape) - else: - w_spatial = _evaluate_gaussian_distribution(distances, sigma_spatial) - - if sigma_align is None: - w_align = np.ones(np.append(filter_shape, (len(directions),))) - else: - cos_theta = np.clip(grid_directions.dot(directions.T), -1.0, 1.0) - theta = np.arccos(cos_theta) - theta[filter_shape[0] // 2, - filter_shape[1] // 2, - filter_shape[2] // 2] = 0.0 - w_align = _evaluate_gaussian_distribution(theta, sigma_align) - - # resulting filter - w = w_spatial[..., None] * w_align - - if exclude_center: - w[filter_shape[0] // 2, - filter_shape[1] // 2, - filter_shape[2] // 2] = 0.0 - - # normalize and return - w /= np.sum(w, axis=(0, 1, 2), keepdims=True) - return w + directions = np.ascontiguousarray(directions.astype(np.float32)) + + half_width = int(round(3 * sigma_spatial)) + nx_weights = np.zeros((2 * half_width + 1, 2 * half_width + 1, + 2 * half_width + 1, len(directions)), + dtype=np.float32) + + for i in range(-half_width, half_width + 1): + for j in range(-half_width, half_width + 1): + for k in range(-half_width, half_width + 1): + dxy = np.array([[i, j, k]], dtype=np.float32) + len_xy = np.sqrt(dxy[0, 0]**2 + dxy[0, 1]**2 + dxy[0, 2]**2) + + if disable_spatial: + w_spatial = 1.0 + else: + # the length controls spatial weight + w_spatial = np.exp(-len_xy**2 / (2 * sigma_spatial**2)) + + # the direction controls the align weight + if i == j == k == 0 or disable_align: + # hack for main direction to have maximal weight + # w_align = np.ones((1, len(directions)), dtype=np.float32) + w_align = np.zeros((1, len(directions)), dtype=np.float32) + else: + dxy /= len_xy + w_align = np.arccos(np.clip(np.dot(dxy, directions.T), + -1.0, 1.0)) # 1, N + w_align = np.exp(-w_align**2 / (2 * sigma_align**2)) + + nx_weights[half_width + i, half_width + j, half_width + k] =\ + w_align * w_spatial + + if j_invariance: + # A filter is j-invariant if its prediction does not + # depend on the content of the current voxel + nx_weights[half_width, half_width, half_width, :] = 0.0 + + for ui in range(len(directions)): + w_sum = np.sum(nx_weights[..., ui]) + nx_weights /= w_sum + + return nx_weights def _get_sf_range(sh_data, B_mat): @@ -273,13 +271,22 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, """ nb_sf = len(sphere.vertices) mean_sf = np.zeros(sh_data.shape[:-1] + (nb_sf,)) + sh_data = np.ascontiguousarray(sh_data, dtype=np.float64) + B_mat = np.ascontiguousarray(B_mat, dtype=np.float64) + + h_w, h_h, h_d = nx_filter.shape[:3] + half_w, half_h, half_d = h_w // 2, h_h // 2, h_d // 2 + sh_data_padded = np.ascontiguousarray(np.pad( + sh_data, + ((half_w, half_w), (half_h, half_h), (half_d, half_d), (0, 0)), + mode='constant' + ), dtype=np.float64) # Apply filter to each sphere vertice - for u_sph_id in range(nb_sf): - if u_sph_id % 20 == 0: - logging.info('Processing direction: {}/{}' - .format(u_sph_id, nb_sf)) - mean_sf[..., u_sph_id] = _correlate(sh_data, nx_filter, uv_filter, + for u_sph_id in tqdm(range(nb_sf)): + + mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, + nx_filter, uv_filter, sigma_range, u_sph_id, B_mat) out_sh = np.array([np.dot(i, B_inv) for i in mean_sf], @@ -287,7 +294,9 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, return out_sh -def _correlate(sh_data, nx_filter, uv_filter, sigma_range, u_index, B_mat): +@njit(fastmath=True, parallel=True) +def _correlate(sh_data, sh_data_padded, nx_filter, uv_filter, + sigma_range, u_index, B_mat): """ Apply the filters to the SH image for the sphere direction described by `u_index`. @@ -296,6 +305,8 @@ def _correlate(sh_data, nx_filter, uv_filter, sigma_range, u_index, B_mat): ---------- sh_data: ndarray Input SH coefficients. + sh_data: ndarray + Input SH coefficients, pre-padded. nx_filter: ndarray Combined spatial and alignment filter. uv_filter: ndarray @@ -317,25 +328,35 @@ def _correlate(sh_data, nx_filter, uv_filter, sigma_range, u_index, B_mat): h_w, h_h, h_d = nx_filter.shape[:3] half_w, half_h, half_d = h_w // 2, h_h // 2, h_d // 2 out_sf = np.zeros(sh_data.shape[:3]) - sh_data = np.pad(sh_data, ((half_w, half_w), - (half_h, half_h), - (half_d, half_d), - (0, 0))) - sf_u = np.dot(sh_data, B_mat[:, u_index]) - sf_v = np.dot(sh_data, B_mat[:, v_indices]) - uv_filter = uv_filter[u_index, v_indices] + # sf_u = np.dot(sh_data, B_mat[:, u_index]) + # sf_v = np.dot(sh_data, B_mat[:, v_indices]) + sf_u = np.zeros(sh_data_padded.shape[:3]) + sf_v = np.zeros(sh_data_padded.shape[:3] + (len(v_indices),)) + for i in prange(sh_data_padded.shape[0]): + for j in range(sh_data_padded.shape[1]): + for k in range(sh_data_padded.shape[2]): + for c in range(sh_data_padded.shape[3]): + sf_u[i, j, k] += sh_data_padded[i, + j, k, c] * B_mat[c, u_index] + for vi in range(len(v_indices)): + sf_v[i, j, k, vi] += sh_data_padded[i, + j, k, c] * B_mat[c, v_indices[vi]] - _get_range = _evaluate_gaussian_distribution\ - if sigma_range is not None else lambda x, _: np.ones_like(x) + uv_filter = uv_filter[u_index, v_indices] - for ii in range(out_sf.shape[0]): + for ii in prange(out_sf.shape[0]): for jj in range(out_sf.shape[1]): for kk in range(out_sf.shape[2]): a = sf_v[ii:ii + h_w, jj:jj + h_h, kk:kk + h_d] b = sf_u[ii + half_w, jj + half_h, kk + half_d] x_range = a - b - range_filter = _get_range(x_range, sigma_range) + + if sigma_range is None: + range_filter = np.ones_like(x_range) + else: + range_filter = _evaluate_gaussian_distribution( + x_range, sigma_range) # the resulting filter for the current voxel and v_index res_filter = range_filter * nx_filter[..., None] @@ -349,6 +370,7 @@ def _correlate(sh_data, nx_filter, uv_filter, sigma_range, u_index, B_mat): return out_sf +@njit(fastmath=True, cache=True) def _evaluate_gaussian_distribution(x, sigma): """ 1-dimensional 0-centered Gaussian distribution @@ -366,28 +388,7 @@ def _evaluate_gaussian_distribution(x, sigma): out: ndarray or float Values at x. """ - assert sigma > 0.0, "Sigma must be greater than 0." + if sigma <= 0.0: + raise ValueError("Sigma must be greater than 0.") cnorm = 1.0 / sigma / np.sqrt(2.0 * np.pi) - return cnorm * np.exp(-x**2 / 2 / sigma**2) - - -def _get_window_directions(shape): - """ - Get directions from center voxel to all neighbours - for a window of given shape. - - Parameters - ---------- - shape: tuple - Dimensions of the window. - - Returns - ------- - grid: ndarray - Grid containing the direction from the center voxel to - the current position for all positions inside the window. - """ - grid = np.indices(shape) - grid = np.moveaxis(grid, 0, -1) - grid = grid - np.asarray(shape) // 2 - return grid + return cnorm * np.exp(-x**2 / 2.0 / sigma**2) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 28d808a2f..49373b005 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -9,7 +9,7 @@ import AFQ.recognition.utils as abu from AFQ._fixes import gaussian_weights - +from sklearn.ensemble import IsolationForest logger = logging.getLogger('AFQ') @@ -192,3 +192,65 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, return out, idx else: return out + + +def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, + min_sl=20, n_jobs=None): + """ + Use Isolation Forest (IF) to clean streamlines. + Nodes are passed to IF, and outlier nodes are identified. + These are re-mapped back on to the streamlines, and streamlines + with too many outlier nodes are removed. This is better for + cleaning bundles that are not tube-like. + + Parameters + ---------- + tg : StatefulTractogram class instance or ArraySequence + A whole-brain tractogram to be segmented. + n_points : int, optional + Number of points to resample streamlines to. + Default: 100 + percent_outlier_thresh : int, optional + Percentage of outliers allowed in the streamline. + Default: 15 + min_sl : int, optional. + Number of streamlines in a bundle under which we will + not bother with cleaning outliers. Default: 20. + n_jobs : int, optional + Number of parallel jobs to use for LOF. + Default: None (single-threaded). + + Returns + ------- + indicies of streamlines that passed cleaning + """ + if hasattr(tg, "streamlines"): + streamlines = tg.streamlines + else: + streamlines = dts.Streamlines(tg) + + # We don't even bother if there aren't enough streamlines: + if len(streamlines) < min_sl: + logger.warning(( + "LOD cleaning not performed" + " due to low streamline count")) + return np.ones(len(streamlines), dtype=bool) + + # Resample once up-front: + fgarray = np.asarray(abu.resample_tg(streamlines, n_points)) + fgarray_flat = fgarray.reshape((-1, 3)) + idx = np.arange(len(fgarray)) + + lof = IsolationForest(n_jobs=n_jobs) + outliers = lof.fit_predict(fgarray_flat) + outliers = outliers.reshape(fgarray.shape[:2]) + outliers = np.sum(outliers == -1, axis=1) + + idx_belong = outliers * 100 <= n_points * percent_outlier_thresh + + if np.sum(idx_belong) < min_sl: + # need to sort and return exactly min_sl: + return idx[np.argsort(outliers)[:min_sl].astype(int)] + else: + # Update by selection: + return idx[idx_belong] diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 33d823803..5e6de63a9 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -22,10 +22,15 @@ import AFQ.recognition.roi as abr import AFQ.recognition.other_bundles as abo -bundle_criterion_order = [ +criteria_order_pre_other_bundles = [ "prob_map", "cross_midline", "start", "end", "length", "primary_axis", "include", "exclude", - "curvature", "recobundles", "qb_thresh"] + "curvature", "recobundles"] + + +criteria_order_post_other_bundles = [ + "isolation_forest", "qb_thresh"] + valid_noncriterion = [ "space", "mahal", "primary_axis_percentage", @@ -306,6 +311,20 @@ def clean_by_other_bundle(b_sls, bundle_def, b_sls.select(cleaned_idx, other_bundle_name) +def isolation_forest(b_sls, bundle_def, parallel_segmentation, **kwargs): + b_sls.initiate_selection("isolation_forest") + if parallel_segmentation["engine"] == "serial": + n_jobs = None + else: + n_jobs = parallel_segmentation.get("n_jobs", -1) + accept_idx = abc.clean_by_isolation_forest( + b_sls.get_selected_sls(), + percent_outlier_thresh=bundle_def["isolation_forest"].get( + "percent_outlier_thresh", 25), + n_jobs=n_jobs) + b_sls.select(accept_idx, "isolation_forest") + + def mahalanobis(b_sls, bundle_def, clip_edges, cleaning_params, **kwargs): b_sls.initiate_selection("Mahalanobis") clean_params = bundle_def.get("mahal", {}) @@ -378,18 +397,20 @@ def check_space(roi): inputs[key] = value for potential_criterion in bundle_def.keys(): - if (potential_criterion not in bundle_criterion_order) and\ - (potential_criterion not in bundle_dict.bundle_names) and\ + if (potential_criterion not in criteria_order_post_other_bundles) and\ + (potential_criterion not in criteria_order_pre_other_bundles) and\ + (potential_criterion not in bundle_dict.bundle_names) and\ (potential_criterion not in valid_noncriterion): raise ValueError(( "Invalid criterion in bundle definition:\n" f"{potential_criterion} in bundle {bundle_name}.\n" "Valid criteria are:\n" - f"{bundle_criterion_order}\n" + f"{criteria_order_pre_other_bundles}\n" + f"{criteria_order_post_other_bundles}\n" f"{bundle_dict.bundle_names}\n" f"{valid_noncriterion}\n")) - for criterion in bundle_criterion_order: + for criterion in criteria_order_pre_other_bundles: if b_sls and criterion in bundle_def: inputs[criterion] = globals()[criterion](**inputs) if b_sls: @@ -400,8 +421,12 @@ def check_space(roi): **inputs, other_bundle_name=bundle_name, other_bundle_sls=tg.streamlines[idx]) + for criterion in criteria_order_post_other_bundles: + if b_sls and criterion in bundle_def: + inputs[criterion] = globals()[criterion](**inputs) if b_sls: - mahalanobis(**inputs) + if "mahal" in bundle_def or "isolation_forest" not in bundle_def: + mahalanobis(**inputs) if b_sls and not b_sls.oriented_yet: raise ValueError( diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 084189e71..319475fa6 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -640,7 +640,7 @@ def msmt_apm(msmtcsd_params): return pmap, dict(MSMTCSDParamsFile=msmtcsd_params) -@immlib.calc("msmt_aodf") +@immlib.calc("msmt_aodf_params") @as_file(suffix='_model-msmtcsd_param-aodf_dwimap.nii.gz', subfolder="models") @as_img @@ -657,6 +657,7 @@ def msmt_aodf(msmtcsd_params): """ sh_coeff = nib.load(msmtcsd_params).get_fdata() + logger.info("Applying unified filtering to MSMT CSD ODFs...") aodf = unified_filtering( sh_coeff, get_sphere(name="repulsion724")) diff --git a/AFQ/tractography/tractography.py b/AFQ/tractography/tractography.py index aef299c26..a41cbc5fd 100644 --- a/AFQ/tractography/tractography.py +++ b/AFQ/tractography/tractography.py @@ -13,6 +13,7 @@ from dipy.tracking.stopping_criterion import (ThresholdStoppingCriterion, CmcStoppingCriterion, ActStoppingCriterion) +from dipy.reconst import shm from nibabel.streamlines.tractogram import LazyTractogram @@ -174,6 +175,16 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, from_lower_triangular(model_params)) odf = tensor_odf(evals, evecs, sphere) dg = dg.from_pmf(odf, max_angle=max_angle, sphere=sphere) + elif "AODF" in odf_model: + sh_order = shm.order_from_ncoef( + model_params.shape[3], full_basis=True) + pmf = shm.sh_to_sf( + model_params, sphere, + sh_order_max=sh_order, full_basis=True) + pmf[pmf < 0] = 0 + dg = dg.from_pmf( + np.asarray(pmf, dtype=float), + max_angle=max_angle, sphere=sphere) else: dg = dg.from_shcoeff(model_params, max_angle=max_angle, sphere=sphere, basis_type=basis_type, legacy=legacy) diff --git a/setup.cfg b/setup.cfg index 24f57886c..09963f455 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,7 @@ install_requires = # core packages scikit_image>=0.14.2 dipy>=1.11.0,<1.12.0 + scikit-learn pandas pybids>=0.16.2 templateflow>=0.8 From fff369b55a870429de4a866a66fea4d1b3a2bb09 Mon Sep 17 00:00:00 2001 From: 36000 Date: Sun, 29 Jun 2025 12:14:16 -0700 Subject: [PATCH 004/116] move dam/dki fitting to other files, start requiring t1 --- AFQ/api/group.py | 10 ++++ AFQ/api/participant.py | 5 ++ AFQ/models/dam.py | 128 +++++++++++++++++++++++++++++++++++++++ AFQ/models/dki.py | 113 ++++++++++++++++++++++++++++++++++ AFQ/tasks/data.py | 133 ++++------------------------------------- 5 files changed, 269 insertions(+), 120 deletions(-) create mode 100644 AFQ/models/dam.py diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 1e06259f6..3054f707a 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -307,6 +307,14 @@ def __init__(self, bval_file = bids_layout.get_bval( dwi_data_file, **bids_filters) + t1_files = bids_layout.get(suffix="T1w", **bids_filters) + if (not len(t1_files)): + self.logger.warning( + f"No T1w found for subject {subject} and session " + f"{session}. Skipping.") + continue + t1_file = t1_files[0] + if suffix is not None: bids_filters["suffix"] = suffix @@ -386,12 +394,14 @@ def __init__(self, this_pAFQ_inputs = _ParticipantAFQInputs( dwi_data_file, bval_file, bvec_file, + t1_file, results_dir, this_kwargs) this_pAFQ = ParticipantAFQ( this_pAFQ_inputs.dwi_data_file, this_pAFQ_inputs.bval_file, this_pAFQ_inputs.bvec_file, + this_pAFQ_inputs.t1_file, this_pAFQ_inputs.results_dir, **this_pAFQ_inputs.kwargs) self.plans_dict[subject][str(session)] = this_pAFQ.plans_dict diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index e908bd2d3..17a4fc5b3 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -35,6 +35,7 @@ class ParticipantAFQ(object): def __init__(self, dwi_data_file, bval_file, bvec_file, + t1_file, output_dir, **kwargs): """ @@ -48,6 +49,9 @@ def __init__(self, Path to bval file. bvec_file : str Path to bvec file. + t1_file : str + Path to T1-weighted image file. Must already be registered + to the DWI data. output_dir : str Path to output directory. kwargs : additional optional parameters @@ -99,6 +103,7 @@ def __init__(self, dwi_data_file=dwi_data_file, bval_file=bval_file, bvec_file=bvec_file, + t1_file=t1_file, output_dir=output_dir, base_fname=get_base_fname(output_dir, dwi_data_file), **kwargs) diff --git a/AFQ/models/dam.py b/AFQ/models/dam.py new file mode 100644 index 000000000..21c108a98 --- /dev/null +++ b/AFQ/models/dam.py @@ -0,0 +1,128 @@ +import logging +import numpy as np +from tqdm import tqdm + +from scipy.ndimage import gaussian_filter1d +from scipy.signal import find_peaks + +from dipy.segment.tissue import compute_directional_average + + +logger = logging.getLogger('AFQ') + + +def fit_dam(data, gtab, b0_img, dam_low_signal_thresh=50): + """ + direction-averaged signal map (DAM) [1] slope and intercept + + Parameters + ---------- + data : ndarray + The diffusion data with shape (x, y, z, b). + gtab : GradientTable + The gradient table containing b-values and b-vectors. + b0_img : Nifti1Image + The b0 image used to compute the mean signal. + This should be a 3D image with shape (x, y, z). + dam_low_signal_thresh : float, optional + The threshold below which a voxel is considered to have low signal. + Default: 50 + + References + ---------- + .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., + & Garyfallidis, E. (2020). Segmentation of the brain using + direction-averaged signal of DWI images. + Magnetic Resonance Imaging, 69, 1-7. Elsevier. + https://doi.org/10.1016/j.mri.2020.02.010 + """ + # Precompute unique b-values, masks + unique_bvals = np.unique(gtab.bvals) + if len(unique_bvals) <= 2: + raise ValueError(("Insufficient unique b-values for fitting DAM. " + "Note, DAM requires multi-shell data")) + masks = gtab.bvals[:, np.newaxis] == unique_bvals[np.newaxis, 1:] + + b0_data = b0_img.get_fdata() + + # If the mean signal for b=0 is too low, + # set those voxels to 0 for both P and V + valid_voxels = b0_data >= dam_low_signal_thresh + + params_map = np.zeros((*data.shape[:-1], 2)) + logger.info("Fitting directional average map (DAM)...") + for idx in tqdm(range(data.shape[0] * data.shape[1] * data.shape[2])): + i, j, k = np.unravel_index(idx, data.shape[:-1]) + if valid_voxels[i, j, k]: + aa, bb = compute_directional_average( + data[i, j, k, :], + gtab.bvals, + masks=masks, + b0_mask=gtab.b0s_mask, + s0_map=b0_data[i, j, k], + low_signal_threshold=dam_low_signal_thresh, + ) + if aa > 0.01 and bb < 0: + params_map[i, j, k, 0] = aa + params_map[i, j, k, 1] = -bb + return params_map + + +def csf_dam(dam_intercept_data): + """ + CSF probability map from DAM intercept + + Parameters + ---------- + dam_intercept_data : ndarray + The DAM intercept data with shape (x, y, z). + """ + beta_values = dam_intercept_data.flatten() + beta_values = beta_values[beta_values != 0] + + # Make a smoothed histogram + hist, bin_edges = np.histogram(beta_values, bins=200, density=True) + bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 + smoothed_hist = gaussian_filter1d(hist, sigma=2) + + # Find the main peak + peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) + if len(peaks) == 0: + raise ValueError(( + "DAM intercept: No peaks found in the histogram, " + "required for DAM CSF estimate")) + + main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] + main_peak_val = bin_centers[main_peak_idx] + + # Find the threshold symmetric to 0 with respect to the peak center + threshold = 2 * main_peak_val + + return dam_intercept_data > threshold, threshold + + +def t1_dam(dam_slope_data, dam_csf_data): + """ + T1 map from DAM slope and CSF probability map + + Parameters + ---------- + dam_slope_data : ndarray + The DAM slope data with shape (x, y, z). + dam_csf_data : ndarray + The DAM CSF probability map with shape (x, y, z). + """ + + dam_slope_min = np.percentile( + dam_slope_data[dam_slope_data != 0], 0.5) + dam_slope_data[dam_slope_data < dam_slope_min] = 0 + + dam_slope_max = np.percentile( + dam_slope_data[dam_slope_data != 0], 99.5) + dam_slope_data[dam_slope_data > dam_slope_max] = 0 + + dam_slope_data[dam_slope_data != 0] = dam_slope_max - \ + dam_slope_data[dam_slope_data != 0] + pseudo_t1 = (1.0 - dam_csf_data) * dam_slope_data + + return pseudo_t1 diff --git a/AFQ/models/dki.py b/AFQ/models/dki.py index 06cb702cd..0d4fcce72 100644 --- a/AFQ/models/dki.py +++ b/AFQ/models/dki.py @@ -4,6 +4,10 @@ import numpy as np import nibabel as nib +from scipy.signal import find_peaks, peak_widths +from scipy.special import erf +from scipy.ndimage import gaussian_filter1d + from dipy.reconst import dki from dipy.reconst import dki_micro from dipy.core.ndindex import ndindex @@ -354,3 +358,112 @@ def predict(params_file, gtab, S0_file=None, out_dir=None): nib.save(nib.Nifti1Image(pred, img.affine), fname) return fname + + +def dki_csf(dki_md_data): + """ + CSF probability map from DKI MD inspired by [1] + + References + ---------- + .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., + & Garyfallidis, E. (2020). Segmentation of the brain using + direction-averaged signal of DWI images. + Magnetic Resonance Imaging, 69, 1-7. Elsevier. + https://doi.org/10.1016/j.mri.2020.02.010 + """ + + beta_values = dki_md_data.flatten() + beta_values = beta_values[beta_values != 0] + + # Make a smoothed histogram + hist, bin_edges = np.histogram(beta_values, bins=200, density=True) + bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 + smoothed_hist = gaussian_filter1d(hist, sigma=2) + + # Find the main peak + peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) + if len(peaks) == 0: + raise ValueError(( + "DKI MD: No peaks found in the histogram, " + "required for DKI CSF estimate")) + + main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] + main_peak_val = bin_centers[main_peak_idx] + + # Estimate uncertainty in peak center location + # The FWHM of a Gaussian is 2.355*sigma, so sigma ≈ FWHM/2.355 + width_results = peak_widths( + smoothed_hist, [main_peak_idx], rel_height=0.5) + peak_width_md = abs( + bin_centers[1] - bin_centers[0]) * width_results[0][0] + peak_sigma = peak_width_md / 2.355 + + # Find the threshold symmetric to 0 with respect to the peak center + main_peak_val = 2 * main_peak_val + peak_sigma = 2 * peak_sigma + + dki_md_data[dki_md_data != 0] = \ + 0.5 * (1 + erf((dki_md_data[dki_md_data != 0] - main_peak_val) + / (peak_sigma * np.sqrt(2)))) + + return dki_md_data, main_peak_val, peak_sigma + + +def dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul): + """ + WM probability map from DKI FA + + Parameters + ---------- + dki_fa_data : ndarray + DKI FA data from which to calculate the WM probability map. + dki_wm_ll : float + Lower limit of FA in white matter to calculate probability mask. + dki_gm_ul : float + Upper limit of FA in gray matter to calculate probability mask. + """ + + uncertain_slice = np.logical_and( + dki_fa_data > dki_wm_ll, + dki_fa_data <= dki_gm_ul) + wm_data = dki_fa_data.copy() + wm_data[dki_fa_data >= dki_gm_ul] = 1.0 + wm_data[uncertain_slice] = (dki_fa_data[uncertain_slice] - dki_wm_ll) /\ + (dki_gm_ul - dki_wm_ll) + wm_data[dki_fa_data < dki_wm_ll] = 0.0 + + return wm_data + + +def dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul): + """ + GM probability map from DKI FA + + Parameters + ---------- + dki_fa_data : ndarray + DKI FA data from which to calculate the GM probability map. + dki_csf_data : ndarray + DKI CSF data to remove CSF contribution from GM probability map. + dki_wm_ll : float + Lower limit of FA in white matter to calculate probability mask. + dki_gm_ul : float + Upper limit of FA in gray matter to calculate probability mask. + """ + + uncertain_slice = np.logical_and( + dki_fa_data > dki_wm_ll, + dki_fa_data <= dki_gm_ul) + gm_data = dki_fa_data.copy() + gm_data[dki_fa_data >= dki_gm_ul] = 0.0 + gm_data[uncertain_slice] = (dki_gm_ul - dki_fa_data[uncertain_slice]) /\ + (dki_gm_ul - dki_wm_ll) + gm_data[np.logical_and( + dki_fa_data < dki_wm_ll, + dki_fa_data != 0)] = 1.0 + + gm_data = gm_data - dki_csf_data # Remove CSF contribution + gm_data[gm_data < 0.0] = 0.0 + + return gm_data diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 319475fa6..6a138cbc6 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -1,17 +1,13 @@ import nibabel as nib import numpy as np import logging -from tqdm import tqdm from dipy.io.gradients import read_bvals_bvecs import dipy.core.gradients as dpg from dipy.data import default_sphere, get_sphere -from scipy.signal import find_peaks, peak_widths -from scipy.ndimage import gaussian_filter1d -from scipy.special import erf - import immlib +import pimms import dipy.reconst.dki as dpy_dki import dipy.reconst.dti as dpy_dti @@ -21,7 +17,6 @@ from dipy.reconst.rumba import RumbaSDModel, RumbaFit from dipy.reconst import shm from dipy.reconst.dki_micro import axonal_water_fraction -from dipy.segment.tissue import compute_directional_average from dipy.reconst.mcsd import ( MultiShellDeconvModel, mask_for_response_msmt, @@ -43,12 +38,14 @@ from AFQ.models.csd import _fit as csd_fit_model from AFQ.models.csd import CsdNanResponseError from AFQ.models.dki import _fit as dki_fit_model +from AFQ.models.dki import dki_csf, dki_gm, dki_wm from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model from AFQ.models.msmt import fit as msmt_fit from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) from AFQ.models.asym_filtering import unified_filtering +from AFQ.models.dam import fit_dam, csf_dam, t1_dam logger = logging.getLogger('AFQ') @@ -156,35 +153,10 @@ def dam_fit(data, gtab, masked_b0, Magnetic Resonance Imaging, 69, 1-7. Elsevier. https://doi.org/10.1016/j.mri.2020.02.010 """ - # Precompute unique b-values, masks - unique_bvals = np.unique(gtab.bvals) - if len(unique_bvals) <= 2: - raise ValueError(("Insufficient unique b-values for fitting DAM. " - "Note, DAM requires multi-shell data")) - masks = gtab.bvals[:, np.newaxis] == unique_bvals[np.newaxis, 1:] - - b0_data = nib.load(masked_b0).get_fdata() - - # If the mean signal for b=0 is too low, - # set those voxels to 0 for both P and V - valid_voxels = b0_data >= dam_low_signal_thresh - - params_map = np.zeros((*data.shape[:-1], 2)) - logger.info("Fitting directional average map (DAM)...") - for idx in tqdm(range(data.shape[0] * data.shape[1] * data.shape[2])): - i, j, k = np.unravel_index(idx, data.shape[:-1]) - if valid_voxels[i, j, k]: - aa, bb = compute_directional_average( - data[i, j, k, :], - gtab.bvals, - masks=masks, - b0_mask=gtab.b0s_mask, - s0_map=b0_data[i, j, k], - low_signal_threshold=dam_low_signal_thresh, - ) - if aa > 0.01 and bb < 0: - params_map[i, j, k, 0] = aa - params_map[i, j, k, 1] = -bb + b0_img = nib.load(masked_b0) + params_map = fit_dam( + data, gtab, b0_img, + dam_low_signal_thresh=dam_low_signal_thresh) return params_map, dict(low_signal_thresh=dam_low_signal_thresh) @@ -198,28 +170,9 @@ def dam_csf(dam_params): CSF probability map from DAM intercept """ dam_intercept_data = nib.load(dam_params).get_fdata()[..., 1] - beta_values = dam_intercept_data.flatten() - beta_values = beta_values[beta_values != 0] - - # Make a smoothed histogram - hist, bin_edges = np.histogram(beta_values, bins=200, density=True) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - smoothed_hist = gaussian_filter1d(hist, sigma=2) - - # Find the main peak - peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) - if len(peaks) == 0: - raise ValueError(( - "DAM intercept: No peaks found in the histogram, " - "required for DAM CSF estimate")) + csf, threshold = csf_dam(dam_intercept_data) - main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] - main_peak_val = bin_centers[main_peak_idx] - - # Find the threshold symmetric to 0 with respect to the peak center - threshold = 2 * main_peak_val - - return dam_intercept_data > threshold, dict( + return csf, dict( DAMParamsFile=dam_params, threshold=threshold) @@ -235,17 +188,7 @@ def dam_pseudot1(dam_params, dam_csf): dam_slope_data = nib.load(dam_params).get_fdata()[..., 0] dam_csf_data = nib.load(dam_csf).get_fdata() - dam_slope_min = np.percentile( - dam_slope_data[dam_slope_data != 0], 0.5) - dam_slope_data[dam_slope_data < dam_slope_min] = 0 - - dam_slope_max = np.percentile( - dam_slope_data[dam_slope_data != 0], 99.5) - dam_slope_data[dam_slope_data > dam_slope_max] = 0 - - dam_slope_data[dam_slope_data != 0] = dam_slope_max - \ - dam_slope_data[dam_slope_data != 0] - pseudo_t1 = (1.0 - dam_csf_data) * dam_slope_data + pseudo_t1 = t1_dam(dam_slope_data, dam_csf_data) return pseudo_t1, dict(source=dam_params) @@ -267,39 +210,8 @@ def dki_csf(dki_md): https://doi.org/10.1016/j.mri.2020.02.010 """ dki_md_data = nib.load(dki_md).get_fdata() - beta_values = dki_md_data.flatten() - beta_values = beta_values[beta_values != 0] - - # Make a smoothed histogram - hist, bin_edges = np.histogram(beta_values, bins=200, density=True) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - smoothed_hist = gaussian_filter1d(hist, sigma=2) - - # Find the main peak - peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) - if len(peaks) == 0: - raise ValueError(( - "DKI MD: No peaks found in the histogram, " - "required for DKI CSF estimate")) - - main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] - main_peak_val = bin_centers[main_peak_idx] - - # Estimate uncertainty in peak center location - # The FWHM of a Gaussian is 2.355*sigma, so sigma ≈ FWHM/2.355 - width_results = peak_widths( - smoothed_hist, [main_peak_idx], rel_height=0.5) - peak_width_md = abs( - bin_centers[1] - bin_centers[0]) * width_results[0][0] - peak_sigma = peak_width_md / 2.355 - - # Find the threshold symmetric to 0 with respect to the peak center - main_peak_val = 2 * main_peak_val - peak_sigma = 2 * peak_sigma - dki_md_data[dki_md_data != 0] = \ - 0.5 * (1 + erf((dki_md_data[dki_md_data != 0] - main_peak_val) - / (peak_sigma * np.sqrt(2)))) + dki_md_data, main_peak_val, peak_sigma = dki_csf(dki_md_data) return dki_md_data, dict( DKI_MD_source=dki_md, @@ -326,14 +238,7 @@ def dki_wm(dki_fa, dki_wm_ll=0.1, dki_gm_ul=0.3): """ dki_fa_data = nib.load(dki_fa).get_fdata() - uncertain_slice = np.logical_and( - dki_fa_data > dki_wm_ll, - dki_fa_data <= dki_gm_ul) - wm_data = dki_fa_data.copy() - wm_data[dki_fa_data >= dki_gm_ul] = 1.0 - wm_data[uncertain_slice] = (dki_fa_data[uncertain_slice] - dki_wm_ll) /\ - (dki_gm_ul - dki_wm_ll) - wm_data[dki_fa_data < dki_wm_ll] = 0.0 + wm_data = dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul) return wm_data, dict( DKI_FA_source=dki_fa, @@ -361,19 +266,7 @@ def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): dki_fa_data = nib.load(dki_fa).get_fdata() dki_csf_data = nib.load(dki_csf).get_fdata() - uncertain_slice = np.logical_and( - dki_fa_data > dki_wm_ll, - dki_fa_data <= dki_gm_ul) - gm_data = dki_fa_data.copy() - gm_data[dki_fa_data >= dki_gm_ul] = 0.0 - gm_data[uncertain_slice] = (dki_gm_ul - dki_fa_data[uncertain_slice]) /\ - (dki_gm_ul - dki_wm_ll) - gm_data[np.logical_and( - dki_fa_data < dki_wm_ll, - dki_fa_data != 0)] = 1.0 - - gm_data = gm_data - dki_csf_data # Remove CSF contribution - gm_data[gm_data < 0.0] = 0.0 + gm_data = dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul) return gm_data, dict( DKI_FA_source=dki_fa, From 902872d40ee5eef3a6acde8ef06488474b77b138 Mon Sep 17 00:00:00 2001 From: 36000 Date: Sun, 29 Jun 2025 17:28:04 -0700 Subject: [PATCH 005/116] WMGM interface seeding --- AFQ/api/group.py | 12 +++++- AFQ/definitions/image.py | 75 ++++++++++++++---------------------- AFQ/models/wmgm_interface.py | 58 ++++++++++++++++++++++++++++ AFQ/tasks/data.py | 74 +++++++++++++++++++++++++++++++++++ AFQ/tasks/tractography.py | 26 +++++-------- NOTICE.md | 4 ++ 6 files changed, 184 insertions(+), 65 deletions(-) create mode 100644 AFQ/models/wmgm_interface.py create mode 100644 NOTICE.md diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 3054f707a..6ccba211a 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -60,11 +60,13 @@ def clean_pandas_df(df): class _ParticipantAFQInputs: def __init__( - self, dwi_data_file, bval_file, bvec_file, results_dir, + self, dwi_data_file, bval_file, bvec_file, + t1_file, results_dir, kwargs): self.dwi_data_file = dwi_data_file self.bval_file = bval_file self.bvec_file = bvec_file + self.t1_file = t1_file self.results_dir = results_dir self.kwargs = kwargs @@ -315,6 +317,14 @@ def __init__(self, continue t1_file = t1_files[0] + self.logger.info( + f"Using the following files for subject {subject} " + f"and session {session}:") + self.logger.info(f" DWI: {dwi_data_file}") + self.logger.info(f" BVAL: {bval_file}") + self.logger.info(f" BVEC: {bvec_file}") + self.logger.info(f" T1: {t1_file}") + if suffix is not None: bids_filters["suffix"] = suffix diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index 704603d6c..a331d5617 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -10,10 +10,11 @@ from skimage.morphology import convex_hull_image, binary_opening + __all__ = [ "ImageFile", "FullImage", "RoiImage", "B0Image", "LabelledImageFile", "ThresholdedImageFile", "ScalarImage", "ThresholdedScalarImage", - "TemplateImage", "GQImage", "DKI3TImage", "DkiGmWmInterfaceImage"] + "TemplateImage", "GQImage", "ThreeTImage"] logger = logging.getLogger('AFQ') @@ -701,7 +702,7 @@ class PFTImage(ImageDefinition): afm.ImageFile(suffix="CSFprobseg")) api.GroupAFQ(tracking_params={ "stop_image": stop_image, - "stop_threshold": "CMC", + "stop_threshold": "ACT", "tracker": "pft"}) """ @@ -728,71 +729,51 @@ def get_image_getter(self, task_name): for probseg in self.probsegs] -class DKI3TImage(PFTImage): +class ThreeTImage(ImageDefinition): """ Define an image for use in PFT tractography. Only use if tracker set to 'pft' in tractography. Use to generate - WM/GM/CSF probsegs from DKI data. + WM/GM/CSF probsegs from T1w. Examples -------- api.GroupAFQ(tracking_params={ - "stop_image": DKI3TImage(), - "stop_threshold": "CMC", + "stop_image": ThreeTImage(), + "stop_threshold": "ACT", "tracker": "pft"}) """ - def __init__(self): - self.probsegs = ( - ScalarImage("dki_wm"), - ScalarImage("dki_gm"), - ScalarImage("dki_csf")) - - def get_name(self): - return "dki3t" - - -class DkiGmWmInterfaceImage(ImageDefinition): - """ - Define an image of the WM/GM interface. - This is based on WM/GM/CSF probsegs from DKI data. - Typically used for seeding. - - Examples - -------- - api.GroupAFQ(tracking_params={ - "seed_image": DkiGmWmInterfaceImage()}) - """ - def __init__(self): pass def get_name(self): - return "DkiGmWmInterface" + return "ThreeT" def get_image_getter(self, task_name): if task_name == "data": raise ValueError(( - "ScalarImage cannot be used in this context, as they" + "ThreeTImage cannot be used in this context, as they" "require later derivatives to be calculated")) - # Note: in the future, we may want to weight seeding, - # using something like this: - # data[data > 0.5] = 1 - data[data > 0.5] - # Or, weight by gradient like in MRTrix - # (more seeds in places where transition is sharper) - def image_getter(data_imap): - wm_img = nib.load(data_imap["dki_wm"]) - gm_img = nib.load(data_imap["dki_gm"]) - gmwmi = wm_img.get_fdata() - gmwmi[gmwmi == 1] = 0 # exclude pure WM - gmwmi[gm_img.get_fdata() == 0] = 0 # exclude CSF interface - gmwmi = (gmwmi > 0).astype(np.float32) # convert to binary - - return nib.Nifti1Image(gmwmi, wm_img.affine), dict( - FromWM=data_imap["dki_wm"], - FromGM=data_imap["dki_gm"]) - return image_getter + def csf_getter(data_imap): + PVE = nib.load(data_imap["t1w_pve"]) + return nib.Nifti1Image( + PVE.get_fdata()[..., 0].astype(np.float32), + PVE.affine) + + def gm_getter(data_imap): + PVE = nib.load(data_imap["t1w_pve"]) + return nib.Nifti1Image( + PVE.get_fdata()[..., 1].astype(np.float32), + PVE.affine) + + def wm_getter(data_imap): + PVE = nib.load(data_imap["t1w_pve"]) + return nib.Nifti1Image( + PVE.get_fdata()[..., 2].astype(np.float32), + PVE.affine) + + return [wm_getter, gm_getter, csf_getter] class TemplateImage(ImageDefinition): diff --git a/AFQ/models/wmgm_interface.py b/AFQ/models/wmgm_interface.py new file mode 100644 index 000000000..09bf0c461 --- /dev/null +++ b/AFQ/models/wmgm_interface.py @@ -0,0 +1,58 @@ +import numpy as np +import nibabel as nib + +from dipy.align import resample + +from skimage.morphology import remove_small_objects +from scipy.ndimage import gaussian_filter +from skimage.segmentation import find_boundaries + + +def fit_wm_gm_interface(PVE_img, dwiref_img): + """ + Compute the white matter/gray matter interface from a PVE image. + + Parameters + ---------- + PVE_img : Nifti1Image + PVE image containing CSF, GM, and WM segmentations from T1 + dwiref_img : Nifti1Image + Reference image to find boundary in that space. + """ + PVE = PVE_img.get_fdata() + + csf = PVE[..., 0].copy() + gm = PVE[..., 1].copy() + wm = PVE[..., 2].copy() + + # Put in diffusion space + wm = resample( + wm, + dwiref_img.get_fdata(), + moving_affine=PVE_img.affine, + static_affine=dwiref_img.affine).get_fdata() + gm = resample( + gm, + dwiref_img.get_fdata(), + moving_affine=PVE_img.affine, + static_affine=dwiref_img.affine).get_fdata() + csf = resample( + csf, + dwiref_img.get_fdata(), + moving_affine=PVE_img.affine, + static_affine=dwiref_img.affine).get_fdata() + + # 13824 = 2^9*3^3 + # Needs to be enough to + # remove small objects in the skull + wm = remove_small_objects(wm > 0.5, min_size=13824) + + wm_boundary = find_boundaries(wm, mode='inner') + gm_smoothed = gaussian_filter(gm, 1) + csf_smoothed = gaussian_filter(csf, 1) + + wm_boundary[~gm_smoothed.astype(np.bool)] = 0 + wm_boundary[csf_smoothed > gm_smoothed] = 0 + + return nib.Nifti1Image( + wm_boundary.astype(np.float32), dwiref_img.affine) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 6a138cbc6..24a5dc898 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -23,6 +23,8 @@ multi_shell_fiber_response, response_from_mask_msmt) from dipy.core.gradients import unique_bvals_tolerance +from dipy.segment.tissue import TissueClassifierHMRF +from dipy.align import resample from AFQ.tasks.decorators import as_file, as_img, as_fit_deriv from AFQ.tasks.utils import get_fname, with_name, str_to_desc @@ -46,6 +48,7 @@ extract_odf, anisotropic_index, anisotropic_power) from AFQ.models.asym_filtering import unified_filtering from AFQ.models.dam import fit_dam, csf_dam, t1_dam +from AFQ.models.wmgm_interface import fit_wm_gm_interface logger = logging.getLogger('AFQ') @@ -275,6 +278,76 @@ def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): dki_gm_ul=dki_gm_ul) +@immlib.calc("t1w_pve") +@as_file(suffix='_desc-pve_probseg.nii.gz') +def t1w_pve(t1_file, brain_mask, pve_nclass=3, pve_beta=0.1): + """ + Tissue classification using the + Markov Random Fields modeling approach on the T1w image [1, 2] + + Parameters + ---------- + pve_nclass : int, optional + The number of tissue classes to segment + Default: 3 + + pve_beta : float, optional + The beta parameter for the HMRF model (smoothness) + Default: 0.1 + + References + ---------- + [1] Zhang et al., 2001 + Yongyue Zhang, Michael Brady, and Stephen Smith. + "Segmentation of brain MR images through a hidden Markov + random field model and the expectation-maximization algorithm." + IEEE Transactions on Medical Imaging, 20(1):45–57, 2001. + https://doi.org/10.1109/42.906424 + + [2] Avants et al., 2011 + Brian B. Avants, Nicholas J. Tustison, Jue Wu, + Philip A. Cook, and James C. Gee. + "An Open Source Multivariate Framework for n-Tissue + Segmentation with Evaluation on Public Data." + Neuroinformatics, 9(4):381–400, 2011. + https://doi.org/10.1007/s12021-011-9109-y + """ + t1w = nib.load(t1_file) + bm = nib.load(brain_mask) + + bm_in_tw1 = resample( + bm.get_fdata(), + t1w.get_fdata(), + moving_affine=bm.affine, + static_affine=t1w.affine).get_fdata() + + t1w_masked = t1w.get_fdata().copy() + t1w_masked[~bm_in_tw1.astype(np.bool)] = 0 + + hmrf = TissueClassifierHMRF() + logger.info(( + "Generating Tissue Segmentations from T1w image " + "using HMRF, this could take a minute...")) + _, _, PVE = hmrf.classify(t1w_masked, pve_nclass, pve_beta) + + return nib.Nifti1Image(PVE, t1w.affine), dict( + T1wFile=t1_file, + BrainMaskFile=brain_mask, + TissueClasses=pve_nclass, + Beta=pve_beta,) + + +@immlib.calc("wm_gm_interface") +@as_file(suffix='_desc-wmgmi_mask.nii.gz') +def wm_gm_interface(t1w_pve, b0): + PVE_img = nib.load(t1w_pve) + b0_img = nib.load(b0) + + wmgmi_img = fit_wm_gm_interface(PVE_img, b0_img) + + return wmgmi_img, dict(FromPVE=t1w_pve) + + @immlib.calc("dti_tf") def dti_fit(dti_params, gtab): """DTI TensorFit object""" @@ -1483,6 +1556,7 @@ def get_data_plan(kwargs): data_tasks = with_name([ get_data_gtab, b0, b0_mask, brain_mask, + t1w_pve, wm_gm_interface, dam_fit, dam_csf, dam_pseudot1, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, csd_anisotropic_index, diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 7b5f6bd31..3ccf0c7de 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -13,7 +13,7 @@ from AFQ.definitions.utils import Definition import AFQ.tractography.tractography as aft from AFQ.tasks.utils import get_default_args -from AFQ.definitions.image import ScalarImage, DKI3TImage, DkiGmWmInterfaceImage +from AFQ.definitions.image import ScalarImage, ThreeTImage from AFQ.tractography.utils import gen_seeds, get_percentile_threshold from trx.trx_file_memmap import TrxFile @@ -450,11 +450,12 @@ def get_tractography_plan(kwargs): kwargs["tracking_params"]["odf_model"].upper() if kwargs["tracking_params"]["seed_mask"] is None: if kwargs["tracking_params"]["tracker"] == "pft": - kwargs["tracking_params"]["seed_mask"] = DkiGmWmInterfaceImage() + kwargs["tracking_params"]["seed_mask"] = ScalarImage( + "wm_gm_interface") kwargs["tracking_params"]["seed_threshold"] = 0.5 logger.info(( "No seed mask given, using GM-WM interface " - "from 3T prob maps esimated from DKI")) + "from 3T prob maps esimated from T1w")) else: kwargs["tracking_params"]["seed_mask"] = ScalarImage( kwargs["best_scalar"]) @@ -464,20 +465,11 @@ def get_tractography_plan(kwargs): "(or first scalar if none are FA) " "thresholded to 0.2")) if kwargs["tracking_params"]["stop_mask"] is None: - if kwargs["tracking_params"]["tracker"] == "pft": - kwargs["tracking_params"]["stop_threshold"] = "CMC" - kwargs["tracking_params"]["stop_mask"] = DKI3TImage() - logger.info(( - "No stop mask given, using CMC " - "and 3T prob maps esimated from DKI")) - else: - kwargs["tracking_params"]["stop_mask"] = ScalarImage( - kwargs["best_scalar"]) - kwargs["tracking_params"]["stop_threshold"] = 0.2 - logger.info(( - "No stop mask given, using FA " - "(or first scalar if none are FA) " - "thresholded to 0.2")) + kwargs["tracking_params"]["stop_threshold"] = "ACT" + kwargs["tracking_params"]["stop_mask"] = ThreeTImage() + logger.info(( + "No stop mask given, using ACT " + "and 3T prob maps esimated from T1w")) stop_mask = kwargs["tracking_params"]['stop_mask'] seed_mask = kwargs["tracking_params"]['seed_mask'] diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 000000000..16b046bbc --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,4 @@ +## Third-Party Code Attribution +- `AFQ/models/asym_filtering.py` from Scilpy (github.com/scilus/scilpy) + Copyright (c) 2012-- Sherbrooke Connectivity Imaging Lab [SCIL], Université de Sherbrooke. + Used under the MIT License. From 07460b7e66f156b6ff129653c43317c52bacfdce Mon Sep 17 00:00:00 2001 From: 36000 Date: Sun, 29 Jun 2025 19:49:11 -0700 Subject: [PATCH 006/116] ray integration with numba in msmt/aodf fitting --- AFQ/models/asym_filtering.py | 96 +++++++++++++++++++++++++----------- AFQ/models/msmt.py | 86 ++++++++++++++++++++++++++------ AFQ/tasks/data.py | 5 +- AFQ/utils/stats.py | 5 ++ 4 files changed, 145 insertions(+), 47 deletions(-) diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index f9f8970d0..6c2132a25 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -7,11 +7,17 @@ # Replaced with numba import numpy as np -from numba import njit, prange, config +import multiprocessing from tqdm import tqdm + +from numba import njit, prange, config, set_num_threads +import ray + from dipy.reconst.shm import sh_to_sf_matrix from dipy.data import get_sphere +from AFQ.utils.stats import chunk_indices + config.THREADING_LAYER = 'workqueue' @@ -39,7 +45,7 @@ def unified_filtering(sh_data, sphere, sh_basis='descoteaux07', is_legacy=False, sigma_spatial=1.0, sigma_align=0.8, sigma_angle=None, rel_sigma_range=0.2, - win_hwidth=None, exclude_center=False): + n_threads=None, n_cpus=None): """ Unified asymmetric filtering as described in [1]. @@ -68,15 +74,14 @@ def unified_filtering(sh_data, sphere, rel_sigma_range: float or None Standard deviation of the range filter, relative to the range of SF amplitudes. `None` disables range filtering. - disable_spatial: bool, optional - Replace gaussian filter by a mean filter for spatial filter. - The value from `sigma_spatial` is still used for setting the - size of the filtering window. - win_hwidth: int, optional - Half-width of the filtering window. When None, the - filtering window half-width is given by (6*sigma_spatial + 1). - exclude_center: bool, optional - Assign a weight of 0 to the center voxel of the filter. + n_threads: int or None + Number of threads to use for numba. If None, uses + the number of available threads. + Default: None. + n_cpus: int or None + Number of CPUs to use for parallel processing with Ray. + If None, uses the number of available CPUs minus one. + Default: None. References ---------- @@ -84,17 +89,12 @@ def unified_filtering(sh_data, sphere, Estimating Asymmetric Orientation Distribution Functions", Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 """ - if sigma_spatial is None and win_hwidth is None: - raise ValueError('sigma_spatial and win_hwidth cannot both be None') - if isinstance(sphere, str): sphere = get_sphere(name=sphere) if sigma_spatial is not None: if sigma_spatial <= 0.0: raise ValueError('sigma_spatial cannot be <= 0.') - # calculate half-width from sigma_spatial - half_width = int(round(3 * sigma_spatial)) if sigma_align is not None: if sigma_align <= 0.0: raise ValueError('sigma_align cannot be <= 0.') @@ -102,12 +102,13 @@ def unified_filtering(sh_data, sphere, if sigma_angle <= 0.0: raise ValueError('sigma_align cannot be <= 0.') - sh_order, full_basis = _get_sh_order_and_fullness(sh_data.shape[-1]) + if n_threads is not None: + set_num_threads(n_threads) - # overwrite half-width if win_hwidth is supplied - if win_hwidth is not None: - half_width = win_hwidth - filter_shape = (half_width * 2 + 1, half_width * 2 + 1, half_width * 2 + 1) + if n_cpus is None: + n_cpus = multiprocessing.cpu_count() - 1 + + sh_order, full_basis = _get_sh_order_and_fullness(sh_data.shape[-1]) # build filters uv_filter = _unified_filter_build_uv(sigma_angle, @@ -130,7 +131,8 @@ def unified_filtering(sh_data, sphere, return _unified_filter_call_python( sh_data, nx_filter, uv_filter, - sigma_range, B, B_inv, sphere) + sigma_range, B, B_inv, sphere, + n_cpus) @njit(fastmath=True, cache=True) @@ -243,7 +245,7 @@ def _get_sf_range(sh_data, B_mat): def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, - B_mat, B_inv, sphere): + B_mat, B_inv, sphere, n_cpus): """ Run filtering using pure python implementation. @@ -263,6 +265,8 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, SF to SH projection matrix. sphere: DIPY sphere Sphere for SH to SF projection. + n_cpus: int + Number of CPUs to use for parallel processing with Ray. Returns ------- @@ -283,11 +287,47 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, ), dtype=np.float64) # Apply filter to each sphere vertice - for u_sph_id in tqdm(range(nb_sf)): - - mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, - nx_filter, uv_filter, - sigma_range, u_sph_id, B_mat) + if n_cpus > 1: + ray.init(ignore_reinit_error=True) + + sh_data_id = ray.put(sh_data) + sh_data_padded_id = ray.put(sh_data_padded) + nx_filter_id = ray.put(nx_filter) + uv_filter_id = ray.put(uv_filter) + B_mat_id = ray.put(B_mat) + + @ray.remote(num_cpus=n_cpus) + def _correlate_batch_remote(batch_u_ids, sh_data, sh_data_padded, + nx_filter, uv_filter, sigma_range, B_mat): + results = [] + for u_sph_id in batch_u_ids: + corr = _correlate( + sh_data, sh_data_padded, nx_filter, + uv_filter, sigma_range, u_sph_id, B_mat + ) + results.append((u_sph_id, corr)) + return results + + all_u_ids = list(range(nb_sf)) + futures = [ + _correlate_batch_remote.remote( + batch, sh_data_id, sh_data_padded_id, + nx_filter_id, uv_filter_id, + sigma_range, B_mat_id + ) + for batch in chunk_indices(all_u_ids, n_cpus * 2) + ] + + # Gather and write results + for future in tqdm(futures): + batch_results = ray.get(future) + for u_sph_id, correlation in batch_results: + mean_sf[..., u_sph_id] = correlation + else: + for u_sph_id in tqdm(range(nb_sf)): + mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, + nx_filter, uv_filter, + sigma_range, u_sph_id, B_mat) out_sh = np.array([np.dot(i, B_inv) for i in mean_sf], dtype=sh_data.dtype) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 70f4c3423..331539c7d 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -1,9 +1,16 @@ +import multiprocessing import numpy as np + from scipy.optimize import minimize + from numba import njit, prange, config, set_num_threads from tqdm import tqdm +import ray from dipy.reconst.mcsd import MSDeconvFit +from dipy.reconst.mcsd import MultiShellDeconvModel + +from AFQ.utils.stats import chunk_indices config.THREADING_LAYER = 'workqueue' @@ -221,10 +228,14 @@ def find_analytic_center(A, b, x0): @njit(parallel=True, fastmath=True) -def _process_slice(slice_data, slice_mask, slice_results, +def _process_slice(slice_data, slice_mask, Rt, R_pinv, G, A, b, x0, max_iter, tol, use_chol): + results = np.zeros( + slice_data.shape[:2] + (A.shape[1],), + dtype=np.float64) + for j in prange(slice_data.shape[0]): x_prev = x0.copy() for k in range(slice_data.shape[1]): @@ -239,19 +250,23 @@ def _process_slice(slice_data, slice_mask, slice_results, max_iter, tol, use_chol) if success: - slice_results[j, k] = x_prev + results[j, k] = x_prev else: - slice_results[j, k] = np.zeros(A.shape[1]) + results[j, k] = np.zeros(A.shape[1]) else: - slice_results[j, k] = np.zeros(A.shape[1]) + results[j, k] = np.zeros(A.shape[1]) + return results -def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, - n_threads=None, use_chol=False): +def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, + n_threads=None, n_cpus=1, use_chol=False): # Note cholesky is ~50% slower but more robust if n_threads is not None: set_num_threads(n_threads) + if n_cpus is None: + n_cpus = multiprocessing.cpu_count() - 1 + m, n = self.fitter._reg.shape coeff = np.zeros((*data.shape[:3], n), dtype=np.float64) if mask is None: @@ -281,15 +296,54 @@ def fit(self, data, mask=None, max_iter=1e6, tol=1e-6, b = np.ascontiguousarray(b, dtype=np.float64) x0 = np.ascontiguousarray(x0, dtype=np.float64) - for ii in tqdm(range(data.shape[0])): - _process_slice( - data[ii], mask[ii], coeff[ii], - Rt, - R_pinv, - Q, - A, - b, - x0, - max_iter, tol, use_chol) + if n_cpus > 1: + ray.init(ignore_reinit_error=True) + + data_id = ray.put(data) + mask_id = ray.put(mask) + Rt_id = ray.put(Rt) + R_pinv_id = ray.put(R_pinv) + Q_id = ray.put(Q) + A_id = ray.put(A) + b_id = ray.put(b) + x0_id = ray.put(x0) + + @ray.remote(num_cpus=n_cpus) + def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, + Q, A, b, x0, max_iter, tol, use_chol): + return [_process_slice( + data[ii], mask[ii], Rt, R_pinv, Q, A, b, x0, max_iter, tol, use_chol + ) for ii in batch_indices] + + # Launch tasks in chunks + all_indices = list(range(data.shape[0])) + futures = [ + process_batch_remote.remote(batch, data_id, mask_id, + Rt_id, R_pinv_id, + Q_id, A_id, b_id, x0_id, + max_iter, tol, use_chol) + for batch in chunk_indices(all_indices, n_cpus * 2) + ] + + # Collect and assign results + for batch, future in zip( + chunk_indices(all_indices, n_cpus * 2), tqdm(futures)): + results = ray.get(future) + for i, ii in enumerate(batch): + coeff[ii] = results[i] + else: + for ii in tqdm(range(data.shape[0])): + coeff[ii] = _process_slice( + data[ii], mask[ii], + Rt, + R_pinv, + Q, + A, + b, + x0, + max_iter, tol, use_chol) return MSDeconvFit(self, coeff, None) + + +MultiShellDeconvModel.fit = _fit diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 24a5dc898..11e614f20 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -18,7 +18,6 @@ from dipy.reconst import shm from dipy.reconst.dki_micro import axonal_water_fraction from dipy.reconst.mcsd import ( - MultiShellDeconvModel, mask_for_response_msmt, multi_shell_fiber_response, response_from_mask_msmt) @@ -43,7 +42,7 @@ from AFQ.models.dki import dki_csf, dki_gm, dki_wm from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model -from AFQ.models.msmt import fit as msmt_fit +from AFQ.models.msmt import MultiShellDeconvModel from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) from AFQ.models.asym_filtering import unified_filtering @@ -584,7 +583,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") - mcsd_fit = msmt_fit(mcsd_model, data, mask) + mcsd_fit = mcsd_model.fit(data, mask) meta = dict( SphericalHarmonicDegree=msmt_sh_order, diff --git a/AFQ/utils/stats.py b/AFQ/utils/stats.py index 66e5e653b..3450e126a 100644 --- a/AFQ/utils/stats.py +++ b/AFQ/utils/stats.py @@ -1,3 +1,8 @@ +def chunk_indices(indices, batch_size): + for i in range(0, len(indices), batch_size): + yield indices[i:i + batch_size] + + def contrast_index(x1, x2, double=True): """ Calculate the contrast index between two arrays. From bcadea825424fe37a0fd77f3f209c6f2cd3a8a73 Mon Sep 17 00:00:00 2001 From: 36000 Date: Sun, 29 Jun 2025 20:33:33 -0700 Subject: [PATCH 007/116] Add MSMT tests --- AFQ/models/msmt.py | 6 +- AFQ/tests/test_msmt.py | 458 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 463 insertions(+), 1 deletion(-) create mode 100644 AFQ/tests/test_msmt.py diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 331539c7d..83dd568a7 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -267,6 +267,10 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, if n_cpus is None: n_cpus = multiprocessing.cpu_count() - 1 + og_data_shape = data.shape + if len(data.shape) < 4: + data = data.reshape((1,) * (4 - data.ndim) + data.shape) + m, n = self.fitter._reg.shape coeff = np.zeros((*data.shape[:3], n), dtype=np.float64) if mask is None: @@ -281,7 +285,6 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, A[i] /= np.linalg.norm(A[i]) Q = R.T @ R - # Q += 1e-1 * np.eye(n) # Strong Regularization # TODO x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) x0 = find_analytic_center(A, b, x0) @@ -343,6 +346,7 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, x0, max_iter, tol, use_chol) + coeff = coeff.reshape(og_data_shape[:-1] + (n,)) return MSDeconvFit(self, coeff, None) diff --git a/AFQ/tests/test_msmt.py b/AFQ/tests/test_msmt.py new file mode 100644 index 000000000..fedafa57d --- /dev/null +++ b/AFQ/tests/test_msmt.py @@ -0,0 +1,458 @@ +# Original source: github.com/dipy/dipy +# Copyright (c) 2008-2025, dipy developers. +# Licensed under the 3-clause BSD license +# Modified by John Kruper for numba MSMT testing +# Will ultimately be moved upstream to DIPY + +import warnings + +import numpy as np +import numpy.testing as npt + +from dipy.core.gradients import GradientTable +from dipy.data import default_sphere, get_3shell_gtab +from dipy.reconst import shm +from dipy.reconst.mcsd import ( + auto_response_msmt, + mask_for_response_msmt, + multi_shell_fiber_response, + response_from_mask_msmt, +) +from AFQ.models.msmt import MultiShellDeconvModel +from dipy.sims.voxel import add_noise, multi_tensor, single_tensor +from dipy.testing.decorators import set_random_number_generator + +wm_response = np.array( + [ + [1.7e-3, 0.4e-3, 0.4e-3, 25.0], + [1.7e-3, 0.4e-3, 0.4e-3, 25.0], + [1.7e-3, 0.4e-3, 0.4e-3, 25.0], + ] +) +csf_response = np.array( + [ + [3.0e-3, 3.0e-3, 3.0e-3, 100.0], + [3.0e-3, 3.0e-3, 3.0e-3, 100.0], + [3.0e-3, 3.0e-3, 3.0e-3, 100.0], + ] +) +gm_response = np.array( + [ + [4.0e-4, 4.0e-4, 4.0e-4, 40.0], + [4.0e-4, 4.0e-4, 4.0e-4, 40.0], + [4.0e-4, 4.0e-4, 4.0e-4, 40.0], + ] +) + + +def get_test_data(rng): + gtab = get_3shell_gtab() + evals_list = [ + np.array([1.7e-3, 0.4e-3, 0.4e-3]), + np.array([6.0e-4, 4.0e-4, 4.0e-4]), + np.array([3.0e-3, 3.0e-3, 3.0e-3]), + ] + s0 = [0.8, 1, 4] + signals = [single_tensor(gtab, x[0], evals=x[1]) for x in zip(s0, evals_list)] + tissues = [0, 0, 2, 0, 1, 0, 0, 1, 2] # wm=0, gm=1, csf=2 + data = [add_noise(signals[tissue], 80, s0[0], rng=rng) for tissue in tissues] + data = np.asarray(data).reshape((3, 3, 1, len(signals[0]))) + tissues = np.asarray(tissues).reshape((3, 3, 1)) + masks = [np.where(tissues == x, 1, 0) for x in range(3)] + responses = [np.concatenate((x[0], [x[1]])) for x in zip(evals_list, s0)] + return gtab, data, masks, responses + + +def _expand(m, iso, coeff): + params = np.zeros(len(m)) + params[m == 0] = coeff[iso:] + params = np.concatenate([coeff[:iso], params]) + return params + + +def test_mcsd_model_delta(): + sh_order_max = 8 + gtab = get_3shell_gtab() + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, [0, 1000, 2000, 3500], wm_response, gm_response, csf_response + ) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + model = MultiShellDeconvModel(gtab, response) + iso = response.iso + + theta, phi = default_sphere.theta, default_sphere.phi + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + B = shm.real_sh_descoteaux_from_index( + response.m_values, response.l_values, theta[:, None], phi[:, None] + ) + + wm_delta = model.delta.copy() + # set isotropic components to zero + wm_delta[:iso] = 0.0 + wm_delta = _expand(model.m_values, iso, wm_delta) + + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + for i, s in enumerate([0, 1000, 2000, 3500]): + g = GradientTable(default_sphere.vertices * s) + signal = model.predict(wm_delta, gtab=g) + expected = np.dot(response.response[i, iso:], B.T) + npt.assert_array_almost_equal(signal, expected) + + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + signal = model.predict(wm_delta, gtab=gtab) + fit = model.fit(signal) + m = model.m_values + npt.assert_array_almost_equal(fit.shm_coeff[m != 0], 0.0, 2) + + +def test_MultiShellDeconvModel_response(): + gtab = get_3shell_gtab() + + sh_order_max = 8 + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, [0, 1000, 2000, 3500], wm_response, gm_response, csf_response + ) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + model_1 = MultiShellDeconvModel(gtab, response, sh_order_max=sh_order_max) + responses = np.array([wm_response, gm_response, csf_response]) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + model_2 = MultiShellDeconvModel(gtab, responses, sh_order_max=sh_order_max) + response_1 = model_1.response.response + response_2 = model_2.response.response + npt.assert_array_almost_equal(response_1, response_2, 0) + + npt.assert_raises(ValueError, MultiShellDeconvModel, gtab, np.ones((4, 3, 4))) + npt.assert_raises( + ValueError, MultiShellDeconvModel, gtab, np.ones((3, 3, 4)), iso=3 + ) + + +def test_MultiShellDeconvModel(): + gtab = get_3shell_gtab() + + mevals = np.array([wm_response[0, :3], wm_response[0, :3]]) + angles = [(0, 0), (60, 0)] + + S_wm, sticks = multi_tensor( + gtab, + mevals, + S0=wm_response[0, 3], + angles=angles, + fractions=[30.0, 70.0], + snr=None, + ) + S_gm = gm_response[0, 3] * np.exp(-gtab.bvals * gm_response[0, 0]) + S_csf = csf_response[0, 3] * np.exp(-gtab.bvals * csf_response[0, 0]) + + sh_order_max = 8 + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, [0, 1000, 2000, 3500], wm_response, gm_response, csf_response + ) + model = MultiShellDeconvModel(gtab, response) + vf = [0.325, 0.2, 0.475] + signal = sum(i * j for i, j in zip(vf, [S_csf, S_gm, S_wm])) + fit = model.fit(signal) + + # Testing both ways to predict + S_pred_fit = fit.predict() + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + S_pred_model = model.predict(fit.all_shm_coeff) + + npt.assert_array_almost_equal(S_pred_fit, S_pred_model, 0) + npt.assert_array_almost_equal(S_pred_fit, signal, 0) + + +def test_MSDeconvFit(): + gtab = get_3shell_gtab() + + mevals = np.array([wm_response[0, :3], wm_response[0, :3]]) + angles = [(0, 0), (60, 0)] + + S_wm, sticks = multi_tensor( + gtab, + mevals, + S0=wm_response[0, 3], + angles=angles, + fractions=[30.0, 70.0], + snr=None, + ) + S_gm = gm_response[0, 3] * np.exp(-gtab.bvals * gm_response[0, 0]) + S_csf = csf_response[0, 3] * np.exp(-gtab.bvals * csf_response[0, 0]) + + sh_order_max = 8 + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, [0, 1000, 2000, 3500], wm_response, gm_response, csf_response + ) + model = MultiShellDeconvModel(gtab, response) + vf = [0.325, 0.2, 0.475] + signal = sum(i * j for i, j in zip(vf, [S_csf, S_gm, S_wm])) + fit = model.fit(signal) + + # Testing volume fractions + npt.assert_array_almost_equal(fit.volume_fractions, vf, 1) + + +def test_multi_shell_fiber_response(): + sh_order_max = 8 + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, [0, 1000, 2000, 3500], wm_response, gm_response, csf_response + ) + + npt.assert_equal(response.response.shape, (4, 7)) + + btens = ["LTE", "PTE", "STE", "CTE"] + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=shm.descoteaux07_legacy_msg, + category=PendingDeprecationWarning, + ) + response = multi_shell_fiber_response( + sh_order_max, + [0, 1000, 2000, 3500], + wm_response, + gm_response, + csf_response, + btens=btens, + ) + + npt.assert_equal(response.response.shape, (4, 7)) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", category=PendingDeprecationWarning) + response = multi_shell_fiber_response( + sh_order_max, [1000, 2000, 3500], wm_response, gm_response, csf_response + ) + # Test that the number of warnings raised is greater than 1, with + # deprecation warnings being raised from using legacy SH bases as well + # as a warning from multi_shell_fiber_response + npt.assert_(len(w) > 1) + # The last warning in list is the one from multi_shell_fiber_response + npt.assert_(issubclass(w[-1].category, UserWarning)) + npt.assert_("""No b0 given. Proceeding either way.""" in str(w[-1].message)) + npt.assert_equal(response.response.shape, (3, 7)) + + +@set_random_number_generator() +def test_mask_for_response_msmt(rng): + gtab, data, masks_gt, _ = get_test_data(rng) + + with warnings.catch_warnings(record=True) as w: + wm_mask, gm_mask, csf_mask = mask_for_response_msmt( + gtab, + data, + roi_center=None, + roi_radii=(1, 1, 0), + wm_fa_thr=0.7, + gm_fa_thr=0.3, + csf_fa_thr=0.15, + gm_md_thr=0.001, + csf_md_thr=0.0032, + ) + + npt.assert_equal(len(w), 1) + npt.assert_(issubclass(w[0].category, UserWarning)) + npt.assert_("""Some b-values are higher than 1200.""" in str(w[0].message)) + + # Verifies that masks are not empty: + masks_sum = int(np.sum(wm_mask) + np.sum(gm_mask) + np.sum(csf_mask)) + npt.assert_equal(masks_sum != 0, True) + + npt.assert_array_almost_equal(masks_gt[0], wm_mask) + npt.assert_array_almost_equal(masks_gt[1], gm_mask) + npt.assert_array_almost_equal(masks_gt[2], csf_mask) + + +@set_random_number_generator() +def test_mask_for_response_msmt_nvoxels(rng): + gtab, data, _, _ = get_test_data(rng) + + with warnings.catch_warnings(record=True) as w: + wm_mask, gm_mask, csf_mask = mask_for_response_msmt( + gtab, + data, + roi_center=None, + roi_radii=(1, 1, 0), + wm_fa_thr=0.7, + gm_fa_thr=0.3, + csf_fa_thr=0.15, + gm_md_thr=0.001, + csf_md_thr=0.0032, + ) + + npt.assert_equal(len(w), 1) + npt.assert_(issubclass(w[0].category, UserWarning)) + npt.assert_("""Some b-values are higher than 1200.""" in str(w[0].message)) + + wm_nvoxels = np.sum(wm_mask) + gm_nvoxels = np.sum(gm_mask) + csf_nvoxels = np.sum(csf_mask) + npt.assert_equal(wm_nvoxels, 5) + npt.assert_equal(gm_nvoxels, 2) + npt.assert_equal(csf_nvoxels, 2) + + with warnings.catch_warnings(record=True) as w: + wm_mask, gm_mask, csf_mask = mask_for_response_msmt( + gtab, + data, + roi_center=None, + roi_radii=(1, 1, 0), + wm_fa_thr=1, + gm_fa_thr=0, + csf_fa_thr=0, + gm_md_thr=0, + csf_md_thr=0, + ) + npt.assert_equal(len(w), 6) + npt.assert_(issubclass(w[0].category, UserWarning)) + npt.assert_("""Some b-values are higher than 1200.""" in str(w[0].message)) + npt.assert_("No voxel with a FA higher than 1 were found" in str(w[1].message)) + npt.assert_("No voxel with a FA lower than 0 were found" in str(w[2].message)) + npt.assert_("No voxel with a MD lower than 0 were found" in str(w[3].message)) + npt.assert_("No voxel with a FA lower than 0 were found" in str(w[4].message)) + npt.assert_("No voxel with a MD lower than 0 were found" in str(w[5].message)) + + wm_nvoxels = np.sum(wm_mask) + gm_nvoxels = np.sum(gm_mask) + csf_nvoxels = np.sum(csf_mask) + npt.assert_equal(wm_nvoxels, 0) + npt.assert_equal(gm_nvoxels, 0) + npt.assert_equal(csf_nvoxels, 0) + + +@set_random_number_generator() +def test_response_from_mask_msmt(rng): + gtab, data, masks_gt, responses_gt = get_test_data(rng) + + response_wm, response_gm, response_csf = response_from_mask_msmt( + gtab, data, masks_gt[0], masks_gt[1], masks_gt[2], tol=20 + ) + + # Verifying that csf's response is greater than gm's + npt.assert_equal(np.sum(response_csf[:, :3]) > np.sum(response_gm[:, :3]), True) + # Verifying that csf and gm are described by spheres + npt.assert_almost_equal(response_csf[:, 1], response_csf[:, 2]) + npt.assert_allclose(response_csf[:, 0], response_csf[:, 1], rtol=1, atol=0) + npt.assert_almost_equal(response_gm[:, 1], response_gm[:, 2]) + npt.assert_allclose(response_gm[:, 0], response_gm[:, 1], rtol=1, atol=0) + # Verifying that wm is anisotropic in one direction + npt.assert_almost_equal(response_wm[:, 1], response_wm[:, 2]) + npt.assert_equal(response_wm[:, 0] > 2.5 * response_wm[:, 1], True) + + # Verifying with ground truth for the first bvalue + npt.assert_array_almost_equal(response_wm[0], responses_gt[0], 1) + npt.assert_array_almost_equal(response_gm[0], responses_gt[1], 1) + npt.assert_array_almost_equal(response_csf[0], responses_gt[2], 1) + + +@set_random_number_generator() +def test_auto_response_msmt(rng): + gtab, data, _, _ = get_test_data(rng) + + with warnings.catch_warnings(record=True) as w: + response_auto_wm, response_auto_gm, response_auto_csf = auto_response_msmt( + gtab, + data, + tol=20, + roi_center=None, + roi_radii=(1, 1, 0), + wm_fa_thr=0.7, + gm_fa_thr=0.3, + csf_fa_thr=0.15, + gm_md_thr=0.001, + csf_md_thr=0.0032, + ) + + npt.assert_(issubclass(w[0].category, UserWarning)) + npt.assert_( + """Some b-values are higher than 1200. + The DTI fit might be affected. It is advised to use + mask_for_response_msmt with bvalues lower than 1200, followed by + response_from_mask_msmt with all bvalues to overcome this.""" + in str(w[0].message) + ) + + mask_wm, mask_gm, mask_csf = mask_for_response_msmt( + gtab, + data, + roi_center=None, + roi_radii=(1, 1, 0), + wm_fa_thr=0.7, + gm_fa_thr=0.3, + csf_fa_thr=0.15, + gm_md_thr=0.001, + csf_md_thr=0.0032, + ) + + response_from_mask_wm, response_from_mask_gm, response_from_mask_csf = ( + response_from_mask_msmt(gtab, data, mask_wm, mask_gm, mask_csf, tol=20) + ) + + npt.assert_array_equal(response_auto_wm, response_from_mask_wm) + npt.assert_array_equal(response_auto_gm, response_from_mask_gm) + npt.assert_array_equal(response_auto_csf, response_from_mask_csf) From 585e7e93aa0be7051ee2a11c9f708b4f7ad1c809 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 30 Jun 2025 16:42:02 -0700 Subject: [PATCH 008/116] better cleaning in mahal and forest for endpoints --- AFQ/recognition/cleaning.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 49373b005..3a45c43b4 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -69,7 +69,7 @@ def clean_by_orientation(streamlines, primary_axis, affine, tol=None): def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, length_threshold=4, min_sl=20, stat='mean', - return_idx=False): + core_only=True, return_idx=False): """ Clean a segmented fiber group based on the Mahalnobis distance of each streamline @@ -97,6 +97,13 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, stat : callable or str, optional. The statistic of each node relative to which the Mahalanobis is calculated. Default: `np.mean` (but can also use median, etc.) + core_only : bool, optional + If True, only the core of the bundle is used for cleaning. + The core is defined as the middle 60% of each streamline. + This means streamlines are allowed to deviate in the starting + and ending 20% of the bundle. This is useful for allowing more + diverse endpoints. + Default: True. return_idx : bool Whether to return indices in the original streamlines. Default: False. @@ -127,6 +134,8 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, # Resample once up-front: fgarray = np.asarray(abu.resample_tg(streamlines, n_points)) + if core_only: + fgarray = fgarray[:, 20:80, :] # Crop to middle 60% # Keep this around, so you can use it for indexing at the very end: idx = np.arange(len(fgarray)) @@ -238,11 +247,17 @@ def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, # Resample once up-front: fgarray = np.asarray(abu.resample_tg(streamlines, n_points)) - fgarray_flat = fgarray.reshape((-1, 3)) + fgarray_dists = np.zeros_like(fgarray) + fgarray_dists[:, 1:, :] = fgarray[:, 1:, :] - fgarray[:, :-1, :] + fgarray_dists[:, 0, :] = fgarray_dists[:, 1, :] + X_ = np.concatenate(( + fgarray.reshape((-1, 3)), + fgarray_dists.reshape((-1, 3))), + axis=1) idx = np.arange(len(fgarray)) lof = IsolationForest(n_jobs=n_jobs) - outliers = lof.fit_predict(fgarray_flat) + outliers = lof.fit_predict(X_) outliers = outliers.reshape(fgarray.shape[:2]) outliers = np.sum(outliers == -1, axis=1) From dad6c254bbd6ac1596beca80d6528102dd6d9df6 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 30 Jun 2025 17:01:53 -0700 Subject: [PATCH 009/116] bf --- AFQ/recognition/cleaning.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 3a45c43b4..0d4e15882 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -135,7 +135,9 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, # Resample once up-front: fgarray = np.asarray(abu.resample_tg(streamlines, n_points)) if core_only: - fgarray = fgarray[:, 20:80, :] # Crop to middle 60% + fgarray = fgarray[:, + int(n_points * 0.2):int(n_points * 0.8), + :] # Crop to middle 60% # Keep this around, so you can use it for indexing at the very end: idx = np.arange(len(fgarray)) From 3195c0e7fa8678bf38fadbcf243fa5d77ce98d7b Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 1 Jul 2025 16:57:53 -0700 Subject: [PATCH 010/116] bf --- AFQ/models/dki.py | 6 +++--- AFQ/tasks/data.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AFQ/models/dki.py b/AFQ/models/dki.py index 0d4fcce72..c03978236 100644 --- a/AFQ/models/dki.py +++ b/AFQ/models/dki.py @@ -360,7 +360,7 @@ def predict(params_file, gtab, S0_file=None, out_dir=None): return fname -def dki_csf(dki_md_data): +def fit_dki_csf(dki_md_data): """ CSF probability map from DKI MD inspired by [1] @@ -410,7 +410,7 @@ def dki_csf(dki_md_data): return dki_md_data, main_peak_val, peak_sigma -def dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul): +def fit_dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul): """ WM probability map from DKI FA @@ -436,7 +436,7 @@ def dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul): return wm_data -def dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul): +def fit_dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul): """ GM probability map from DKI FA diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 11e614f20..11b0dc79a 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -39,7 +39,7 @@ from AFQ.models.csd import _fit as csd_fit_model from AFQ.models.csd import CsdNanResponseError from AFQ.models.dki import _fit as dki_fit_model -from AFQ.models.dki import dki_csf, dki_gm, dki_wm +from AFQ.models.dki import fit_dki_csf, fit_dki_gm, fit_dki_wm from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model from AFQ.models.msmt import MultiShellDeconvModel @@ -213,7 +213,7 @@ def dki_csf(dki_md): """ dki_md_data = nib.load(dki_md).get_fdata() - dki_md_data, main_peak_val, peak_sigma = dki_csf(dki_md_data) + dki_md_data, main_peak_val, peak_sigma = fit_dki_csf(dki_md_data) return dki_md_data, dict( DKI_MD_source=dki_md, @@ -240,7 +240,7 @@ def dki_wm(dki_fa, dki_wm_ll=0.1, dki_gm_ul=0.3): """ dki_fa_data = nib.load(dki_fa).get_fdata() - wm_data = dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul) + wm_data = fit_dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul) return wm_data, dict( DKI_FA_source=dki_fa, @@ -268,7 +268,7 @@ def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): dki_fa_data = nib.load(dki_fa).get_fdata() dki_csf_data = nib.load(dki_csf).get_fdata() - gm_data = dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul) + gm_data = fit_dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul) return gm_data, dict( DKI_FA_source=dki_fa, From 54f5fb13c88b41669881a2e1f00c93b70ca445ba Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 1 Jul 2025 18:39:51 -0700 Subject: [PATCH 011/116] control over cpus in msmt --- AFQ/models/msmt.py | 2 +- AFQ/tasks/data.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 83dd568a7..494e3462b 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -259,7 +259,7 @@ def _process_slice(slice_data, slice_mask, def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, - n_threads=None, n_cpus=1, use_chol=False): + n_threads=None, n_cpus=None, use_chol=False): # Note cholesky is ~50% slower but more robust if n_threads is not None: set_num_threads(n_threads) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 11b0dc79a..7d22af542 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -534,7 +534,8 @@ def msdki_msk(msdki_tf): def msmt_params(brain_mask, gtab, data, dki_wm, dki_gm, dki_csf, msmt_sh_order=8, - msmt_fa_thr=0.7): + msmt_fa_thr=0.7, + ray_n_cpus=None): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -548,6 +549,9 @@ def msmt_params(brain_mask, gtab, data, The threshold on the FA used to calculate the multi shell auto response. Can be useful to reduce for baby subjects. Default: 0.7 + ray_n_cpus : float, optional. + The number of CPUs to use for the MSMT CSD fit. + Default: None References ---------- @@ -583,7 +587,8 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") - mcsd_fit = mcsd_model.fit(data, mask) + mcsd_fit = mcsd_model.fit( + data, mask, n_cpus=ray_n_cpus) meta = dict( SphericalHarmonicDegree=msmt_sh_order, From 462607a2ee92207f975bfd81f11feed072530745 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 1 Jul 2025 19:29:21 -0700 Subject: [PATCH 012/116] bf --- AFQ/utils/stats.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AFQ/utils/stats.py b/AFQ/utils/stats.py index 3450e126a..a146f5bc1 100644 --- a/AFQ/utils/stats.py +++ b/AFQ/utils/stats.py @@ -1,4 +1,5 @@ -def chunk_indices(indices, batch_size): +def chunk_indices(indices, num_batches): + batch_size = (len(indices) + num_batches - 1) // num_batches for i in range(0, len(indices), batch_size): yield indices[i:i + batch_size] From bcf00de3b04c01c1086b68f389024d3f12011e0c Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 2 Jul 2025 00:02:31 -0700 Subject: [PATCH 013/116] testing --- AFQ/tasks/data.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 7d22af542..5607da855 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -535,7 +535,8 @@ def msmt_params(brain_mask, gtab, data, dki_wm, dki_gm, dki_csf, msmt_sh_order=8, msmt_fa_thr=0.7, - ray_n_cpus=None): + ray_n_cpus=None, + numba_n_threads=None): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -549,9 +550,13 @@ def msmt_params(brain_mask, gtab, data, The threshold on the FA used to calculate the multi shell auto response. Can be useful to reduce for baby subjects. Default: 0.7 - ray_n_cpus : float, optional. + ray_n_cpus : int, optional. The number of CPUs to use for the MSMT CSD fit. Default: None + numba_n_threads : int, optional. + The number of threads to use for the MSMT CSD fit. + Default: None, which will use the default number of threads + for the system. References ---------- @@ -588,7 +593,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data, mask, n_cpus=ray_n_cpus) + data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads) meta = dict( SphericalHarmonicDegree=msmt_sh_order, From 9953cacd42a9aa24935966ea756e7588d3d359e2 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 2 Jul 2025 09:52:07 -0700 Subject: [PATCH 014/116] try threading layer control --- AFQ/models/asym_filtering.py | 5 +---- AFQ/models/msmt.py | 5 +---- AFQ/tasks/data.py | 12 ++++++++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index 6c2132a25..d68a472db 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -10,7 +10,7 @@ import multiprocessing from tqdm import tqdm -from numba import njit, prange, config, set_num_threads +from numba import njit, prange, set_num_threads import ray from dipy.reconst.shm import sh_to_sf_matrix @@ -19,9 +19,6 @@ from AFQ.utils.stats import chunk_indices -config.THREADING_LAYER = 'workqueue' - - __all__ = ["unified_filtering"] diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 494e3462b..9fef6c545 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -3,7 +3,7 @@ from scipy.optimize import minimize -from numba import njit, prange, config, set_num_threads +from numba import njit, prange, set_num_threads from tqdm import tqdm import ray @@ -13,9 +13,6 @@ from AFQ.utils.stats import chunk_indices -config.THREADING_LAYER = 'workqueue' - - __all__ = ["fit"] diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 5607da855..374b8d13b 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -45,7 +45,6 @@ from AFQ.models.msmt import MultiShellDeconvModel from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) -from AFQ.models.asym_filtering import unified_filtering from AFQ.models.dam import fit_dam, csf_dam, t1_dam from AFQ.models.wmgm_interface import fit_wm_gm_interface @@ -536,7 +535,8 @@ def msmt_params(brain_mask, gtab, data, msmt_sh_order=8, msmt_fa_thr=0.7, ray_n_cpus=None, - numba_n_threads=None): + numba_n_threads=None, + numba_threading_layer="workqueue"): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -557,6 +557,9 @@ def msmt_params(brain_mask, gtab, data, The number of threads to use for the MSMT CSD fit. Default: None, which will use the default number of threads for the system. + numba_threading_layer : str, optional. + The threading layer to use for Numba. + Default: "workqueue". References ---------- @@ -565,6 +568,9 @@ def msmt_params(brain_mask, gtab, data, deconvolution for improved analysis of multi-shell diffusion MRI data. NeuroImage, 103 (2014), pp. 411–426 """ + from numba import config + config.THREADING_LAYER = numba_threading_layer + mask =\ nib.load(brain_mask).get_fdata() @@ -630,6 +636,8 @@ def msmt_aodf(msmtcsd_params): Estimating Asymmetric Orientation Distribution Functions", Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 """ + from AFQ.models.asym_filtering import unified_filtering + sh_coeff = nib.load(msmtcsd_params).get_fdata() logger.info("Applying unified filtering to MSMT CSD ODFs...") From 24fc53df91f7869423b9c22449408df9be892124 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 2 Jul 2025 10:02:16 -0700 Subject: [PATCH 015/116] suppress unavoidable warning --- AFQ/models/msmt.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 9fef6c545..c070f3f7d 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -1,9 +1,11 @@ import multiprocessing +import warnings import numpy as np from scipy.optimize import minimize from numba import njit, prange, set_num_threads +from numba.core.errors import NumbaPerformanceWarning from tqdm import tqdm import ray @@ -296,6 +298,8 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, b = np.ascontiguousarray(b, dtype=np.float64) x0 = np.ascontiguousarray(x0, dtype=np.float64) + warnings.simplefilter('ignore', category=NumbaPerformanceWarning) + if n_cpus > 1: ray.init(ignore_reinit_error=True) From 21b1d16a96de0b50d96fffca8f97c1486fa7dc48 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 4 Jul 2025 13:58:50 -0700 Subject: [PATCH 016/116] wip --- AFQ/models/msmt.py | 51 +++++++++++++++++++++++++++++++++++++++++----- AFQ/tasks/data.py | 15 ++++++++------ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index c070f3f7d..527181a19 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -4,7 +4,7 @@ from scipy.optimize import minimize -from numba import njit, prange, set_num_threads +from numba import njit, prange, set_num_threads, config from numba.core.errors import NumbaPerformanceWarning from tqdm import tqdm import ray @@ -78,7 +78,7 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, tau = 0.9 shur_regularization = 1e-6 - for _ in range(max_iter): + for ii in range(max_iter): mu = (y @ l) / m # Predictor step @@ -88,12 +88,16 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, rhs1l = -(G @ x - A.T @ l + c) rhs1 = rhs1l + A.T @ (Z * rhs2) - schur = G + A.T @ np.diag(Z) @ A - schur += np.eye(schur.shape[0]) * shur_regularization if use_chol: + schur = G + A.T @ np.diag(Z) @ A + schur += np.eye(schur.shape[0]) * shur_regularization + L = np.linalg.cholesky(schur) dx = cholesky_solve(L, rhs1) else: + schur = G + A.T @ np.diag(Z) @ A + schur += np.eye(schur.shape[0]) * shur_regularization + schur_diag = np.diag(schur) dx = conjugate_gradient_precond( schur, rhs1, dx, @@ -258,11 +262,15 @@ def _process_slice(slice_data, slice_mask, def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, + numba_threading_layer="workqueue", n_threads=None, n_cpus=None, use_chol=False): # Note cholesky is ~50% slower but more robust if n_threads is not None: set_num_threads(n_threads) + if numba_threading_layer != "default": + config.THREADING_LAYER = numba_threading_layer + if n_cpus is None: n_cpus = multiprocessing.cpu_count() - 1 @@ -312,7 +320,10 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, b_id = ray.put(b) x0_id = ray.put(x0) - @ray.remote(num_cpus=n_cpus) + @ray.remote( + num_cpus=n_cpus, + runtime_env={"env_vars": { + "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, Q, A, b, x0, max_iter, tol, use_chol): return [_process_slice( @@ -352,3 +363,33 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, MultiShellDeconvModel.fit = _fit + + +if __name__ == "__main__": + from AFQ.api.group import GroupAFQ + import AFQ.definitions.image as afm + from dipy.data import get_sphere + + brain_mask_definition = afm.ImageFile( + suffix="mask", + filters={"scope": "qsiprep", "desc": "brain"}) + + myafq = GroupAFQ( + "/Users/john/AFQ_data/HBN", + participant_labels=["NDARAA948VFH"], + preproc_pipeline="qsiprep", + tracking_params={ + "tracker": "pft", + "odf_model": "msmt_aodf", + "sphere": get_sphere(name="repulsion724"), + "seed_mask": afm.ScalarImage("wm_gm_interface"), + "seed_threshold": 0.5, + "stop_mask": afm.ThreeTImage(), + "stop_threshold": "ACT", + "n_seeds": 500000, + "random_seeds": True}, + ray_n_cpus=1, + # segmentation_params={"parallel_segmentation": {"engine": "ray"}}, + brain_mask_definition=brain_mask_definition) + + myafq.export("msmtcsd_params") diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 374b8d13b..5e8bab674 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -42,11 +42,11 @@ from AFQ.models.dki import fit_dki_csf, fit_dki_gm, fit_dki_wm from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model -from AFQ.models.msmt import MultiShellDeconvModel from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) from AFQ.models.dam import fit_dam, csf_dam, t1_dam from AFQ.models.wmgm_interface import fit_wm_gm_interface +from AFQ.models.msmt import MultiShellDeconvModel logger = logging.getLogger('AFQ') @@ -536,7 +536,8 @@ def msmt_params(brain_mask, gtab, data, msmt_fa_thr=0.7, ray_n_cpus=None, numba_n_threads=None, - numba_threading_layer="workqueue"): + numba_threading_layer="workqueue", + msmt_use_chol=True): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -560,6 +561,10 @@ def msmt_params(brain_mask, gtab, data, numba_threading_layer : str, optional. The threading layer to use for Numba. Default: "workqueue". + msmt_use_chol : bool, optional. + Whether to use the Cholesky decomposition for the MSMT CSD fit. + If False, it will use conjugate gradients. + Default: True References ---------- @@ -568,9 +573,6 @@ def msmt_params(brain_mask, gtab, data, deconvolution for improved analysis of multi-shell diffusion MRI data. NeuroImage, 103 (2014), pp. 411–426 """ - from numba import config - config.THREADING_LAYER = numba_threading_layer - mask =\ nib.load(brain_mask).get_fdata() @@ -599,7 +601,8 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads) + data[:25], mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, + use_chol=msmt_use_chol, numba_threading_layer=numba_threading_layer) meta = dict( SphericalHarmonicDegree=msmt_sh_order, From 8cf8909d808f5d4eb1a0c19f3d91effe13a5ca89 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 4 Jul 2025 14:21:36 -0700 Subject: [PATCH 017/116] bf --- AFQ/tasks/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 5e8bab674..b75720145 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -601,7 +601,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data[:25], mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, + data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, use_chol=msmt_use_chol, numba_threading_layer=numba_threading_layer) meta = dict( From 0195ff5887d037fa5cd3baa91f18eaee2a1cbe8a Mon Sep 17 00:00:00 2001 From: 36000 Date: Sat, 5 Jul 2025 22:35:48 -0700 Subject: [PATCH 018/116] msmt efficiency gains --- AFQ/models/msmt.py | 277 ++++++++++++++++++++++++++------------------- 1 file changed, 161 insertions(+), 116 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 527181a19..84613f7c4 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -24,7 +24,7 @@ # parallelized to solve 10s-100s of thousands of QPs # ultimately used to fit MSMT CSD @njit(fastmath=True) -def solve_qp(Rt, R_pinv, G, A, b, x0, +def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, d, max_iter, tol, use_chol): ''' Solves 1/2*x^t*G*x+(Rt*d)^t*x given Ax>=b @@ -75,35 +75,107 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, dx = np.zeros(n) dy = np.zeros(m) dl = np.zeros(m) - - tau = 0.9 + dy_aff = np.zeros(m) + dl_aff = np.zeros(m) + + rhs1 = np.empty(n) + rhs2 = np.empty(m) + Gx = np.empty(n) + ATl = np.empty(n) + rhs1l = np.empty(n) + Zrhs2 = np.empty(m) + ATZrhs2 = np.empty(n) + schur = np.empty((n, n)) + temp = np.empty(m) + tempn1 = np.empty(n) + tempn2 = np.empty(n) + + tau = 0.99 shur_regularization = 1e-6 for ii in range(max_iter): - mu = (y @ l) / m + # mu = (y @ l) / m + mu = 0.0 + for i in range(m): + mu += y[i] * l[i] + mu /= m # Predictor step Z = safe_divide_vec(l, y) - dy = A @ x - y - b - rhs2 = -dy - y - rhs1l = -(G @ x - A.T @ l + c) - rhs1 = rhs1l + A.T @ (Z * rhs2) - - if use_chol: - schur = G + A.T @ np.diag(Z) @ A - schur += np.eye(schur.shape[0]) * shur_regularization - - L = np.linalg.cholesky(schur) - dx = cholesky_solve(L, rhs1) - else: - schur = G + A.T @ np.diag(Z) @ A - schur += np.eye(schur.shape[0]) * shur_regularization - - schur_diag = np.diag(schur) - dx = conjugate_gradient_precond( - schur, rhs1, dx, - schur_diag, 5, 1e-4 * tol) - dl_aff = Z * (rhs2 - A @ dx) - dy_aff = dy + A @ dx + + # dy = A @ x - y - b + for i in range(m): + ax = 0.0 + for j in range(n): + ax += A[i, j] * x[j] + dy[i] = ax - y[i] - b[i] + + # rhs2 = -dy - y + for i in range(m): + rhs2[i] = -dy[i] - y[i] + + # rhs1l = -(G @ x - A.T @ l + c) + # G @ x + for i in range(n): + s = 0.0 + for j in range(n): + s += G[i, j] * x[j] + Gx[i] = s + + # Then compute A.T @ l + for i in range(n): + s = 0.0 + for j in range(m): + s += A[j, i] * l[j] + ATl[i] = s + + # Now compute rhs1l = -(Gx - ATl + c) + for i in range(n): + rhs1l[i] = -(Gx[i] - ATl[i] + c[i]) + + # rhs1 = rhs1l + A.T @ (Z * rhs2) + # Z * rhs2 + for i in range(m): + Zrhs2[i] = Z[i] * rhs2[i] + + # A.T @ (Z * rhs2) + for i in range(n): + s = 0.0 + for j in range(m): + s += A[j, i] * Zrhs2[j] + ATZrhs2[i] = s + + # rhs1 = rhs1l + ATZrhs2 + for i in range(n): + rhs1[i] = rhs1l[i] + ATZrhs2[i] + + for i in range(n): + for j in range(i, n): + s = G[i, j] + for k in range(m): + s += Z[k] * A_outer[k, i, j] + schur[i, j] = s + schur[j, i] = s # fill symmetric + + for i in range(n): + schur[i, i] += shur_regularization + + L = np.linalg.cholesky(schur) + dx = cholesky_solve(L, rhs1, tempn1, tempn2) + + # temp = A @ dx + for i in range(m): + s = 0.0 + for j in range(n): + s += A[i, j] * dx[j] + temp[i] = s + + # dl_aff = Z * (rhs2 - A @ dx) + for i in range(m): + dl_aff[i] = Z[i] * (rhs2[i] - temp[i]) + + # dy_aff = dy + A @ dx + for i in range(m): + dy_aff[i] += temp[i] alpha_aff_pri = 1.0 alpha_aff_dual = 1.0 @@ -114,22 +186,49 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, alpha_aff_dual = min(alpha_aff_dual, -l[ii] / dl_aff[ii]) alpha_aff = min(tau * alpha_aff_pri, tau * alpha_aff_dual) - mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m + # mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m + mu_aff = 0.0 + for i in range(m): + mu_aff += (y[i] + alpha_aff * dy[i]) * (l[i] + alpha_aff * dl[i]) + mu_aff /= m + sigma = (mu_aff / mu) ** 3 mu = sigma * mu - # Main corrector step + # rhs2 = -dy + (-y + mu / l) rhs2 = -dy + (-y + safe_divide_vec(mu, l)) - rhs1 = rhs1l + A.T @ (Z * rhs2) - if use_chol: - dx = cholesky_solve(L, rhs1) - else: - dx = conjugate_gradient_precond( - schur, rhs1, dx, - schur_diag, max_iter, max(tol, 1e-2 * mu)) - dl = Z * (rhs2 - A @ dx) - dy += A @ dx + # Zrhs2 = Z * rhs2 + for i in range(m): + Zrhs2[i] = Z[i] * rhs2[i] + + # ATZrhs2 = A.T @ Zrhs2 + for i in range(n): + s = 0.0 + for j in range(m): + s += A[j, i] * Zrhs2[j] + ATZrhs2[i] = s + + # rhs1 = rhs1l + ATZrhs2 + for i in range(n): + rhs1[i] = rhs1l[i] + ATZrhs2[i] + + dx = cholesky_solve(L, rhs1, tempn1, tempn2) + + # temp = A @ dx + for i in range(m): + s = 0.0 + for j in range(n): + s += A[i, j] * dx[j] + temp[i] = s + + # dl = Z * (rhs2 - A @ dx) + for i in range(m): + dl[i] = Z[i] * (rhs2[i] - temp[i]) + + # dy += A @ dx + for i in range(m): + dy[i] += temp[i] beta = 1.0 sigma = 1.0 @@ -157,55 +256,23 @@ def solve_qp(Rt, R_pinv, G, A, b, x0, return False, x0 -@njit(fastmath=True) -def cholesky_solve(L, b): +@njit(fastmath=True, inline='always') +def cholesky_solve(L, b, y, x): n = L.shape[0] - # Forward substitution: L * y = b - y = np.zeros_like(b) + # Forward substitution: L y = b for i in range(n): - y[i] = (b[i] - np.dot(L[i, :i], y[:i])) / L[i, i] + s = 0.0 + for j in range(i): + s += L[i, j] * y[j] + y[i] = (b[i] - s) / L[i, i] - # Backward substitution: L^T * x = y - x = np.zeros_like(b) + # Backward substitution: L.T x = y for i in range(n - 1, -1, -1): - x[i] = (y[i] - np.dot(L[i + 1:, i], x[i + 1:])) / L[i, i] - - return x - - -@njit(fastmath=True) -def conjugate_gradient_precond(A, b, x, diag, max_iter, tol): - """ - Solves A x = b with Jacobi preconditioner (diag) using Conjugate Gradient. - - Args: - A : 2D np.ndarray, symmetric positive-definite matrix - b : 1D np.ndarray, RHS vector - x : 1D np.ndarray, initial guess (will be modified in-place) - diag : 1D np.ndarray, diagonal of preconditioner (Jacobi) - max_iter : int, maximum number of iterations - tol : float, stopping threshold - - Returns: - x : Updated solution - """ - r = b - A @ x - r /= diag - p = r.copy() - rs_old = np.dot(r * r, diag) - - for _ in range(max_iter): - Ap = A @ p - alpha = rs_old / np.dot(p, Ap) - x += alpha * p - r -= alpha * Ap / diag - rs_new = np.dot(r * r, diag) - if rs_new < tol: - break - beta = rs_new / rs_old - p = r + beta * p - rs_old = rs_new + s = 0.0 + for j in range(i + 1, n): + s += L[j, i] * x[j] + x[i] = (y[i] - s) / L[i, i] return x @@ -233,7 +300,7 @@ def find_analytic_center(A, b, x0): @njit(parallel=True, fastmath=True) def _process_slice(slice_data, slice_mask, Rt, R_pinv, - G, A, b, x0, + G, A, A_outer, b, x0, max_iter, tol, use_chol): results = np.zeros( slice_data.shape[:2] + (A.shape[1],), @@ -247,7 +314,7 @@ def _process_slice(slice_data, slice_mask, # More complicated warm starts are slower x_prev = np.clip(x_prev, -1.0, 1.0) success, x_prev = solve_qp( - Rt, R_pinv, G, A, b, + Rt, R_pinv, G, A, A_outer, b, x_prev, slice_data[j, k], max_iter, tol, use_chol) @@ -261,7 +328,7 @@ def _process_slice(slice_data, slice_mask, return results -def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, +def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, numba_threading_layer="workqueue", n_threads=None, n_cpus=None, use_chol=False): # Note cholesky is ~50% slower but more robust @@ -291,6 +358,12 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, for i in range(A.shape[0]): A[i] /= np.linalg.norm(A[i]) + A_outer = np.empty((m, n, n), dtype=np.float64) + for k in range(m): + for i in range(n): + for j in range(n): + A_outer[k, i, j] = A[k, i] * A[k, j] + Q = R.T @ R x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) x0 = find_analytic_center(A, b, x0) @@ -317,6 +390,7 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, R_pinv_id = ray.put(R_pinv) Q_id = ray.put(Q) A_id = ray.put(A) + A_outer_id = ray.put(A_outer) b_id = ray.put(b) x0_id = ray.put(x0) @@ -325,9 +399,9 @@ def _fit(self, data, mask=None, max_iter=1e6, tol=1e-6, runtime_env={"env_vars": { "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, - Q, A, b, x0, max_iter, tol, use_chol): + Q, A, A_outer, b, x0, max_iter, tol, use_chol): return [_process_slice( - data[ii], mask[ii], Rt, R_pinv, Q, A, b, x0, max_iter, tol, use_chol + data[ii], mask[ii], Rt, R_pinv, Q, A, A_outer, b, x0, max_iter, tol, use_chol ) for ii in batch_indices] # Launch tasks in chunks @@ -335,7 +409,7 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, futures = [ process_batch_remote.remote(batch, data_id, mask_id, Rt_id, R_pinv_id, - Q_id, A_id, b_id, x0_id, + Q_id, A_id, A_outer_id, b_id, x0_id, max_iter, tol, use_chol) for batch in chunk_indices(all_indices, n_cpus * 2) ] @@ -354,6 +428,7 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, R_pinv, Q, A, + A_outer, b, x0, max_iter, tol, use_chol) @@ -363,33 +438,3 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, MultiShellDeconvModel.fit = _fit - - -if __name__ == "__main__": - from AFQ.api.group import GroupAFQ - import AFQ.definitions.image as afm - from dipy.data import get_sphere - - brain_mask_definition = afm.ImageFile( - suffix="mask", - filters={"scope": "qsiprep", "desc": "brain"}) - - myafq = GroupAFQ( - "/Users/john/AFQ_data/HBN", - participant_labels=["NDARAA948VFH"], - preproc_pipeline="qsiprep", - tracking_params={ - "tracker": "pft", - "odf_model": "msmt_aodf", - "sphere": get_sphere(name="repulsion724"), - "seed_mask": afm.ScalarImage("wm_gm_interface"), - "seed_threshold": 0.5, - "stop_mask": afm.ThreeTImage(), - "stop_threshold": "ACT", - "n_seeds": 500000, - "random_seeds": True}, - ray_n_cpus=1, - # segmentation_params={"parallel_segmentation": {"engine": "ray"}}, - brain_mask_definition=brain_mask_definition) - - myafq.export("msmtcsd_params") From 9b047abea61992f42c5b69bc39d67eff0f72abfc Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 7 Jul 2025 12:23:23 -0700 Subject: [PATCH 019/116] bf to solver --- AFQ/models/msmt.py | 57 +++++++++++++++++++++++++--------------------- AFQ/tasks/data.py | 11 +++------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 84613f7c4..644ad5652 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -24,8 +24,8 @@ # parallelized to solve 10s-100s of thousands of QPs # ultimately used to fit MSMT CSD @njit(fastmath=True) -def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, - d, max_iter, tol, use_chol): +def solve_qp(Rt, R_pinv, G, A, At, A_outer, b, x0, + d, max_iter, tol): ''' Solves 1/2*x^t*G*x+(Rt*d)^t*x given Ax>=b In MSMT, G, R, A, b are the same across voxels, @@ -70,8 +70,8 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, c = Rt @ d x = x0.copy() - y = np.ones(m) - l = np.ones(m) + y = np.maximum(np.abs(A @ x - b), 1.0) + l = np.ones(m) / np.max(y) dx = np.zeros(n) dy = np.zeros(m) dl = np.zeros(m) @@ -90,7 +90,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, tempn1 = np.empty(n) tempn2 = np.empty(n) - tau = 0.99 + tau = 0.95 shur_regularization = 1e-6 for ii in range(max_iter): # mu = (y @ l) / m @@ -125,7 +125,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, for i in range(n): s = 0.0 for j in range(m): - s += A[j, i] * l[j] + s += At[i, j] * l[j] ATl[i] = s # Now compute rhs1l = -(Gx - ATl + c) @@ -141,7 +141,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, for i in range(n): s = 0.0 for j in range(m): - s += A[j, i] * Zrhs2[j] + s += At[i, j] * Zrhs2[j] ATZrhs2[i] = s # rhs1 = rhs1l + ATZrhs2 @@ -152,7 +152,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, for j in range(i, n): s = G[i, j] for k in range(m): - s += Z[k] * A_outer[k, i, j] + s += Z[k] * A_outer[i, j, k] schur[i, j] = s schur[j, i] = s # fill symmetric @@ -175,7 +175,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, # dy_aff = dy + A @ dx for i in range(m): - dy_aff[i] += temp[i] + dy_aff[i] = dy[i] + temp[i] alpha_aff_pri = 1.0 alpha_aff_dual = 1.0 @@ -189,7 +189,8 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, # mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m mu_aff = 0.0 for i in range(m): - mu_aff += (y[i] + alpha_aff * dy[i]) * (l[i] + alpha_aff * dl[i]) + mu_aff += (y[i] + alpha_aff * dy_aff[i]) \ + * (l[i] + alpha_aff * dl_aff[i]) mu_aff /= m sigma = (mu_aff / mu) ** 3 @@ -206,7 +207,7 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, for i in range(n): s = 0.0 for j in range(m): - s += A[j, i] * Zrhs2[j] + s += At[i, j] * Zrhs2[j] ATZrhs2[i] = s # rhs1 = rhs1l + ATZrhs2 @@ -252,7 +253,6 @@ def solve_qp(Rt, R_pinv, G, A, A_outer, b, x0, return True, x else: return False, x0 - return False, x0 @@ -297,11 +297,11 @@ def find_analytic_center(A, b, x0): return result.x -@njit(parallel=True, fastmath=True) +@njit(fastmath=True) def _process_slice(slice_data, slice_mask, Rt, R_pinv, - G, A, A_outer, b, x0, - max_iter, tol, use_chol): + G, A, At, A_outer, b, x0, + max_iter, tol): results = np.zeros( slice_data.shape[:2] + (A.shape[1],), dtype=np.float64) @@ -314,10 +314,10 @@ def _process_slice(slice_data, slice_mask, # More complicated warm starts are slower x_prev = np.clip(x_prev, -1.0, 1.0) success, x_prev = solve_qp( - Rt, R_pinv, G, A, A_outer, b, + Rt, R_pinv, G, A, At, A_outer, b, x_prev, slice_data[j, k], - max_iter, tol, use_chol) + max_iter, tol) if success: results[j, k] = x_prev @@ -328,9 +328,9 @@ def _process_slice(slice_data, slice_mask, return results -def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, +def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, numba_threading_layer="workqueue", - n_threads=None, n_cpus=None, use_chol=False): + n_threads=None, n_cpus=None): # Note cholesky is ~50% slower but more robust if n_threads is not None: set_num_threads(n_threads) @@ -358,11 +358,11 @@ def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, for i in range(A.shape[0]): A[i] /= np.linalg.norm(A[i]) - A_outer = np.empty((m, n, n), dtype=np.float64) + A_outer = np.empty((n, n, m), dtype=np.float64) for k in range(m): for i in range(n): for j in range(n): - A_outer[k, i, j] = A[k, i] * A[k, j] + A_outer[i, j, k] = A[k, i] * A[k, j] Q = R.T @ R x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) @@ -376,6 +376,7 @@ def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, R_pinv = np.ascontiguousarray(R_pinv, dtype=np.float64) Q = np.ascontiguousarray(Q, dtype=np.float64) A = np.ascontiguousarray(A, dtype=np.float64) + At = np.ascontiguousarray(A.T, dtype=np.float64) b = np.ascontiguousarray(b, dtype=np.float64) x0 = np.ascontiguousarray(x0, dtype=np.float64) @@ -390,6 +391,7 @@ def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, R_pinv_id = ray.put(R_pinv) Q_id = ray.put(Q) A_id = ray.put(A) + At_id = ray.put(At) A_outer_id = ray.put(A_outer) b_id = ray.put(b) x0_id = ray.put(x0) @@ -399,9 +401,10 @@ def _fit(self, data, mask=None, max_iter=1e2, tol=1e-6, runtime_env={"env_vars": { "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, - Q, A, A_outer, b, x0, max_iter, tol, use_chol): + Q, A, At, A_outer, b, x0, max_iter, tol): return [_process_slice( - data[ii], mask[ii], Rt, R_pinv, Q, A, A_outer, b, x0, max_iter, tol, use_chol + data[ii], mask[ii], Rt, R_pinv, Q, A, At, + A_outer, b, x0, max_iter, tol ) for ii in batch_indices] # Launch tasks in chunks @@ -409,8 +412,9 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, futures = [ process_batch_remote.remote(batch, data_id, mask_id, Rt_id, R_pinv_id, - Q_id, A_id, A_outer_id, b_id, x0_id, - max_iter, tol, use_chol) + Q_id, A_id, At_id, + A_outer_id, b_id, x0_id, + max_iter, tol) for batch in chunk_indices(all_indices, n_cpus * 2) ] @@ -428,10 +432,11 @@ def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, R_pinv, Q, A, + At, A_outer, b, x0, - max_iter, tol, use_chol) + max_iter, tol) coeff = coeff.reshape(og_data_shape[:-1] + (n,)) return MSDeconvFit(self, coeff, None) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index b75720145..262d89f53 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -536,8 +536,7 @@ def msmt_params(brain_mask, gtab, data, msmt_fa_thr=0.7, ray_n_cpus=None, numba_n_threads=None, - numba_threading_layer="workqueue", - msmt_use_chol=True): + numba_threading_layer="workqueue"): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -561,10 +560,6 @@ def msmt_params(brain_mask, gtab, data, numba_threading_layer : str, optional. The threading layer to use for Numba. Default: "workqueue". - msmt_use_chol : bool, optional. - Whether to use the Cholesky decomposition for the MSMT CSD fit. - If False, it will use conjugate gradients. - Default: True References ---------- @@ -601,8 +596,8 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, - use_chol=msmt_use_chol, numba_threading_layer=numba_threading_layer) + data[20:30], mask[20:30], n_cpus=ray_n_cpus, n_threads=numba_n_threads, + numba_threading_layer=numba_threading_layer) meta = dict( SphericalHarmonicDegree=msmt_sh_order, From 64e876a0810974c59f92c0d7dc67460afd898555 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 7 Jul 2025 20:54:16 -0700 Subject: [PATCH 020/116] use osqpy --- AFQ/models/msmt.py | 265 ++++++++++++++++++++++++++++----------------- AFQ/tasks/data.py | 19 +++- setup.cfg | 1 + 3 files changed, 181 insertions(+), 104 deletions(-) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index 644ad5652..d85d49ec7 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -1,13 +1,16 @@ import multiprocessing import warnings import numpy as np +from tqdm import tqdm +import ray from scipy.optimize import minimize +from scipy.sparse import csr_matrix from numba import njit, prange, set_num_threads, config from numba.core.errors import NumbaPerformanceWarning -from tqdm import tqdm -import ray + +import osqp from dipy.reconst.mcsd import MSDeconvFit from dipy.reconst.mcsd import MultiShellDeconvModel @@ -179,11 +182,11 @@ def solve_qp(Rt, R_pinv, G, A, At, A_outer, b, x0, alpha_aff_pri = 1.0 alpha_aff_dual = 1.0 - for ii in range(m): - if dy_aff[ii] < 0: - alpha_aff_pri = min(alpha_aff_pri, -y[ii] / dy_aff[ii]) - if dl_aff[ii] < 0: - alpha_aff_dual = min(alpha_aff_dual, -l[ii] / dl_aff[ii]) + for i in range(m): + if dy_aff[i] < 0: + alpha_aff_pri = min(alpha_aff_pri, -y[i] / dy_aff[i]) + if dl_aff[i] < 0: + alpha_aff_dual = min(alpha_aff_dual, -l[i] / dl_aff[i]) alpha_aff = min(tau * alpha_aff_pri, tau * alpha_aff_dual) # mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m @@ -233,11 +236,11 @@ def solve_qp(Rt, R_pinv, G, A, At, A_outer, b, x0, beta = 1.0 sigma = 1.0 - for ii in range(m): - if dy[ii] < 0 and dy[ii] * sigma < -y[ii]: - sigma = -y[ii] / dy[ii] - if dl[ii] < 0 and dl[ii] * beta < -l[ii]: - beta = -l[ii] / dl[ii] + for i in range(m): + if dy[i] < 0 and dy[i] * sigma < -y[i]: + sigma = -y[i] / dy[i] + if dl[i] < 0 and dl[i] * beta < -l[i]: + beta = -l[i] / dl[i] beta *= tau sigma *= tau alpha = min(beta, sigma) @@ -246,14 +249,11 @@ def solve_qp(Rt, R_pinv, G, A, At, A_outer, b, x0, y += alpha * dy l += alpha * dl - if (alpha * np.sum(np.abs(dx)) < n * tol - and alpha * np.sum(np.abs(dy)) < m * tol - and alpha * np.sum(np.abs(dl)) < m * tol): + if alpha * np.dot(dx, dx) < n * tol: if np.all(A @ x - b >= -tol): return True, x - else: - return False, x0 - return False, x0 + + return False, x @njit(fastmath=True, inline='always') @@ -307,20 +307,16 @@ def _process_slice(slice_data, slice_mask, dtype=np.float64) for j in prange(slice_data.shape[0]): - x_prev = x0.copy() for k in range(slice_data.shape[1]): if slice_mask[j, k]: - # previous values warm start next solver - # More complicated warm starts are slower - x_prev = np.clip(x_prev, -1.0, 1.0) - success, x_prev = solve_qp( + success, result = solve_qp( Rt, R_pinv, G, A, At, A_outer, b, - x_prev, + x0, slice_data[j, k], max_iter, tol) if success: - results[j, k] = x_prev + results[j, k] = result else: results[j, k] = np.zeros(A.shape[1]) else: @@ -329,6 +325,7 @@ def _process_slice(slice_data, slice_mask, def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, + use_osqppy=True, numba_threading_layer="workqueue", n_threads=None, n_cpus=None): # Note cholesky is ~50% slower but more robust @@ -365,81 +362,151 @@ def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, A_outer[i, j, k] = A[k, i] * A[k, j] Q = R.T @ R - x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) - x0 = find_analytic_center(A, b, x0) - - Rt = -R.T - R_pinv = np.linalg.pinv(R).astype(np.float64) - data = np.ascontiguousarray(data, dtype=np.float64) - - Rt = np.ascontiguousarray(Rt, dtype=np.float64) - R_pinv = np.ascontiguousarray(R_pinv, dtype=np.float64) - Q = np.ascontiguousarray(Q, dtype=np.float64) - A = np.ascontiguousarray(A, dtype=np.float64) - At = np.ascontiguousarray(A.T, dtype=np.float64) - b = np.ascontiguousarray(b, dtype=np.float64) - x0 = np.ascontiguousarray(x0, dtype=np.float64) - - warnings.simplefilter('ignore', category=NumbaPerformanceWarning) - - if n_cpus > 1: - ray.init(ignore_reinit_error=True) - - data_id = ray.put(data) - mask_id = ray.put(mask) - Rt_id = ray.put(Rt) - R_pinv_id = ray.put(R_pinv) - Q_id = ray.put(Q) - A_id = ray.put(A) - At_id = ray.put(At) - A_outer_id = ray.put(A_outer) - b_id = ray.put(b) - x0_id = ray.put(x0) - - @ray.remote( - num_cpus=n_cpus, - runtime_env={"env_vars": { - "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) - def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, - Q, A, At, A_outer, b, x0, max_iter, tol): - return [_process_slice( - data[ii], mask[ii], Rt, R_pinv, Q, A, At, - A_outer, b, x0, max_iter, tol - ) for ii in batch_indices] - - # Launch tasks in chunks - all_indices = list(range(data.shape[0])) - futures = [ - process_batch_remote.remote(batch, data_id, mask_id, - Rt_id, R_pinv_id, - Q_id, A_id, At_id, - A_outer_id, b_id, x0_id, - max_iter, tol) - for batch in chunk_indices(all_indices, n_cpus * 2) - ] - - # Collect and assign results - for batch, future in zip( - chunk_indices(all_indices, n_cpus * 2), tqdm(futures)): - results = ray.get(future) - for i, ii in enumerate(batch): - coeff[ii] = results[i] + if use_osqppy: + if n_cpus > 1: + ray.init(ignore_reinit_error=True) + + data_id = ray.put(data) + mask_id = ray.put(mask) + Q_id = ray.put(Q) + A_id = ray.put(A) + b_id = ray.put(b) + R_id = ray.put(R) + + @ray.remote( + num_cpus=n_cpus) + def process_batch_remote(batch_indices, data, mask, + Q, A, b, R): + from scipy.sparse import csr_matrix + import osqp + import numpy as np + + m = osqp.OSQP() + m.setup( + P=csr_matrix(Q), A=csr_matrix(A), l=b, + u=None, q=None, + verbose=False) + return_values = np.zeros( + (len(batch_indices),) + data.shape[1:3] + (A.shape[1],), + dtype=np.float64) + for i, ii in enumerate(batch_indices): + for jj in range(data.shape[1]): + for kk in range(data.shape[2]): + if mask[ii, jj, kk]: + c = np.dot(-R.T, data[ii, jj, kk]) + m.update(q=c) + results = m.solve() + return_values[i, jj, kk] = results.x + return return_values + + # Launch tasks in chunks + all_indices = list(range(data.shape[0])) + indices_chunked = list(chunk_indices(all_indices, n_cpus * 2)) + futures = [ + process_batch_remote.remote(batch, data_id, mask_id, + Q_id, A_id, + b_id, R_id) + for batch in indices_chunked + ] + + # Collect and assign results + for batch, future in zip( + indices_chunked, tqdm(futures)): + results = ray.get(future) + for i, ii in enumerate(batch): + coeff[ii] = results[i] + else: + m = osqp.OSQP() + m.setup( + P=csr_matrix(Q), A=csr_matrix(A), l=b, + u=None, q=None, + verbose=False, adaptive_rho=True) + for ii in tqdm(range(data.shape[0])): + for jj in range(data.shape[1]): + for kk in range(data.shape[2]): + if mask[ii, jj, kk]: + c = np.dot(-R.T, data[ii, jj, kk]) + m.update(q=c) + results = m.solve() + coeff[ii, jj, kk] = results.x + coeff = coeff.reshape(og_data_shape[:-1] + (n,)) + return MSDeconvFit(self, coeff, None) else: - for ii in tqdm(range(data.shape[0])): - coeff[ii] = _process_slice( - data[ii], mask[ii], - Rt, - R_pinv, - Q, - A, - At, - A_outer, - b, - x0, - max_iter, tol) - - coeff = coeff.reshape(og_data_shape[:-1] + (n,)) - return MSDeconvFit(self, coeff, None) + x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) + x0 = find_analytic_center(A, b, x0) + + Rt = -R.T + R_pinv = np.linalg.pinv(R).astype(np.float64) + data = np.ascontiguousarray(data, dtype=np.float64) + + Rt = np.ascontiguousarray(Rt, dtype=np.float64) + R_pinv = np.ascontiguousarray(R_pinv, dtype=np.float64) + Q = np.ascontiguousarray(Q, dtype=np.float64) + A = np.ascontiguousarray(A, dtype=np.float64) + At = np.ascontiguousarray(A.T, dtype=np.float64) + b = np.ascontiguousarray(b, dtype=np.float64) + x0 = np.ascontiguousarray(x0, dtype=np.float64) + + warnings.simplefilter('ignore', category=NumbaPerformanceWarning) + + if n_cpus > 1: + ray.init(ignore_reinit_error=True) + + data_id = ray.put(data) + mask_id = ray.put(mask) + Rt_id = ray.put(Rt) + R_pinv_id = ray.put(R_pinv) + Q_id = ray.put(Q) + A_id = ray.put(A) + At_id = ray.put(At) + A_outer_id = ray.put(A_outer) + b_id = ray.put(b) + x0_id = ray.put(x0) + + @ray.remote( + num_cpus=n_cpus, + runtime_env={"env_vars": { + "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) + def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, + Q, A, At, A_outer, b, x0, max_iter, tol): + return [_process_slice( + data[ii], mask[ii], Rt, R_pinv, Q, A, At, + A_outer, b, x0, max_iter, tol + ) for ii in batch_indices] + + # Launch tasks in chunks + all_indices = list(range(data.shape[0])) + futures = [ + process_batch_remote.remote(batch, data_id, mask_id, + Rt_id, R_pinv_id, + Q_id, A_id, At_id, + A_outer_id, b_id, x0_id, + max_iter, tol) + for batch in chunk_indices(all_indices, n_cpus * 2) + ] + + # Collect and assign results + for batch, future in zip( + chunk_indices(all_indices, n_cpus * 2), tqdm(futures)): + results = ray.get(future) + for i, ii in enumerate(batch): + coeff[ii] = results[i] + else: + for ii in tqdm(range(data.shape[0])): + coeff[ii] = _process_slice( + data[ii], mask[ii], + Rt, + R_pinv, + Q, + A, + At, + A_outer, + b, + x0, + max_iter, tol) + + coeff = coeff.reshape(og_data_shape[:-1] + (n,)) + return MSDeconvFit(self, coeff, None) MultiShellDeconvModel.fit = _fit diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 262d89f53..4aaec95c7 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -531,7 +531,7 @@ def msdki_msk(msdki_tf): subfolder="models") @as_img def msmt_params(brain_mask, gtab, data, - dki_wm, dki_gm, dki_csf, + dwi_affine, t1w_pve, msmt_sh_order=8, msmt_fa_thr=0.7, ray_n_cpus=None, @@ -571,6 +571,15 @@ def msmt_params(brain_mask, gtab, data, mask =\ nib.load(brain_mask).get_fdata() + pve_img = nib.load(t1w_pve) + pve_data = pve_img.get_fdata() + csf = resample(pve_data[..., 0], data[..., 0], + pve_img.affine, dwi_affine).get_fdata() + gm = resample(pve_data[..., 1], data[..., 0], + pve_img.affine, dwi_affine).get_fdata() + wm = resample(pve_data[..., 2], data[..., 0], + pve_img.affine, dwi_affine).get_fdata() + mask_wm, mask_gm, mask_csf = mask_for_response_msmt( gtab, data, @@ -580,9 +589,9 @@ def msmt_params(brain_mask, gtab, data, csf_fa_thr=0.15, gm_md_thr=0.001, csf_md_thr=0.0032) - mask_wm *= nib.load(dki_wm).get_fdata() > 0.5 - mask_gm *= nib.load(dki_gm).get_fdata() > 0.5 - mask_csf *= nib.load(dki_csf).get_fdata() > 0.5 + mask_wm *= wm > 0.5 + mask_gm *= gm > 0.5 + mask_csf *= csf > 0.5 response_wm, response_gm, response_csf = response_from_mask_msmt( gtab, data, mask_wm, mask_gm, mask_csf) ubvals = unique_bvals_tolerance(gtab.bvals) @@ -596,7 +605,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data[20:30], mask[20:30], n_cpus=ray_n_cpus, n_threads=numba_n_threads, + data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, numba_threading_layer=numba_threading_layer) meta = dict( diff --git a/setup.cfg b/setup.cfg index 09963f455..70d0bd17f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,7 @@ install_requires = templateflow>=0.8 immlib numba + osqp pydra trx-python ray From cf7b3bc27326303deaf6ad50f6e5d5ed5a8916d3 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 8 Jul 2025 12:48:29 -0700 Subject: [PATCH 021/116] add wmgmi to roiimage as option --- AFQ/definitions/image.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index a331d5617..7107aedd8 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -269,12 +269,14 @@ def __init__(self, use_waypoints=True, use_presegment=False, use_endpoints=False, + only_wmgmi=False, tissue_property=None, tissue_property_n_voxel=None, tissue_property_threshold=None): self.use_waypoints = use_waypoints self.use_presegment = use_presegment self.use_endpoints = use_endpoints + self.only_wmgmi = only_wmgmi self.tissue_property = tissue_property self.tissue_property_n_voxel = tissue_property_n_voxel self.tissue_property_threshold = tissue_property_threshold @@ -336,6 +338,17 @@ def _image_getter_helper(mapping, raise ValueError(( "BundleDict does not have enough ROIs to generate " f"an ROI Image: {bundle_dict._dict}")) + + if self.only_wmgmi: + wmgmi = nib.load( + data_imap["wm_gm_interface"]).get_fdata() + image_data = np.logical_and( + image_data, wmgmi) + if np.sum(image_data) == 0: + raise ValueError(( + "BundleDict does not have enough ROIs to generate " + "an ROI Image with WM/GM interface applied.")) + return nib.Nifti1Image( image_data.astype(np.float32), data_imap["dwi_affine"]), dict(source="ROIs") From 216365e23eb391ea456f69200a4d6ffa7b534080 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 12:37:36 -0700 Subject: [PATCH 022/116] update pAF and VOF implementations for new system --- AFQ/api/bundle_dict.py | 8 +++-- AFQ/recognition/criteria.py | 8 +++++ AFQ/recognition/other_bundles.py | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index d4d421897..431035855 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -230,6 +230,8 @@ def default18_bd(): 'exclude': [templates['SLF_roi1_L']], 'space': 'template', 'start': templates['pARC_L_start'], + 'Left Arcuate': { + 'overlap': 30}, 'primary_axis': 'I/S', 'primary_axis_percentage': 40}, 'Right Posterior Arcuate': {'cross_midline': False, @@ -237,6 +239,8 @@ def default18_bd(): 'exclude': [templates['SLF_roi1_R']], 'space': 'template', 'start': templates['pARC_R_start'], + 'Right Arcuate': { + 'overlap': 30}, 'primary_axis': 'I/S', 'primary_axis_percentage': 40}, 'Left Vertical Occipital': {'cross_midline': False, @@ -246,7 +250,7 @@ def default18_bd(): 'Left Arcuate': { 'node_thresh': 20}, 'Left Posterior Arcuate': { - 'node_thresh': 1, + 'node_thresh': 20, 'core': 'Anterior'}, 'Left Inferior Longitudinal': { 'core': 'Right'}, @@ -261,7 +265,7 @@ def default18_bd(): 'Right Arcuate': { 'node_thresh': 20}, 'Right Posterior Arcuate': { - 'node_thresh': 1, + 'node_thresh': 20, 'core': 'Anterior'}, 'Right Inferior Longitudinal': { 'core': 'Left'}, diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 5e6de63a9..9dc1d4ad2 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -292,6 +292,14 @@ def clean_by_other_bundle(b_sls, bundle_def, cleaned_idx = b_sls.initiate_selection(other_bundle_name) cleaned_idx = 1 + if 'overlap' in bundle_def[other_bundle_name]: + cleaned_idx_overlap = abo.clean_by_overlap( + b_sls.get_selected_sls(), + other_bundle_sls, + bundle_def[other_bundle_name]["overlap"], + img.affine) + cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_overlap) + if 'node_thresh' in bundle_def[other_bundle_name]: cleaned_idx_node_thresh = abo.clean_by_other_density_map( b_sls.get_selected_sls(), diff --git a/AFQ/recognition/other_bundles.py b/AFQ/recognition/other_bundles.py index ddeffe7b4..b09022617 100644 --- a/AFQ/recognition/other_bundles.py +++ b/AFQ/recognition/other_bundles.py @@ -9,6 +9,56 @@ logger = logging.getLogger('AFQ') +def clean_by_overlap(this_bundle_sls, other_bundle_sls, + overlap, img): + """ + Cleans a set of streamlines by only keeping those with + significant overlap with another set of streamlines. + + Parameters + ---------- + this_bundle_sls : array-like + A list or array of streamlines to be cleaned. + other_bundle_sls : array-like + A reference list or array of streamlines to determine overlapping regions. + overlap : int + The minimum number of nodes allowed to overlap between `this_bundle_sls` + and `other_bundle_sls`. Streamlines with overlaps beyond this threshold + are removed. + img : nibabel.Nifti1Image or ndarray + A reference 3D image that defines the spatial dimensions for the density + map. + + Returns + ------- + cleaned_idx : ndarray of bool + An array of boolean values indicating which streamlines from + `this_bundle_sls` pass the overlap threshold (True for streamlines to + keep, False for streamlines to discard). + + Notes + ----- + This function computes a density map from `other_bundle_sls` to represent + the spatial occupancy of the streamlines. It then calculates the probability + of each streamline in `this_bundle_sls` overlapping with this map. + Streamlines that overlap in less than `node_thresh` nodes are flagged for + removal. + + Examples + -------- + >>> clean_idx = clean_by_other_density_map(bundle1, bundle2, 5, img) + >>> cleaned_bundle = [s for i, s in enumerate(bundle1) if clean_idx[i]] + """ + other_bundle_density_map = dtu.density_map( + other_bundle_sls, np.eye(4), img.shape[:3]) + fiber_probabilities = dts.values_from_volume( + other_bundle_density_map, this_bundle_sls, np.eye(4)) + cleaned_idx = np.zeros(len(this_bundle_sls), dtype=np.bool_) + for ii, fp in enumerate(fiber_probabilities): + cleaned_idx[ii] = np.sum(np.asarray(fp) >= 1) > overlap + return cleaned_idx + + def clean_by_other_density_map(this_bundle_sls, other_bundle_sls, node_thresh, img): """ From 7261d082716d60c981f00e3b577bb82e31473901 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 13:31:40 -0700 Subject: [PATCH 023/116] bf --- AFQ/recognition/criteria.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 9dc1d4ad2..da24b5fe4 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -297,7 +297,7 @@ def clean_by_other_bundle(b_sls, bundle_def, b_sls.get_selected_sls(), other_bundle_sls, bundle_def[other_bundle_name]["overlap"], - img.affine) + img) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_overlap) if 'node_thresh' in bundle_def[other_bundle_name]: From 629fe94b31037ccb87c6f48885a3666b42a07e47 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 14:02:56 -0700 Subject: [PATCH 024/116] adjusting vof definition --- AFQ/api/bundle_dict.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index 431035855..ac529f455 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -245,7 +245,7 @@ def default18_bd(): 'primary_axis_percentage': 40}, 'Left Vertical Occipital': {'cross_midline': False, 'space': 'template', - 'start': templates['VOF_L_start'], + # 'start': templates['VOF_L_start'], 'end': templates['VOF_L_end'], 'Left Arcuate': { 'node_thresh': 20}, @@ -260,7 +260,7 @@ def default18_bd(): 'primary_axis_percentage': 40}, 'Right Vertical Occipital': {'cross_midline': False, 'space': 'template', - 'start': templates['VOF_R_start'], + # 'start': templates['VOF_R_start'], 'end': templates['VOF_R_end'], 'Right Arcuate': { 'node_thresh': 20}, From 85d96c170eb593b581612b1cd5c564b1bac38abf Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 14:29:57 -0700 Subject: [PATCH 025/116] better relative core cleaning; good VOF start/end changes --- AFQ/api/bundle_dict.py | 2 -- AFQ/recognition/other_bundles.py | 9 ++------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index ac529f455..6d1f85f4a 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -245,7 +245,6 @@ def default18_bd(): 'primary_axis_percentage': 40}, 'Left Vertical Occipital': {'cross_midline': False, 'space': 'template', - # 'start': templates['VOF_L_start'], 'end': templates['VOF_L_end'], 'Left Arcuate': { 'node_thresh': 20}, @@ -260,7 +259,6 @@ def default18_bd(): 'primary_axis_percentage': 40}, 'Right Vertical Occipital': {'cross_midline': False, 'space': 'template', - # 'start': templates['VOF_R_start'], 'end': templates['VOF_R_end'], 'Right Arcuate': { 'node_thresh': 20}, diff --git a/AFQ/recognition/other_bundles.py b/AFQ/recognition/other_bundles.py index b09022617..d07895d3e 100644 --- a/AFQ/recognition/other_bundles.py +++ b/AFQ/recognition/other_bundles.py @@ -193,11 +193,6 @@ def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine): core_bundle = np.median(other_fgarray, axis=0) cleaned_idx_core = np.zeros(this_fgarray.shape[0], dtype=np.bool_) for ii, sl in enumerate(this_fgarray): - dist_matrix = cdist(core_bundle, sl, 'sqeuclidean') - min_dist_indices = np.unravel_index(np.argmin(dist_matrix), - dist_matrix.shape) - closest_core = core_bundle[min_dist_indices[0], core_axis] - closest_sl = sl[min_dist_indices[1], core_axis] - - cleaned_idx_core[ii] = core_direc * (closest_sl - closest_core) > 0 + cleaned_idx_core[ii] = np.all( + core_direc * (sl[:, core_axis] - core_bundle[:, core_axis]) > 0) return cleaned_idx_core From 0b7c2c953786a400e4bb101f208b939a73ccad12 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 15:33:54 -0700 Subject: [PATCH 026/116] much more specific relative bundle recognition options --- AFQ/api/bundle_dict.py | 4 +- AFQ/recognition/criteria.py | 16 ++++-- AFQ/recognition/other_bundles.py | 90 ++++++++++++-------------------- 3 files changed, 46 insertions(+), 64 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index 6d1f85f4a..3f215ff5d 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -250,7 +250,7 @@ def default18_bd(): 'node_thresh': 20}, 'Left Posterior Arcuate': { 'node_thresh': 20, - 'core': 'Anterior'}, + 'entire_core': 'Anterior'}, 'Left Inferior Longitudinal': { 'core': 'Right'}, 'isolation_forest': { @@ -264,7 +264,7 @@ def default18_bd(): 'node_thresh': 20}, 'Right Posterior Arcuate': { 'node_thresh': 20, - 'core': 'Anterior'}, + 'entire_core': 'Anterior'}, 'Right Inferior Longitudinal': { 'core': 'Left'}, 'isolation_forest': { diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index da24b5fe4..7749ee462 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -297,15 +297,15 @@ def clean_by_other_bundle(b_sls, bundle_def, b_sls.get_selected_sls(), other_bundle_sls, bundle_def[other_bundle_name]["overlap"], - img) + img, False) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_overlap) if 'node_thresh' in bundle_def[other_bundle_name]: - cleaned_idx_node_thresh = abo.clean_by_other_density_map( + cleaned_idx_node_thresh = abo.clean_by_overlap( b_sls.get_selected_sls(), other_bundle_sls, bundle_def[other_bundle_name]["node_thresh"], - img) + img, True) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_node_thresh) if 'core' in bundle_def[other_bundle_name]: @@ -313,7 +313,15 @@ def clean_by_other_bundle(b_sls, bundle_def, bundle_def[other_bundle_name]['core'].lower(), preproc_imap["fgarray"][b_sls.selected_fiber_idxs], np.array(abu.resample_tg(other_bundle_sls, 20)), - img.affine) + img.affine, False) + cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_core) + + if 'entire_core' in bundle_def[other_bundle_name]: + cleaned_idx_core = abo.clean_relative_to_other_core( + bundle_def[other_bundle_name]['core'].lower(), + preproc_imap["fgarray"][b_sls.selected_fiber_idxs], + np.array(abu.resample_tg(other_bundle_sls, 20)), + img.affine, True) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_core) b_sls.select(cleaned_idx, other_bundle_name) diff --git a/AFQ/recognition/other_bundles.py b/AFQ/recognition/other_bundles.py index d07895d3e..eb2b82ca8 100644 --- a/AFQ/recognition/other_bundles.py +++ b/AFQ/recognition/other_bundles.py @@ -10,9 +10,9 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, - overlap, img): + overlap, img, remove): """ - Cleans a set of streamlines by only keeping those with + Cleans a set of streamlines by only keeping (or removing) those with significant overlap with another set of streamlines. Parameters @@ -28,56 +28,10 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, img : nibabel.Nifti1Image or ndarray A reference 3D image that defines the spatial dimensions for the density map. - - Returns - ------- - cleaned_idx : ndarray of bool - An array of boolean values indicating which streamlines from - `this_bundle_sls` pass the overlap threshold (True for streamlines to - keep, False for streamlines to discard). - - Notes - ----- - This function computes a density map from `other_bundle_sls` to represent - the spatial occupancy of the streamlines. It then calculates the probability - of each streamline in `this_bundle_sls` overlapping with this map. - Streamlines that overlap in less than `node_thresh` nodes are flagged for - removal. - - Examples - -------- - >>> clean_idx = clean_by_other_density_map(bundle1, bundle2, 5, img) - >>> cleaned_bundle = [s for i, s in enumerate(bundle1) if clean_idx[i]] - """ - other_bundle_density_map = dtu.density_map( - other_bundle_sls, np.eye(4), img.shape[:3]) - fiber_probabilities = dts.values_from_volume( - other_bundle_density_map, this_bundle_sls, np.eye(4)) - cleaned_idx = np.zeros(len(this_bundle_sls), dtype=np.bool_) - for ii, fp in enumerate(fiber_probabilities): - cleaned_idx[ii] = np.sum(np.asarray(fp) >= 1) > overlap - return cleaned_idx - - -def clean_by_other_density_map(this_bundle_sls, other_bundle_sls, - node_thresh, img): - """ - Cleans a set of streamlines by removing those with significant overlap with - another set of streamlines. - - Parameters - ---------- - this_bundle_sls : array-like - A list or array of streamlines to be cleaned. - other_bundle_sls : array-like - A reference list or array of streamlines to determine overlapping regions. - node_thresh : int - The maximum number of nodes allowed to overlap between `this_bundle_sls` - and `other_bundle_sls`. Streamlines with overlaps beyond this threshold + remove : bool + If True, streamlines that overlap in less than `overlap` nodes are + removed. If False, streamlines that overlap in more than `overlap` nodes are removed. - img : nibabel.Nifti1Image or ndarray - A reference 3D image that defines the spatial dimensions for the density - map. Returns ------- @@ -91,12 +45,12 @@ def clean_by_other_density_map(this_bundle_sls, other_bundle_sls, This function computes a density map from `other_bundle_sls` to represent the spatial occupancy of the streamlines. It then calculates the probability of each streamline in `this_bundle_sls` overlapping with this map. - Streamlines that overlap in more than `node_thresh` nodes are flagged for - removal. + Streamlines that overlap in less than `overlap` nodes are flagged for + removal (or more, if remove is True). Examples -------- - >>> clean_idx = clean_by_other_density_map(bundle1, bundle2, 5, img) + >>> clean_idx = clean_by_other_density_map(bundle1, bundle2, 5, img, True) >>> cleaned_bundle = [s for i, s in enumerate(bundle1) if clean_idx[i]] """ other_bundle_density_map = dtu.density_map( @@ -105,11 +59,15 @@ def clean_by_other_density_map(this_bundle_sls, other_bundle_sls, other_bundle_density_map, this_bundle_sls, np.eye(4)) cleaned_idx = np.zeros(len(this_bundle_sls), dtype=np.bool_) for ii, fp in enumerate(fiber_probabilities): - cleaned_idx[ii] = np.sum(np.asarray(fp) >= 1) <= node_thresh + if remove: + cleaned_idx[ii] = np.sum(np.asarray(fp) >= 1) <= overlap + else: + cleaned_idx[ii] = np.sum(np.asarray(fp) >= 1) > overlap return cleaned_idx -def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine): +def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine, + entire): """ Removes streamlines from a set that lie on the opposite side of a specified core axis compared to another set of streamlines. @@ -126,6 +84,10 @@ def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine): An array of reference streamlines to define the core. affine : ndarray The affine transformation matrix. + entire : bool + If True, the entire streamline must lie on the correct side of the core + to be retained. If False, only the closest point on the streamline to + the core is considered. Returns ------- @@ -193,6 +155,18 @@ def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine): core_bundle = np.median(other_fgarray, axis=0) cleaned_idx_core = np.zeros(this_fgarray.shape[0], dtype=np.bool_) for ii, sl in enumerate(this_fgarray): - cleaned_idx_core[ii] = np.all( - core_direc * (sl[:, core_axis] - core_bundle[:, core_axis]) > 0) + if entire: + cleaned_idx_core[ii] = np.all( + core_direc * ( + sl[:, core_axis] - core_bundle[:, core_axis]) > 0) + else: + dist_matrix = cdist(core_bundle, sl, 'sqeuclidean') + min_dist_indices = np.unravel_index(np.argmin(dist_matrix), + dist_matrix.shape) + closest_core = core_bundle[min_dist_indices[0], core_axis] + closest_sl = sl[min_dist_indices[1], core_axis] + + cleaned_idx_core[ii] = core_direc * ( + closest_sl - closest_core) > 0 + return cleaned_idx_core From 384a9491fe352e97ba9f17226ef7060192381e57 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 15:45:24 -0700 Subject: [PATCH 027/116] bf --- AFQ/recognition/criteria.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 7749ee462..0ff7ad78a 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -318,7 +318,7 @@ def clean_by_other_bundle(b_sls, bundle_def, if 'entire_core' in bundle_def[other_bundle_name]: cleaned_idx_core = abo.clean_relative_to_other_core( - bundle_def[other_bundle_name]['core'].lower(), + bundle_def[other_bundle_name]['entire_core'].lower(), preproc_imap["fgarray"][b_sls.selected_fiber_idxs], np.array(abu.resample_tg(other_bundle_sls, 20)), img.affine, True) From 09efa13562c9381ef0095d45123bc4315d4a2c2d Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 16:14:46 -0700 Subject: [PATCH 028/116] try inclusive core --- AFQ/api/bundle_dict.py | 4 ++-- AFQ/recognition/criteria.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index 3f215ff5d..c6035a637 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -252,7 +252,7 @@ def default18_bd(): 'node_thresh': 20, 'entire_core': 'Anterior'}, 'Left Inferior Longitudinal': { - 'core': 'Right'}, + 'inclusive_core': 'Right'}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', @@ -266,7 +266,7 @@ def default18_bd(): 'node_thresh': 20, 'entire_core': 'Anterior'}, 'Right Inferior Longitudinal': { - 'core': 'Left'}, + 'inclusive_core': 'Left'}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 0ff7ad78a..2a741ee0d 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -324,6 +324,20 @@ def clean_by_other_bundle(b_sls, bundle_def, img.affine, True) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_core) + if 'inclusive_core' in bundle_def[other_bundle_name]: + cleaned_idx_core = abo.clean_relative_to_other_core( + bundle_def[other_bundle_name]['inclusive_core'].lower(), + preproc_imap["fgarray"][b_sls.selected_fiber_idxs], + np.array(abu.resample_tg(other_bundle_sls, 20)), + img.affine, False) + cleaned_idx_overlap = abo.clean_by_overlap( + b_sls.get_selected_sls(), + other_bundle_sls, + 50, + img, False) + cleaned_idx = np.logical_and(cleaned_idx, np.logical_or( + cleaned_idx_core, cleaned_idx_overlap)) + b_sls.select(cleaned_idx, other_bundle_name) From 7caaac4e0970dbfff18d1843fb2a78f5613f595d Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 9 Jul 2025 16:38:38 -0700 Subject: [PATCH 029/116] adjust --- AFQ/recognition/criteria.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 2a741ee0d..f20561566 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -333,7 +333,7 @@ def clean_by_other_bundle(b_sls, bundle_def, cleaned_idx_overlap = abo.clean_by_overlap( b_sls.get_selected_sls(), other_bundle_sls, - 50, + 20, img, False) cleaned_idx = np.logical_and(cleaned_idx, np.logical_or( cleaned_idx_core, cleaned_idx_overlap)) From 31a205e878ab1a9015a765e337a6a2e8715a0abd Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 14 Jul 2025 09:40:29 -0700 Subject: [PATCH 030/116] use IFOF --- AFQ/api/bundle_dict.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index c6035a637..b8a716b78 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -251,8 +251,8 @@ def default18_bd(): 'Left Posterior Arcuate': { 'node_thresh': 20, 'entire_core': 'Anterior'}, - 'Left Inferior Longitudinal': { - 'inclusive_core': 'Right'}, + 'Left Inferior Fronto-occipital': { + 'core': 'Right'}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', @@ -265,8 +265,8 @@ def default18_bd(): 'Right Posterior Arcuate': { 'node_thresh': 20, 'entire_core': 'Anterior'}, - 'Right Inferior Longitudinal': { - 'inclusive_core': 'Left'}, + 'Right Inferior Fronto-occipital': { + 'core': 'Left'}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', From d4bcc165c77c35d9918f7a24574ea2e5566ee1e9 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 14 Jul 2025 10:17:19 -0700 Subject: [PATCH 031/116] remove unused code --- AFQ/recognition/criteria.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index f20561566..0ff7ad78a 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -324,20 +324,6 @@ def clean_by_other_bundle(b_sls, bundle_def, img.affine, True) cleaned_idx = np.logical_and(cleaned_idx, cleaned_idx_core) - if 'inclusive_core' in bundle_def[other_bundle_name]: - cleaned_idx_core = abo.clean_relative_to_other_core( - bundle_def[other_bundle_name]['inclusive_core'].lower(), - preproc_imap["fgarray"][b_sls.selected_fiber_idxs], - np.array(abu.resample_tg(other_bundle_sls, 20)), - img.affine, False) - cleaned_idx_overlap = abo.clean_by_overlap( - b_sls.get_selected_sls(), - other_bundle_sls, - 20, - img, False) - cleaned_idx = np.logical_and(cleaned_idx, np.logical_or( - cleaned_idx_core, cleaned_idx_overlap)) - b_sls.select(cleaned_idx, other_bundle_name) From 2456b468167fb8305fa3eee12e948d472dff5958 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 15 Jul 2025 13:10:03 -0700 Subject: [PATCH 032/116] try some of this mahal orientation stuff --- AFQ/api/bundle_dict.py | 6 +++++ AFQ/recognition/cleaning.py | 50 +++++++++++++++++++++++++++++++++++++ AFQ/recognition/criteria.py | 14 +++++++++-- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index b8a716b78..c4fa1b56b 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -253,6 +253,9 @@ def default18_bd(): 'entire_core': 'Anterior'}, 'Left Inferior Fronto-occipital': { 'core': 'Right'}, + 'orient_mahal': { + 'distance_threshold': 3, + 'clean_rounds': 5}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', @@ -267,6 +270,9 @@ def default18_bd(): 'entire_core': 'Anterior'}, 'Right Inferior Fronto-occipital': { 'core': 'Left'}, + 'orient_mahal': { + 'distance_threshold': 3, + 'clean_rounds': 5}, 'isolation_forest': { 'percent_outlier_thresh': 50}, 'primary_axis': 'I/S', diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 0d4e15882..5013b6ac8 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -67,6 +67,56 @@ def clean_by_orientation(streamlines, primary_axis, affine, tol=None): return cleaned_idx +def clean_by_orientation_mahalanobis(tg, n_points=100, + core_only=True, min_sl=20, + distance_threshold=3, + clean_rounds=5): + fgarray = np.array(abu.resample_tg(tg.streamlines, n_points)) + + if core_only: + fgarray = fgarray[:, + int(n_points * 0.2):int(n_points * 0.8), + :] # Crop to middle 60% + + fgarray_dists = fgarray[:, 1:, :] - fgarray[:, :-1, :] + idx = np.arange(len(fgarray)) + rounds_elapsed = 0 + idx_dist = idx + while rounds_elapsed < clean_rounds: + # This calculates the Mahalanobis for each streamline/node: + m_dist = gaussian_weights( + fgarray_dists, return_mahalnobis=True, + n_points=None, stat="mean") + logger.debug(f"Shape of fgarray: {np.asarray(fgarray_dists).shape}") + logger.debug(( + f"Maximum m_dist for each fiber: " + f"{np.max(m_dist, axis=1)}")) + + if not (np.any(m_dist >= distance_threshold)): + break + idx_dist = np.all(m_dist < distance_threshold, axis=-1) + + if np.sum(idx_dist) < min_sl: + # need to sort and return exactly min_sl: + idx = idx[np.argsort(np.sum( + m_dist, axis=-1))[:min_sl].astype(int)] + logger.debug(( + f"At rounds elapsed {rounds_elapsed}, " + "minimum streamlines reached")) + break + else: + # Update by selection: + idx = idx[idx_dist] + fgarray_dists = fgarray_dists[idx_dist] + rounds_elapsed += 1 + logger.debug(( + f"Rounds elapsed: {rounds_elapsed}, " + f"num kept: {len(idx)}")) + logger.debug(f"Kept indicies: {idx}") + + return idx + + def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, length_threshold=4, min_sl=20, stat='mean', core_only=True, return_idx=False): diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 0ff7ad78a..60e5fe891 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -29,7 +29,7 @@ criteria_order_post_other_bundles = [ - "isolation_forest", "qb_thresh"] + "orient_mahal", "isolation_forest", "qb_thresh"] valid_noncriterion = [ @@ -327,6 +327,14 @@ def clean_by_other_bundle(b_sls, bundle_def, b_sls.select(cleaned_idx, other_bundle_name) +def orient_mahal(b_sls, bundle_def): + b_sls.initiate_selection("orient_mahal") + accept_idx = abc.clean_by_orientation_mahalanobis( + b_sls.get_selected_sls(), + **bundle_def.get("orient_mahal", {})) + b_sls.select(accept_idx, "orient_mahal") + + def isolation_forest(b_sls, bundle_def, parallel_segmentation, **kwargs): b_sls.initiate_selection("isolation_forest") if parallel_segmentation["engine"] == "serial": @@ -441,7 +449,9 @@ def check_space(roi): if b_sls and criterion in bundle_def: inputs[criterion] = globals()[criterion](**inputs) if b_sls: - if "mahal" in bundle_def or "isolation_forest" not in bundle_def: + if "mahal" in bundle_def or ( + "isolation_forest" not in bundle_def + and "orient_mahal" not in bundle_def): mahalanobis(**inputs) if b_sls and not b_sls.oriented_yet: From 279d1f7e1a6540d0053b6f5f3bfb0dfad7755df2 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 16 Jul 2025 11:15:51 -0700 Subject: [PATCH 033/116] bf --- AFQ/recognition/criteria.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 60e5fe891..312ba1f56 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -327,7 +327,7 @@ def clean_by_other_bundle(b_sls, bundle_def, b_sls.select(cleaned_idx, other_bundle_name) -def orient_mahal(b_sls, bundle_def): +def orient_mahal(b_sls, bundle_def, **kwargs): b_sls.initiate_selection("orient_mahal") accept_idx = abc.clean_by_orientation_mahalanobis( b_sls.get_selected_sls(), From baed4d087f5c227fbeee14ab67c7579ebeabd198 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 16 Jul 2025 11:22:37 -0700 Subject: [PATCH 034/116] bf --- AFQ/recognition/cleaning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 5013b6ac8..e91e273c6 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -67,11 +67,11 @@ def clean_by_orientation(streamlines, primary_axis, affine, tol=None): return cleaned_idx -def clean_by_orientation_mahalanobis(tg, n_points=100, +def clean_by_orientation_mahalanobis(streamlines, n_points=100, core_only=True, min_sl=20, distance_threshold=3, clean_rounds=5): - fgarray = np.array(abu.resample_tg(tg.streamlines, n_points)) + fgarray = np.array(abu.resample_tg(streamlines, n_points)) if core_only: fgarray = fgarray[:, From 3cb49c7c27dc9b0c29952ce85bdec62e73cf80ea Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 16 Jul 2025 11:42:07 -0700 Subject: [PATCH 035/116] bf --- AFQ/recognition/cleaning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index e91e273c6..617453095 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -86,7 +86,7 @@ def clean_by_orientation_mahalanobis(streamlines, n_points=100, # This calculates the Mahalanobis for each streamline/node: m_dist = gaussian_weights( fgarray_dists, return_mahalnobis=True, - n_points=None, stat="mean") + n_points=None, stat=np.mean) logger.debug(f"Shape of fgarray: {np.asarray(fgarray_dists).shape}") logger.debug(( f"Maximum m_dist for each fiber: " @@ -118,7 +118,7 @@ def clean_by_orientation_mahalanobis(streamlines, n_points=100, def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, - length_threshold=4, min_sl=20, stat='mean', + length_threshold=4, min_sl=20, stat=np.mean, core_only=True, return_idx=False): """ Clean a segmented fiber group based on the Mahalnobis distance of From 40af9e3106ecd72b44f0f9def40d987bc75bc04f Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 16 Jul 2025 19:48:48 -0700 Subject: [PATCH 036/116] allow vtk --- AFQ/tasks/segmentation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index 21ece86d2..5e4cf5886 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -58,7 +58,9 @@ def segment(data_imap, mapping_imap, bundle_dict = data_imap["bundle_dict"] reg_template = data_imap["reg_template"] streamlines = tractography_imap["streamlines"] - if streamlines.endswith(".trk") or streamlines.endswith(".tck"): + if streamlines.endswith(".trk") or\ + streamlines.endswith(".tck") or\ + streamlines.endswith(".vtk"): tg = load_tractogram( streamlines, data_imap["dwi"], Space.VOX, bbox_valid_check=False) From 855955a12ca292b5cb33d61a21d7df95e930a8a7 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 21 Jul 2025 19:27:15 -0700 Subject: [PATCH 037/116] add asymmetry maps --- AFQ/models/asym_filtering.py | 144 ++++++++++++++++++++++++++++++++++- AFQ/tasks/data.py | 97 ++++++++++++++++++++++- 2 files changed, 236 insertions(+), 5 deletions(-) diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index d68a472db..67d043617 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -13,13 +13,15 @@ from numba import njit, prange, set_num_threads import ray -from dipy.reconst.shm import sh_to_sf_matrix +from dipy.reconst.shm import sh_to_sf_matrix, sph_harm_ind_list, sh_to_sf +from dipy.direction import peak_directions from dipy.data import get_sphere from AFQ.utils.stats import chunk_indices -__all__ = ["unified_filtering"] +__all__ = ["unified_filtering", "compute_asymmetry_index", + "compute_odd_power_map", "compute_nufid_asym"] def _get_sh_order_and_fullness(ncoeffs): @@ -429,3 +431,141 @@ def _evaluate_gaussian_distribution(x, sigma): raise ValueError("Sigma must be greater than 0.") cnorm = 1.0 / sigma / np.sqrt(2.0 * np.pi) return cnorm * np.exp(-x**2 / 2.0 / sigma**2) + + +def compute_asymmetry_index(sh_coeffs, mask): + """ + Compute asymmetry index (ASI) [1] from + asymmetric ODF volume expressed in full SH basis. + + Parameters + ---------- + sh_coeffs: ndarray (x, y, z, ncoeffs) + Input spherical harmonics coefficients. + mask: ndarray (x, y, z), bool + Mask inside which ASI should be computed. + + Returns + ------- + asi_map: ndarray (x, y, z) + Asymmetry index map. + + References + ---------- + [1] 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. + """ + order, full_basis = _get_sh_order_and_fullness(sh_coeffs.shape[-1]) + + _, l_list = sph_harm_ind_list(order, full_basis=full_basis) + + sign = np.power(-1.0, l_list) + sign = np.reshape(sign, (1, 1, 1, len(l_list))) + sh_squared = sh_coeffs**2 + mask = np.logical_and(sh_squared.sum(axis=-1) > 0., mask) + + asi_map = np.zeros(sh_coeffs.shape[:-1]) + asi_map[mask] = np.sum(sh_squared * sign, axis=-1)[mask] / \ + np.sum(sh_squared, axis=-1)[mask] + + # Negatives should not happen (amplitudes always positive) + asi_map = np.clip(asi_map, 0.0, 1.0) + asi_map = np.sqrt(1 - asi_map**2) * mask + + return asi_map + + +def compute_odd_power_map(sh_coeffs, mask): + """ + Compute odd-power map [1] from + asymmetric ODF volume expressed in full SH basis. + + Parameters + ---------- + sh_coeffs: ndarray (x, y, z, ncoeffs) + Input spherical harmonics coefficients. + mask: ndarray (x, y, z), bool + Mask inside which odd-power map should be computed. + + Returns + ------- + odd_power_map: ndarray (x, y, z) + Odd-power map. + + References + ---------- + [1] 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. + """ + order, full_basis = _get_sh_order_and_fullness(sh_coeffs.shape[-1]) + _, l_list = sph_harm_ind_list(order, full_basis=full_basis) + odd_l_list = (l_list % 2 == 1).reshape((1, 1, 1, -1)) + + odd_order_norm = np.linalg.norm(sh_coeffs * odd_l_list, + ord=2, axis=-1) + + full_order_norm = np.linalg.norm(sh_coeffs, ord=2, axis=-1) + + asym_map = np.zeros(sh_coeffs.shape[:-1]) + mask = np.logical_and(full_order_norm > 0, mask) + asym_map[mask] = odd_order_norm[mask] / full_order_norm[mask] + + return asym_map + + +def compute_nufid_asym(sh_coeffs, sphere, csf, mask): + """ + Number of fiber directions (nufid) map [1]. + + Parameters + ---------- + sh_coeffs: ndarray (x, y, z, ncoeffs) + Input spherical harmonics coefficients. + + sphere: DIPY sphere + Sphere for SH to SF projection. + + csf: ndarray (x, y, z) + CSF probability map, used to guess the absolute threshold. + + mask: ndarray (x, y, z), bool + Mask inside which ASI should be computed. + + 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 + """ + sh_order, full_basis = _get_sh_order_and_fullness(sh_coeffs.shape[-1]) + odf = sh_to_sf( + sh_coeffs, sphere, + sh_order_max=sh_order, + basis_type='descoteaux07', + full_basis=full_basis, + legacy=False) + + # Guess at threshold from 2.0 * mean of ODF maxes in CSF + absolute_threshold = 2.0 * np.mean(np.max(odf[csf > 0.99], axis=-1)) + odf[odf < absolute_threshold] = 0. + + nufid_data = np.zeros(sh_coeffs.shape[:-1], dtype=np.float32) + for ii in tqdm(range(sh_coeffs.shape[0])): + for jj in range(sh_coeffs.shape[1]): + for kk in range(sh_coeffs.shape[2]): + if mask[ii, jj, kk]: + _, peaks, _ = peak_directions( + odf[ii, jj, kk], sphere, + is_symmetric=False) + + nufid_data[ii, jj, kk] = np.count_nonzero(peaks) + + return nufid_data diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 4aaec95c7..44343c8dc 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -47,7 +47,9 @@ from AFQ.models.dam import fit_dam, csf_dam, t1_dam from AFQ.models.wmgm_interface import fit_wm_gm_interface from AFQ.models.msmt import MultiShellDeconvModel - +from AFQ.models.asym_filtering import ( + unified_filtering, compute_asymmetry_index, + compute_odd_power_map, compute_nufid_asym) logger = logging.getLogger('AFQ') @@ -643,8 +645,6 @@ def msmt_aodf(msmtcsd_params): Estimating Asymmetric Orientation Distribution Functions", Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 """ - from AFQ.models.asym_filtering import unified_filtering - sh_coeff = nib.load(msmtcsd_params).get_fdata() logger.info("Applying unified filtering to MSMT CSD ODFs...") @@ -657,6 +657,96 @@ def msmt_aodf(msmtcsd_params): Sphere="repulsion724") +@immlib.calc("msmt_aodf_asi") +@as_file(suffix='_model-msmtcsd_param-asi_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_aodf_asi(msmt_aodf_params, brain_mask): + """ + full path to a nifti file containing + the MSMT CSD Asymmetric Index (ASI) [1] + + References + ---------- + [1] 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. + """ + + aodf = nib.load(msmt_aodf_params).get_fdata() + brain_mask = nib.load(brain_mask).get_fdata().astype(bool) + asi = compute_asymmetry_index(aodf, brain_mask) + + return asi, dict(MSMTCSDParamsFile=msmt_aodf_params) + + +@immlib.calc("msmt_aodf_opm") +@as_file(suffix='_model-msmtcsd_param-opm_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_aodf_opm(msmt_aodf_params, brain_mask): + """ + full path to a nifti file containing + the MSMT CSD odd-power map [1] + + References + ---------- + [1] 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. + """ + + aodf = nib.load(msmt_aodf_params).get_fdata() + brain_mask = nib.load(brain_mask).get_fdata().astype(bool) + opm = compute_odd_power_map(aodf, brain_mask) + + return opm, dict(MSMTCSDParamsFile=msmt_aodf_params) + + +@immlib.calc("msmt_aodf_nufid") +@as_file(suffix='_model-msmtcsd_param-nufid_dwimap.nii.gz', + subfolder="models") +@as_img +def msmt_aodf_nufid(msmt_aodf_params, brain_mask, + t1w_pve): + """ + full path to a nifti file containing + the MSMT CSD Number of fiber directions (nufid) map [1] + + 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 + """ + pve_img = nib.load(t1w_pve) + pve_data = pve_img.get_fdata() + + aodf_img = nib.load(msmt_aodf_params) + aodf = aodf_img.get_fdata() + + csf = resample(pve_data[..., 0], aodf[..., 0], + pve_img.affine, aodf_img.affine).get_fdata() + + # Only sphere we use for AODF currently + sphere = get_sphere(name="repulsion724") + + brain_mask = nib.load(brain_mask).get_fdata().astype(bool) + + logger.info("Number of fiber directions (nufid) map from AODF...") + nufid = compute_nufid_asym(aodf, sphere, csf, brain_mask) + + return nufid, dict( + MSMTCSDParamsFile=msmt_aodf_params, + PVE=t1w_pve) + + @immlib.calc("csd_params") @as_file(suffix='_model-csd_param-fod_dwimap.nii.gz', subfolder="models") @@ -1585,6 +1675,7 @@ def get_data_plan(kwargs): dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, csd_anisotropic_index, msmt_params, msmt_apm, msmt_aodf, + msmt_aodf_asi, msmt_aodf_opm, msmt_aodf_nufid, dti_fa, dti_lt, dti_cfa, dti_pdd, dti_md, dki_kt, dki_lt, dki_fa, gq, gq_pmap, gq_ai, opdt_params, opdt_pmap, opdt_ai, csa_params, csa_pmap, csa_ai, From d74efffaf49a3c43135077cebe0e03cbf999132b Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 21 Jul 2025 19:51:42 -0700 Subject: [PATCH 038/116] add spaces to log message --- AFQ/recognition/recognize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AFQ/recognition/recognize.py b/AFQ/recognition/recognize.py index 92a6ef216..5c401bf2f 100644 --- a/AFQ/recognition/recognize.py +++ b/AFQ/recognition/recognize.py @@ -206,8 +206,8 @@ def recognize( logger.warning(( "Conflicts in bundle assignment detected. " f"{conflicts} conflicts detected in total out of " - f"{n_streamlines} total streamlines." - "Defaulting to whichever bundle appears first" + f"{n_streamlines} total streamlines. " + "Defaulting to whichever bundle appears first " "in the bundle_dict.")) bundle_decisions = np.concatenate(( bundle_decisions, np.ones((n_streamlines, 1))), axis=1) From 626966033a9fb25eb693bb7e677028939e7fb34b Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 21 Jul 2025 20:49:35 -0700 Subject: [PATCH 039/116] endpoint distance map --- AFQ/tasks/segmentation.py | 57 ++++++++++++++++++++++++++++++++++++++- AFQ/utils/volume.py | 2 +- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index 5e4cf5886..99a83d0dd 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -16,6 +16,7 @@ from AFQ.tasks.utils import get_default_args import AFQ.utils.volume as auv from AFQ._fixes import gaussian_weights +import AFQ.recognition.utils as abu try: from trx.io import load as load_trx @@ -27,17 +28,23 @@ from dipy.io.streamline import load_tractogram, save_tractogram from dipy.io.stateful_tractogram import Space +from dipy.io.utils import get_reference_info from dipy.stats.analysis import afq_profile from dipy.tracking.streamline import set_number_of_points, values_from_volume from nibabel.affines import voxel_sizes from nibabel.orientations import aff2axcodes from dipy.io.stateful_tractogram import StatefulTractogram +from dipy.align import resample +from dipy.tracking.utils import density_map import gzip import shutil import os.path as op from tempfile import mkdtemp +from scipy.ndimage import distance_transform_edt + + logger = logging.getLogger('AFQ') @@ -274,8 +281,55 @@ def export_density_maps(bundles, data_imap): source=bundles, bundles=list(seg_sft.bundle_names)) -@immlib.calc("profiles") @as_file('_desc-profiles_tractography.csv') +@immlib.calc("endpoint_maps") +@as_file('_desc-endpoints_tractography.nii.gz', + include_track=True, + include_seg=True) +def export_endpoint_maps(bundles, data_imap): + """ + full path to a NIfTI file containing endpoint maps for each bundle + """ + seg_sft = aus.SegmentedSFT.fromfile(bundles) + entire_endpoint_map = np.zeros(( + *data_imap["data"].shape[:3], + len(seg_sft.bundle_names))) + + b0_img = nib.load(data_imap["b0"]) + pve_img = nib.load(data_imap["t1w_pve"]) + pve_data = pve_img.get_fdata() + gm = resample(pve_data[..., 1], b0_img.get_fdata(), + pve_img.affine, b0_img.affine).get_fdata() + + R = b0_img.affine[0:3, 0:3] + vox_to_mm = np.mean(np.diag(np.linalg.cholesky(R.T.dot(R)))) + + for ii, bundle_name in enumerate(seg_sft.bundle_names): + bundle_sl = seg_sft.get_bundle(bundle_name) + if len(bundle_sl.streamlines) == 0: + continue + + bundle_sl.to_vox() + + endpoints = np.vstack([s[0] for s in bundle_sl.streamlines] + + [s[-1] for s in bundle_sl.streamlines]) + + tractogram_density = density_map( + endpoints, np.eye(4), b0_img.get_fdata().shape) + + tractogram_distance = distance_transform_edt( + tractogram_density == 0) + + entire_endpoint_map[..., ii] = tractogram_distance * ( + gm > 0.5).astype(np.float32) * vox_to_mm + + return nib.Nifti1Image( + entire_endpoint_map, data_imap["dwi_affine"]), dict( + source=bundles, bundles=list(seg_sft.bundle_names)) + + +@immlib.calc("profiles") +@as_file('_desc-profiles_tractography.csv', include_track=True, include_seg=True) def tract_profiles(bundles, scalar_dict, data_imap, profile_weights="gauss", @@ -436,6 +490,7 @@ def get_segmentation_plan(kwargs): export_bundle_lengths, export_bundles, export_density_maps, + export_endpoint_maps, segment, tract_profiles]) diff --git a/AFQ/utils/volume.py b/AFQ/utils/volume.py index fd399b64d..5cc57782e 100644 --- a/AFQ/utils/volume.py +++ b/AFQ/utils/volume.py @@ -2,7 +2,7 @@ import numpy as np import scipy.ndimage as ndim -from skimage.morphology import binary_dilation, convex_hull_image +from skimage.morphology import binary_dilation from scipy.spatial.distance import dice import nibabel as nib From fc9717301ff033706d08b56db9939614fd74f052 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 23 Jul 2025 13:17:06 -0700 Subject: [PATCH 040/116] BFs --- AFQ/api/group.py | 20 ++++++-- AFQ/api/participant.py | 16 +++++-- AFQ/api/utils.py | 5 ++ AFQ/models/asym_filtering.py | 88 ++++++++++++++++++------------------ AFQ/tasks/data.py | 29 +++++++++++- AFQ/utils/path.py | 40 +++++++++++----- 6 files changed, 136 insertions(+), 62 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 6ccba211a..5ce033369 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -630,8 +630,8 @@ def export_all(self, viz=True, afqbrowser=True, xforms=True, self.logger.info( f"Time taken for export all: {str(time() - start_time)}") - def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], - suffix=""): + def cmd_outputs(self, cmd="rm", dependent_on=None, up_to=None, + exceptions=[], suffix=""): """ Perform some command some or all outputs of pyafq. This is useful if you change a parameter and need @@ -651,6 +651,15 @@ def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], If "recog", perform on all derivatives that depend on the bundle recognition. Default: None + up_to : str or None + If None, will perform on all derivatives. + If "track", will perform on all derivatives up to + (but not including) tractography. + If "recog", will perform on all derivatives up to + (but not including) bundle recognition. + If "prof", will perform on all derivatives up to + (but not including) bundle profiling. + Default: None exceptions : list of str Name outputs that the command should not be applied to. Default: [] @@ -668,7 +677,12 @@ def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], suffix="~/my_other_folder/") """ for pAFQ in self.pAFQ_list: - pAFQ.cmd_outputs(cmd, dependent_on, exceptions, suffix=suffix) + pAFQ.cmd_outputs( + cmd=cmd, + dependent_on=dependent_on, + up_to=up_to, + exceptions=exceptions, + suffix=suffix) clobber = cmd_outputs # alias for default of cmd_outputs diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index 17a4fc5b3..de709c4c3 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -372,8 +372,8 @@ def _save_file(curr_img): _save_file(curr_img) return all_fnames - def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], - suffix=""): + def cmd_outputs(self, cmd="rm", dependent_on=None, up_to=None, + exceptions=[], suffix=""): """ Perform some command some or all outputs of pyafq. This is useful if you change a parameter and need @@ -395,6 +395,15 @@ def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], If "prof", perform on all derivatives that depend on the bundle profiling. Default: None + up_to : str or None + If None, will perform on all derivatives. + If "track", will perform on all derivatives up to + (but not including) tractography. + If "recog", will perform on all derivatives up to + (but not including) bundle recognition. + If "prof", will perform on all derivatives up to + (but not including) bundle profiling. + Default: None exceptions : list of str Name outputs that the command should not be applied to. Default: [] @@ -418,7 +427,8 @@ def cmd_outputs(self, cmd="rm", dependent_on=None, exceptions=[], cmd=cmd, exception_file_names=exception_file_names, suffix=suffix, - dependent_on=dependent_on + dependent_on=dependent_on, + up_to=up_to, ) # do not assume previous calculations are still valid diff --git a/AFQ/api/utils.py b/AFQ/api/utils.py index 4264233d5..542e3b9dc 100644 --- a/AFQ/api/utils.py +++ b/AFQ/api/utils.py @@ -36,10 +36,15 @@ "dwi_data_file": "data", "bval_file": "data", "bvec_file": "data", + "t1_file": "data", "output_dir": "data", "best_scalar": "tractography", "base_fname": "data", + "pve_wm": "tractography", + "pve_gm": "tractography", + "pve_csf": "tractography", } + kwargs_descriptors = {} for task_module in task_modules: kwargs_descriptors[task_module] = {} diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index 67d043617..1cdd942da 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -10,7 +10,7 @@ import multiprocessing from tqdm import tqdm -from numba import njit, prange, set_num_threads +from numba import njit, prange, set_num_threads, config import ray from dipy.reconst.shm import sh_to_sf_matrix, sph_harm_ind_list, sh_to_sf @@ -277,6 +277,8 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, sh_data = np.ascontiguousarray(sh_data, dtype=np.float64) B_mat = np.ascontiguousarray(B_mat, dtype=np.float64) + config.THREADING_LAYER = "workqueue" + h_w, h_h, h_d = nx_filter.shape[:3] half_w, half_h, half_d = h_w // 2, h_h // 2, h_d // 2 sh_data_padded = np.ascontiguousarray(np.pad( @@ -285,48 +287,48 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, mode='constant' ), dtype=np.float64) - # Apply filter to each sphere vertice - if n_cpus > 1: - ray.init(ignore_reinit_error=True) - - sh_data_id = ray.put(sh_data) - sh_data_padded_id = ray.put(sh_data_padded) - nx_filter_id = ray.put(nx_filter) - uv_filter_id = ray.put(uv_filter) - B_mat_id = ray.put(B_mat) - - @ray.remote(num_cpus=n_cpus) - def _correlate_batch_remote(batch_u_ids, sh_data, sh_data_padded, - nx_filter, uv_filter, sigma_range, B_mat): - results = [] - for u_sph_id in batch_u_ids: - corr = _correlate( - sh_data, sh_data_padded, nx_filter, - uv_filter, sigma_range, u_sph_id, B_mat - ) - results.append((u_sph_id, corr)) - return results - - all_u_ids = list(range(nb_sf)) - futures = [ - _correlate_batch_remote.remote( - batch, sh_data_id, sh_data_padded_id, - nx_filter_id, uv_filter_id, - sigma_range, B_mat_id - ) - for batch in chunk_indices(all_u_ids, n_cpus * 2) - ] - - # Gather and write results - for future in tqdm(futures): - batch_results = ray.get(future) - for u_sph_id, correlation in batch_results: - mean_sf[..., u_sph_id] = correlation - else: - for u_sph_id in tqdm(range(nb_sf)): - mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, - nx_filter, uv_filter, - sigma_range, u_sph_id, B_mat) + # # Apply filter to each sphere vertice + # if n_cpus > 1: + # ray.init(ignore_reinit_error=True) + + # sh_data_id = ray.put(sh_data) + # sh_data_padded_id = ray.put(sh_data_padded) + # nx_filter_id = ray.put(nx_filter) + # uv_filter_id = ray.put(uv_filter) + # B_mat_id = ray.put(B_mat) + + # @ray.remote(num_cpus=n_cpus) + # def _correlate_batch_remote(batch_u_ids, sh_data, sh_data_padded, + # nx_filter, uv_filter, sigma_range, B_mat): + # results = [] + # for u_sph_id in batch_u_ids: + # corr = _correlate( + # sh_data, sh_data_padded, nx_filter, + # uv_filter, sigma_range, u_sph_id, B_mat + # ) + # results.append((u_sph_id, corr)) + # return results + + # all_u_ids = list(range(nb_sf)) + # futures = [ + # _correlate_batch_remote.remote( + # batch, sh_data_id, sh_data_padded_id, + # nx_filter_id, uv_filter_id, + # sigma_range, B_mat_id + # ) + # for batch in chunk_indices(all_u_ids, n_cpus * 2) + # ] + + # # Gather and write results + # for future in tqdm(futures): + # batch_results = ray.get(future) + # for u_sph_id, correlation in batch_results: + # mean_sf[..., u_sph_id] = correlation + # else: + for u_sph_id in tqdm(range(nb_sf)): + mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, + nx_filter, uv_filter, + sigma_range, u_sph_id, B_mat) out_sh = np.array([np.dot(i, B_inv) for i in mean_sf], dtype=sh_data.dtype) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 44343c8dc..cdb0008f5 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -821,6 +821,33 @@ def csd_params(dwi, brain_mask, gtab, data, return csdf.shm_coeff, meta +@immlib.calc("csd_aodf_params") +@as_file(suffix='_model-csd_param-aodf_dwimap.nii.gz', + subfolder="models") +@as_img +def csd_aodf(csd_params): + """ + full path to a nifti file containing + SSST CSD ODFs filtered by unified filtering [1] + + References + ---------- + [1] Poirier and Descoteaux, 2024, "A Unified Filtering Method for + Estimating Asymmetric Orientation Distribution Functions", + Neuroimage, https://doi.org/10.1016/j.neuroimage.2024.120516 + """ + sh_coeff = nib.load(csd_params).get_fdata() + + logger.info("Applying unified filtering to CSD ODFs...") + aodf = unified_filtering( + sh_coeff, + get_sphere(name="repulsion724")) + + return aodf, dict( + CSDParamsFile=csd_params, + Sphere="repulsion724") + + @immlib.calc("csd_pmap") @as_file(suffix='_model-csd_param-apm_dwimap.nii.gz', subfolder="models") @@ -1673,7 +1700,7 @@ def get_data_plan(kwargs): t1w_pve, wm_gm_interface, dam_fit, dam_csf, dam_pseudot1, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, - csd_anisotropic_index, + csd_anisotropic_index, csd_aodf, msmt_params, msmt_apm, msmt_aodf, msmt_aodf_asi, msmt_aodf_opm, msmt_aodf_nufid, dti_fa, dti_lt, dti_cfa, dti_pdd, dti_md, dki_kt, dki_lt, dki_fa, diff --git a/AFQ/utils/path.py b/AFQ/utils/path.py index 6075e4212..f3312072b 100644 --- a/AFQ/utils/path.py +++ b/AFQ/utils/path.py @@ -56,19 +56,35 @@ def space_from_fname(dwi_fname): def apply_cmd_to_afq_derivs( derivs_dir, base_fname, cmd="rm", exception_file_names=[], suffix="", - dependent_on=None): - if dependent_on is None: - dependent_on_list = ["dwi", "trk", "rec", "prof"] - elif dependent_on.lower() == "track": - dependent_on_list = ["trk", "rec", "prof"] - elif dependent_on.lower() == "recog": - dependent_on_list = ["rec", "prof"] - elif dependent_on.lower() == "prof": - dependent_on_list = ["prof"] - else: + dependent_on=None, up_to=None): + dependent_options = { + None: ["dwi", "trk", "rec", "prof"], + "track": ["trk", "rec", "prof"], + "recog": ["rec", "prof"], + "prof": ["prof"] + } + if dependent_on is not None: + dependent_on = dependent_on.lower() + dependent_on_list = dependent_options.get(dependent_on) + if dependent_on_list is None: raise ValueError(( - "dependent_on must be one of " - "None, 'track', 'recog', 'prof'.")) + "dependent_on must be one of None, " + "'track', 'recog', 'prof'.")) + + removal_patterns = { + "track": ["trk", "rec", "prof"], + "recog": ["rec", "prof"], + "prof": ["prof"] + } + if up_to is not None: + up_to = up_to.lower() + if up_to in removal_patterns: + dependent_on_list = [item for item in dependent_on_list + if item not in removal_patterns[up_to]] + else: + raise ValueError(( + "up_to must be one of None, " + "'track', 'recog', 'prof'.")) if cmd == "rm" or cmd == "cp": cmd = cmd + " -r" From ade805640a6f601f61753712c490bf51d39c0f20 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 24 Jul 2025 14:28:35 -0700 Subject: [PATCH 041/116] gpu tracking compat with act --- AFQ/tasks/tractography.py | 2 +- AFQ/tractography/gputractography.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 3ccf0c7de..25f31da43 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -368,7 +368,7 @@ def gpu_tractography(data_imap, tracking_params, fodf, seed, stop, sft = gpu_track( data, data_imap["gtab"], - nib.load(seed), nib.load(stop), + seed, stop, tracking_params["odf_model"], sphere, tracking_params["directions"], diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index ebf8941dd..98b591a12 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -1,6 +1,7 @@ import cuslines import numpy as np +import nibabel as nib from math import radians from tqdm import tqdm import logging @@ -21,7 +22,7 @@ # Modified from https://github.com/dipy/GPUStreamlines/blob/master/run_dipy_gpu.py -def gpu_track(data, gtab, seed_img, stop_img, +def gpu_track(data, gtab, seed_path, stop_path, odf_model, sphere, directions, seed_threshold, stop_threshold, thresholds_as_percentages, max_angle, step_size, n_seeds, random_seeds, rng_seed, use_trx, ngpus, @@ -35,18 +36,18 @@ def gpu_track(data, gtab, seed_img, stop_img, DWI data. gtab : GradientTable The gradient table. - seed_img : Nifti1Image + seed_path : str Float or binary mask describing the ROI within which we seed for tracking. - stop_img : Nifti1Image + stop_path : str A float or binary mask that determines a stopping criterion (e.g. FA). odf_model : str, optional One of {"OPDT", "CSA"} seed_threshold : float - The value of the seed_img above which tracking is seeded. + The value of the seed_path above which tracking is seeded. stop_threshold : float - The value of the stop_img below which tracking is + The value of the stop_path below which tracking is terminated. thresholds_as_percentages : bool Interpret seed_threshold and stop_threshold as percentages of the @@ -80,6 +81,14 @@ def gpu_track(data, gtab, seed_img, stop_img, """ sh_order_max = 8 + # Roughly handle ACT/CMC for now + if isinstance(stop_threshold, str): + stop_threshold = 0.5 + stop_img = nib.load(stop_path[0]) + else: + stop_img = nib.load(stop_path) + + seed_img = nib.load(seed_path) seed_data = seed_img.get_fdata() stop_data = stop_img.get_fdata() From 4865ba1e5b8c2be4c740a651b6a84629d65115d6 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 24 Jul 2025 15:16:14 -0700 Subject: [PATCH 042/116] bf --- AFQ/tractography/gputractography.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index 98b591a12..a10ea55d1 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -84,7 +84,7 @@ def gpu_track(data, gtab, seed_path, stop_path, # Roughly handle ACT/CMC for now if isinstance(stop_threshold, str): stop_threshold = 0.5 - stop_img = nib.load(stop_path[0]) + stop_img = stop_path[0] else: stop_img = nib.load(stop_path) From 18fa7544853b866dcb08bfd951113ee2360b2a21 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 24 Jul 2025 16:21:02 -0700 Subject: [PATCH 043/116] bf --- AFQ/tractography/gputractography.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index a10ea55d1..4965165cf 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -9,6 +9,7 @@ from dipy.reconst.shm import OpdtModel, CsaOdfModel from dipy.reconst import shm from dipy.io.stateful_tractogram import StatefulTractogram, Space +from dipy.align import resample from nibabel.streamlines.array_sequence import concatenate from nibabel.streamlines.tractogram import Tractogram @@ -81,14 +82,21 @@ def gpu_track(data, gtab, seed_path, stop_path, """ sh_order_max = 8 + seed_img = nib.load(seed_path) + # Roughly handle ACT/CMC for now if isinstance(stop_threshold, str): - stop_threshold = 0.5 - stop_img = stop_path[0] + stop_threshold = 0.3 + stop_img = stop_path[0] # Grab WM + + stop_img = resample( + stop_img.get_fdata(), + seed_img.get_fdata(), + moving_affine=stop_img.affine, + static_affine=seed_img.affine) else: stop_img = nib.load(stop_path) - seed_img = nib.load(seed_path) seed_data = seed_img.get_fdata() stop_data = stop_img.get_fdata() From b7e1216c0c1753a863603b2edc06386e594e1d6d Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 25 Jul 2025 01:20:06 -0700 Subject: [PATCH 044/116] bf --- AFQ/tractography/gputractography.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index 4965165cf..ea5dc19bd 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -80,8 +80,6 @@ def gpu_track(data, gtab, seed_path, stop_path, Returns ------- """ - sh_order_max = 8 - seed_img = nib.load(seed_path) # Roughly handle ACT/CMC for now @@ -106,9 +104,30 @@ def gpu_track(data, gtab, seed_path, stop_path, theta = sphere.theta phi = sphere.phi - sampling_matrix, _, _ = shm.real_sym_sh_basis(sh_order_max, theta, phi) if directions == "boot": + sh_order_max = 6 + full_basis = False + else: + # Determine sh_order and full_basis + sym_order = (-3.0 + np.sqrt(1.0 + 8.0 * data.shape[3])) / 2.0 + if sym_order.is_integer(): + sh_order_max = sym_order + full_basis = False + full_order = np.sqrt(data.shape[3]) - 1.0 + if full_order.is_integer(): + sh_order_max = full_order + full_basis = True + + sampling_matrix, _, _ = shm.real_sh_descoteaux( + sh_order_max, theta, phi, + full_basis=full_basis, + legacy=False) + + if directions == "boot": + sh_order_max = 6 + full_basis = False + if odf_model.lower() == "opdt": model_type = cuslines.ModelType.OPDT model = OpdtModel( From 1e6e73ce38b97dd417ba68fc4396c373c5b48566 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 28 Jul 2025 15:41:34 -0700 Subject: [PATCH 045/116] Fix endpoint distance calculus, add example --- AFQ/tasks/segmentation.py | 34 +++- AFQ/tractography/gputractography.py | 3 - .../_static/endpoint_maps_threshold_2.png | Bin 0 -> 109391 bytes .../_static/endpoint_maps_threshold_3.png | Bin 0 -> 113169 bytes .../_static/endpoint_maps_threshold_4.png | Bin 0 -> 115835 bytes docs/source/reference/kwargs.rst | 30 +++ docs/source/reference/methods.rst | 76 +++++++ .../tutorial_examples/viz_008_endpoints.py | 189 ++++++++++++++++++ 8 files changed, 319 insertions(+), 13 deletions(-) create mode 100644 docs/source/_static/endpoint_maps_threshold_2.png create mode 100644 docs/source/_static/endpoint_maps_threshold_3.png create mode 100644 docs/source/_static/endpoint_maps_threshold_4.png create mode 100644 examples/tutorial_examples/viz_008_endpoints.py diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index 99a83d0dd..ffcb1304d 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -28,23 +28,20 @@ from dipy.io.streamline import load_tractogram, save_tractogram from dipy.io.stateful_tractogram import Space -from dipy.io.utils import get_reference_info from dipy.stats.analysis import afq_profile from dipy.tracking.streamline import set_number_of_points, values_from_volume from nibabel.affines import voxel_sizes from nibabel.orientations import aff2axcodes from dipy.io.stateful_tractogram import StatefulTractogram from dipy.align import resample -from dipy.tracking.utils import density_map +from scipy.spatial import cKDTree + import gzip import shutil import os.path as op from tempfile import mkdtemp -from scipy.ndimage import distance_transform_edt - - logger = logging.getLogger('AFQ') @@ -286,9 +283,17 @@ def export_density_maps(bundles, data_imap): @as_file('_desc-endpoints_tractography.nii.gz', include_track=True, include_seg=True) -def export_endpoint_maps(bundles, data_imap): +def export_endpoint_maps(bundles, data_imap, endpoint_threshold=3): """ full path to a NIfTI file containing endpoint maps for each bundle + + Parameters + ---------- + endpoint_threshold : float, optional + The threshold for the endpoint maps. + If None, no endpoint maps are exported as distance to endpoints maps, + which the user can then threshold as needed. + Default: 3 """ seg_sft = aus.SegmentedSFT.fromfile(bundles) entire_endpoint_map = np.zeros(( @@ -314,15 +319,24 @@ def export_endpoint_maps(bundles, data_imap): endpoints = np.vstack([s[0] for s in bundle_sl.streamlines] + [s[-1] for s in bundle_sl.streamlines]) - tractogram_density = density_map( - endpoints, np.eye(4), b0_img.get_fdata().shape) + shape = b0_img.get_fdata().shape + xv, yv, zv = np.meshgrid(np.arange(shape[0]), + np.arange(shape[1]), + np.arange(shape[2]), indexing='ij') + grid_points = np.column_stack([xv.ravel(), yv.ravel(), zv.ravel()]) - tractogram_distance = distance_transform_edt( - tractogram_density == 0) + kdtree = cKDTree(endpoints) + distances, _ = kdtree.query(grid_points) + tractogram_distance = distances.reshape(shape) entire_endpoint_map[..., ii] = tractogram_distance * ( gm > 0.5).astype(np.float32) * vox_to_mm + if endpoint_threshold is not None: + entire_endpoint_map = np.logical_and( + entire_endpoint_map < endpoint_threshold, + entire_endpoint_map != 0.0).astype(np.float32) + return nib.Nifti1Image( entire_endpoint_map, data_imap["dwi_affine"]), dict( source=bundles, bundles=list(seg_sft.bundle_names)) diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index ea5dc19bd..166ac2c04 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -125,9 +125,6 @@ def gpu_track(data, gtab, seed_path, stop_path, legacy=False) if directions == "boot": - sh_order_max = 6 - full_basis = False - if odf_model.lower() == "opdt": model_type = cuslines.ModelType.OPDT model = OpdtModel( diff --git a/docs/source/_static/endpoint_maps_threshold_2.png b/docs/source/_static/endpoint_maps_threshold_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6e59b37cb3763a1dc7e7e6c47ce1a2a8d2886a48 GIT binary patch literal 109391 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�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

MwQQXZ&NWnKRvJ{QCIK)jj25 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 Hod^F5f{^l1 diff --git a/docs/source/_static/endpoint_maps_threshold_4.png b/docs/source/_static/endpoint_maps_threshold_4.png index c5d3cbe031156bfa03b0c89f1f1c7bb724b42dba..7863cc7dc42c6edf06a5c5dc4f15773af05d3e22 100644 GIT binary patch literal 109540 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%0DtRs*0^Xk_ZSIhJ^Vf1{gK7!ly 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 z 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 From 6658326226ab4c3d61985deb0dc7ab782ca3890c Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 28 Jul 2025 20:04:23 -0700 Subject: [PATCH 047/116] update bundle rec docs --- docs/Makefile | 4 +- docs/source/reference/bundledict.rst | 177 ++++++++++++++++++--------- 2 files changed, 120 insertions(+), 61 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 1ed10da5f..f12775089 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -42,7 +42,9 @@ distclean: clean # delete more files than distclean. this would also remove the data files. realclean: distclean - @echo Removing data files from $(HOME) + @echo "WARNING: This will delete all data in your AFQ_data folder and .dipy directory." + @echo -n "Proceed? [y/N] " && read ans && [ $${ans:-N} = y ] + rm -rf $(HOME)/.dipy/ rm -rf $(HOME)/AFQ_data/ diff --git a/docs/source/reference/bundledict.rst b/docs/source/reference/bundledict.rst index cde8dad90..8d5003216 100644 --- a/docs/source/reference/bundledict.rst +++ b/docs/source/reference/bundledict.rst @@ -6,41 +6,88 @@ pyAFQ has a system for defining custom bundles. Custom bundles are defined by passing a custom `bundle_info` dictionary to :class:`AFQ.api.bundle_dict.BundleDict`: The keys of `bundle_info` are bundle names; the values are another dictionary describing the bundle, with these -key-value pairs:: - - - 'include' : a list of paths to Nifti files containing inclusion ROI(s). - One must either have at least 1 include ROI, or 'start' or 'end' ROIs. - - 'exclude' : a list of paths to Nifti files containing exclusion ROI(s), - optional. - - 'start' : path to a Nifti file containing the start ROI, optional - - 'end' : path to a Nifti file containing the end ROI, optional - - 'cross_midline' : boolean describing whether the bundle is required to - cross the midline (True) or prohibited from crossing (False), optional. - If None, the bundle may or may not cross the midline. - - 'space' : a string which is either 'template' or 'subject', optional +key-value pairs: + +- 'include' : a list of paths to Nifti files containing inclusion ROI(s). + One must either have at least 1 include ROI, or 'start' or 'end' ROIs. +- 'exclude' : a list of paths to Nifti files containing exclusion ROI(s), + optional. +- 'start' : path to a Nifti file containing the start ROI, optional +- 'end' : path to a Nifti file containing the end ROI, optional +- 'cross_midline' : boolean describing whether the bundle is required to + cross the midline (True) or prohibited from crossing (False), optional. + If None, the bundle may or may not cross the midline. +- 'space' : a string which is either 'template' or 'subject', optional If this field is not given or 'template' is given, the ROI will be transformed from template to subject space before being used. - - 'prob_map' : path to a Nifti file which is the probability map, - optional. - - 'inc_addtol' : List of floats describing how much tolerance to add or - subtract in mm from each of the inclusion ROIs. The list must be the - same length as 'include'. optional. - - 'exc_addtol' : List of floats describing how much tolerance to add or - subtract in mm from each of the exclusion ROIs. The list must be the - same length as 'exclude'. optional. - - 'mahal': Dict describing the parameters for cleaning. By default, we - use the default behavior of the seg.clean_bundle function. - - 'recobundles': Dict which should contain an 'sl' key and 'centroid' - key. The 'sl' key should be the reference streamline and the 'centroid' - key should be the centroid threshold for Recobundles. - - 'qb_thresh': Float which is the threshold for Quickbundles cleaning. - - 'primary_axis': string which is the primary axis the - bundle should travel in. Can be one of: 'L/R', 'P/A', 'I/S'. - - 'primary_axis_percentage': Used with primary_axis, defines what fraction - of a streamlines movement should be in the primary axis. - - 'length': dicitonary containing 'min_len' and 'max_len' +- 'prob_map' : path to a Nifti file which is the probability map, + optional. +- 'inc_addtol' : List of floats describing how much tolerance to add or + subtract in mm from each of the inclusion ROIs. The list must be the + same length as 'include'. optional. +- 'exc_addtol' : List of floats describing how much tolerance to add or + subtract in mm from each of the exclusion ROIs. The list must be the + same length as 'exclude'. optional. +- 'mahal': Dict describing the parameters for cleaning. By default, we + use the default behavior of the seg.clean_bundle function. +- 'recobundles': Dict which should contain an 'sl' key and 'centroid' + key. The 'sl' key should be the reference streamline and the 'centroid' + key should be the centroid threshold for Recobundles. +- 'qb_thresh': Float which is the threshold for Quickbundles cleaning. +- 'primary_axis': string which is the primary axis the + bundle should travel in. Can be one of: 'L/R', 'P/A', 'I/S'. +- 'primary_axis_percentage': Used with primary_axis, defines what fraction + of a streamlines movement should be in the primary axis. +- 'length': dicitonary containing 'min_len' and 'max_len' +- 'curvature': +- 'mahal': done by default unless orient_mahal or isolation_forest + are specified. Dictionary with optional keys 'n_points', 'core_only', + 'min_sl', 'distance_threshold', and 'clean_rounds'. These parameters + control the Mahalanobis distance cleaning of the bundle, further + information can be found at :func:`AFQ.recognition.cleaning.clean_bundle`. +- 'orient_mahal': cleans streamlines based on Mahalanobis distance of their + orientation to the mean orientation of the bundle. It should be a + dictionary which can be empty or contain n_points, core_only, min_sl, + distance_threshold, or clean_rounds as in 'mahal'. +- 'isolation_forest': dictionary with optional key 'percent_outlier_thresh', + which gives the percentage threshold for outliers (default 25). + + +Filtering by Other Bundles +========================== +Custom bundle definitions can also include keys that match the names of other +bundles in the same `BundleDict`. This allows you to filter streamlines in one +bundle based on their spatial relationship to another bundle. Note bundles are +segmented in the order they appear in their `BundleDict`, so later bundles cannot +be used to segment earlier bundles. The following options are supported: + +- **`overlap`** - Keeps streamlines that spatially overlap with another bundle + by at least the given node threshold. +- **`node_thresh`** - Remove streamlines that share at least the specified number + of nodes with another bundle. +- **`core`** - Removes streamlines based on whether their closest point lies + on the specified side of the *core* of another bundle. The value should be + one of `'Left'`, `'Right'`, `'Anterior'`, `'Posterior'`, `'Superior'`, + or `'Inferior'`. +- **`entire_core`** - Similar to `core`, but the entire streamline must lie on + the correct side of the core to be retained, not just the closest point. + +These references allow defining tracts relative to previously recognized +bundles. For example, the Vertical Occipital Fasciculus (VOF) can be defined in +relation to the Left Arcuate and Inferior Longitudinal fasciculi: +.. code-block:: python + 'Left Vertical Occipital': { + 'cross_midline': False, + 'start': templates['VOF_L_start'], + 'end': templates['VOF_L_end'], + 'Left Arcuate': {'node_thresh': 20}, + 'Left Inferior Longitudinal': {'core': 'Left'}, + } + +Filtering Order +=============== When doing bundle recognition, streamlines are filtered out from the whole tractography according to the series of steps defined in the bundle dictionaries. Of course, no one bundle uses every step, but here is the order @@ -52,49 +99,59 @@ of the steps: 5. Min and Max length 6. Primary axis 7. Include - 8. Curvature - 9. Exclude + 8. Exclude + 9. Curvature 10. Recobundles - 11. Quickbundles Cleaning - 12. Mahalanobis Cleaning + 11. Cleaning by other bundles + 12. Mahalanobis Orientation Cleaning + 13. Isoldation Forest Cleaning + 14. Quickbundles Cleaning + 15. Mahalanobis Cleaning If a streamline passes all steps for a bundle, it is included in that bundle. If a streamline passess all steps for multiple bundles, then a warning is thrown and the tie goes to whichever bundle is first in the bundle dictionary. - -If, for debugging purposes, you want to save out the streamlines -remaining after each step, set `save_intermediates` to a path in -`segmentation_params`. Then the streamlines will be saved out after each step -to that path. Only do this for one subject at a time. +.. note:: + If, for debugging purposes, you want to save out the streamlines + remaining after each step, set `save_intermediates` to a path in + `segmentation_params`. Then the streamlines will be saved out after each step + to that path. Only do this for one subject at a time. +Examples +======== Custom bundle definitions such as the OR, and the standard BundleDict can be combined through addition. For an example, see `Plotting the Optic Radiations `_. Some tracts, such as the Vertical Occipital Fasciculus, may be defined relative to other tracts. In those cases, the custom tract definitions should appear in the BundleDict object after the reference tracts have been defined. These reference tracts can -be included as keys in the same dictionary for that tract. For example:: - - newVOF = abd.BundleDict({ - 'Left Vertical Occipital': {'cross_midline': False, - 'space': 'template', - 'start': templates['VOF_L_start'], - 'end': templates['VOF_L_end'], - 'inc_addtol': [4, 0], - 'Left Arcuate': { - 'node_thresh': 20}, - 'Left Posterior Arcuate': { - 'node_thresh': 1, - 'core': 'Posterior'}, - 'Left Inferior Longitudinal': { - 'core': 'Left'}, - 'primary_axis': 'I/S', - 'primary_axis_percentage': 40} - }) +be included as keys in the same dictionary for that tract. For example: + +.. code-block:: python + + newVOF = abd.BundleDict({ + 'Left Vertical Occipital': { + 'cross_midline': False, + 'space': 'template', + 'start': templates['VOF_L_start'], + 'end': templates['VOF_L_end'], + 'inc_addtol': [4, 0], + 'Left Arcuate': { + 'node_thresh': 20}, + 'Left Posterior Arcuate': { + 'node_thresh': 1, + 'core': 'Posterior'}, + 'Left Inferior Longitudinal': { + 'core': 'Left'}, + 'primary_axis': 'I/S', + 'primary_axis_percentage': 40} + }) This definition of the VOF in the custom BundleDict would first require left ARC, left pARC, and left ILF to be defined, in the same way the tiebreaker above works. You would then construct your custom -BundleDict like this. The order of addition matters here:: +BundleDict like this. The order of addition matters here: - BundleDictCustomVOF = abd.default18_bd() + newVOF +.. code-block:: python + + BundleDictCustomVOF = abd.default18_bd() + newVOF From 5ea22bdf61ad8942ce84b7805c99c480e474c976 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 29 Jul 2025 11:42:43 -0700 Subject: [PATCH 048/116] change defaults and parallelization setup --- .github/workflows/nightly_pft_test.yml | 32 --------- .github/workflows/test.yml | 2 +- AFQ/api/group.py | 12 ++-- AFQ/models/asym_filtering.py | 58 +--------------- AFQ/models/msmt.py | 3 +- AFQ/recognition/criteria.py | 25 ++----- AFQ/recognition/recognize.py | 13 ++-- AFQ/tasks/data.py | 58 ++++++++++------ AFQ/tasks/tractography.py | 28 +++----- AFQ/tests/test_api.py | 69 +++---------------- AFQ/tractography/tractography.py | 10 +-- docs/source/reference/kwargs.rst | 15 ++-- docs/source/reference/methods.rst | 8 +++ examples/howto_examples/add_custom_bundle.py | 1 - .../plot_002_participant_afq_api.py | 2 - pyproject.toml | 1 - 16 files changed, 96 insertions(+), 241 deletions(-) delete mode 100644 .github/workflows/nightly_pft_test.yml diff --git a/.github/workflows/nightly_pft_test.yml b/.github/workflows/nightly_pft_test.yml deleted file mode 100644 index 2d52269b9..000000000 --- a/.github/workflows/nightly_pft_test.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Nightly PFT test suite - -on: - schedule: - - cron: '0 7 * * *' # every day at midnight, PST - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: ["3.12"] - - steps: - - name: Checkout repo - uses: actions/checkout@v1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install - run: | - python -m pip install --upgrade pip - pip install .[dev,fury,afqbrowser,plot] - - name: Lint - run: | - flake8 --ignore N802,N806,W503 --select W504 `find . -name \*.py | grep -v setup.py | grep -v version.py | grep -v __init__.py | grep -v /docs/` - - name: Test - run: | - cd && mkdir for_test && cd for_test && pytest --pyargs AFQ --cov-report term-missing --cov=AFQ -m "nightly_pft" --durations=0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cc61de94..d14aa73f0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,4 +39,4 @@ jobs: flake8 --ignore N802,N806,W503 --select W504 `find . -name \*.py | grep -v setup.py | grep -v version.py | grep -v __init__.py | grep -v /docs/` - name: Test run: | - cd && mkdir for_test && cd for_test && pytest --pyargs AFQ --cov-report term-missing --cov=AFQ -m "not nightly and not nightly_basic and not nightly_custom and not nightly_anisotropic and not nightly_slr and not nightly_pft and not nightly_reco and not nightly_reco80" --durations=0 + cd && mkdir for_test && cd for_test && pytest --pyargs AFQ --cov-report term-missing --cov=AFQ -m "not nightly and not nightly_basic and not nightly_custom and not nightly_anisotropic and not nightly_slr and not nightly_reco and not nightly_reco80" --durations=0 diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 5ce033369..2d8c2d8f9 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -252,15 +252,13 @@ def __init__(self, if len(self.sessions) * len(self.subjects) < 2: self.parallel_params["engine"] = "serial" - # do not parallelize segmentation if parallelizing across + # do not parallelize within subject if parallelizing across # subject-sessions if self.parallel_params["engine"] != "serial": - if "segmentation_params" not in kwargs: - kwargs["segmentation_params"] = {} - if "parallel_segmentation" not in kwargs["segmentation_params"]: - kwargs["segmentation_params"]["parallel_segmentation"] = {} - kwargs["segmentation_params"]["parallel_segmentation"]["engine"] =\ - "serial" + if "ray_n_cpus" not in kwargs: + kwargs["ray_n_cpus"] = 1 + if "numba_n_threads" not in kwargs: + kwargs["numba_n_threads"] = 1 self.valid_sub_list = [] self.valid_ses_list = [] diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index 1cdd942da..8a84a01f1 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -7,18 +7,14 @@ # Replaced with numba import numpy as np -import multiprocessing from tqdm import tqdm from numba import njit, prange, set_num_threads, config -import ray from dipy.reconst.shm import sh_to_sf_matrix, sph_harm_ind_list, sh_to_sf from dipy.direction import peak_directions from dipy.data import get_sphere -from AFQ.utils.stats import chunk_indices - __all__ = ["unified_filtering", "compute_asymmetry_index", "compute_odd_power_map", "compute_nufid_asym"] @@ -44,7 +40,7 @@ def unified_filtering(sh_data, sphere, sh_basis='descoteaux07', is_legacy=False, sigma_spatial=1.0, sigma_align=0.8, sigma_angle=None, rel_sigma_range=0.2, - n_threads=None, n_cpus=None): + n_threads=None): """ Unified asymmetric filtering as described in [1]. @@ -77,10 +73,6 @@ def unified_filtering(sh_data, sphere, Number of threads to use for numba. If None, uses the number of available threads. Default: None. - n_cpus: int or None - Number of CPUs to use for parallel processing with Ray. - If None, uses the number of available CPUs minus one. - Default: None. References ---------- @@ -104,9 +96,6 @@ def unified_filtering(sh_data, sphere, if n_threads is not None: set_num_threads(n_threads) - if n_cpus is None: - n_cpus = multiprocessing.cpu_count() - 1 - sh_order, full_basis = _get_sh_order_and_fullness(sh_data.shape[-1]) # build filters @@ -130,8 +119,7 @@ def unified_filtering(sh_data, sphere, return _unified_filter_call_python( sh_data, nx_filter, uv_filter, - sigma_range, B, B_inv, sphere, - n_cpus) + sigma_range, B, B_inv, sphere) @njit(fastmath=True, cache=True) @@ -244,7 +232,7 @@ def _get_sf_range(sh_data, B_mat): def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, - B_mat, B_inv, sphere, n_cpus): + B_mat, B_inv, sphere): """ Run filtering using pure python implementation. @@ -264,8 +252,6 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, SF to SH projection matrix. sphere: DIPY sphere Sphere for SH to SF projection. - n_cpus: int - Number of CPUs to use for parallel processing with Ray. Returns ------- @@ -287,44 +273,6 @@ def _unified_filter_call_python(sh_data, nx_filter, uv_filter, sigma_range, mode='constant' ), dtype=np.float64) - # # Apply filter to each sphere vertice - # if n_cpus > 1: - # ray.init(ignore_reinit_error=True) - - # sh_data_id = ray.put(sh_data) - # sh_data_padded_id = ray.put(sh_data_padded) - # nx_filter_id = ray.put(nx_filter) - # uv_filter_id = ray.put(uv_filter) - # B_mat_id = ray.put(B_mat) - - # @ray.remote(num_cpus=n_cpus) - # def _correlate_batch_remote(batch_u_ids, sh_data, sh_data_padded, - # nx_filter, uv_filter, sigma_range, B_mat): - # results = [] - # for u_sph_id in batch_u_ids: - # corr = _correlate( - # sh_data, sh_data_padded, nx_filter, - # uv_filter, sigma_range, u_sph_id, B_mat - # ) - # results.append((u_sph_id, corr)) - # return results - - # all_u_ids = list(range(nb_sf)) - # futures = [ - # _correlate_batch_remote.remote( - # batch, sh_data_id, sh_data_padded_id, - # nx_filter_id, uv_filter_id, - # sigma_range, B_mat_id - # ) - # for batch in chunk_indices(all_u_ids, n_cpus * 2) - # ] - - # # Gather and write results - # for future in tqdm(futures): - # batch_results = ray.get(future) - # for u_sph_id, correlation in batch_results: - # mean_sf[..., u_sph_id] = correlation - # else: for u_sph_id in tqdm(range(nb_sf)): mean_sf[..., u_sph_id] = _correlate(sh_data, sh_data_padded, nx_filter, uv_filter, diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index d85d49ec7..e8fee1564 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -336,7 +336,8 @@ def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, config.THREADING_LAYER = numba_threading_layer if n_cpus is None: - n_cpus = multiprocessing.cpu_count() - 1 + n_cpus = max( + multiprocessing.cpu_count() - 1 - n_threads, 1) og_data_shape = data.shape if len(data.shape) < 4: diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 312ba1f56..1605bccca 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -128,7 +128,7 @@ def primary_axis(b_sls, bundle_def, img, **kwargs): def include(b_sls, bundle_def, preproc_imap, max_includes, - parallel_segmentation, **kwargs): + n_cpus, **kwargs): accept_idx = b_sls.initiate_selection("include") flip_using_include = len(bundle_def["include"]) > 1\ and not b_sls.oriented_yet @@ -144,13 +144,13 @@ def include(b_sls, bundle_def, preproc_imap, max_includes, # with parallel segmentation, the first for loop will # only collect streamlines and does not need tqdm - if parallel_segmentation["engine"] != "serial": + if n_cpus > 1: inc_results = paramap( abr.check_sl_with_inclusion, b_sls.get_selected_sls(), func_args=[ bundle_def["include"], include_roi_tols], - **parallel_segmentation) - + engine="ray", + n_jobs=n_cpus,) else: inc_results = abr.check_sls_with_inclusion( b_sls.get_selected_sls(), @@ -182,14 +182,7 @@ def include(b_sls, bundle_def, preproc_imap, max_includes, accept_idx[sl_idx] = 1 else: accept_idx[sl_idx] = 1 - # see https://github.com/joblib/joblib/issues/945 - if ( - (parallel_segmentation.get( - "engine", "joblib") != "serial") - and (parallel_segmentation.get( - "backend", "loky") == "loky")): - from joblib.externals.loky import get_reusable_executor - get_reusable_executor().shutdown(wait=True) + b_sls.roi_closest = roi_closest.T if flip_using_include: b_sls.reorient(to_flip) @@ -335,17 +328,13 @@ def orient_mahal(b_sls, bundle_def, **kwargs): b_sls.select(accept_idx, "orient_mahal") -def isolation_forest(b_sls, bundle_def, parallel_segmentation, **kwargs): +def isolation_forest(b_sls, bundle_def, n_cpus, **kwargs): b_sls.initiate_selection("isolation_forest") - if parallel_segmentation["engine"] == "serial": - n_jobs = None - else: - n_jobs = parallel_segmentation.get("n_jobs", -1) accept_idx = abc.clean_by_isolation_forest( b_sls.get_selected_sls(), percent_outlier_thresh=bundle_def["isolation_forest"].get( "percent_outlier_thresh", 25), - n_jobs=n_jobs) + n_jobs=n_cpus) b_sls.select(accept_idx, "isolation_forest") diff --git a/AFQ/recognition/recognize.py b/AFQ/recognition/recognize.py index 5c401bf2f..8996d9751 100644 --- a/AFQ/recognition/recognize.py +++ b/AFQ/recognition/recognize.py @@ -21,10 +21,10 @@ def recognize( mapping, bundle_dict, reg_template, + n_cpus, nb_points=False, nb_streamlines=False, clip_edges=False, - parallel_segmentation={"engine": "serial"}, rb_recognize_params=dict( model_clust_thr=1.25, reduction_thr=25, @@ -53,6 +53,8 @@ def recognize( Dictionary of bundles to segment. reg_template : str, nib.Nifti1Image Template image for registration. + n_cpus : int + Number of CPUs to use for parallelization. nb_points : int, boolean Resample streamlines to nb_points number of points. If False, no resampling is done. Default: False @@ -62,13 +64,6 @@ def recognize( clip_edges : bool Whether to clip the streamlines to be only in between the ROIs. Default: False - parallel_segmentation : dict or AFQ.api.BundleDict - How to parallelize segmentation across processes when performing - waypoint ROI segmentation. Set to {"engine": "serial"} to not - perform parallelization. Some engines may cause errors, depending - on the system. See ``dipy.utils.parallel.paramap`` for - details. - Default: {"engine": "serial"} rb_recognize_params : dict RecoBundles parameters for the recognize function. Default: dict(model_clust_thr=1.25, reduction_thr=25, pruning_thr=12) @@ -185,7 +180,7 @@ def recognize( bundle_name, bundle_idx, bundle_to_flip, bundle_roi_closest, bundle_decisions, clip_edges=clip_edges, - parallel_segmentation=parallel_segmentation, + n_cpus=n_cpus, rb_recognize_params=rb_recognize_params, prob_threshold=prob_threshold, refine_reco=refine_reco, diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index cdb0008f5..1420d9f4a 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -1,6 +1,7 @@ import nibabel as nib import numpy as np import logging +import multiprocessing from dipy.io.gradients import read_bvals_bvecs import dipy.core.gradients as dpg @@ -101,6 +102,33 @@ def get_data_gtab(dwi_data_file, bval_file, bvec_file, min_bval=-np.inf, return data, gtab, img, img.affine +@immlib.calc("n_cpus", "n_threads") +def configure_ncpus_nthreads(ray_n_cpus=None, numba_n_threads=None): + """ + Configure the number of CPUs to use for parallel processing with Ray, + the number of threads to use for Numba + + Parameters + ---------- + ray_n_cpus : int, optional + The number of CPUs to use for parallel processing with Ray. + If None, uses the number of available CPUs minus one. + Tractography and Recognition use Ray. + Default: None + numba_n_threads : int, optional + The number of threads to use for Numba. + If None, uses the number of available CPUs minus one. + MSMT and ASYM fits use Numba. + Default: None + """ + if ray_n_cpus is None: + ray_n_cpus = multiprocessing.cpu_count() - 1 + if numba_n_threads is None: + numba_n_threads = multiprocessing.cpu_count() - 1 + + return ray_n_cpus, numba_n_threads + + @immlib.calc("b0") @as_file('_b0ref.nii.gz') @as_img @@ -534,11 +562,9 @@ def msdki_msk(msdki_tf): @as_img def msmt_params(brain_mask, gtab, data, dwi_affine, t1w_pve, + n_threads, msmt_sh_order=8, - msmt_fa_thr=0.7, - ray_n_cpus=None, - numba_n_threads=None, - numba_threading_layer="workqueue"): + msmt_fa_thr=0.7): """ full path to a nifti file containing parameters for the MSMT CSD fit @@ -552,16 +578,6 @@ def msmt_params(brain_mask, gtab, data, The threshold on the FA used to calculate the multi shell auto response. Can be useful to reduce for baby subjects. Default: 0.7 - ray_n_cpus : int, optional. - The number of CPUs to use for the MSMT CSD fit. - Default: None - numba_n_threads : int, optional. - The number of threads to use for the MSMT CSD fit. - Default: None, which will use the default number of threads - for the system. - numba_threading_layer : str, optional. - The threading layer to use for Numba. - Default: "workqueue". References ---------- @@ -607,8 +623,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data, mask, n_cpus=ray_n_cpus, n_threads=numba_n_threads, - numba_threading_layer=numba_threading_layer) + data, mask, n_threads=n_threads) meta = dict( SphericalHarmonicDegree=msmt_sh_order, @@ -634,7 +649,7 @@ def msmt_apm(msmtcsd_params): @as_file(suffix='_model-msmtcsd_param-aodf_dwimap.nii.gz', subfolder="models") @as_img -def msmt_aodf(msmtcsd_params): +def msmt_aodf(msmtcsd_params, n_threads): """ full path to a nifti file containing MSMT CSD ODFs filtered by unified filtering [1] @@ -650,7 +665,8 @@ def msmt_aodf(msmtcsd_params): logger.info("Applying unified filtering to MSMT CSD ODFs...") aodf = unified_filtering( sh_coeff, - get_sphere(name="repulsion724")) + get_sphere(name="repulsion724"), + n_threads=n_threads) return aodf, dict( MSMTCSDParamsFile=msmtcsd_params, @@ -825,7 +841,7 @@ def csd_params(dwi, brain_mask, gtab, data, @as_file(suffix='_model-csd_param-aodf_dwimap.nii.gz', subfolder="models") @as_img -def csd_aodf(csd_params): +def csd_aodf(csd_params, n_threads): """ full path to a nifti file containing SSST CSD ODFs filtered by unified filtering [1] @@ -841,7 +857,8 @@ def csd_aodf(csd_params): logger.info("Applying unified filtering to CSD ODFs...") aodf = unified_filtering( sh_coeff, - get_sphere(name="repulsion724")) + get_sphere(name="repulsion724"), + n_threads=n_threads) return aodf, dict( CSDParamsFile=csd_params, @@ -1697,6 +1714,7 @@ def get_data_plan(kwargs): data_tasks = with_name([ get_data_gtab, b0, b0_mask, brain_mask, + configure_ncpus_nthreads, t1w_pve, wm_gm_interface, dam_fit, dam_csf, dam_pseudot1, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 25f31da43..fbffea286 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -184,10 +184,7 @@ def streamlines(data_imap, seed, stop, fodf, is_trx = this_tracking_params.get("trx", False) - num_chunks = this_tracking_params.pop("num_chunks", False) - - if num_chunks is True: - num_chunks = multiprocessing.cpu_count() - 1 + num_chunks = data_imap["n_cpus"] if is_trx: start_time = time() @@ -448,22 +445,15 @@ def get_tractography_plan(kwargs): if isinstance(kwargs["tracking_params"]["odf_model"], str): kwargs["tracking_params"]["odf_model"] =\ kwargs["tracking_params"]["odf_model"].upper() + if kwargs["tracking_params"]["seed_mask"] is None: - if kwargs["tracking_params"]["tracker"] == "pft": - kwargs["tracking_params"]["seed_mask"] = ScalarImage( - "wm_gm_interface") - kwargs["tracking_params"]["seed_threshold"] = 0.5 - logger.info(( - "No seed mask given, using GM-WM interface " - "from 3T prob maps esimated from T1w")) - else: - kwargs["tracking_params"]["seed_mask"] = ScalarImage( - kwargs["best_scalar"]) - kwargs["tracking_params"]["seed_threshold"] = 0.2 - logger.info(( - "No seed mask given, using FA " - "(or first scalar if none are FA) " - "thresholded to 0.2")) + kwargs["tracking_params"]["seed_mask"] = ScalarImage( + "wm_gm_interface") + kwargs["tracking_params"]["seed_threshold"] = 0.5 + logger.info(( + "No seed mask given, using GM-WM interface " + "from 3T prob maps esimated from T1w")) + if kwargs["tracking_params"]["stop_mask"] is None: kwargs["tracking_params"]["stop_threshold"] = "ACT" kwargs["tracking_params"]["stop_mask"] = ThreeTImage() diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index b3155bd56..76a166a53 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -22,9 +22,7 @@ import dipy.tracking.utils as dtu import dipy.tracking.streamline as dts -from dipy.data import get_fnames from dipy.testing.decorators import xvfb_it -from dipy.io.streamline import load_tractogram import AFQ.api.bundle_dict as abd from AFQ.api.group import GroupAFQ, ParallelGroupAFQ @@ -33,8 +31,7 @@ import AFQ.utils.streamlines as aus import AFQ.utils.bin as afb from AFQ.definitions.mapping import SynMap, AffMap, SlrMap, IdentityMap -from AFQ.definitions.image import (RoiImage, PFTImage, ImageFile, ScalarImage, - TemplateImage) +from AFQ.definitions.image import (RoiImage, ImageFile, ScalarImage, TemplateImage) def touch(fname, times=None): @@ -310,11 +307,12 @@ def test_AFQ_fury(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + tracking_params={"n_seeds": 250000}, viz_backend_spec="fury") myafq.export("all_bundles_figure") -@pytest.mark.nightly_pft +@pytest.mark.nightly_custom def test_AFQ_trx(): tmpdir = tempfile.TemporaryDirectory() bids_path = op.join(tmpdir.name, "stanford_hardi") @@ -325,7 +323,7 @@ def test_AFQ_trx(): preproc_pipeline='vistasoft', # should throw warning but not error scalars=["dti_fa", "dti_md", ImageFile(suffix="DNE")], - tracking_params={"trx": True}) + tracking_params={"trx": True, "n_seeds": 250000}) myafq.export("all_bundles_figure") @@ -561,8 +559,8 @@ def test_AFQ_slr(): 'stanford_hardi_tractography', 'full_segmented_cleaned_tractography.trk'), segmentation_params={ - "dist_to_waypoint": 10, - "parallel_segmentation": {"engine": "serial"}}, + "dist_to_waypoint": 10}, + n_cpus=1, bundle_info=bd, mapping_definition=SlrMap(slr_kwargs={ "rng": np.random.RandomState(seed)})) @@ -585,6 +583,7 @@ def test_AFQ_reco(): viz_backend_spec="plotly", profile_weights="median", bundle_info=abd.reco_bd(16), + tracking_params={"n_seeds": 1e6}, segmentation_params={ 'rng': 42}) @@ -612,6 +611,7 @@ def test_AFQ_reco80(): preproc_pipeline='vistasoft', tracking_params=tracking_params, bundle_info=abd.reco_bd(16), + tracking_params={"n_seeds": 1e6}, segmentation_params={ 'rng': 42}) @@ -635,59 +635,6 @@ def test_AFQ_filterb(): myafq.export("b0") -@pytest.mark.nightly_pft -def test_AFQ_pft(): - """ - Test pft interface for AFQ - """ - _, bids_path, sub_path = get_temp_hardi() - - bundle_names = abd.default18_bd()[ - "Left Superior Longitudinal", - "Right Superior Longitudinal", - "Left Arcuate", - "Right Arcuate", - "Left Corticospinal", - "Right Corticospinal", - "Forceps Minor"] - - f_pve_csf, f_pve_gm, f_pve_wm = get_fnames('stanford_pve_maps') - os.rename(f_pve_wm, op.join(sub_path, - "sub-01_ses-01_label-WM_probseg.nii.gz")) - os.rename(f_pve_gm, op.join(sub_path, - "sub-01_ses-01_label-GM_probseg.nii.gz")) - os.rename(f_pve_csf, op.join(sub_path, - "sub-01_ses-01_label-CSF_probseg.nii.gz")) - - stop_mask = PFTImage( - ImageFile(suffix="probseg", filters={"label": "WM"}), - ImageFile(suffix="probseg", filters={"label": "GM"}), - ImageFile(suffix="probseg", filters={"label": "CSF"})) - t_output_dir = tempfile.TemporaryDirectory() - - myafq = GroupAFQ( - bids_path, - preproc_pipeline='vistasoft', - bundle_info=bundle_names, - output_dir=t_output_dir.name, - tracking_params={ - "stop_mask": stop_mask, - "stop_threshold": "CMC", - "tracker": "pft", - "maxlen": 150, - }) - sl_file = myafq.export("streamlines")["01"] - dwi_file = myafq.export("dwi")["01"] - sls = load_tractogram( - sl_file, - dwi_file, - bbox_valid_check=False, - trk_header_check=False).streamlines - for sl in sls: - # double the maxlen, due to step size of 0.5 - assert len(sl) <= 300 - - @pytest.mark.nightly_custom def test_AFQ_custom_subject_reg(): """ diff --git a/AFQ/tractography/tractography.py b/AFQ/tractography/tractography.py index a41cbc5fd..b8fef514b 100644 --- a/AFQ/tractography/tractography.py +++ b/AFQ/tractography/tractography.py @@ -25,10 +25,10 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, seed_mask=None, seed_threshold=0.5, thresholds_as_percentages=False, - n_seeds=1, random_seeds=False, rng_seed=None, stop_mask=None, + n_seeds=2000000, random_seeds=True, rng_seed=None, stop_mask=None, stop_threshold=0.5, step_size=0.5, minlen=50, maxlen=250, odf_model="CSD", basis_type="descoteaux07", legacy=True, - tracker="local", trx=False): + tracker="pft", trx=False): """ Tractography @@ -58,10 +58,10 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, voxel on each dimension (for example, 2 => [2, 2, 2]). If this is a 2D array, these are the coordinates of the seeds. Unless random_seeds is set to True, in which case this is the total number of random seeds - to generate within the mask. Default: 1 + to generate within the mask. Default: 2000000 random_seeds : bool Whether to generate a total of n_seeds random seeds in the mask. - Default: False. + Default: True rng_seed : int random seed used to generate random seeds if random_seeds is set to True. Default: None @@ -109,7 +109,7 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, tracker : str, optional Which strategy to use in tracking. This can be the standard local tracking ("local") or Particle Filtering Tracking ([Girard2014]_). - One of {"local", "pft"}. Default: "local" + One of {"local", "pft"}. Default: "pft" trx : bool, optional Whether to return the streamlines compatible with input to TRX file (i.e., as a LazyTractogram class instance). diff --git a/docs/source/reference/kwargs.rst b/docs/source/reference/kwargs.rst index 23c4bc445..9aff8d7b2 100644 --- a/docs/source/reference/kwargs.rst +++ b/docs/source/reference/kwargs.rst @@ -29,6 +29,12 @@ filter_b: bool b0_threshold: int The value of b under which it is considered to be b0. Default: 50. +ray_n_cpus: int + The number of CPUs to use for parallel processing with Ray. If None, uses the number of available CPUs minus one. Tractography and Recognition use Ray. Default: None + +numba_n_threads: int + The number of threads to use for Numba. If None, uses the number of available CPUs minus one. MSMT and ASYM fits use Numba. Default: None + dam_low_signal_thresh: float The threshold below which a voxel is considered to have low signal. Default: 50 @@ -50,15 +56,6 @@ msmt_sh_order: int msmt_fa_thr: float The threshold on the FA used to calculate the multi shell auto response. Can be useful to reduce for baby subjects. Default: 0.7 -ray_n_cpus: int - The number of CPUs to use for the MSMT CSD fit. Default: None - -numba_n_threads: int - The number of threads to use for the MSMT CSD fit. Default: None, which will use the default number of threads for the system. - -numba_threading_layer: str - The threading layer to use for Numba. Default: "workqueue". - csd_response: tuple or None The response function to be used by CSD, as a tuple with two elements. The first is the eigen-values as an (3,) ndarray and the second is the signal value for the response function without diffusion-weighting (i.e. S0). If not provided, auto_response will be used to calculate these values. Default: None diff --git a/docs/source/reference/methods.rst b/docs/source/reference/methods.rst index 1a72c1a4f..43aa04c98 100644 --- a/docs/source/reference/methods.rst +++ b/docs/source/reference/methods.rst @@ -51,6 +51,14 @@ dwi_affine: the affine transformation of the DWI data +n_cpus: + Configure the number of CPUs to use for parallel processing with Ray + + +n_threads: + the number of threads to use for Numba + + b0: full path to a nifti file containing the mean b0 diff --git a/examples/howto_examples/add_custom_bundle.py b/examples/howto_examples/add_custom_bundle.py index 9462ed6cc..ed223cde8 100644 --- a/examples/howto_examples/add_custom_bundle.py +++ b/examples/howto_examples/add_custom_bundle.py @@ -184,7 +184,6 @@ "directions": "prob", "odf_model": "CSD", "seed_mask": RoiImage()}, - segmentation_params={"parallel_segmentation": {"engine": "serial"}}, bundle_info=bundles) # If you want to redo different stages you can use the `clobber` method. diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 913a07521..50a87e9ae 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -10,11 +10,9 @@ import matplotlib.pyplot as plt import nibabel as nib import plotly -import pandas as pd from AFQ.api.participant import ParticipantAFQ import AFQ.data.fetch as afd -import AFQ.viz.altair as ava ########################################################################## # Example data diff --git a/pyproject.toml b/pyproject.toml index e98405e73..c52a3b9a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,6 @@ markers = [ "nightly_anisotropic", "nightly_reco", "nightly_reco80", - "nightly_pft", "nightly_custom", "nightly", ] \ No newline at end of file From b4d7ca7a8c128f1427d6656ddfb6c8056268e0e8 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 29 Jul 2025 11:52:32 -0700 Subject: [PATCH 049/116] bf --- AFQ/tests/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 76a166a53..918a6d467 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -611,7 +611,6 @@ def test_AFQ_reco80(): preproc_pipeline='vistasoft', tracking_params=tracking_params, bundle_info=abd.reco_bd(16), - tracking_params={"n_seeds": 1e6}, segmentation_params={ 'rng': 42}) From 6a491b6ce45b0fda75def05d16bd930d79dc445f Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 29 Jul 2025 16:59:24 -0700 Subject: [PATCH 050/116] BFs --- AFQ/api/group.py | 2 ++ AFQ/models/msmt.py | 6 +++-- AFQ/recognition/tests/test_recognition.py | 18 +++++++------- AFQ/tasks/segmentation.py | 1 + AFQ/tasks/tractography.py | 2 ++ AFQ/tests/test_api.py | 1 + AFQ/tests/test_tractography.py | 7 +++--- AFQ/tractography/tractography.py | 29 ++++++++++++++++------- 8 files changed, 42 insertions(+), 24 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 2d8c2d8f9..f09959de3 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -1127,6 +1127,7 @@ def export_sub(pAFQ_kwargs, attr_name): pAFQ_kwargs.dwi_data_file, pAFQ_kwargs.bval_file, pAFQ_kwargs.bvec_file, + pAFQ_kwargs.t1_file, pAFQ_kwargs.results_dir, **pAFQ_kwargs.kwargs) pAFQ.export(attr_name) @@ -1176,6 +1177,7 @@ def export_sub( pAFQ_kwargs.dwi_data_file, pAFQ_kwargs.bval_file, pAFQ_kwargs.bvec_file, + pAFQ_kwargs.t1_file, pAFQ_kwargs.results_dir, **pAFQ_kwargs.kwargs) pAFQ.export_all(viz, xforms, indiv) diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index e8fee1564..b44157d26 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -336,8 +336,10 @@ def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, config.THREADING_LAYER = numba_threading_layer if n_cpus is None: - n_cpus = max( - multiprocessing.cpu_count() - 1 - n_threads, 1) + if n_threads is None: + n_cpus = max(multiprocessing.cpu_count() - 1, 1) + else: + n_cpus = max(multiprocessing.cpu_count() - n_threads - 1, 1) og_data_shape = data.shape if len(data.shape) < 4: diff --git a/AFQ/recognition/tests/test_recognition.py b/AFQ/recognition/tests/test_recognition.py index 24eafea11..56706c27d 100644 --- a/AFQ/recognition/tests/test_recognition.py +++ b/AFQ/recognition/tests/test_recognition.py @@ -69,7 +69,7 @@ def test_segment(): nib.load(hardi_fdata), mapping, bundles, - reg_template) + reg_template, 1) # We asked for 2 fiber groups: npt.assert_equal(len(fiber_groups), 2) @@ -107,7 +107,7 @@ def test_segment_no_prob(): nib.load(hardi_fdata), mapping, bundles_no_prob, - reg_template) + reg_template, 1) # This condition should still hold npt.assert_equal(len(fiber_groups), 2) @@ -121,7 +121,7 @@ def test_segment_return_idx(): nib.load(hardi_fdata), mapping, bundles, - reg_template, + reg_template, 1, return_idx=True) npt.assert_equal(len(fiber_groups), 2) @@ -137,7 +137,7 @@ def test_segment_clip_edges_api(): nib.load(hardi_fdata), mapping, bundles, - reg_template, + reg_template, 1, clip_edges=True) npt.assert_equal(len(fiber_groups), 2) npt.assert_(len(fiber_groups['Right Corticospinal']) > 0) @@ -157,7 +157,7 @@ def test_segment_reco(): nib.load(hardi_fdata), mapping, bundles_reco, - reg_template, + reg_template, 1, rng=np.random.RandomState(seed=8)) # This condition should still hold @@ -191,7 +191,7 @@ def test_exclusion_ROI(): nib.load(hardi_fdata), mapping, slf_bundle, - reg_template) + reg_template, 1) npt.assert_equal(len(fiber_groups["Left Superior Longitudinal"]), 2) @@ -203,7 +203,7 @@ def test_exclusion_ROI(): nib.load(hardi_fdata), mapping, slf_bundle, - reg_template) + reg_template, 1) npt.assert_equal(len(fiber_groups["Left Superior Longitudinal"]), 1) @@ -214,7 +214,7 @@ def test_segment_sampled_streamlines(): nib.load(hardi_fdata), mapping, bundles, - reg_template) + reg_template, 1) # Already using a subsampled tck # the Right Corticospinal has two streamlines and @@ -230,7 +230,7 @@ def test_segment_sampled_streamlines(): nib.load(hardi_fdata), mapping, bundles, - reg_template, + reg_template, 1, nb_streamlines=nb_streamlines) # sampled streamlines should be subset of the original streamlines diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index ffcb1304d..58c54ba80 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -119,6 +119,7 @@ def segment(data_imap, mapping_imap, mapping_imap["mapping"], bundle_dict, reg_template, + data_imap["n_cpus"], **segmentation_params) seg_sft = aus.SegmentedSFT(bundles, Space.VOX) diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index fbffea286..e5548661b 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -179,6 +179,8 @@ def streamlines(data_imap, seed, stop, fodf, this_tracking_params['seed_mask'] = nib.load(seed).get_fdata() if isinstance(stop, str): this_tracking_params['stop_mask'] = nib.load(stop).get_fdata() + elif isinstance(stop, nib.Nifti1Image): + this_tracking_params['stop_mask'] = stop.get_fdata() else: this_tracking_params['stop_mask'] = stop diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 918a6d467..728baafa6 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -796,6 +796,7 @@ def test_AFQ_data_waypoint(): op.join(vista_folder, "sub-01_ses-01_dwi.nii.gz"), op.join(vista_folder, "sub-01_ses-01_dwi.bval"), op.join(vista_folder, "sub-01_ses-01_dwi.bvec"), + t1_path, afq_folder, bundle_info=bundle_info, scalars=[ diff --git a/AFQ/tests/test_tractography.py b/AFQ/tests/test_tractography.py index 14de5e980..43b17cb54 100644 --- a/AFQ/tests/test_tractography.py +++ b/AFQ/tests/test_tractography.py @@ -84,10 +84,9 @@ def test_pft_tracking(): ["DTI", "CSD"]): img = nib.load(fdata) data_shape = img.shape - data_affine = img.affine - pve_wm_data = nib.Nifti1Image(np.ones(data_shape[:3]), img.affine) - pve_gm_data = nib.Nifti1Image(np.zeros(data_shape[:3]), img.affine) - pve_csf_data = nib.Nifti1Image(np.zeros(data_shape[:3]), img.affine) + pve_wm_data = np.ones(data_shape[:3]) + pve_gm_data = np.ones(data_shape[:3]) + pve_csf_data = np.ones(data_shape[:3]) stop_mask = (pve_wm_data, pve_gm_data, pve_csf_data) for directions in ["det", "prob"]: diff --git a/AFQ/tractography/tractography.py b/AFQ/tractography/tractography.py index b8fef514b..03dac05e5 100644 --- a/AFQ/tractography/tractography.py +++ b/AFQ/tractography/tractography.py @@ -199,28 +199,33 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, "3 iterable for `stop_mask`. " "Expected a (pve_wm, pve_gm, pve_csf) tuple.") pves = [] - pve_imgs = [] + pve_affines = [] for pve in stop_mask: if isinstance(pve, str): - img = nib.load(pve) + seg_data = nib.load(pve).get_fdata() + seg_affine = nib.load(pve).affine + elif isinstance(pve, nib.Nifti1Image): + seg_data = pve.get_fdata() + seg_affine = pve.affine else: - img = pve - pve_imgs.append(img) - pves.append(pve_imgs[-1].get_fdata()) + seg_data = pve + seg_affine = params_img.affine + pves.append(seg_data) + pve_affines.append(seg_affine) - pve_wm_img, pve_gm_img, pve_csf_img = pve_imgs pve_wm_data, pve_gm_data, pve_csf_data = pves + pve_wm_affine, pve_gm_affine, pve_csf_affine = pve_affines pve_wm_data = resample( pve_wm_data, model_params[..., 0], - moving_affine=pve_wm_img.affine, + moving_affine=pve_wm_affine, static_affine=params_img.affine).get_fdata() pve_gm_data = resample( pve_gm_data, model_params[..., 0], - moving_affine=pve_gm_img.affine, + moving_affine=pve_gm_affine, static_affine=params_img.affine).get_fdata() pve_csf_data = resample( pve_csf_data, model_params[..., 0], - moving_affine=pve_csf_img.affine, + moving_affine=pve_csf_affine, static_affine=params_img.affine).get_fdata() if stop_threshold == "CMC": @@ -237,6 +242,12 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, pve_gm_data, pve_csf_data) else: + if len(stop_mask) == 3: + raise RuntimeError( + "You are not using CMC/ACT stropping, but provided tissue " + "probability maps in `stop_mask`. Please provide a single " + "3D array for `stop_mask` or use CMC/ACT") + if len(np.unique(stop_mask)) <= 2: stopping_criterion = ThresholdStoppingCriterion(stop_mask, 0.5) From c22392448d2d0c70a63d123da35be24f83a4f851 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 30 Jul 2025 11:27:40 -0700 Subject: [PATCH 051/116] bug fixing and test updates for recognition code --- AFQ/recognition/criteria.py | 29 ++++++++++---- AFQ/recognition/other_bundles.py | 2 +- AFQ/recognition/roi.py | 42 ++++++++++----------- AFQ/recognition/tests/test_other_bundles.py | 39 +++++++++++++++++-- AFQ/recognition/tests/test_recognition.py | 2 +- AFQ/recognition/tests/test_rois.py | 9 ++--- AFQ/tests/test_api.py | 22 ++++++----- 7 files changed, 95 insertions(+), 50 deletions(-) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 1605bccca..3e0688890 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -3,11 +3,11 @@ import numpy as np import nibabel as nib +import ray from scipy.ndimage import distance_transform_edt import dipy.tracking.streamline as dts -from dipy.utils.parallel import paramap from dipy.segment.clustering import QuickBundles from dipy.segment.metricspeed import AveragePointwiseEuclideanMetric from dipy.segment.featurespeed import ResampleFeature @@ -21,6 +21,8 @@ import AFQ.recognition.curvature as abv import AFQ.recognition.roi as abr import AFQ.recognition.other_bundles as abo +from AFQ.utils.stats import chunk_indices + criteria_order_pre_other_bundles = [ "prob_map", "cross_midline", "start", "end", @@ -145,12 +147,25 @@ def include(b_sls, bundle_def, preproc_imap, max_includes, # with parallel segmentation, the first for loop will # only collect streamlines and does not need tqdm if n_cpus > 1: - inc_results = paramap( - abr.check_sl_with_inclusion, b_sls.get_selected_sls(), - func_args=[ - bundle_def["include"], include_roi_tols], - engine="ray", - n_jobs=n_cpus,) + inc_results = np.zeros(len(b_sls), dtype=tuple) + + inc_rois_id = ray.put(bundle_def["include"]) + inc_roi_tols_id = ray.put(include_roi_tols) + + _check_inc_parallel = ray.remote( + num_cpus=n_cpus)(abr.check_sls_with_inclusion) + + sls_chunks = list(chunk_indices(np.arange(len(b_sls)), n_cpus)) + futures = [ + _check_inc_parallel.remote( + b_sls.get_selected_sls()[sls_chunk], + inc_rois_id, + inc_roi_tols_id) + for sls_chunk in sls_chunks + ] + + for ii, future in enumerate(futures): + inc_results[sls_chunks[ii]] = ray.get(future) else: inc_results = abr.check_sls_with_inclusion( b_sls.get_selected_sls(), diff --git a/AFQ/recognition/other_bundles.py b/AFQ/recognition/other_bundles.py index eb2b82ca8..d6f395bf9 100644 --- a/AFQ/recognition/other_bundles.py +++ b/AFQ/recognition/other_bundles.py @@ -50,7 +50,7 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, Examples -------- - >>> clean_idx = clean_by_other_density_map(bundle1, bundle2, 5, img, True) + >>> clean_idx = clean_by_overlap(bundle1, bundle2, 5, img, True) >>> cleaned_bundle = [s for i, s in enumerate(bundle1) if clean_idx[i]] """ other_bundle_density_map = dtu.density_map( diff --git a/AFQ/recognition/roi.py b/AFQ/recognition/roi.py index 7868bf432..69556ed75 100644 --- a/AFQ/recognition/roi.py +++ b/AFQ/recognition/roi.py @@ -6,30 +6,28 @@ def _interp3d(roi, sl): return interpolate_scalar_3d(roi.get_fdata(), np.asarray(sl))[0] -def check_sls_with_inclusion(sls, include_rois, include_roi_tols): - for sl in sls: - yield check_sl_with_inclusion( - sl, - include_rois, - include_roi_tols) +def check_sls_with_inclusion( + sls, include_rois, include_roi_tols): + inc_results = np.zeros(len(sls), dtype=tuple) + include_rois = [roi_.get_fdata().copy() for roi_ in include_rois] + for jj, sl in enumerate(sls): + closest = np.zeros(len(include_rois), dtype=np.int32) + sl = np.asarray(sl) + valid = True + for ii, roi in enumerate(include_rois): + dist = interpolate_scalar_3d(roi, sl)[0] + closest[ii] = np.argmin(dist) + if dist[closest[ii]] > include_roi_tols[ii]: + # Too far from one of them: + inc_results[jj] = (False, []) + valid = False + break -def check_sl_with_inclusion(sl, include_rois, - include_roi_tols): - """ - Helper function to check that a streamline is close to a list of - inclusion ROIS. - """ - closest = np.zeros(len(include_rois), dtype=np.int32) - for ii, roi in enumerate(include_rois): - dist = _interp3d(roi, sl) - closest[ii] = np.argmin(dist) - if dist[closest[ii]] > include_roi_tols[ii]: - # Too far from one of them: - return False, [] - - # Apparently you checked all the ROIs and it was close to all of them - return True, closest + # Checked all the ROIs and it was close to all of them + if valid: + inc_results[jj] = (True, closest) + return inc_results def check_sl_with_exclusion(sl, exclude_rois, diff --git a/AFQ/recognition/tests/test_other_bundles.py b/AFQ/recognition/tests/test_other_bundles.py index 0dbc43e70..4af3c6775 100644 --- a/AFQ/recognition/tests/test_other_bundles.py +++ b/AFQ/recognition/tests/test_other_bundles.py @@ -14,17 +14,28 @@ node_thresh_sample = 1 -def test_clean_by_other_density_map(): - cleaned_idx = abo.clean_by_other_density_map( +def test_clean_by_overlap(): + cleaned_idx = abo.clean_by_overlap( this_bundle_sls_sample, other_bundle_sls_sample, node_thresh_sample, - img_sample + img_sample, + True ) assert isinstance(cleaned_idx, np.ndarray) assert cleaned_idx.shape[0] == this_bundle_sls_sample.shape[0] assert np.all(cleaned_idx == [False, True]) + cleaned_idx = abo.clean_by_overlap( + this_bundle_sls_sample, + other_bundle_sls_sample, + node_thresh_sample, + img_sample, + False + ) + assert isinstance(cleaned_idx, np.ndarray) + assert cleaned_idx.shape[0] == this_bundle_sls_sample.shape[0] + assert np.all(cleaned_idx == [True, False]) def test_clean_relative_to_other_core(): for core in ['anterior', 'posterior', 'superior', 'inferior', 'right', 'left']: @@ -32,7 +43,8 @@ def test_clean_relative_to_other_core(): core, this_bundle_sls_sample, other_bundle_sls_sample, - np.eye(4) + np.eye(4), + entire=False ) assert isinstance(cleaned_idx_core, np.ndarray) @@ -41,3 +53,22 @@ def test_clean_relative_to_other_core(): assert np.all(cleaned_idx_core == [True, True]) else: assert np.all(cleaned_idx_core == [False, False]) + + cleaned_idx_core = abo.clean_relative_to_other_core( + core, + this_bundle_sls_sample, + other_bundle_sls_sample, + np.eye(4), + entire=True + ) + + assert isinstance(cleaned_idx_core, np.ndarray) + assert cleaned_idx_core.shape[0] == this_bundle_sls_sample.shape[0] + if core == "inferior": + assert np.all(cleaned_idx_core == [True, True]) + elif core == "right": + assert np.all(cleaned_idx_core == [True, False]) + elif core == "posterior" or core == "left": + assert np.all(cleaned_idx_core == [False, True]) + else: + assert np.all(cleaned_idx_core == [False, False]) diff --git a/AFQ/recognition/tests/test_recognition.py b/AFQ/recognition/tests/test_recognition.py index 56706c27d..0f5688ab1 100644 --- a/AFQ/recognition/tests/test_recognition.py +++ b/AFQ/recognition/tests/test_recognition.py @@ -69,7 +69,7 @@ def test_segment(): nib.load(hardi_fdata), mapping, bundles, - reg_template, 1) + reg_template, 2) # We asked for 2 fiber groups: npt.assert_equal(len(fiber_groups), 2) diff --git a/AFQ/recognition/tests/test_rois.py b/AFQ/recognition/tests/test_rois.py index 1827676a1..1908b5351 100644 --- a/AFQ/recognition/tests/test_rois.py +++ b/AFQ/recognition/tests/test_rois.py @@ -6,7 +6,6 @@ from scipy.ndimage import distance_transform_edt from AFQ.recognition.roi import ( check_sls_with_inclusion, - check_sl_with_inclusion, check_sl_with_exclusion) shape = (15, 15, 15) @@ -83,15 +82,15 @@ def test_check_sls_with_inclusion(): def test_check_sl_with_inclusion_pass(): - result, dists = check_sl_with_inclusion( - streamline1, include_rois, include_roi_tols) + result, dists = check_sls_with_inclusion( + [streamline1], include_rois, include_roi_tols)[0] assert result is True assert len(dists) == 2 def test_check_sl_with_inclusion_fail(): - result, dists = check_sl_with_inclusion( - streamline2, include_rois, include_roi_tols) + result, dists = check_sls_with_inclusion( + [streamline2], include_rois, include_roi_tols)[0] assert result is False assert dists == [] diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 728baafa6..be0cf6158 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -751,6 +751,9 @@ def test_AFQ_data_waypoint(): vista_folder = op.join( bids_path, "derivatives/vistasoft/sub-01/ses-01/dwi") + freesurfer_folder = op.join( + bids_path, + "derivatives/freesurfer/sub-01/ses-01/anat") # Prepare LV1 ROI lv1_files, lv1_folder = afd.fetch_stanford_hardi_lv1() @@ -784,8 +787,7 @@ def test_AFQ_data_waypoint(): } tracking_params = dict(odf_model="csd", - seed_mask=RoiImage(), - n_seeds=200, + n_seeds=5000, random_seeds=True, rng_seed=42) segmentation_params = dict(return_idx=True) @@ -796,7 +798,7 @@ def test_AFQ_data_waypoint(): op.join(vista_folder, "sub-01_ses-01_dwi.nii.gz"), op.join(vista_folder, "sub-01_ses-01_dwi.bval"), op.join(vista_folder, "sub-01_ses-01_dwi.bvec"), - t1_path, + op.join(freesurfer_folder, "sub-01_ses-01_T1w.nii.gz"), afq_folder, bundle_info=bundle_info, scalars=[ @@ -839,14 +841,14 @@ def test_AFQ_data_waypoint(): seg_sft = aus.SegmentedSFT.fromfile( myafq.export("bundles")) npt.assert_(len(seg_sft.get_bundle( - 'Left Corticospinal').streamlines) > 0) + 'Left Superior Longitudinal').streamlines) > 0) # Test bundles exporting: myafq.export("indiv_bundles") assert op.exists(op.join( myafq.export("output_dir"), 'bundles', - 'sub-01_ses-01_desc-LeftCorticospinal_tractography.trk')) # noqa + 'sub-01_ses-01_desc-RightSuperiorLongitudinal_tractography.trk')) # noqa tract_profile_fname = myafq.export("profiles") tract_profiles = pd.read_csv(tract_profile_fname) @@ -880,11 +882,11 @@ def test_AFQ_data_waypoint(): # Set up config to use the same parameters as above: # ROI mask needs to be put in quotes in config - tracking_params = dict(odf_model="CSD", - seed_mask="RoiImage()", - n_seeds=200, - random_seeds=True, - rng_seed=42) + tracking_params = dict( + odf_model="CSD", + n_seeds=5000, + random_seeds=True, + rng_seed=42) bundle_dict_as_str = ( 'default18_bd()[' '"Left Superior Longitudinal",' From d340dbdc48404833da55df484c0f2caab42677e8 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 30 Jul 2025 13:39:25 -0700 Subject: [PATCH 052/116] recobundles fixes --- AFQ/data/fetch.py | 4 ++++ AFQ/recognition/criteria.py | 5 +++-- AFQ/recognition/recognize.py | 2 +- examples/howto_examples/plot_recobundles.py | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AFQ/data/fetch.py b/AFQ/data/fetch.py index 9007e5468..ee46418dc 100644 --- a/AFQ/data/fetch.py +++ b/AFQ/data/fetch.py @@ -1165,6 +1165,10 @@ def organize_stanford_data(path=None, clear_previous_afq=None): if not op.exists(derivatives_path): logger.info(f'creating derivatives directory: {derivatives_path}') + os.makedirs(dmriprep_folder, exist_ok=True) + os.makedirs(freesurfer_folder, exist_ok=True) + os.makedirs(afq_folder, exist_ok=True) + # anatomical data anat_folder = op.join(freesurfer_folder, 'sub-01', 'ses-01', 'anat') os.makedirs(anat_folder, exist_ok=True) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 3e0688890..45be0f9de 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -261,15 +261,16 @@ def recobundles(b_sls, mapping, bundle_def, reg_template, img, refine_reco, StatefulTractogram(b_sls.get_selected_sls(), img, Space.VOX), "template", mapping, reg_template, save_intermediates=save_intermediates).streamlines + moved_sl_resampled = abu.resample_tg(moved_sl, 100) rb = RecoBundles(moved_sl, verbose=True, rng=rng) _, rec_labels = rb.recognize( bundle_def['recobundles']['sl'], **rb_recognize_params) if refine_reco: _, rec_labels = rb.refine( - bundle_def['recobundles']['sl'], moved_sl[rec_labels], + bundle_def['recobundles']['sl'], moved_sl_resampled[rec_labels], **rb_recognize_params) - if not b_sls.oriented_yet: + if not b_sls.oriented_yet and np.sum(rec_labels) > 0: standard_sl = next(iter(bundle_def['recobundles']['centroid'])) oriented_idx = abu.orient_by_streamline( moved_sl[rec_labels], diff --git a/AFQ/recognition/recognize.py b/AFQ/recognition/recognize.py index 8996d9751..12f9aa453 100644 --- a/AFQ/recognition/recognize.py +++ b/AFQ/recognition/recognize.py @@ -27,7 +27,7 @@ def recognize( clip_edges=False, rb_recognize_params=dict( model_clust_thr=1.25, - reduction_thr=25, + reduction_thr=50, pruning_thr=12), refine_reco=False, prob_threshold=0, diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index bfd9c042f..356121e7b 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -20,7 +20,7 @@ afd.organize_stanford_data(clear_previous_afq="track") -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=100000, random_seeds=True, rng_seed=42) From dcfd3e22bcc6141e0cf5592c171de7c2114fa999 Mon Sep 17 00:00:00 2001 From: John Kruper <36000@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:45:20 -0700 Subject: [PATCH 053/116] extra space Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AFQ/tasks/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 1420d9f4a..f1e3cfa0a 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -115,7 +115,7 @@ def configure_ncpus_nthreads(ray_n_cpus=None, numba_n_threads=None): If None, uses the number of available CPUs minus one. Tractography and Recognition use Ray. Default: None - numba_n_threads : int, optional + numba_n_threads : int, optional The number of threads to use for Numba. If None, uses the number of available CPUs minus one. MSMT and ASYM fits use Numba. From 18cd5c8309b2e78e9dfa70585a42a93a21eb9ebb Mon Sep 17 00:00:00 2001 From: John Kruper <36000@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:45:56 -0700 Subject: [PATCH 054/116] sigma angle misspelling Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AFQ/models/asym_filtering.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/models/asym_filtering.py b/AFQ/models/asym_filtering.py index 8a84a01f1..e488869ab 100644 --- a/AFQ/models/asym_filtering.py +++ b/AFQ/models/asym_filtering.py @@ -91,7 +91,7 @@ def unified_filtering(sh_data, sphere, raise ValueError('sigma_align cannot be <= 0.') if sigma_angle is not None: if sigma_angle <= 0.0: - raise ValueError('sigma_align cannot be <= 0.') + raise ValueError('sigma_angle cannot be <= 0.') if n_threads is not None: set_num_threads(n_threads) From e8ce3c0938a0781cbdd5dc24eeff7b7ffb052856 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 30 Jul 2025 14:48:22 -0700 Subject: [PATCH 055/116] try this to enforce no concurrency --- .github/workflows/docbuild.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docbuild.yml b/.github/workflows/docbuild.yml index e153686b1..7618fb1e9 100644 --- a/.github/workflows/docbuild.yml +++ b/.github/workflows/docbuild.yml @@ -3,7 +3,7 @@ name: Documentation build on: [push, pull_request] concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d14aa73f0..5aee256bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ name: Test suite on: [push, pull_request] concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: From b9a17e85f3afb68e168f194c3c149ba7888c4318 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 30 Jul 2025 14:49:55 -0700 Subject: [PATCH 056/116] add concurrency restrictions to docker images --- .github/workflows/docker_pyafq.yml | 4 ++++ .github/workflows/docker_pyafq_cuda12.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/docker_pyafq.yml b/.github/workflows/docker_pyafq.yml index aa05de72c..ada2c8c74 100644 --- a/.github/workflows/docker_pyafq.yml +++ b/.github/workflows/docker_pyafq.yml @@ -2,6 +2,10 @@ name: Build and Push pyAFQ Docker Image on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/docker_pyafq_cuda12.yml b/.github/workflows/docker_pyafq_cuda12.yml index cf4204433..9924cba8b 100644 --- a/.github/workflows/docker_pyafq_cuda12.yml +++ b/.github/workflows/docker_pyafq_cuda12.yml @@ -2,6 +2,10 @@ name: Build and Push pyAFQ CUDA12 Image on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest From 81fabafba5a2f5d5a363661d580e7d0341738d33 Mon Sep 17 00:00:00 2001 From: John Kruper <36000@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:50:21 -0700 Subject: [PATCH 057/116] deprecated np.bool Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AFQ/models/wmgm_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/models/wmgm_interface.py b/AFQ/models/wmgm_interface.py index 09bf0c461..89b4766a7 100644 --- a/AFQ/models/wmgm_interface.py +++ b/AFQ/models/wmgm_interface.py @@ -51,7 +51,7 @@ def fit_wm_gm_interface(PVE_img, dwiref_img): gm_smoothed = gaussian_filter(gm, 1) csf_smoothed = gaussian_filter(csf, 1) - wm_boundary[~gm_smoothed.astype(np.bool)] = 0 + wm_boundary[~gm_smoothed.astype(bool)] = 0 wm_boundary[csf_smoothed > gm_smoothed] = 0 return nib.Nifti1Image( From 4f45488044522d96870536a24ca80f73d4df6654 Mon Sep 17 00:00:00 2001 From: John Kruper <36000@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:51:04 -0700 Subject: [PATCH 058/116] deprecated np.bool Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AFQ/tasks/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index f1e3cfa0a..7e4a81c7d 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -350,7 +350,7 @@ def t1w_pve(t1_file, brain_mask, pve_nclass=3, pve_beta=0.1): static_affine=t1w.affine).get_fdata() t1w_masked = t1w.get_fdata().copy() - t1w_masked[~bm_in_tw1.astype(np.bool)] = 0 + t1w_masked[~bm_in_tw1.astype(np.bool_)] = 0 hmrf = TissueClassifierHMRF() logger.info(( From bb0e0da7d68a60016ea0b82826ea8dd7f7f57838 Mon Sep 17 00:00:00 2001 From: John Kruper <36000@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:51:27 -0700 Subject: [PATCH 059/116] Typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AFQ/recognition/cleaning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 617453095..c62361559 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -293,7 +293,7 @@ def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, # We don't even bother if there aren't enough streamlines: if len(streamlines) < min_sl: logger.warning(( - "LOD cleaning not performed" + "Isolation Forest cleaning not performed" " due to low streamline count")) return np.ones(len(streamlines), dtype=bool) From 1e6b0cd69c8d539d8cfe6a707ef3b9a89514287b Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 30 Jul 2025 17:00:59 -0700 Subject: [PATCH 060/116] purge num_chunks from examples --- examples/tutorial_examples/plot_001_group_afq_api.py | 8 ++------ .../tutorial_examples/plot_002_participant_afq_api.py | 8 ++------ examples/tutorial_examples/plot_003_rerun.py | 6 ++---- examples/tutorial_examples/plot_004_export.py | 3 +-- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index f726a9889..ad4fe60df 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -57,16 +57,12 @@ # We make create a `tracking_params` variable, which we will pass to the # GroupAFQ object which specifies that we want 25,000 seeds randomly # distributed in the white matter. We only do this to make this example -# faster and consume less space. We also set ``num_chunks`` to `True`, -# which will use ray to parallelize the tracking across all cores. -# This can be removed to process in serial, or set to use a particular -# distribution of work by setting `n_chunks` to an integer number. +# faster and consume less space. tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2022, - trx=True, - num_chunks=True) + trx=True) ########################################################################## # Initialize a GroupAFQ object: diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 50a87e9ae..136a666de 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -76,16 +76,12 @@ # We make create a `tracking_params` variable, which we will pass to the # ParticipantAFQ object which specifies that we want 25,000 seeds randomly # distributed in the white matter. We only do this to make this example -# faster and consume less space. We also set ``num_chunks`` to `True`, -# which will use ray to parallelize the tracking across all cores. -# This can be removed to process in serial, or set to use a particular -# distribution of work by setting `n_chunks` to an integer number. +# faster and consume less space. tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2022, - trx=True, - num_chunks=True) + trx=True) ########################################################################## # Initialize a ParticipantAFQ object: diff --git a/examples/tutorial_examples/plot_003_rerun.py b/examples/tutorial_examples/plot_003_rerun.py index e46698706..5b4084a08 100644 --- a/examples/tutorial_examples/plot_003_rerun.py +++ b/examples/tutorial_examples/plot_003_rerun.py @@ -32,8 +32,7 @@ tracking_params = dict(n_seeds=100, random_seeds=True, rng_seed=2022, - trx=True, - num_chunks=True) + trx=True) myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), @@ -99,8 +98,7 @@ random_seeds=True, max_angle=60, rng_seed=12, - trx=True, - num_chunks=True) + trx=True) myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index 56f5bc683..e5f47da69 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -46,8 +46,7 @@ "n_seeds": 25000, "random_seeds": True, "rng_seed": 2022, - "trx": True, - "num_chunks": True + "trx": True }, ) From 47193b545e268fce5c8ba9db6ffb18549ab6982e Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 31 Jul 2025 09:50:32 -0700 Subject: [PATCH 061/116] add t1 file for tests/example --- AFQ/api/participant.py | 4 ++-- AFQ/tests/test_api.py | 9 --------- .../tutorial_examples/plot_002_participant_afq_api.py | 4 ++++ examples/tutorial_examples/plot_004_export.py | 4 ++++ 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index de709c4c3..702ba02ab 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -61,10 +61,10 @@ def __init__(self, Examples -------- api.ParticipantAFQ( - dwi_data_file, bval_file, bvec_file, output_dir, + dwi_data_file, bval_file, bvec_file, t1_file, output_dir, csd_sh_order_max=4) api.ParticipantAFQ( - dwi_data_file, bval_file, bvec_file, output_dir, + dwi_data_file, bval_file, bvec_file, t1_file, output_dir, reg_template_spec="mni_t2", reg_subject_spec="b0") Notes diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index be0cf6158..fc7daf702 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -269,15 +269,6 @@ def test_AFQ_custom_tract(): "scope": "vistasoft" }) - # equivalent ParticipantAFQ version of call may be useful as reference - # myafq = ParticipantAFQ( - # sub_path + "/sub-01_ses-01_dwi.nii.gz", - # sub_path + "/sub-01_ses-01_dwi.bval", - # sub_path + "/sub-01_ses-01_dwi.bvec", - # output_dir=sub_path, - # bundle_info=bundle_names, - # import_tract=sub_path + '/subsampled_tractography.trk') - myafq.export("streamlines") diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 136a666de..4fb7ce9c6 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -61,6 +61,9 @@ dwi_data_file = op.join(data_dir, "sub-01_ses-01_dwi.nii.gz") bval_file = op.join(data_dir, "sub-01_ses-01_dwi.bval") bvec_file = op.join(data_dir, "sub-01_ses-01_dwi.bvec") +t1_file = op.join(afd.afq_home, "stanford_hardi", "derivatives", + "freesurfer", "sub-01", "ses-01", "anat", + "sub-01_ses-01_T1w.nii.gz") # You will also need to define the output directory where you want to store the # results. The output directory needs to exist before exporting ParticipantAFQ @@ -105,6 +108,7 @@ dwi_data_file=dwi_data_file, bval_file=bval_file, bvec_file=bvec_file, + t1_file=t1_file, output_dir=output_dir, tracking_params=tracking_params, ) diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index e5f47da69..b36dfeb8e 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -31,6 +31,9 @@ dwi_data_file = op.join(data_dir, "sub-01_ses-01_dwi.nii.gz") bval_file = op.join(data_dir, "sub-01_ses-01_dwi.bval") bvec_file = op.join(data_dir, "sub-01_ses-01_dwi.bvec") +t1_file = op.join(afd.afq_home, "stanford_hardi", "derivatives", + "freesurfer", "sub-01", "ses-01", "anat", + "sub-01_ses-01_T1w.nii.gz") output_dir = op.join(afd.afq_home, "stanford_hardi", "derivatives", "afq", "sub-01", "ses-01", "dwi") @@ -41,6 +44,7 @@ dwi_data_file=dwi_data_file, bval_file=bval_file, bvec_file=bvec_file, + t1_file=t1_file, output_dir=output_dir, tracking_params={ "n_seeds": 25000, From 22ba7ad9ebdca0da5c29df01e2a6ddddee227590 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 31 Jul 2025 12:48:42 -0700 Subject: [PATCH 062/116] update default resample_subject_to --- AFQ/api/bundle_dict.py | 2 +- AFQ/tasks/data.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index c4fa1b56b..359bb515c 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -741,7 +741,7 @@ class BundleDict(MutableMapping): If there are bundles in bundle_info with the 'space' attribute set to 'subject', their images (all ROIs and probability maps) will be resampled to the affine and shape of this image. - If None, resamples to DWI. Be careful if you use this, + If True, resamples to DWI. Be careful if you use this, that this is the correct choice. If False, no resampling will be done. Default: False diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 7e4a81c7d..df4ccb6af 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -1698,7 +1698,7 @@ def get_bundle_dict(brain_mask, b0, bundle_info, resample_to=reg_template) - if bundle_dict.resample_subject_to is None: + if bundle_dict.resample_subject_to == True: bundle_dict.resample_subject_to = b0 return bundle_dict, reg_template, reg_template_space_name From 99f354179808271c29514dc5ec8837082c299f34 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 31 Jul 2025 21:30:01 -0700 Subject: [PATCH 063/116] integrate Brainchop for segmenting T1 --- AFQ/api/bundle_dict.py | 6 +- AFQ/definitions/image.py | 89 +------- AFQ/models/wmgm_interface.py | 56 ++++- AFQ/tasks/data.py | 209 ++++++++++-------- AFQ/tests/test_api.py | 17 +- .../howto_examples/acoustic_radiations.py | 7 - examples/howto_examples/add_custom_bundle.py | 7 - .../howto_examples/cerebellar_peduncles.py | 13 -- examples/howto_examples/cloudknot_example.py | 9 - .../howto_examples/cloudknot_hcp_example.py | 7 - examples/howto_examples/optic_radiations.py | 7 - examples/howto_examples/run_afq_fwdti.py | 19 +- .../plot_001_group_afq_api.py | 7 +- .../plot_002_participant_afq_api.py | 4 +- .../tutorial_examples/viz_008_endpoints.py | 7 +- setup.cfg | 1 + 16 files changed, 190 insertions(+), 275 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index 359bb515c..2c4c33109 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -110,8 +110,7 @@ def default18_bd(): 'exclude': [], 'space': 'template', 'prob_map': templates['CST_L_prob_map'], - 'end': templates['CST_L_start'], - 'start': templates['CST_L_end']}, + 'end': templates['CST_L_start']}, 'Right Corticospinal': { 'cross_midline': False, 'include': [templates['CST_roi2_R'], @@ -119,8 +118,7 @@ def default18_bd(): 'exclude': [], 'space': 'template', 'prob_map': templates['CST_R_prob_map'], - 'end': templates['CST_R_start'], - 'start': templates['CST_R_end']}, + 'end': templates['CST_R_start']}, 'Left Inferior Fronto-occipital': { 'cross_midline': False, 'include': [templates['IFO_roi2_L'], diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index 7107aedd8..858f47f78 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -3,7 +3,6 @@ import nibabel as nib -from dipy.segment.mask import median_otsu from dipy.align import resample from AFQ.definitions.utils import Definition, find_file, name_from_path @@ -12,9 +11,9 @@ __all__ = [ - "ImageFile", "FullImage", "RoiImage", "B0Image", "LabelledImageFile", + "ImageFile", "FullImage", "RoiImage", "LabelledImageFile", "ThresholdedImageFile", "ScalarImage", "ThresholdedScalarImage", - "TemplateImage", "GQImage", "ThreeTImage"] + "TemplateImage", "ThreeTImage"] logger = logging.getLogger('AFQ') @@ -374,90 +373,6 @@ def image_getter( return image_getter -class GQImage(ImageDefinition): - """ - Threshold the anisotropic diffusion component of the - Generalized Q-Sampling Model to generate a brain mask - which will include the eyes, optic nerve, and cerebrum - but will exclude most or all of the skull. - - Examples - -------- - api.GroupAFQ(brain_mask_definition=GQImage()) - """ - - def __init__(self): - pass - - def get_name(self): - return "GQ" - - def get_image_getter(self, task_name): - def image_getter_helper(gq_aso): - gq_aso_img = nib.load(gq_aso) - gq_aso_data = gq_aso_img.get_fdata() - ASO_mask = convex_hull_image( - binary_opening( - gq_aso_data > 0.1)) - - return nib.Nifti1Image( - ASO_mask.astype(np.float32), - gq_aso_img.affine), dict( - source=gq_aso, - technique="GQ ASO thresholded maps") - - if task_name == "data": - return image_getter_helper - else: - return lambda data_imap: image_getter_helper( - data_imap["gq_aso"]) - - -class B0Image(ImageDefinition): - """ - Define an image using b0 and dipy's median_otsu. - - Parameters - ---------- - median_otsu_kwargs: dict, optional - Optional arguments to pass into dipy's median_otsu. - Default: {} - - Examples - -------- - brain_image_definition = B0Image() - api.GroupAFQ(brain_image_definition=brain_image_definition) - """ - - def __init__(self, median_otsu_kwargs={}): - self.median_otsu_kwargs = median_otsu_kwargs - - def get_name(self): - return "b0" - - def get_image_getter(self, task_name): - def image_getter_helper(b0): - mean_b0_img = nib.load(b0) - mean_b0 = mean_b0_img.get_fdata() - logger.warning(( - "It is recommended that you provide a brain mask. " - "It is provided with the brain_mask_definition argument. " - "Otherwise, the default brain mask is calculated " - "by using OTSU on the median-filtered B0 image. " - "This can be unreliable. ")) - _, image_data = median_otsu(mean_b0, **self.median_otsu_kwargs) - return nib.Nifti1Image( - image_data.astype(np.float32), - mean_b0_img.affine), dict( - source=b0, - technique="median_otsu applied to b0", - median_otsu_kwargs=self.median_otsu_kwargs) - if task_name == "data": - return image_getter_helper - else: - return lambda data_imap: image_getter_helper(data_imap["b0"]) - - class LabelledImageFile(ImageFile, CombineImageMixin): """ Define an image based on labels in a file. diff --git a/AFQ/models/wmgm_interface.py b/AFQ/models/wmgm_interface.py index 89b4766a7..b11216ca9 100644 --- a/AFQ/models/wmgm_interface.py +++ b/AFQ/models/wmgm_interface.py @@ -3,7 +3,6 @@ from dipy.align import resample -from skimage.morphology import remove_small_objects from scipy.ndimage import gaussian_filter from skimage.segmentation import find_boundaries @@ -21,9 +20,9 @@ def fit_wm_gm_interface(PVE_img, dwiref_img): """ PVE = PVE_img.get_fdata() - csf = PVE[..., 0].copy() - gm = PVE[..., 1].copy() - wm = PVE[..., 2].copy() + csf = PVE[..., 0] + gm = PVE[..., 1] + wm = PVE[..., 2] # Put in diffusion space wm = resample( @@ -42,11 +41,6 @@ def fit_wm_gm_interface(PVE_img, dwiref_img): moving_affine=PVE_img.affine, static_affine=dwiref_img.affine).get_fdata() - # 13824 = 2^9*3^3 - # Needs to be enough to - # remove small objects in the skull - wm = remove_small_objects(wm > 0.5, min_size=13824) - wm_boundary = find_boundaries(wm, mode='inner') gm_smoothed = gaussian_filter(gm, 1) csf_smoothed = gaussian_filter(csf, 1) @@ -56,3 +50,47 @@ def fit_wm_gm_interface(PVE_img, dwiref_img): return nib.Nifti1Image( wm_boundary.astype(np.float32), dwiref_img.affine) + + +def pve_from_subcortex(t1_subcortex_data): + """ + Compute the PVE (Partial Volume Estimation) from the subcortex T1 image. + + Parameters + ---------- + t1_subcortex_data : ndarray + T1 subcortex data from brainchop + + Returns + ------- + pve_img : ndarray + PVE data with CSF, GM, and WM segmentations. + """ + CSF_labels = [0, 3, 4, 11, 12] + GM_labels = [2, 6, 7, 8, 9, 10, 14, 15, 16] + WM_labels = [1, 5] + mixed_labels = [13, 17] + + PVE = np.zeros(t1_subcortex_data.shape + (3,), dtype=np.float32) + + PVE[np.isin(t1_subcortex_data, CSF_labels), 0] = 1.0 + PVE[np.isin(t1_subcortex_data, GM_labels), 1] = 1.0 + PVE[np.isin(t1_subcortex_data, WM_labels), 2] = 1.0 + + # For mixed labels, we assume they are WM interior, GM exterior + # This is a simplification, basically so they do not cause problems + # with ACT + wm_fuzzed = gaussian_filter(PVE[..., 2], 1) + nwm_fuzzed = gaussian_filter(PVE[..., 0] + PVE[..., 1], 1) + bs_exterior = np.logical_and( + find_boundaries( + np.isin(t1_subcortex_data, mixed_labels), + mode='inner'), + nwm_fuzzed >= wm_fuzzed) + bs_interior = np.logical_and( + np.isin(t1_subcortex_data, mixed_labels), + ~bs_exterior) + PVE[bs_exterior, 1] = 1.0 + PVE[bs_interior, 2] = 1.0 + + return PVE diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index df4ccb6af..b40b21c47 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -2,6 +2,8 @@ import numpy as np import logging import multiprocessing +import subprocess +import os.path as op from dipy.io.gradients import read_bvals_bvecs import dipy.core.gradients as dpg @@ -23,18 +25,16 @@ multi_shell_fiber_response, response_from_mask_msmt) from dipy.core.gradients import unique_bvals_tolerance -from dipy.segment.tissue import TissueClassifierHMRF from dipy.align import resample from AFQ.tasks.decorators import as_file, as_img, as_fit_deriv -from AFQ.tasks.utils import get_fname, with_name, str_to_desc +from AFQ.tasks.utils import get_fname, with_name import AFQ.api.bundle_dict as abd import AFQ.data.fetch as afd from AFQ.utils.path import drop_extension, write_json from AFQ._fixes import gwi_odf from AFQ.definitions.utils import Definition -from AFQ.definitions.image import B0Image from AFQ.models.dti import noise_from_b0 from AFQ.models.csd import _fit as csd_fit_model @@ -46,7 +46,7 @@ from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) from AFQ.models.dam import fit_dam, csf_dam, t1_dam -from AFQ.models.wmgm_interface import fit_wm_gm_interface +from AFQ.models.wmgm_interface import fit_wm_gm_interface, pve_from_subcortex from AFQ.models.msmt import MultiShellDeconvModel from AFQ.models.asym_filtering import ( unified_filtering, compute_asymmetry_index, @@ -308,61 +308,17 @@ def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): @immlib.calc("t1w_pve") @as_file(suffix='_desc-pve_probseg.nii.gz') -def t1w_pve(t1_file, brain_mask, pve_nclass=3, pve_beta=0.1): +def t1w_pve(t1_subcortex): """ - Tissue classification using the - Markov Random Fields modeling approach on the T1w image [1, 2] - - Parameters - ---------- - pve_nclass : int, optional - The number of tissue classes to segment - Default: 3 - - pve_beta : float, optional - The beta parameter for the HMRF model (smoothness) - Default: 0.1 + WM, GM, CSF segmentations from subcortex segmentation + from brainchop on T1w image + """ + t1_subcortex_img = nib.load(t1_subcortex) + PVE = pve_from_subcortex(t1_subcortex_img.get_fdata()) - References - ---------- - [1] Zhang et al., 2001 - Yongyue Zhang, Michael Brady, and Stephen Smith. - "Segmentation of brain MR images through a hidden Markov - random field model and the expectation-maximization algorithm." - IEEE Transactions on Medical Imaging, 20(1):45–57, 2001. - https://doi.org/10.1109/42.906424 - - [2] Avants et al., 2011 - Brian B. Avants, Nicholas J. Tustison, Jue Wu, - Philip A. Cook, and James C. Gee. - "An Open Source Multivariate Framework for n-Tissue - Segmentation with Evaluation on Public Data." - Neuroinformatics, 9(4):381–400, 2011. - https://doi.org/10.1007/s12021-011-9109-y - """ - t1w = nib.load(t1_file) - bm = nib.load(brain_mask) - - bm_in_tw1 = resample( - bm.get_fdata(), - t1w.get_fdata(), - moving_affine=bm.affine, - static_affine=t1w.affine).get_fdata() - - t1w_masked = t1w.get_fdata().copy() - t1w_masked[~bm_in_tw1.astype(np.bool_)] = 0 - - hmrf = TissueClassifierHMRF() - logger.info(( - "Generating Tissue Segmentations from T1w image " - "using HMRF, this could take a minute...")) - _, _, PVE = hmrf.classify(t1w_masked, pve_nclass, pve_beta) - - return nib.Nifti1Image(PVE, t1w.affine), dict( - T1wFile=t1_file, - BrainMaskFile=brain_mask, - TissueClasses=pve_nclass, - Beta=pve_beta,) + return nib.Nifti1Image(PVE, t1_subcortex_img.affine), dict( + SubCortexParcellation=t1_subcortex, + labels=["csf", "gm", "wm"],) @immlib.calc("wm_gm_interface") @@ -1599,27 +1555,115 @@ def dki_ak(dki_tf): return dki_tf.ak -@immlib.calc("brain_mask") -@as_file('_desc-brain_mask.nii.gz') -def brain_mask(b0, brain_mask_definition=None): +@immlib.calc("t1_brain_mask", "t1_masked") +def t1_brain_mask(base_fname, t1_file): """ - full path to a nifti file containing - the brain mask + full path to a nifti file containing brain mask from T1w image, + full path to a nifti file containing T1w image masked by the brain mask - Parameters + References ---------- - brain_mask_definition : instance from `AFQ.definitions.image`, optional - This will be used to create - the brain mask, which gets applied before registration to a - template. - If you want no brain mask to be applied, use FullImage. - If None, use B0Image() - Default: None + [1] Masoud, M., Hu, F., & Plis, S. (2023). Brainchop: In-browser MRI + volumetric segmentation and rendering. Journal of Open Source + Software, 8(83), 5098. + https://doi.org/10.21105/joss.05098 + """ + bm_file = get_fname( + base_fname, + f"_desc-brain_mask.nii.gz") + bm_file_json = get_fname( + base_fname, + f"_desc-brain_mask.json") + t1_masked_file = get_fname( + base_fname, + f"_desc-masked_T1w.nii.gz") + t1_masked_json = get_fname( + base_fname, + f"_desc-masked_T1w.json") + + if not op.exists(bm_file) or not op.exists(t1_masked_file): + logger.info( + "Creating brain mask using Brainchop...") + + command = [ + "brainchop", + t1_file, + "-o", t1_masked_file, + "-a", bm_file, + "-m", "mindgrab" + ] + + subprocess.run(command, check=True, capture_output=True, text=True) + + meta = dict( + T1w=t1_file, + model="brainchop") + + write_json(bm_file_json, meta) + write_json(t1_masked_json, meta) + + return bm_file, t1_masked_file + + +@immlib.calc("t1_subcortex") +def t1_subcortex(base_fname, t1_masked): + """ + full path to a nifti file containing segmentation of + subcortical structures from T1w image using Brainchop + + References + ---------- + [1] Masoud, M., Hu, F., & Plis, S. (2023). Brainchop: In-browser MRI + volumetric segmentation and rendering. Journal of Open Source + Software, 8(83), 5098. + https://doi.org/10.21105/joss.05098 + """ + t1_subcortex_file = get_fname( + base_fname, + f"_desc-subcortex_probseg.nii.gz") + t1_subcortex_json = get_fname( + base_fname, + f"_desc-subcortex_probseg.json") + + if not op.exists(t1_subcortex_file): + logger.info( + "Creating subcortical segmentation using Brainchop...") + + command = [ + "brainchop", + t1_masked, + "-i", + "-o", t1_subcortex_file, + "-m", "subcortical" + ] + + subprocess.run(command, check=True, capture_output=True, text=True) + + meta = dict( + T1w=t1_masked, + model="subcortical", + labels=[ + "Unknown", "Cerebral-White-Matter", "Cerebral-Cortex", + "Lateral-Ventricle", "Inferior-Lateral-Ventricle", + "Cerebellum-White-Matter", "Cerebellum-Cortex", + "Thalamus", "Caudate", "Putamen", "Pallidum", + "3rd-Ventricle", "4th-Ventricle", "Brain-Stem", + "Hippocampus", "Amygdala", "Accumbens-area", "VentralDC"]) + + write_json(t1_subcortex_json, meta) + + return t1_subcortex_file + + +@immlib.calc("brain_mask") +@as_file('_desc-brain_mask.nii.gz') +@as_img +def brain_mask(t1_brain_mask, b0): + """ + full path to a nifti file containing the brain mask """ - # Note that any case where brain_mask_definition is not None - # is handled in get_data_plan - # This is just the default - return B0Image().get_image_getter("data")(b0) + return resample(t1_brain_mask, b0), dict( + BrainMaskinT1w=t1_brain_mask) @immlib.calc("bundle_dict", "reg_template", "tmpl_name") @@ -1713,7 +1757,8 @@ def get_data_plan(kwargs): "strings/scalar definitions") data_tasks = with_name([ - get_data_gtab, b0, b0_mask, brain_mask, + get_data_gtab, b0, b0_mask, brain_mask, t1_brain_mask, + t1_subcortex, configure_ncpus_nthreads, t1w_pve, wm_gm_interface, dam_fit, dam_csf, dam_pseudot1, @@ -1753,18 +1798,4 @@ def get_data_plan(kwargs): scalars.append(scalar) kwargs["scalars"] = scalars - bm_def = kwargs.get( - "brain_mask_definition", None) - if bm_def is not None: - if not isinstance(bm_def, Definition): - raise TypeError( - "brain_mask_definition must be a Definition") - del kwargs["brain_mask_definition"] - data_tasks["brain_mask_res"] = immlib.calc("brain_mask")( - as_file( - suffix=( - f'_desc-{str_to_desc(bm_def.get_name())}' - '_mask.nii.gz'), - subfolder="models")(bm_def.get_image_getter("data"))) - return immlib.plan(**data_tasks) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index fc7daf702..ddea32c33 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -448,24 +448,17 @@ def test_API_type_checking(): raise e del myafq - with pytest.raises( - TypeError, - match="brain_mask_definition must be a Definition"): - myafq = GroupAFQ( - bids_path, - preproc_pipeline='vistasoft', - brain_mask_definition="not a brain mask") - with pytest.raises( ValueError, match=r"No file found with these parameters:\n*"): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', - brain_mask_definition=ImageFile( - suffix='dne_dne', - filters={'scope': 'dne_dne'})) - myafq.export("brain_mask") + tracking_params=dict( + seed_mask=ImageFile( + suffix='dne_dne', + filters={'scope': 'dne_dne'}))) + myafq.export("seed_mask") with pytest.raises( TypeError, diff --git a/examples/howto_examples/acoustic_radiations.py b/examples/howto_examples/acoustic_radiations.py index 3216f797a..31a05bbef 100644 --- a/examples/howto_examples/acoustic_radiations.py +++ b/examples/howto_examples/acoustic_radiations.py @@ -79,18 +79,11 @@ # are passed as `bundle_info=bundles`. The call to `my_afq.export_all()` # initiates the pipeline. -brain_mask_definition = ImageFile( - suffix="mask", - filters={'desc': 'brain', - 'space': 'T1w', - 'scope': 'qsiprep'}) - my_afq = GroupAFQ( bids_path=study_dir, preproc_pipeline="qsiprep", participant_labels=["NDARAA948VFH"], output_dir=op.join(study_dir, "derivatives", "afq_ar"), - brain_mask_definition=brain_mask_definition, tracking_params={"n_seeds": 4, "directions": "prob", "odf_model": "CSD", diff --git a/examples/howto_examples/add_custom_bundle.py b/examples/howto_examples/add_custom_bundle.py index ed223cde8..db34d06dd 100644 --- a/examples/howto_examples/add_custom_bundle.py +++ b/examples/howto_examples/add_custom_bundle.py @@ -169,17 +169,10 @@ # are passed as `bundle_info=bundles`. The call to `my_afq.export_all()` # initiates the pipeline. -brain_mask_definition = ImageFile( - suffix="mask", - filters={'desc': 'brain', - 'space': 'T1w', - 'scope': 'qsiprep'}) - my_afq = GroupAFQ( bids_path=study_dir, preproc_pipeline="qsiprep", output_dir=op.join(study_dir, "derivatives", "afq_slf"), - brain_mask_definition=brain_mask_definition, tracking_params={"n_seeds": 4, "directions": "prob", "odf_model": "CSD", diff --git a/examples/howto_examples/cerebellar_peduncles.py b/examples/howto_examples/cerebellar_peduncles.py index 3659d54ca..5e6e8bed2 100644 --- a/examples/howto_examples/cerebellar_peduncles.py +++ b/examples/howto_examples/cerebellar_peduncles.py @@ -49,18 +49,6 @@ """ The bundle dict has been defined, and now we are ready to run the AFQ pipeline. -In this case, we are using data that has been preprocessed with QSIprep, so -we have a brain mask that was generated from the T1w data of this subject. -""" - -brain_mask_definition = ImageFile( - suffix="mask", - filters={'desc': 'brain', - 'space': 'T1w', - 'scope': 'qsiprep'}) - - -""" Next, we define a GroupAFQ object. In this case, the tracking parameters focus specifically on the CP, by using the ``RoiImage`` class to define the seed region. We seed extensively in the ROIs that define the CPs. @@ -70,7 +58,6 @@ name="cp_afq", bids_path=bids_path, preproc_pipeline="qsiprep", - brain_mask_definition=brain_mask_definition, tracking_params={ "n_seeds": 4, "directions": "prob", diff --git a/examples/howto_examples/cloudknot_example.py b/examples/howto_examples/cloudknot_example.py index 1f82a3f02..dd5391d6b 100644 --- a/examples/howto_examples/cloudknot_example.py +++ b/examples/howto_examples/cloudknot_example.py @@ -62,19 +62,10 @@ def afq_process_subject(subject): "local_bids_dir", include_derivs=["pipeline_name"]) - # you can optionally provide your own segmentation file - # in this case, we look for a file with suffix 'seg' - # in the 'pipeline_name' pipeline, - # and we consider all non-zero labels to be a part of the brain - brain_mask_definition = afm.LabelledImageFile( - suffix='seg', filters={'scope': 'pipeline_name'}, - exclusive_labels=[0]) - # define the api AFQ object myafq = GroupAFQ( "local_bids_dir", preproc_pipeline="pipeline_name", - brain_mask_definition=brain_mask_definition, viz_backend_spec='plotly', # this will generate both interactive html and GIFs # noqa scalars=["dki_fa", "dki_md"]) diff --git a/examples/howto_examples/cloudknot_hcp_example.py b/examples/howto_examples/cloudknot_hcp_example.py index 9f25b88c0..b58da085d 100644 --- a/examples/howto_examples/cloudknot_hcp_example.py +++ b/examples/howto_examples/cloudknot_hcp_example.py @@ -76,16 +76,9 @@ def afq_process_subject(subject, seed_mask, n_seeds, "n_seeds": n_seeds, "random_seeds": random_seeds} - # use segmentation file from HCP to get a brain mask, - # where everything not labelled 0 is considered a part of the brain - brain_mask_definition = afm.LabelledImageFile( - suffix='seg', filters={'scope': 'dmriprep'}, - exclusive_labels=[0]) - # define the api GroupAFQ object myafq = GroupAFQ( hcp_bids, - brain_mask_definition=brain_mask_definition, tracking_params=tracking_params) # export_all runs the entire pipeline and creates many useful derivates diff --git a/examples/howto_examples/optic_radiations.py b/examples/howto_examples/optic_radiations.py index dd07d933d..9c5d05d67 100644 --- a/examples/howto_examples/optic_radiations.py +++ b/examples/howto_examples/optic_radiations.py @@ -86,18 +86,11 @@ # are passed as `bundle_info=bundles`. The call to `my_afq.export_all()` # initiates the pipeline. -brain_mask_definition = ImageFile( - suffix="mask", - filters={'desc': 'brain', - 'space': 'T1w', - 'scope': 'qsiprep'}) - my_afq = GroupAFQ( bids_path=study_dir, preproc_pipeline="qsiprep", participant_labels=["NDARAA948VFH"], output_dir=op.join(study_dir, "derivatives", "afq_or"), - brain_mask_definition=brain_mask_definition, tracking_params={"n_seeds": 4, "directions": "prob", "odf_model": "CSD", diff --git a/examples/howto_examples/run_afq_fwdti.py b/examples/howto_examples/run_afq_fwdti.py index 0e21478a3..32e41e8c4 100644 --- a/examples/howto_examples/run_afq_fwdti.py +++ b/examples/howto_examples/run_afq_fwdti.py @@ -45,20 +45,14 @@ # -------------------- # In addition to preprocessd dMRI data, HBN-POD2 contains brain mask and mapping # information for each subject. We can use this information in our pipeline, by -# inserting this information as `mapping_definition` and `brain_mask_definition` -# inputs to the `GroupAFQ` class initializer. When initializing this object, we -# will also ask for the fwDTI scalars to be computed. For expedience, we will -# limit our investigation to the bilateral arcuate fasciculus and track only -# around that bundle. If you would like to do this for all bundles, you would -# remove the `bundle_dict` and `tracking_params` inputs to the initializer that +# inserting this information as `mapping_definition` inputs to the `GroupAFQ` class +# initializer. When initializing this object, we will also ask for the fwDTI scalars +# to be computed. For expedience, we will limit our investigation to the bilateral +# arcuate fasciculus and track only around that bundle. If you would like to do this +# for all bundles, you would remove the `bundle_dict` and `tracking_params` inputs +# to the initializer that # are provided below. -brain_mask_definition = ImageFile( - suffix="mask", - filters={'desc': 'brain', - 'space': 'T1w', - 'scope': 'qsiprep'}) - bundle_names = ["Left Arcuate", "Right Arcuate"] bundle_dict = abd.default18_bd()[bundle_names] @@ -72,7 +66,6 @@ "random_seeds": True, "seed_mask": RoiImage(use_waypoints=True, use_endpoints=True), }, - brain_mask_definition=brain_mask_definition, scalars=["fwdti_fa", "fwdti_md", "fwdti_fwf", "dti_fa", "dti_md"]) ############################################################################# diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index ad4fe60df..05eadbdf8 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -49,7 +49,8 @@ # stored in the `~/AFQ_data/stanford_hardi/` BIDS directory. Set it to None if # you want to use the results of previous runs. -afd.organize_stanford_data(clear_previous_afq="track") +# clear_previous_afq="track" +afd.organize_stanford_data(clear_previous_afq="all") ########################################################################## # Set tractography parameters (optional) @@ -59,9 +60,9 @@ # distributed in the white matter. We only do this to make this example # faster and consume less space. -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=100000, random_seeds=True, - rng_seed=2022, + rng_seed=2025, trx=True) ########################################################################## diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 4fb7ce9c6..e48e6505a 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -81,9 +81,9 @@ # distributed in the white matter. We only do this to make this example # faster and consume less space. -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=100000, random_seeds=True, - rng_seed=2022, + rng_seed=2025, trx=True) ########################################################################## diff --git a/examples/tutorial_examples/viz_008_endpoints.py b/examples/tutorial_examples/viz_008_endpoints.py index a009e47a7..4ae7a3357 100644 --- a/examples/tutorial_examples/viz_008_endpoints.py +++ b/examples/tutorial_examples/viz_008_endpoints.py @@ -33,10 +33,6 @@ ses_id = "HBNsiteRU" # Example session ID _, study_dir = afd.fetch_hbn_preproc([subject_id]) -brain_mask_definition = afm.ImageFile( - suffix="mask", - filters={"scope": "qsiprep", "desc": "brain"}) - endpoint_maps = { # Endpoint maps by threshold in mm "2": {}, "3": {}, @@ -85,8 +81,7 @@ "n_seeds": 2000000, "random_seeds": True}, output_dir=output_dir, - endpoint_threshold=None, - brain_mask_definition=brain_mask_definition) + endpoint_threshold=None) endpoints_maps = myafq.export("endpoint_maps") diff --git a/setup.cfg b/setup.cfg index 70d0bd17f..31f4d8ad9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,6 +36,7 @@ install_requires = immlib numba osqp + brainchop pydra trx-python ray From b0cc82bfb554b1d1fd1785c8996853098881175f Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 1 Aug 2025 16:02:02 -0700 Subject: [PATCH 064/116] fix pydra test, better integration with brainchop --- AFQ/api/group.py | 27 ++++------- AFQ/definitions/utils.py | 2 + AFQ/nn/__init__.py | 0 AFQ/nn/brainchop.py | 64 ++++++++++++++++++++++++ AFQ/tasks/data.py | 102 +++++++++++---------------------------- AFQ/tests/test_api.py | 13 +++-- 6 files changed, 115 insertions(+), 93 deletions(-) create mode 100644 AFQ/nn/__init__.py create mode 100644 AFQ/nn/brainchop.py diff --git a/AFQ/api/group.py b/AFQ/api/group.py index f09959de3..16eedcdce 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -15,6 +15,7 @@ import AFQ.utils.streamlines as aus from AFQ.viz.utils import get_eye from AFQ.data.utils import aws_import_msg_error +from AFQ.definitions.utils import find_file from dipy.utils.parallel import paramap from dipy.io.stateful_tractogram import StatefulTractogram, Space @@ -300,20 +301,18 @@ def __init__(self, # files. Maintain input ``bids_filters`` in case user wants to # specify acquisition labels, but pop suffix since it is # already specified inside ``get_bvec()`` and ``get_bval()`` - suffix = bids_filters.pop("suffix", None) + nearby_filters = {**bids_filters, "scope": preproc_pipeline} + nearby_filters.pop("suffix", None) bvec_file = bids_layout.get_bvec( dwi_data_file, - **bids_filters) + **nearby_filters) bval_file = bids_layout.get_bval( dwi_data_file, - **bids_filters) - t1_files = bids_layout.get(suffix="T1w", **bids_filters) - if (not len(t1_files)): - self.logger.warning( - f"No T1w found for subject {subject} and session " - f"{session}. Skipping.") - continue - t1_file = t1_files[0] + **nearby_filters) + t1_file = find_file( + bids_layout, dwi_data_file, + nearby_filters, + "T1w", session, subject) self.logger.info( f"Using the following files for subject {subject} " @@ -323,9 +322,6 @@ def __init__(self, self.logger.info(f" BVEC: {bvec_file}") self.logger.info(f" T1: {t1_file}") - if suffix is not None: - bids_filters["suffix"] = suffix - # Call find path for all definitions for key, value in this_kwargs.items(): if key == "scalars": @@ -1097,7 +1093,7 @@ def _submit_pydra(self, runnable): if "'NoneType' object has no attribute 'replace'" not in str(e): raise - def export(self, attr_name="help", collapse=True): + def export(self, attr_name="help"): f""" Export a specific output. To print a list of available outputs, call export without arguments. @@ -1107,9 +1103,6 @@ def export(self, attr_name="help", collapse=True): ---------- attr_name : str Name of the output to export. Default: "help" - collapse : bool - Whether to collapse session dimension if there is only 1 session. - Default: True Returns ------- diff --git a/AFQ/definitions/utils.py b/AFQ/definitions/utils.py index cadb80524..e69f7aaa4 100644 --- a/AFQ/definitions/utils.py +++ b/AFQ/definitions/utils.py @@ -96,6 +96,8 @@ def find_file(bids_layout, path, filters, suffix, session, subject, Helper function Generic calls to get_nearest to find a file """ + filters = filters.copy() + if "extension" not in filters: filters["extension"] = extension if "suffix" not in filters: diff --git a/AFQ/nn/__init__.py b/AFQ/nn/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py new file mode 100644 index 000000000..a8c322238 --- /dev/null +++ b/AFQ/nn/brainchop.py @@ -0,0 +1,64 @@ +import subprocess +import tempfile + +import numpy as np +import nibabel as nib + +from brainchop.utils import get_model +from brainchop.niimath import ( + conform, + bwlabel) + +from tinygrad import Tensor + +import logging + + +logger = logging.getLogger('AFQ') + + +def run_brainchop(t1_img, model): + """ + Run the Brainchop command line interface with the provided arguments. + """ + model = get_model(model) + output_dtype = "char" + + with tempfile.TemporaryDirectory() as temp_dir: + tmp_t1_file = f"{temp_dir}/t1.nii.gz" + tmp_out_file = f"{temp_dir}/output.nii.gz" + + t1_data = t1_img.get_fdata() + t1_data = np.clip(t1_data, 0, t1_data.max()) + nib.save(nib.Nifti1Image(t1_data, t1_img.affine), tmp_t1_file) + + volume, header = conform(tmp_t1_file) + + image = Tensor(volume.transpose((2, 1, 0)).astype(np.float32)).rearrange( + "... -> 1 1 ..." + ) + + output_channels = model(image) + + output = ( + output_channels.argmax(axis=1) + .rearrange("1 x y z -> z y x") + .numpy() + .astype(np.uint8) + ) + + labels, new_header = bwlabel(header, output) + full_input = new_header + labels.tobytes() + + cmd = [ + "niimath", "-", "-reslice_nn", tmp_t1_file, + "-gz", "1", tmp_out_file, "-odt", output_dtype] + + subprocess.run(cmd, input=full_input, check=True) + + output_img = nib.load(tmp_out_file) + output_img = nib.Nifti1Image( + output_img.get_fdata().copy(), + output_img.affine.copy()) # force into memory + + return output_img diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index b40b21c47..7042d1ab0 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -2,7 +2,6 @@ import numpy as np import logging import multiprocessing -import subprocess import os.path as op from dipy.io.gradients import read_bvals_bvecs @@ -52,6 +51,8 @@ unified_filtering, compute_asymmetry_index, compute_odd_power_map, compute_nufid_asym) +from AFQ.nn.brainchop import run_brainchop + logger = logging.getLogger('AFQ') @@ -1555,11 +1556,11 @@ def dki_ak(dki_tf): return dki_tf.ak -@immlib.calc("t1_brain_mask", "t1_masked") -def t1_brain_mask(base_fname, t1_file): +@immlib.calc("t1_brain_mask") # TODO: t1_masked +@as_file(suffix='_desc-T1w_mask.nii.gz') +def t1_brain_mask(t1_file): """ full path to a nifti file containing brain mask from T1w image, - full path to a nifti file containing T1w image masked by the brain mask References ---------- @@ -1568,45 +1569,14 @@ def t1_brain_mask(base_fname, t1_file): Software, 8(83), 5098. https://doi.org/10.21105/joss.05098 """ - bm_file = get_fname( - base_fname, - f"_desc-brain_mask.nii.gz") - bm_file_json = get_fname( - base_fname, - f"_desc-brain_mask.json") - t1_masked_file = get_fname( - base_fname, - f"_desc-masked_T1w.nii.gz") - t1_masked_json = get_fname( - base_fname, - f"_desc-masked_T1w.json") - - if not op.exists(bm_file) or not op.exists(t1_masked_file): - logger.info( - "Creating brain mask using Brainchop...") - - command = [ - "brainchop", - t1_file, - "-o", t1_masked_file, - "-a", bm_file, - "-m", "mindgrab" - ] - - subprocess.run(command, check=True, capture_output=True, text=True) - - meta = dict( - T1w=t1_file, - model="brainchop") - - write_json(bm_file_json, meta) - write_json(t1_masked_json, meta) - - return bm_file, t1_masked_file + return run_brainchop(nib.load(t1_file), "mindgrab"), dict( + T1w=t1_file, + model="brainchop") @immlib.calc("t1_subcortex") -def t1_subcortex(base_fname, t1_masked): +@as_file(suffix='_desc-subcortex_probseg.nii.gz') +def t1_subcortex(t1_file, t1_brain_mask): """ full path to a nifti file containing segmentation of subcortical structures from T1w image using Brainchop @@ -1618,46 +1588,32 @@ def t1_subcortex(base_fname, t1_masked): Software, 8(83), 5098. https://doi.org/10.21105/joss.05098 """ - t1_subcortex_file = get_fname( - base_fname, - f"_desc-subcortex_probseg.nii.gz") - t1_subcortex_json = get_fname( - base_fname, - f"_desc-subcortex_probseg.json") - - if not op.exists(t1_subcortex_file): - logger.info( - "Creating subcortical segmentation using Brainchop...") - - command = [ - "brainchop", - t1_masked, - "-i", - "-o", t1_subcortex_file, - "-m", "subcortical" - ] + t1_img = nib.load(t1_file) + t1_data = t1_img.get_fdata() + t1_mask = nib.load(t1_brain_mask) + t1_data[t1_mask.get_fdata() == 0] = 0 + t1_img_masked = nib.Nifti1Image( + t1_data, t1_img.affine) - subprocess.run(command, check=True, capture_output=True, text=True) + subcortical_img = run_brainchop( + t1_img_masked, "subcortical") - meta = dict( - T1w=t1_masked, - model="subcortical", - labels=[ - "Unknown", "Cerebral-White-Matter", "Cerebral-Cortex", - "Lateral-Ventricle", "Inferior-Lateral-Ventricle", - "Cerebellum-White-Matter", "Cerebellum-Cortex", - "Thalamus", "Caudate", "Putamen", "Pallidum", - "3rd-Ventricle", "4th-Ventricle", "Brain-Stem", - "Hippocampus", "Amygdala", "Accumbens-area", "VentralDC"]) - - write_json(t1_subcortex_json, meta) + meta = dict( + T1w=t1_file, + model="subcortical", + labels=[ + "Unknown", "Cerebral-White-Matter", "Cerebral-Cortex", + "Lateral-Ventricle", "Inferior-Lateral-Ventricle", + "Cerebellum-White-Matter", "Cerebellum-Cortex", + "Thalamus", "Caudate", "Putamen", "Pallidum", + "3rd-Ventricle", "4th-Ventricle", "Brain-Stem", + "Hippocampus", "Amygdala", "Accumbens-area", "VentralDC"]) - return t1_subcortex_file + return subcortical_img, meta @immlib.calc("brain_mask") @as_file('_desc-brain_mask.nii.gz') -@as_img def brain_mask(t1_brain_mask, b0): """ full path to a nifti file containing the brain mask diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index ddea32c33..5b4251e70 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -31,7 +31,7 @@ import AFQ.utils.streamlines as aus import AFQ.utils.bin as afb from AFQ.definitions.mapping import SynMap, AffMap, SlrMap, IdentityMap -from AFQ.definitions.image import (RoiImage, ImageFile, ScalarImage, TemplateImage) +from AFQ.definitions.image import ImageFile, ScalarImage, TemplateImage def touch(fname, times=None): @@ -603,10 +603,17 @@ def test_AFQ_reco80(): npt.assert_(len(seg_sft.get_bundle('CCMid').streamlines) > 0) +@pytest.mark.skip(reason="fixed in next tinygrad update (0.10.4)") def test_AFQ_pydra(): - _, bids_path = afd.fetch_hbn_preproc(["NDARAA948VFH", "NDARAV554TP2"]) - pga = ParallelGroupAFQ(bids_path, preproc_pipeline="qsiprep") + participants = ["NDARAA948VFH", "NDARAV554TP2"] + _, bids_path = afd.fetch_hbn_preproc(participants) + pga = ParallelGroupAFQ( + bids_path, + output_dir=op.join(bids_path, 'derivatives', 'pydra_afq'), + participant_labels=participants, + preproc_pipeline="qsiprep") pga.export("dti_fa") + pga.export("wm_gm_interface") def test_AFQ_filterb(): From 798880c7d11ef60558403bc543f16f3a33d3d771 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 1 Aug 2025 16:39:34 -0700 Subject: [PATCH 065/116] add ability to specify t1 preproc pipeline --- AFQ/api/group.py | 7 +++ AFQ/tests/test_api.py | 43 +++++++++---------- examples/howto_examples/plot_afq_callosal.py | 1 + examples/howto_examples/plot_recobundles.py | 1 + examples/howto_examples/run_pyAFQ_with_GPU.py | 1 + .../use_subject_space_rois_from_freesurfer.py | 1 + examples/howto_examples/vof_example.py | 1 + .../plot_001_group_afq_api.py | 1 + examples/tutorial_examples/plot_003_rerun.py | 3 ++ .../tutorial_examples/plot_006_bids_layout.py | 1 + 10 files changed, 37 insertions(+), 23 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 16eedcdce..ce3299427 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -79,6 +79,7 @@ def __init__(self, bids_path, bids_filters={"suffix": "dwi"}, preproc_pipeline="all", + t1_pipeline=None, participant_labels=None, output_dir=None, parallel_params={"engine": "serial"}, @@ -99,6 +100,10 @@ def __init__(self, preproc_pipeline : str, optional. The name of the pipeline used to preprocess the DWI data. Default: "all". + t1_pipeline : str or None, optional + The name of the pipeline used to preprocess the T1w data. + If None, defaults to the same as preproc_pipeline. + Default: None participant_labels : list or None, optional List of participant labels (subject IDs) to perform processing on. If None, all subjects are used. @@ -309,6 +314,8 @@ def __init__(self, bval_file = bids_layout.get_bval( dwi_data_file, **nearby_filters) + nearby_filters.pop("scope", None) + nearby_filters["scope"] = t1_pipeline t1_file = find_file( bids_layout, dwi_data_file, nearby_filters, diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 5b4251e70..dfdde5246 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -263,6 +263,7 @@ def test_AFQ_custom_tract(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=bundle_info, import_tract={ "suffix": "tractography", @@ -298,6 +299,7 @@ def test_AFQ_fury(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params={"n_seeds": 250000}, viz_backend_spec="fury") myafq.export("all_bundles_figure") @@ -312,6 +314,7 @@ def test_AFQ_trx(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', # should throw warning but not error scalars=["dti_fa", "dti_md", ImageFile(suffix="DNE")], tracking_params={"trx": True, "n_seeds": 250000}) @@ -367,6 +370,7 @@ def test_AFQ_data(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', mapping_definition=mapping) npt.assert_equal(nib.load(myafq.export("b0")["01"]).shape, myafq.export("dwi")["01"].shape[:3]) @@ -388,6 +392,7 @@ def test_AFQ_anisotropic(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', min_bval=1990, max_bval=2010, b0_threshold=50, @@ -440,6 +445,7 @@ def test_API_type_checking(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', import_tract=["dwi"]) myafq.export("streamlines") except LazyError as e: @@ -454,6 +460,7 @@ def test_API_type_checking(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=dict( seed_mask=ImageFile( suffix='dne_dne', @@ -468,6 +475,7 @@ def test_API_type_checking(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=[2, 3]) try: myafq.export("bundle_dict") @@ -484,6 +492,7 @@ def test_API_type_checking(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', mapping_definition=IdentityMap(), reg_subject_spec="dti_fa_subject", tracking_params={ @@ -508,6 +517,7 @@ def test_API_type_checking(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', viz_backend_spec="matplotlib") try: myafq.export("viz_backend") @@ -535,6 +545,7 @@ def test_AFQ_slr(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', reg_subject_spec='subject_sls', reg_template_spec='hcp_atlas', import_tract=op.join( @@ -564,6 +575,7 @@ def test_AFQ_reco(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', viz_backend_spec="plotly", profile_weights="median", bundle_info=abd.reco_bd(16), @@ -593,6 +605,7 @@ def test_AFQ_reco80(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params, bundle_info=abd.reco_bd(16), segmentation_params={ @@ -621,6 +634,7 @@ def test_AFQ_filterb(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', max_bval=1000) myafq.export("b0") @@ -645,6 +659,7 @@ def test_AFQ_custom_subject_reg(): b0_file = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=bundle_info).export("b0")["01"] # make a different temporary directly to test this custom file in @@ -655,6 +670,7 @@ def test_AFQ_custom_subject_reg(): myafq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=bundle_info, reg_template_spec="mni_T2", reg_subject_spec=ImageFile( @@ -673,6 +689,7 @@ def test_AFQ_FA(): myafq = GroupAFQ( bids_path=bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', reg_template_spec='dti_fa_template', reg_subject_spec='dti_fa_subject') myafq.export("rois") @@ -706,27 +723,6 @@ def test_auto_cli(): afb.parse_config_run_afq(config_file, arg_dict, False) -@pytest.mark.skip(reason="causes segmentation fault") -def test_run_using_auto_cli(): - tmpdir, bids_path, _ = get_temp_hardi() - config_file = op.join(tmpdir.name, 'test.toml') - - arg_dict = afb.func_dict_to_arg_dict() - - # set our custom defaults for the toml file - # It is easier to edit them here, than to parse the file and edit them - # after the file is written - arg_dict['BIDS_PARAMS']['bids_path']['default'] = bids_path - arg_dict['BIDS_PARAMS']['dmriprep']['default'] = 'vistasoft' - arg_dict['DATA']['bundle_info']['default'] = abd.default18_bd()[( - "Left Corticospinal")] - arg_dict['TRACTOGRAPHY_PARAMS']['n_seeds']['default'] = 500 - arg_dict['TRACTOGRAPHY_PARAMS']['random_seeds']['default'] = True - - afb.generate_config(config_file, arg_dict, False) - afb.parse_config_run_afq(config_file, arg_dict, False) - - def test_AFQ_data_waypoint(): """ Test with some actual data again, this time for track segmentation @@ -778,7 +774,7 @@ def test_AFQ_data_waypoint(): } tracking_params = dict(odf_model="csd", - n_seeds=5000, + n_seeds=2000, random_seeds=True, rng_seed=42) segmentation_params = dict(return_idx=True) @@ -893,7 +889,8 @@ def test_AFQ_data_waypoint(): config = dict( BIDS_PARAMS=dict( bids_path=bids_path, - preproc_pipeline='vistasoft'), + preproc_pipeline='vistasoft', + t1_pipeline='freesurfer',), DATA=dict( bundle_info=bundle_dict_as_str), SEGMENTATION=dict( diff --git a/examples/howto_examples/plot_afq_callosal.py b/examples/howto_examples/plot_afq_callosal.py index e38297ef7..3918f557a 100644 --- a/examples/howto_examples/plot_afq_callosal.py +++ b/examples/howto_examples/plot_afq_callosal.py @@ -67,6 +67,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=abd.callosal_bd(), tracking_params=tracking_params, segmentation_params=segmentation_params, diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index 356121e7b..fe8d4ee38 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -41,6 +41,7 @@ # Set the algorithm to use RecoBundles for bundle recognition: bundle_info=abd.reco_bd(16), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params, viz_backend_spec='plotly_no_gif') diff --git a/examples/howto_examples/run_pyAFQ_with_GPU.py b/examples/howto_examples/run_pyAFQ_with_GPU.py index a6f61a493..447533111 100644 --- a/examples/howto_examples/run_pyAFQ_with_GPU.py +++ b/examples/howto_examples/run_pyAFQ_with_GPU.py @@ -40,6 +40,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params, tractography_ngpus=1) diff --git a/examples/howto_examples/use_subject_space_rois_from_freesurfer.py b/examples/howto_examples/use_subject_space_rois_from_freesurfer.py index f322d35bc..0d1fbf3e4 100644 --- a/examples/howto_examples/use_subject_space_rois_from_freesurfer.py +++ b/examples/howto_examples/use_subject_space_rois_from_freesurfer.py @@ -139,6 +139,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params, bundle_info=bundles) diff --git a/examples/howto_examples/vof_example.py b/examples/howto_examples/vof_example.py index e35c8d2b3..26e76d738 100644 --- a/examples/howto_examples/vof_example.py +++ b/examples/howto_examples/vof_example.py @@ -32,6 +32,7 @@ op.join(afd.afq_home, 'stanford_hardi'), bundle_info=bundle_dict, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params={ "n_seeds": 50000, "random_seeds": True, diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index 05eadbdf8..3f6d30159 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -94,6 +94,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params, viz_backend_spec='plotly_no_gif') diff --git a/examples/tutorial_examples/plot_003_rerun.py b/examples/tutorial_examples/plot_003_rerun.py index 5b4084a08..d9387340b 100644 --- a/examples/tutorial_examples/plot_003_rerun.py +++ b/examples/tutorial_examples/plot_003_rerun.py @@ -37,6 +37,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', tracking_params=tracking_params) ################### @@ -62,6 +63,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', b0_threshold=100, tracking_params=tracking_params) @@ -103,6 +105,7 @@ myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', b0_threshold=100, tracking_params=tracking_params) diff --git a/examples/tutorial_examples/plot_006_bids_layout.py b/examples/tutorial_examples/plot_006_bids_layout.py index dd77b49e7..846a6dacd 100644 --- a/examples/tutorial_examples/plot_006_bids_layout.py +++ b/examples/tutorial_examples/plot_006_bids_layout.py @@ -245,6 +245,7 @@ my_afq = GroupAFQ( bids_path, preproc_pipeline='vistasoft', + t1_pipeline='freesurfer', bundle_info=bundle_info, import_tract={ "suffix": "tractography", From e1139cf56c03b1648858fe0724d1aed9f563fd29 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 1 Aug 2025 16:46:11 -0700 Subject: [PATCH 066/116] bf --- AFQ/api/group.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index ce3299427..a774e8566 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -150,6 +150,8 @@ def __init__(self, if not isinstance(bids_filters, dict): raise TypeError("bids_filters must be a dict") # preproc_pipeline typechecking handled by pyBIDS + if t1_pipeline is None: + t1_pipeline = preproc_pipeline if participant_labels is not None\ and not isinstance(participant_labels, list): raise TypeError( From d2fb25314102a64e631bfc143ae75435a914e9da Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 10:07:17 -0700 Subject: [PATCH 067/116] faster examples --- examples/howto_examples/plot_recobundles.py | 2 +- examples/tutorial_examples/plot_001_group_afq_api.py | 9 ++++----- .../tutorial_examples/plot_002_participant_afq_api.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index fe8d4ee38..63ca8d1b6 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -20,7 +20,7 @@ afd.organize_stanford_data(clear_previous_afq="track") -tracking_params = dict(n_seeds=100000, +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=42) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index 3f6d30159..d0c81d6eb 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -49,8 +49,7 @@ # stored in the `~/AFQ_data/stanford_hardi/` BIDS directory. Set it to None if # you want to use the results of previous runs. -# clear_previous_afq="track" -afd.organize_stanford_data(clear_previous_afq="all") +afd.organize_stanford_data(clear_previous_afq="track") ########################################################################## # Set tractography parameters (optional) @@ -60,7 +59,7 @@ # distributed in the white matter. We only do this to make this example # faster and consume less space. -tracking_params = dict(n_seeds=100000, +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2025, trx=True) @@ -221,8 +220,8 @@ for ind in bundle_counts.index: if ind == "Total Recognized": threshold = 1000 - elif "Vertical Occipital" in ind: - threshold = 1 + elif "Callosum" in ind: + threshold = 5 else: threshold = 10 if bundle_counts["n_streamlines"][ind] < threshold: diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index e48e6505a..5bdc6117e 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -81,7 +81,7 @@ # distributed in the white matter. We only do this to make this example # faster and consume less space. -tracking_params = dict(n_seeds=100000, +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2025, trx=True) From fa232859f791b2734d2848ac549fe578b58a3c27 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 11:25:35 -0700 Subject: [PATCH 068/116] small changes here and there --- AFQ/recognition/roi.py | 2 +- AFQ/tasks/data.py | 29 ++++++++++++----------------- AFQ/tests/test_api.py | 1 + 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/AFQ/recognition/roi.py b/AFQ/recognition/roi.py index 69556ed75..d87062f58 100644 --- a/AFQ/recognition/roi.py +++ b/AFQ/recognition/roi.py @@ -56,7 +56,7 @@ def clean_by_endpoints(streamlines, target, target_idx, tol=0, Where N is number of nodes in the array, the collection of streamlines to filter down to. target: Nifti1Image - Nifti1Image containing a boolean representation of the ROI. + Nifti1Image containing a distance transform of the ROI. target_idx: int. Index within each streamline to check if within the target region. Typically 0 for startpoint ROIs or -1 for endpoint ROIs. diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 7042d1ab0..e454c5275 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -9,7 +9,6 @@ from dipy.data import default_sphere, get_sphere import immlib -import pimms import dipy.reconst.dki as dpy_dki import dipy.reconst.dti as dpy_dti @@ -162,7 +161,7 @@ def b0_mask(b0, brain_mask): return masked_data, meta -@pimms.calc("dam_params") +@immlib.calc("dam_params") @as_file(suffix='_model-dam_param-slopeintercept_dwimap.nii.gz', subfolder="models") @as_img @@ -193,7 +192,7 @@ def dam_fit(data, gtab, masked_b0, return params_map, dict(low_signal_thresh=dam_low_signal_thresh) -@pimms.calc("dam_csf") +@immlib.calc("dam_csf") @as_file(suffix='_model-dam_param-csf_probseg.nii.gz', subfolder="models") @as_img @@ -209,7 +208,7 @@ def dam_csf(dam_params): threshold=threshold) -@pimms.calc("dam_pseudot1") +@immlib.calc("dam_pseudot1") @as_file(suffix='_model-dam_param-pseudot1_dwimap.nii.gz', subfolder="models") @as_img @@ -225,7 +224,7 @@ def dam_pseudot1(dam_params, dam_csf): return pseudo_t1, dict(source=dam_params) -@pimms.calc("dki_csf") +@immlib.calc("dki_csf") @as_file(suffix='_model-dki_param-csf_probseg.nii.gz', subfolder="models") @as_img @@ -251,7 +250,7 @@ def dki_csf(dki_md): peak_sigma=peak_sigma) -@pimms.calc("dki_wm") +@immlib.calc("dki_wm") @as_file(suffix='_model-dki_param-wm_probseg.nii.gz', subfolder="models") @as_img @@ -278,7 +277,7 @@ def dki_wm(dki_fa, dki_wm_ll=0.1, dki_gm_ul=0.3): dki_gm_ul=dki_gm_ul) -@pimms.calc("dki_gm") +@immlib.calc("dki_gm") @as_file(suffix='_model-dki_param-gm_probseg.nii.gz', subfolder="models") @as_img @@ -1556,7 +1555,7 @@ def dki_ak(dki_tf): return dki_tf.ak -@immlib.calc("t1_brain_mask") # TODO: t1_masked +@immlib.calc("t1_brain_mask") @as_file(suffix='_desc-T1w_mask.nii.gz') def t1_brain_mask(t1_file): """ @@ -1623,7 +1622,7 @@ def brain_mask(t1_brain_mask, b0): @immlib.calc("bundle_dict", "reg_template", "tmpl_name") -def get_bundle_dict(brain_mask, b0, +def get_bundle_dict(b0, bundle_info=None, reg_template_spec="mni_T1", reg_template_space_name="mni"): """ @@ -1667,24 +1666,20 @@ def get_bundle_dict(brain_mask, b0, if bundle_info is None: bundle_info = abd.default18_bd() + abd.callosal_bd() - use_brain_mask = True - brain_mask = nib.load(brain_mask).get_fdata() - if np.all(brain_mask == 1.0): - use_brain_mask = False if isinstance(reg_template_spec, nib.Nifti1Image): reg_template = reg_template_spec else: img_l = reg_template_spec.lower() if img_l == "mni_t2": reg_template = afd.read_mni_template( - mask=use_brain_mask, weight="T2w") + mask=True, weight="T2w") elif img_l == "mni_t1": reg_template = afd.read_mni_template( - mask=use_brain_mask, weight="T1w") + mask=True, weight="T1w") elif img_l == "dti_fa_template": - reg_template = afd.read_ukbb_fa_template(mask=use_brain_mask) + reg_template = afd.read_ukbb_fa_template(mask=True) elif img_l == "hcp_atlas": - reg_template = afd.read_mni_template(mask=use_brain_mask) + reg_template = afd.read_mni_template(mask=True) elif img_l == "pediatric": reg_template = afd.read_pediatric_templates()[ "UNCNeo-withCerebellum-for-babyAFQ"] diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index dfdde5246..0d9b5e1f3 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -528,6 +528,7 @@ def test_API_type_checking(): del myafq +@pytest.mark.nightly_anisotropic def test_AFQ_slr(): """ Test if API can run using slr map From 0168dd8ee19992f6c9fea517d381a209839462c7 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 13:33:04 -0700 Subject: [PATCH 069/116] avoiding clang --- AFQ/nn/brainchop.py | 8 +++++++- setup.cfg | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index a8c322238..686aa7f9a 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -1,5 +1,6 @@ import subprocess import tempfile +import os import numpy as np import nibabel as nib @@ -10,6 +11,7 @@ bwlabel) from tinygrad import Tensor +from tinygrad.helpers import Context import logging @@ -38,7 +40,11 @@ def run_brainchop(t1_img, model): "... -> 1 1 ..." ) - output_channels = model(image) + use_cpu = (os.getenv("CC") is not None) or \ + (os.getenv("clang") is not None) + + with Context(cpu=use_cpu): + output_channels = model(image) output = ( output_channels.argmax(axis=1) diff --git a/setup.cfg b/setup.cfg index 31f4d8ad9..25279b6dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,12 +34,14 @@ install_requires = pybids>=0.16.2 templateflow>=0.8 immlib + trx-python + # efficiency numba osqp - brainchop pydra - trx-python ray + # neural networks + brainchop # CLI interpretation toml>=0.10.0 setuptools_scm[toml]>=3.4.0,<5.1.0 From b1fc4fd649dbe5c9a56ded8dce6d4001626d369f Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 13:38:16 -0700 Subject: [PATCH 070/116] bf --- AFQ/nn/brainchop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 686aa7f9a..50a483414 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -43,7 +43,7 @@ def run_brainchop(t1_img, model): use_cpu = (os.getenv("CC") is not None) or \ (os.getenv("clang") is not None) - with Context(cpu=use_cpu): + with Context(CPU=use_cpu): output_channels = model(image) output = ( From 8c6848c3db0aff195982ec13d89e0843bd08a448 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 14:14:59 -0700 Subject: [PATCH 071/116] debugging tinygrad --- AFQ/nn/brainchop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 50a483414..20f4c355c 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -43,7 +43,7 @@ def run_brainchop(t1_img, model): use_cpu = (os.getenv("CC") is not None) or \ (os.getenv("clang") is not None) - with Context(CPU=use_cpu): + with Context(DEBUG=7): output_channels = model(image) output = ( From fa50f1713dad297b75cf5c60615ffa624c3e1167 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 14:26:42 -0700 Subject: [PATCH 072/116] turn off JIT --- AFQ/nn/brainchop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 20f4c355c..7b6d0a44a 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -43,7 +43,7 @@ def run_brainchop(t1_img, model): use_cpu = (os.getenv("CC") is not None) or \ (os.getenv("clang") is not None) - with Context(DEBUG=7): + with Context(JIT=0): output_channels = model(image) output = ( From 36f1dcd3bb1dc8ffd52a92204afeeb3ed30cba5e Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 14:39:41 -0700 Subject: [PATCH 073/116] try this --- AFQ/nn/brainchop.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 7b6d0a44a..e1692176f 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -40,10 +40,14 @@ def run_brainchop(t1_img, model): "... -> 1 1 ..." ) - use_cpu = (os.getenv("CC") is not None) or \ - (os.getenv("clang") is not None) - - with Context(JIT=0): + if (os.getenv("CC") is None) and \ + (os.getenv("clang") is None): + logger.info("No C compiler found, brainchop may be slower.") + use_cpu = 0 + else: + use_cpu = 1 + + with Context(ALLOW_DEVICE_USAGE=use_cpu): output_channels = model(image) output = ( From 4311c9e2ad4722bf43bdab89715a0198e1effa61 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 4 Aug 2025 16:00:40 -0700 Subject: [PATCH 074/116] i think we may end up requiring clang --- AFQ/nn/brainchop.py | 10 +--------- AFQ/tests/test_api.py | 1 - setup.cfg | 1 + 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index e1692176f..87fdace70 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -40,15 +40,7 @@ def run_brainchop(t1_img, model): "... -> 1 1 ..." ) - if (os.getenv("CC") is None) and \ - (os.getenv("clang") is None): - logger.info("No C compiler found, brainchop may be slower.") - use_cpu = 0 - else: - use_cpu = 1 - - with Context(ALLOW_DEVICE_USAGE=use_cpu): - output_channels = model(image) + output_channels = model(image) output = ( output_channels.argmax(axis=1) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 0d9b5e1f3..b83f0c8ab 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -617,7 +617,6 @@ def test_AFQ_reco80(): npt.assert_(len(seg_sft.get_bundle('CCMid').streamlines) > 0) -@pytest.mark.skip(reason="fixed in next tinygrad update (0.10.4)") def test_AFQ_pydra(): participants = ["NDARAA948VFH", "NDARAV554TP2"] _, bids_path = afd.fetch_hbn_preproc(participants) diff --git a/setup.cfg b/setup.cfg index 25279b6dc..bdb87aab5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,6 +41,7 @@ install_requires = pydra ray # neural networks + tinygrad @ git+https://github.com/tinygrad/tinygrad.git@846a2826ab4bc00056a366b0dcbd5df17047e016 brainchop # CLI interpretation toml>=0.10.0 From 2448d2fb3b77724be1536f945068cfcf92e361af Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 09:47:16 -0700 Subject: [PATCH 075/116] clang dependency ); --- gpu_docker/Dockerfile | 3 ++- gpu_docker/cuda_track_template.def | 3 ++- pyafq_docker/Dockerfile | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gpu_docker/Dockerfile b/gpu_docker/Dockerfile index 3ee362917..0cdb0461f 100644 --- a/gpu_docker/Dockerfile +++ b/gpu_docker/Dockerfile @@ -8,7 +8,7 @@ ENV DEBIAN_FRONTEND=noninteractive # upgrade RUN apt-get update && apt-get install --assume-yes apt-transport-https \ ca-certificates gnupg software-properties-common \ - gcc git wget curl numactl cmake + gcc git wget curl numactl cmake clang # Miniconda3 RUN curl -L "https://repo.anaconda.com/miniconda/Miniconda3-py312_25.3.1-1-Linux-x86_64.sh" \ @@ -18,6 +18,7 @@ RUN rm -rf /tmp/Miniconda3.sh RUN cd /opt && eval "$(/opt/anaconda/bin/conda shell.bash hook)" ENV PATH /opt/anaconda/bin:${PATH} ENV LD_LIBRARY_PATH /opt/anaconda/lib:${LD_LIBRARY_PATH} +ENV LD_LIBRARY_PATH /usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH} # python prereqs RUN conda install -c conda-forge git diff --git a/gpu_docker/cuda_track_template.def b/gpu_docker/cuda_track_template.def index 3f304bd69..9d6956b8a 100644 --- a/gpu_docker/cuda_track_template.def +++ b/gpu_docker/cuda_track_template.def @@ -8,13 +8,14 @@ From: nvidia/cuda:12.0.1-devel-ubuntu20.04 export DEBIAN_FRONTEND=noninteractive export PATH=/opt/anaconda/bin:${PATH} export LD_LIBRARY_PATH=/opt/anaconda/lib:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH export CXXFLAGS="-ftemplate-depth=2048" %post # System update and basic tools installation apt-get update && apt-get install --assume-yes apt-transport-https \ ca-certificates gnupg software-properties-common \ - gcc git wget curl numactl cmake + gcc git wget curl numactl cmake clang # Miniconda3 curl -L "https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh" \ diff --git a/pyafq_docker/Dockerfile b/pyafq_docker/Dockerfile index 47666b4c0..57d50ec29 100644 --- a/pyafq_docker/Dockerfile +++ b/pyafq_docker/Dockerfile @@ -7,6 +7,9 @@ FROM python:3.11 ARG COMMIT +RUN apt-get update && apt-get install --assume-yes cmake clang +ENV LD_LIBRARY_PATH /usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH} + # Install pyAFQ RUN pip install --no-cache-dir git+https://github.com/tractometry/pyAFQ.git@${COMMIT} RUN pip install fslpy From 77614045dfa368bf9bfe0d9698e78f84806152d6 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 09:52:51 -0700 Subject: [PATCH 076/116] safer numba thread amounts --- AFQ/tasks/data.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index e454c5275..14a787d51 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -122,9 +122,10 @@ def configure_ncpus_nthreads(ray_n_cpus=None, numba_n_threads=None): Default: None """ if ray_n_cpus is None: - ray_n_cpus = multiprocessing.cpu_count() - 1 + ray_n_cpus = max(multiprocessing.cpu_count() - 1, 1) if numba_n_threads is None: - numba_n_threads = multiprocessing.cpu_count() - 1 + numba_n_threads = min( + max(multiprocessing.cpu_count() - 1, 1), 16) return ray_n_cpus, numba_n_threads From 6ae52667b3b1b34f68cc7408bc45b043eadc610e Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 10:23:15 -0700 Subject: [PATCH 077/116] reduce docs runtime --- examples/howto_examples/plot_recobundles.py | 2 +- .../{plot_afq_callosal.py => run_afq_callosal.py} | 2 +- examples/tutorial_examples/plot_002_participant_afq_api.py | 6 +++--- examples/tutorial_examples/plot_004_export.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename examples/howto_examples/{plot_afq_callosal.py => run_afq_callosal.py} (98%) diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index 63ca8d1b6..a3b9d04cc 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -20,7 +20,7 @@ afd.organize_stanford_data(clear_previous_afq="track") -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=5000, random_seeds=True, rng_seed=42) diff --git a/examples/howto_examples/plot_afq_callosal.py b/examples/howto_examples/run_afq_callosal.py similarity index 98% rename from examples/howto_examples/plot_afq_callosal.py rename to examples/howto_examples/run_afq_callosal.py index 3918f557a..0699d6f7a 100644 --- a/examples/howto_examples/plot_afq_callosal.py +++ b/examples/howto_examples/run_afq_callosal.py @@ -35,7 +35,7 @@ # We only do this to make this example faster and consume less space. tracking_params = dict(seed_mask=RoiImage(), - n_seeds=10000, + n_seeds=25000, random_seeds=True, rng_seed=42) diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 5bdc6117e..c0f357b4c 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -77,11 +77,11 @@ # Set tractography parameters (optional) # --------------------------------------- # We make create a `tracking_params` variable, which we will pass to the -# ParticipantAFQ object which specifies that we want 25,000 seeds randomly +# ParticipantAFQ object which specifies that we want 10,000 seeds randomly # distributed in the white matter. We only do this to make this example -# faster and consume less space. +# faster and consume less space. The default is 2 million seeds. -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=10000, random_seeds=True, rng_seed=2025, trx=True) diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index b36dfeb8e..fdadecbc9 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -47,7 +47,7 @@ t1_file=t1_file, output_dir=output_dir, tracking_params={ - "n_seeds": 25000, + "n_seeds": 10000, "random_seeds": True, "rng_seed": 2022, "trx": True From 9e23a923173ffaa12e79457afb70bf91fd2cdda1 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 14:30:42 -0700 Subject: [PATCH 078/116] single thread some examples --- examples/tutorial_examples/plot_001_group_afq_api.py | 4 +++- examples/tutorial_examples/plot_002_participant_afq_api.py | 4 +++- examples/tutorial_examples/plot_004_export.py | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index d0c81d6eb..f28ccabfa 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -88,13 +88,15 @@ # We will also be using plotly to generate an interactive visualization. # The value `plotly_no_gif` indicates that interactive visualizations will be # generated as html web-pages that can be opened in a browser, but not as -# static gif files. +# static gif files. We set ray_num_cpus=1 to avoid memory issues running this +# example on servers. myafq = GroupAFQ( bids_path=op.join(afd.afq_home, 'stanford_hardi'), preproc_pipeline='vistasoft', t1_pipeline='freesurfer', tracking_params=tracking_params, + ray_num_cpus=1, viz_backend_spec='plotly_no_gif') ########################################################################## diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index c0f357b4c..c5ed76f90 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -102,7 +102,8 @@ # # To initialize the object, we will pass in the diffusion data files and specify # the output directory where we want to store the results. We will also -# pass in the tracking parameters we defined above. +# pass in the tracking parameters we defined above. We set ray_num_cpus=1 +# to avoid memory issues running this example on servers. myafq = ParticipantAFQ( dwi_data_file=dwi_data_file, @@ -111,6 +112,7 @@ t1_file=t1_file, output_dir=output_dir, tracking_params=tracking_params, + ray_num_cpus=1, ) ########################################################################## diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index fdadecbc9..08bc7af71 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -46,6 +46,7 @@ bvec_file=bvec_file, t1_file=t1_file, output_dir=output_dir, + ray_num_cpus=1, tracking_params={ "n_seeds": 10000, "random_seeds": True, From f297433e84ebb6870c9c881690e3207cbd2fa05e Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 14:35:07 -0700 Subject: [PATCH 079/116] NOT dependent on clang --- AFQ/nn/brainchop.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 87fdace70..a6b2b5f3f 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -40,7 +40,14 @@ def run_brainchop(t1_img, model): "... -> 1 1 ..." ) - output_channels = model(image) + try: + output_channels = model(image) + except Exception as e: + if "clang" in str(e).lower(): + with Context(PYTHON=1): + output_channels = model(image) + else: + raise output = ( output_channels.argmax(axis=1) From 418d37dc22768a56b2afc1cf3bdb965376c3c0ae Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 14:45:42 -0700 Subject: [PATCH 080/116] try this --- setup.cfg | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index bdb87aab5..887f86e5e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,10 +20,10 @@ description = pyAFQ: Automated Fiber Quantification ... in Python long_description = file:README.md long_description_content_type = text/markdown platforms = OS Independent +version = attr: pyAFQ.__version__ [options] -setup_requires = - setuptools_scm +setup_requires = setuptools_scm>=3.4.0 python_requires = >=3.10, <3.13 install_requires = # core packages @@ -45,7 +45,6 @@ install_requires = brainchop # CLI interpretation toml>=0.10.0 - setuptools_scm[toml]>=3.4.0,<5.1.0 # plotly libraries plotly==5.12.0 kaleido==0.2.1 From d0a7bff8572f7a9b57fa9798b25393791fa2ec3c Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 5 Aug 2025 14:48:26 -0700 Subject: [PATCH 081/116] add this to toml --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c52a3b9a3..08ddf1182 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] +requires = ["setuptools>=42", "wheel", "setuptools_scm>=3.4"] build-backend = "setuptools.build_meta" [tool.pytest.ini_options] @@ -11,4 +11,7 @@ markers = [ "nightly_reco80", "nightly_custom", "nightly", -] \ No newline at end of file +] + +[tool.setuptools_scm] +write_to = "pyAFQ/_version.py" From ed68aa680a86f94dafb9e348a68b7a53b00b62ad Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 09:38:31 -0700 Subject: [PATCH 082/116] try to make tests/examples finish in reasonable time --- AFQ/tests/test_api.py | 1 + examples/howto_examples/plot_recobundles.py | 1 + examples/tutorial_examples/plot_001_group_afq_api.py | 7 +++++-- examples/tutorial_examples/plot_002_participant_afq_api.py | 7 +++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index b83f0c8ab..1db308926 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -420,6 +420,7 @@ def test_AFQ_anisotropic(): 'models/sub-01_ses-01_model-csd_param-apm_dwimap.nii.gz')) +@pytest.mark.nightly_basic def test_API_type_checking(): _, bids_path, _ = get_temp_hardi() seed = 2022 diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index a3b9d04cc..261728cee 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -21,6 +21,7 @@ afd.organize_stanford_data(clear_previous_afq="track") tracking_params = dict(n_seeds=5000, + directions='prob', random_seeds=True, rng_seed=42) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index f28ccabfa..51b5a333f 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -56,10 +56,13 @@ # --------------------------------------- # We make create a `tracking_params` variable, which we will pass to the # GroupAFQ object which specifies that we want 25,000 seeds randomly -# distributed in the white matter. We only do this to make this example -# faster and consume less space. +# distributed in the white matter, propogated using DIPY's probabilistic +# algorithm. We only do this to make this example faster and consume less +# space; normally, we use more seeds and particle filtering tractography +# (PFT) tracking_params = dict(n_seeds=25000, + directions='prob', random_seeds=True, rng_seed=2025, trx=True) diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index c5ed76f90..0cd034bdf 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -78,10 +78,13 @@ # --------------------------------------- # We make create a `tracking_params` variable, which we will pass to the # ParticipantAFQ object which specifies that we want 10,000 seeds randomly -# distributed in the white matter. We only do this to make this example -# faster and consume less space. The default is 2 million seeds. +# distributed in the white matter, propogated using DIPY's probabilistic +# algorithm. We only do this to make this example faster and consume less +# space; normally, we use more seeds and particle filtering tractography +# (PFT) tracking_params = dict(n_seeds=10000, + directions='prob', random_seeds=True, rng_seed=2025, trx=True) From 3ede31357191440c300280f20d7f2e32f8fd1877 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 10:11:42 -0700 Subject: [PATCH 083/116] faster tests --- AFQ/tests/test_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index 1db308926..a3684fda6 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -776,6 +776,7 @@ def test_AFQ_data_waypoint(): tracking_params = dict(odf_model="csd", n_seeds=2000, + directions="prob", # for efficiency random_seeds=True, rng_seed=42) segmentation_params = dict(return_idx=True) @@ -872,7 +873,8 @@ def test_AFQ_data_waypoint(): # ROI mask needs to be put in quotes in config tracking_params = dict( odf_model="CSD", - n_seeds=5000, + n_seeds=2000, + directions="prob", # for efficiency random_seeds=True, rng_seed=42) bundle_dict_as_str = ( From c66afa09665c2c0cf70770b5e001ac4aa07fa6ae Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 12:51:40 -0700 Subject: [PATCH 084/116] bf --- examples/tutorial_examples/plot_001_group_afq_api.py | 4 ++-- examples/tutorial_examples/plot_002_participant_afq_api.py | 4 ++-- examples/tutorial_examples/plot_004_export.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index 51b5a333f..76ad9fcc1 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -91,7 +91,7 @@ # We will also be using plotly to generate an interactive visualization. # The value `plotly_no_gif` indicates that interactive visualizations will be # generated as html web-pages that can be opened in a browser, but not as -# static gif files. We set ray_num_cpus=1 to avoid memory issues running this +# static gif files. We set ray_n_cpus=1 to avoid memory issues running this # example on servers. myafq = GroupAFQ( @@ -99,7 +99,7 @@ preproc_pipeline='vistasoft', t1_pipeline='freesurfer', tracking_params=tracking_params, - ray_num_cpus=1, + ray_n_cpus=1, viz_backend_spec='plotly_no_gif') ########################################################################## diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index 0cd034bdf..ff996295c 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -105,7 +105,7 @@ # # To initialize the object, we will pass in the diffusion data files and specify # the output directory where we want to store the results. We will also -# pass in the tracking parameters we defined above. We set ray_num_cpus=1 +# pass in the tracking parameters we defined above. We set ray_n_cpus=1 # to avoid memory issues running this example on servers. myafq = ParticipantAFQ( @@ -115,7 +115,7 @@ t1_file=t1_file, output_dir=output_dir, tracking_params=tracking_params, - ray_num_cpus=1, + ray_n_cpus=1, ) ########################################################################## diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index 08bc7af71..02a0eb0ad 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -46,7 +46,7 @@ bvec_file=bvec_file, t1_file=t1_file, output_dir=output_dir, - ray_num_cpus=1, + ray_n_cpus=1, tracking_params={ "n_seeds": 10000, "random_seeds": True, From 1054f9f2acbc753cd830ff3fe75f542b85398cc3 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 13:36:05 -0700 Subject: [PATCH 085/116] move pydra test to nightly --- AFQ/tests/test_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/AFQ/tests/test_api.py b/AFQ/tests/test_api.py index a3684fda6..2fbf7ec26 100644 --- a/AFQ/tests/test_api.py +++ b/AFQ/tests/test_api.py @@ -618,6 +618,7 @@ def test_AFQ_reco80(): npt.assert_(len(seg_sft.get_bundle('CCMid').streamlines) > 0) +@pytest.mark.nightly_reco80 def test_AFQ_pydra(): participants = ["NDARAA948VFH", "NDARAV554TP2"] _, bids_path = afd.fetch_hbn_preproc(participants) From eacfe3a5168a657b892c3857ab08a8a13e1bd685 Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 15:50:17 -0700 Subject: [PATCH 086/116] try this --- examples/howto_examples/plot_recobundles.py | 3 ++- examples/tutorial_examples/plot_001_group_afq_api.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/plot_recobundles.py index 261728cee..34d3f40b6 100644 --- a/examples/howto_examples/plot_recobundles.py +++ b/examples/howto_examples/plot_recobundles.py @@ -33,7 +33,8 @@ # Parameters of this process are set through a dictionary input to the # `segmentation_params` argument of the GroupAFQ object. In this case, we # use `abd.reco_bd(16)`, which tells pyAFQ to use the RecoBundles -# algorithm for bundle recognition. +# algorithm for bundle recognition. This uses 16 bundles, there is also +# an atlas `abd.reco_bd(80)` which uses 80 bundles. myafq = GroupAFQ( output_dir=op.join(afd.afq_home, 'stanford_hardi', 'derivatives', diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index 76ad9fcc1..904cf86eb 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -61,7 +61,7 @@ # space; normally, we use more seeds and particle filtering tractography # (PFT) -tracking_params = dict(n_seeds=25000, +tracking_params = dict(n_seeds=10000, directions='prob', random_seeds=True, rng_seed=2025, @@ -226,7 +226,7 @@ if ind == "Total Recognized": threshold = 1000 elif "Callosum" in ind: - threshold = 5 + threshold = 0 else: threshold = 10 if bundle_counts["n_streamlines"][ind] < threshold: From 535d105a51c5bf528bc68012aa0b82674d96205f Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 19:43:17 -0700 Subject: [PATCH 087/116] see if this fixes it --- .../howto_examples/{plot_recobundles.py => run_recobundles.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/howto_examples/{plot_recobundles.py => run_recobundles.py} (100%) diff --git a/examples/howto_examples/plot_recobundles.py b/examples/howto_examples/run_recobundles.py similarity index 100% rename from examples/howto_examples/plot_recobundles.py rename to examples/howto_examples/run_recobundles.py From 704a29c22006572bda72bd7f78753edb68956f8c Mon Sep 17 00:00:00 2001 From: 36000 Date: Wed, 6 Aug 2025 21:11:00 -0700 Subject: [PATCH 088/116] beef the examples back up --- examples/howto_examples/run_recobundles.py | 3 +-- .../tutorial_examples/plot_001_group_afq_api.py | 9 +++------ .../plot_002_participant_afq_api.py | 13 ++++++------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/examples/howto_examples/run_recobundles.py b/examples/howto_examples/run_recobundles.py index 34d3f40b6..91fa9ef23 100644 --- a/examples/howto_examples/run_recobundles.py +++ b/examples/howto_examples/run_recobundles.py @@ -20,8 +20,7 @@ afd.organize_stanford_data(clear_previous_afq="track") -tracking_params = dict(n_seeds=5000, - directions='prob', +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=42) diff --git a/examples/tutorial_examples/plot_001_group_afq_api.py b/examples/tutorial_examples/plot_001_group_afq_api.py index 904cf86eb..85850c214 100644 --- a/examples/tutorial_examples/plot_001_group_afq_api.py +++ b/examples/tutorial_examples/plot_001_group_afq_api.py @@ -56,13 +56,10 @@ # --------------------------------------- # We make create a `tracking_params` variable, which we will pass to the # GroupAFQ object which specifies that we want 25,000 seeds randomly -# distributed in the white matter, propogated using DIPY's probabilistic -# algorithm. We only do this to make this example faster and consume less -# space; normally, we use more seeds and particle filtering tractography -# (PFT) +# distributed in the white matter. We only do this to make this example faster +# and consume less space; normally, we use more seeds -tracking_params = dict(n_seeds=10000, - directions='prob', +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2025, trx=True) diff --git a/examples/tutorial_examples/plot_002_participant_afq_api.py b/examples/tutorial_examples/plot_002_participant_afq_api.py index ff996295c..f07a5f74c 100644 --- a/examples/tutorial_examples/plot_002_participant_afq_api.py +++ b/examples/tutorial_examples/plot_002_participant_afq_api.py @@ -37,7 +37,7 @@ # stored in the ``~/AFQ_data/stanford_hardi/`` BIDS directory. Set it to None if # you want to use the results of previous runs. -afd.organize_stanford_data(clear_previous_afq="track") +afd.organize_stanford_data() ########################################################################## # Defining data files @@ -70,7 +70,8 @@ # results. output_dir = op.join(afd.afq_home, "stanford_hardi", - "derivatives", "afq", "sub-01") + "derivatives", "afq", "sub-01", + "ses-01", "dwi") os.makedirs(output_dir, exist_ok=True) ########################################################################## @@ -79,12 +80,10 @@ # We make create a `tracking_params` variable, which we will pass to the # ParticipantAFQ object which specifies that we want 10,000 seeds randomly # distributed in the white matter, propogated using DIPY's probabilistic -# algorithm. We only do this to make this example faster and consume less -# space; normally, we use more seeds and particle filtering tractography -# (PFT) +# algorithm. We only do this to make this example faster +# and consume less space; normally, we use more seeds -tracking_params = dict(n_seeds=10000, - directions='prob', +tracking_params = dict(n_seeds=25000, random_seeds=True, rng_seed=2025, trx=True) From cde5e11c3fa6f635b4ea522b51faaa7deb9e9661 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 7 Aug 2025 09:51:38 -0700 Subject: [PATCH 089/116] streamline examples --- docs/source/conf.py | 1 + examples/tutorial_examples/plot_004_export.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3c1f9a0a6..97a238084 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -239,6 +239,7 @@ 'gallery_dirs': ['howto/howto_examples', 'tutorials/tutorial_examples'], 'image_scrapers': image_scrapers, 'reset_modules': (reset_progressbars), + 'filename_pattern': r'/plot_(?!003_rerun\.py)', 'show_memory': True, 'abort_on_example_error': True, 'within_subsection_order': FileNameSortKey, diff --git a/examples/tutorial_examples/plot_004_export.py b/examples/tutorial_examples/plot_004_export.py index 02a0eb0ad..1577ceeb6 100644 --- a/examples/tutorial_examples/plot_004_export.py +++ b/examples/tutorial_examples/plot_004_export.py @@ -23,7 +23,7 @@ # :doc:`plot_002_participant_afq_api` example. Please refer to that # example for a detailed description of the parameters. -afd.organize_stanford_data(clear_previous_afq="track") +afd.organize_stanford_data() data_dir = op.join(afd.afq_home, "stanford_hardi", "derivatives", "vistasoft", "sub-01", "ses-01", "dwi") From 2edbca1695be53e8f142635beeb3f5bb7f31cb51 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 7 Aug 2025 10:50:31 -0700 Subject: [PATCH 090/116] dont run rerun example (no visualizations) --- docs/source/conf.py | 2 +- docs/source/reference/kwargs.rst | 6 ------ docs/source/reference/methods.rst | 10 +++++++++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 97a238084..ff61d82ef 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -239,7 +239,7 @@ 'gallery_dirs': ['howto/howto_examples', 'tutorials/tutorial_examples'], 'image_scrapers': image_scrapers, 'reset_modules': (reset_progressbars), - 'filename_pattern': r'/plot_(?!003_rerun\.py)', + 'filename_pattern': r'/plot_(?!.*003_rerun).*\.py$', 'show_memory': True, 'abort_on_example_error': True, 'within_subsection_order': FileNameSortKey, diff --git a/docs/source/reference/kwargs.rst b/docs/source/reference/kwargs.rst index 9aff8d7b2..2c15a4fb6 100644 --- a/docs/source/reference/kwargs.rst +++ b/docs/source/reference/kwargs.rst @@ -44,9 +44,6 @@ dki_wm_ll: float dki_gm_ul: float Upper limit of FA in gray matter to calculate probability mask. Default: 0.3 -pve_nclass: int - The number of tissue classes to segment Default: 3 - robust_tensor_fitting: bool Whether to use robust_tensor_fitting when doing dti. Only applies to dti. Default: False @@ -98,9 +95,6 @@ sphere: Sphere class instance gtol: float This input is to refine kurtosis maxima under the precision of the directions sampled on the sphere class instance. The gradient of the convergence procedure must be less than gtol before successful termination. If gtol is None, fiber direction is directly taken from the initial sampled directions of the given sphere object. Default: 1e-2 -brain_mask_definition: instance from `AFQ.definitions.image` - This will be used to create the brain mask, which gets applied before registration to a template. If you want no brain mask to be applied, use FullImage. If None, use B0Image() Default: None - bundle_info: dict or BundleDict A dictionary or BundleDict for use in segmentation. See `Defining Custom Bundle Dictionaries` in the `usage` section of pyAFQ's documentation for details. If None, will get all appropriate bundles for the chosen segmentation algorithm. Default: None diff --git a/docs/source/reference/methods.rst b/docs/source/reference/methods.rst index 43aa04c98..cf2c6ae69 100644 --- a/docs/source/reference/methods.rst +++ b/docs/source/reference/methods.rst @@ -92,7 +92,7 @@ dki_gm: t1w_pve: - Tissue classification using the Markov Random Fields modeling approach on the T1w image [1, 2] + WM, GM, CSF segmentations from subcortex segmentation from brainchop on T1w image wm_gm_interface: @@ -455,6 +455,14 @@ dki_ak: full path to a nifti file containing the DKI axial kurtosis file +t1_brain_mask: + full path to a nifti file containing brain mask from T1w image, + + +t1_subcortex: + full path to a nifti file containing segmentation of subcortical structures from T1w image using Brainchop + + brain_mask: full path to a nifti file containing the brain mask From 41e1c1c48f33002fab7e0b4e4d108cd29754f048 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 7 Aug 2025 12:55:27 -0700 Subject: [PATCH 091/116] this also doesnt need to be run --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ff61d82ef..064ed2383 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -239,7 +239,7 @@ 'gallery_dirs': ['howto/howto_examples', 'tutorials/tutorial_examples'], 'image_scrapers': image_scrapers, 'reset_modules': (reset_progressbars), - 'filename_pattern': r'/plot_(?!.*003_rerun).*\.py$', + 'filename_pattern': r'/plot_(?!.*(003_rerun|006_bids_layout)).*\.py$', 'show_memory': True, 'abort_on_example_error': True, 'within_subsection_order': FileNameSortKey, From 0800df5b74767a504cdb6b1900250581b13ecd87 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 19 Sep 2025 15:56:31 -0700 Subject: [PATCH 092/116] fix versions --- pyproject.toml | 2 +- setup.cfg | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 08ddf1182..c92e204a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,4 +14,4 @@ markers = [ ] [tool.setuptools_scm] -write_to = "pyAFQ/_version.py" +write_to = "AFQ/version.py" diff --git a/setup.cfg b/setup.cfg index 887f86e5e..8559192f4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,7 +20,6 @@ description = pyAFQ: Automated Fiber Quantification ... in Python long_description = file:README.md long_description_content_type = text/markdown platforms = OS Independent -version = attr: pyAFQ.__version__ [options] setup_requires = setuptools_scm>=3.4.0 From c5d6d9851638cacd9106b62270d025209ecc657c Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 19 Sep 2025 16:16:12 -0700 Subject: [PATCH 093/116] no longer need this with immlib --- AFQ/tasks/segmentation.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index 58c54ba80..29bf91cb9 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -281,9 +281,7 @@ def export_density_maps(bundles, data_imap): @as_file('_desc-profiles_tractography.csv') @immlib.calc("endpoint_maps") -@as_file('_desc-endpoints_tractography.nii.gz', - include_track=True, - include_seg=True) +@as_file('_desc-endpoints_tractography.nii.gz') def export_endpoint_maps(bundles, data_imap, endpoint_threshold=3): """ full path to a NIfTI file containing endpoint maps for each bundle @@ -344,7 +342,7 @@ def export_endpoint_maps(bundles, data_imap, endpoint_threshold=3): @immlib.calc("profiles") -@as_file('_desc-profiles_tractography.csv', include_track=True, include_seg=True) +@as_file('_desc-profiles_tractography.csv') def tract_profiles(bundles, scalar_dict, data_imap, profile_weights="gauss", From 30f91ace7a012a077725c7213760192f0dfd4690 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 19 Sep 2025 17:08:45 -0700 Subject: [PATCH 094/116] rebase on immlib and make compatible --- AFQ/api/group.py | 1 + AFQ/api/utils.py | 7 ++++--- AFQ/definitions/image.py | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index a774e8566..031a4fa26 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -420,6 +420,7 @@ def __init__(self, self.plans_dict[subject][str(session)] = this_pAFQ.plans_dict self.pAFQ_list.append(this_pAFQ) self.pAFQ_inputs_list.append(this_pAFQ_inputs) + self.kwargs = self.pAFQ_list[-1].kwargs def combine_profiles(self): tract_profiles_dict = self.export("profiles") diff --git a/AFQ/api/utils.py b/AFQ/api/utils.py index 542e3b9dc..0cb685b4f 100644 --- a/AFQ/api/utils.py +++ b/AFQ/api/utils.py @@ -147,10 +147,11 @@ def export_all_helper(api_afq_object, xforms, indiv, viz): api_afq_object.export("median_bundle_lengths") api_afq_object.export("profiles") api_afq_object.export("seed_thresh") - try: + stop_threshold = api_afq_object.kwargs.get( + "tracking_params", {}).get( + "stop_threshold", None) + if not isinstance(stop_threshold, str): api_afq_object.export("stop_thresh") - except ValueError: - pass if viz: try: diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index 858f47f78..d3fc99cb6 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -687,19 +687,19 @@ def csf_getter(data_imap): PVE = nib.load(data_imap["t1w_pve"]) return nib.Nifti1Image( PVE.get_fdata()[..., 0].astype(np.float32), - PVE.affine) + PVE.affine), dict(source=data_imap["t1w_pve"]) def gm_getter(data_imap): PVE = nib.load(data_imap["t1w_pve"]) return nib.Nifti1Image( PVE.get_fdata()[..., 1].astype(np.float32), - PVE.affine) + PVE.affine), dict(source=data_imap["t1w_pve"]) def wm_getter(data_imap): PVE = nib.load(data_imap["t1w_pve"]) return nib.Nifti1Image( PVE.get_fdata()[..., 2].astype(np.float32), - PVE.affine) + PVE.affine), dict(source=data_imap["t1w_pve"]) return [wm_getter, gm_getter, csf_getter] From d17aa7bd988651fca63e615fbb11718ba4297f6e Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 10:06:16 -0700 Subject: [PATCH 095/116] Remove unused code --- AFQ/api/participant.py | 2 +- AFQ/api/utils.py | 4 + AFQ/models/dam.py | 128 ---------- AFQ/models/msmt.py | 517 +++++------------------------------------ AFQ/tasks/data.py | 65 ------ 5 files changed, 66 insertions(+), 650 deletions(-) delete mode 100644 AFQ/models/dam.py diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index 702ba02ab..a3024162c 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -51,7 +51,7 @@ def __init__(self, Path to bvec file. t1_file : str Path to T1-weighted image file. Must already be registered - to the DWI data. + to the DWI data, though not resampled. output_dir : str Path to output directory. kwargs : additional optional parameters diff --git a/AFQ/api/utils.py b/AFQ/api/utils.py index 0cb685b4f..7025133fb 100644 --- a/AFQ/api/utils.py +++ b/AFQ/api/utils.py @@ -31,7 +31,11 @@ "output_dir": "Path to output directory", "best_scalar": "Go-to scalar for visualizations", "base_fname": "Base file name for outputs", + "pve_wm": "White matter partial volume estimate map", + "pve_gm": "Gray matter partial volume estimate map", + "pve_csf": "Cerebrospinal fluid partial volume estimate map", } + methods_sections = { "dwi_data_file": "data", "bval_file": "data", diff --git a/AFQ/models/dam.py b/AFQ/models/dam.py deleted file mode 100644 index 21c108a98..000000000 --- a/AFQ/models/dam.py +++ /dev/null @@ -1,128 +0,0 @@ -import logging -import numpy as np -from tqdm import tqdm - -from scipy.ndimage import gaussian_filter1d -from scipy.signal import find_peaks - -from dipy.segment.tissue import compute_directional_average - - -logger = logging.getLogger('AFQ') - - -def fit_dam(data, gtab, b0_img, dam_low_signal_thresh=50): - """ - direction-averaged signal map (DAM) [1] slope and intercept - - Parameters - ---------- - data : ndarray - The diffusion data with shape (x, y, z, b). - gtab : GradientTable - The gradient table containing b-values and b-vectors. - b0_img : Nifti1Image - The b0 image used to compute the mean signal. - This should be a 3D image with shape (x, y, z). - dam_low_signal_thresh : float, optional - The threshold below which a voxel is considered to have low signal. - Default: 50 - - References - ---------- - .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., - & Garyfallidis, E. (2020). Segmentation of the brain using - direction-averaged signal of DWI images. - Magnetic Resonance Imaging, 69, 1-7. Elsevier. - https://doi.org/10.1016/j.mri.2020.02.010 - """ - # Precompute unique b-values, masks - unique_bvals = np.unique(gtab.bvals) - if len(unique_bvals) <= 2: - raise ValueError(("Insufficient unique b-values for fitting DAM. " - "Note, DAM requires multi-shell data")) - masks = gtab.bvals[:, np.newaxis] == unique_bvals[np.newaxis, 1:] - - b0_data = b0_img.get_fdata() - - # If the mean signal for b=0 is too low, - # set those voxels to 0 for both P and V - valid_voxels = b0_data >= dam_low_signal_thresh - - params_map = np.zeros((*data.shape[:-1], 2)) - logger.info("Fitting directional average map (DAM)...") - for idx in tqdm(range(data.shape[0] * data.shape[1] * data.shape[2])): - i, j, k = np.unravel_index(idx, data.shape[:-1]) - if valid_voxels[i, j, k]: - aa, bb = compute_directional_average( - data[i, j, k, :], - gtab.bvals, - masks=masks, - b0_mask=gtab.b0s_mask, - s0_map=b0_data[i, j, k], - low_signal_threshold=dam_low_signal_thresh, - ) - if aa > 0.01 and bb < 0: - params_map[i, j, k, 0] = aa - params_map[i, j, k, 1] = -bb - return params_map - - -def csf_dam(dam_intercept_data): - """ - CSF probability map from DAM intercept - - Parameters - ---------- - dam_intercept_data : ndarray - The DAM intercept data with shape (x, y, z). - """ - beta_values = dam_intercept_data.flatten() - beta_values = beta_values[beta_values != 0] - - # Make a smoothed histogram - hist, bin_edges = np.histogram(beta_values, bins=200, density=True) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - smoothed_hist = gaussian_filter1d(hist, sigma=2) - - # Find the main peak - peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) - if len(peaks) == 0: - raise ValueError(( - "DAM intercept: No peaks found in the histogram, " - "required for DAM CSF estimate")) - - main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] - main_peak_val = bin_centers[main_peak_idx] - - # Find the threshold symmetric to 0 with respect to the peak center - threshold = 2 * main_peak_val - - return dam_intercept_data > threshold, threshold - - -def t1_dam(dam_slope_data, dam_csf_data): - """ - T1 map from DAM slope and CSF probability map - - Parameters - ---------- - dam_slope_data : ndarray - The DAM slope data with shape (x, y, z). - dam_csf_data : ndarray - The DAM CSF probability map with shape (x, y, z). - """ - - dam_slope_min = np.percentile( - dam_slope_data[dam_slope_data != 0], 0.5) - dam_slope_data[dam_slope_data < dam_slope_min] = 0 - - dam_slope_max = np.percentile( - dam_slope_data[dam_slope_data != 0], 99.5) - dam_slope_data[dam_slope_data > dam_slope_max] = 0 - - dam_slope_data[dam_slope_data != 0] = dam_slope_max - \ - dam_slope_data[dam_slope_data != 0] - pseudo_t1 = (1.0 - dam_csf_data) * dam_slope_data - - return pseudo_t1 diff --git a/AFQ/models/msmt.py b/AFQ/models/msmt.py index b44157d26..5815b6e0a 100644 --- a/AFQ/models/msmt.py +++ b/AFQ/models/msmt.py @@ -1,15 +1,10 @@ import multiprocessing -import warnings import numpy as np from tqdm import tqdm import ray -from scipy.optimize import minimize from scipy.sparse import csr_matrix -from numba import njit, prange, set_num_threads, config -from numba.core.errors import NumbaPerformanceWarning - import osqp from dipy.reconst.mcsd import MSDeconvFit @@ -21,325 +16,12 @@ __all__ = ["fit"] -# CPU implementation of primal-dual Interior Point method -# for convex quadratic constrained optimization (QP) -# Specifically, ||Rx-d||_2 where Ax>=b -# parallelized to solve 10s-100s of thousands of QPs -# ultimately used to fit MSMT CSD -@njit(fastmath=True) -def solve_qp(Rt, R_pinv, G, A, At, A_outer, b, x0, - d, max_iter, tol): - ''' - Solves 1/2*x^t*G*x+(Rt*d)^t*x given Ax>=b - In MSMT, G, R, A, b are the same across voxels, - but there are different d for different voxels. - This fact is not used currently. So this is a more general - batched CLS QP solver. - - Let: - c=(Rt*d)^t - L = diag(l) - Y = diag(y) - mu as centering parameter that tends to 0 with iteration number. - - Set up with interior points, this is reformulated to: - | G 0 -A.T | |dx| |0 | | G*x-A.T*l+c | - | A -I 0 | |dy| = |0 | - | A*x-y-b | - | 0 L Y | |dl| |mu| | YL | - - This reduces to: - |G -A.T| |dx| = | -G*x-A.T*l+c | - |A Y\L | |dl| | -(A*x-y-b) + (-y+mu/l)| - with dy = A*dx+(A*x-y-b) - - Solving for dx, dl: - (G+A.T*(Y\L)*A)*dx = -G*x-A.T*l+c - dl = (Y\L)*(-(A*x-y-b) + (-y+mu/l) - A*dx) - - So, the tricky part is solving for dx. - However, note that G+A.T*(Y\L)*A is hermitian positive semidefinite. - So, it can be solved using conjugate gradients. - This is done every iteration and is the longest part of the calculation. - ''' - - n = A.shape[1] - m = A.shape[0] - - # First check if naive solution satisfies constraints - x = R_pinv @ d - if np.all(A @ x - b >= -tol): - return True, x - - c = Rt @ d - - x = x0.copy() - y = np.maximum(np.abs(A @ x - b), 1.0) - l = np.ones(m) / np.max(y) - dx = np.zeros(n) - dy = np.zeros(m) - dl = np.zeros(m) - dy_aff = np.zeros(m) - dl_aff = np.zeros(m) - - rhs1 = np.empty(n) - rhs2 = np.empty(m) - Gx = np.empty(n) - ATl = np.empty(n) - rhs1l = np.empty(n) - Zrhs2 = np.empty(m) - ATZrhs2 = np.empty(n) - schur = np.empty((n, n)) - temp = np.empty(m) - tempn1 = np.empty(n) - tempn2 = np.empty(n) - - tau = 0.95 - shur_regularization = 1e-6 - for ii in range(max_iter): - # mu = (y @ l) / m - mu = 0.0 - for i in range(m): - mu += y[i] * l[i] - mu /= m - - # Predictor step - Z = safe_divide_vec(l, y) - - # dy = A @ x - y - b - for i in range(m): - ax = 0.0 - for j in range(n): - ax += A[i, j] * x[j] - dy[i] = ax - y[i] - b[i] - - # rhs2 = -dy - y - for i in range(m): - rhs2[i] = -dy[i] - y[i] - - # rhs1l = -(G @ x - A.T @ l + c) - # G @ x - for i in range(n): - s = 0.0 - for j in range(n): - s += G[i, j] * x[j] - Gx[i] = s - - # Then compute A.T @ l - for i in range(n): - s = 0.0 - for j in range(m): - s += At[i, j] * l[j] - ATl[i] = s - - # Now compute rhs1l = -(Gx - ATl + c) - for i in range(n): - rhs1l[i] = -(Gx[i] - ATl[i] + c[i]) - - # rhs1 = rhs1l + A.T @ (Z * rhs2) - # Z * rhs2 - for i in range(m): - Zrhs2[i] = Z[i] * rhs2[i] - - # A.T @ (Z * rhs2) - for i in range(n): - s = 0.0 - for j in range(m): - s += At[i, j] * Zrhs2[j] - ATZrhs2[i] = s - - # rhs1 = rhs1l + ATZrhs2 - for i in range(n): - rhs1[i] = rhs1l[i] + ATZrhs2[i] - - for i in range(n): - for j in range(i, n): - s = G[i, j] - for k in range(m): - s += Z[k] * A_outer[i, j, k] - schur[i, j] = s - schur[j, i] = s # fill symmetric - - for i in range(n): - schur[i, i] += shur_regularization - - L = np.linalg.cholesky(schur) - dx = cholesky_solve(L, rhs1, tempn1, tempn2) - - # temp = A @ dx - for i in range(m): - s = 0.0 - for j in range(n): - s += A[i, j] * dx[j] - temp[i] = s - - # dl_aff = Z * (rhs2 - A @ dx) - for i in range(m): - dl_aff[i] = Z[i] * (rhs2[i] - temp[i]) - - # dy_aff = dy + A @ dx - for i in range(m): - dy_aff[i] = dy[i] + temp[i] - - alpha_aff_pri = 1.0 - alpha_aff_dual = 1.0 - for i in range(m): - if dy_aff[i] < 0: - alpha_aff_pri = min(alpha_aff_pri, -y[i] / dy_aff[i]) - if dl_aff[i] < 0: - alpha_aff_dual = min(alpha_aff_dual, -l[i] / dl_aff[i]) - alpha_aff = min(tau * alpha_aff_pri, tau * alpha_aff_dual) - - # mu_aff = ((y + alpha_aff * dy_aff) @ (l + alpha_aff * dl_aff)) / m - mu_aff = 0.0 - for i in range(m): - mu_aff += (y[i] + alpha_aff * dy_aff[i]) \ - * (l[i] + alpha_aff * dl_aff[i]) - mu_aff /= m - - sigma = (mu_aff / mu) ** 3 - mu = sigma * mu - - # rhs2 = -dy + (-y + mu / l) - rhs2 = -dy + (-y + safe_divide_vec(mu, l)) - - # Zrhs2 = Z * rhs2 - for i in range(m): - Zrhs2[i] = Z[i] * rhs2[i] - - # ATZrhs2 = A.T @ Zrhs2 - for i in range(n): - s = 0.0 - for j in range(m): - s += At[i, j] * Zrhs2[j] - ATZrhs2[i] = s - - # rhs1 = rhs1l + ATZrhs2 - for i in range(n): - rhs1[i] = rhs1l[i] + ATZrhs2[i] - - dx = cholesky_solve(L, rhs1, tempn1, tempn2) - - # temp = A @ dx - for i in range(m): - s = 0.0 - for j in range(n): - s += A[i, j] * dx[j] - temp[i] = s - - # dl = Z * (rhs2 - A @ dx) - for i in range(m): - dl[i] = Z[i] * (rhs2[i] - temp[i]) - - # dy += A @ dx - for i in range(m): - dy[i] += temp[i] - - beta = 1.0 - sigma = 1.0 - for i in range(m): - if dy[i] < 0 and dy[i] * sigma < -y[i]: - sigma = -y[i] / dy[i] - if dl[i] < 0 and dl[i] * beta < -l[i]: - beta = -l[i] / dl[i] - beta *= tau - sigma *= tau - alpha = min(beta, sigma) - - x += alpha * dx - y += alpha * dy - l += alpha * dl - - if alpha * np.dot(dx, dx) < n * tol: - if np.all(A @ x - b >= -tol): - return True, x - - return False, x - - -@njit(fastmath=True, inline='always') -def cholesky_solve(L, b, y, x): - n = L.shape[0] - - # Forward substitution: L y = b - for i in range(n): - s = 0.0 - for j in range(i): - s += L[i, j] * y[j] - y[i] = (b[i] - s) / L[i, i] - - # Backward substitution: L.T x = y - for i in range(n - 1, -1, -1): - s = 0.0 - for j in range(i + 1, n): - s += L[j, i] * x[j] - x[i] = (y[i] - s) / L[i, i] - - return x - - -@njit(fastmath=True) -def safe_divide_vec(numer, denom): - safe_divisor = 1e-6 # Used to avoid division by zero - return numer / np.where( - np.abs(denom) < safe_divisor, - np.sign(denom) * safe_divisor, denom) - - -def find_analytic_center(A, b, x0): - """Find the analytic center using scipy.optimize.minimize""" - cons = {'type': 'ineq', 'fun': lambda x: A @ x - b} - result = minimize( - lambda x: 1e-3 * np.linalg.norm(x)**2 - np.sum(np.log(A @ x - b)), - x0, - constraints=cons, - method='SLSQP' - ) - return result.x - - -@njit(fastmath=True) -def _process_slice(slice_data, slice_mask, - Rt, R_pinv, - G, A, At, A_outer, b, x0, - max_iter, tol): - results = np.zeros( - slice_data.shape[:2] + (A.shape[1],), - dtype=np.float64) - - for j in prange(slice_data.shape[0]): - for k in range(slice_data.shape[1]): - if slice_mask[j, k]: - success, result = solve_qp( - Rt, R_pinv, G, A, At, A_outer, b, - x0, - slice_data[j, k], - max_iter, tol) - - if success: - results[j, k] = result - else: - results[j, k] = np.zeros(A.shape[1]) - else: - results[j, k] = np.zeros(A.shape[1]) - return results - - -def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, - use_osqppy=True, - numba_threading_layer="workqueue", - n_threads=None, n_cpus=None): - # Note cholesky is ~50% slower but more robust - if n_threads is not None: - set_num_threads(n_threads) - - if numba_threading_layer != "default": - config.THREADING_LAYER = numba_threading_layer - +def _fit(self, data, mask=None, n_cpus=None): + """ + Use OSQP to fit the multi-shell spherical deconvolution model. + """ if n_cpus is None: - if n_threads is None: - n_cpus = max(multiprocessing.cpu_count() - 1, 1) - else: - n_cpus = max(multiprocessing.cpu_count() - n_threads - 1, 1) + n_cpus = max(multiprocessing.cpu_count() - 1, 1) og_data_shape = data.shape if len(data.shape) < 4: @@ -365,151 +47,74 @@ def _fit(self, data, mask=None, max_iter=1e3, tol=1e-6, A_outer[i, j, k] = A[k, i] * A[k, j] Q = R.T @ R - if use_osqppy: - if n_cpus > 1: - ray.init(ignore_reinit_error=True) - - data_id = ray.put(data) - mask_id = ray.put(mask) - Q_id = ray.put(Q) - A_id = ray.put(A) - b_id = ray.put(b) - R_id = ray.put(R) - - @ray.remote( - num_cpus=n_cpus) - def process_batch_remote(batch_indices, data, mask, - Q, A, b, R): - from scipy.sparse import csr_matrix - import osqp - import numpy as np - - m = osqp.OSQP() - m.setup( - P=csr_matrix(Q), A=csr_matrix(A), l=b, - u=None, q=None, - verbose=False) - return_values = np.zeros( - (len(batch_indices),) + data.shape[1:3] + (A.shape[1],), - dtype=np.float64) - for i, ii in enumerate(batch_indices): - for jj in range(data.shape[1]): - for kk in range(data.shape[2]): - if mask[ii, jj, kk]: - c = np.dot(-R.T, data[ii, jj, kk]) - m.update(q=c) - results = m.solve() - return_values[i, jj, kk] = results.x - return return_values + if n_cpus > 1: + ray.init(ignore_reinit_error=True) + + data_id = ray.put(data) + mask_id = ray.put(mask) + Q_id = ray.put(Q) + A_id = ray.put(A) + b_id = ray.put(b) + R_id = ray.put(R) + + @ray.remote( + num_cpus=n_cpus) + def process_batch_remote(batch_indices, data, mask, + Q, A, b, R): + from scipy.sparse import csr_matrix + import osqp + import numpy as np - # Launch tasks in chunks - all_indices = list(range(data.shape[0])) - indices_chunked = list(chunk_indices(all_indices, n_cpus * 2)) - futures = [ - process_batch_remote.remote(batch, data_id, mask_id, - Q_id, A_id, - b_id, R_id) - for batch in indices_chunked - ] - - # Collect and assign results - for batch, future in zip( - indices_chunked, tqdm(futures)): - results = ray.get(future) - for i, ii in enumerate(batch): - coeff[ii] = results[i] - else: m = osqp.OSQP() m.setup( P=csr_matrix(Q), A=csr_matrix(A), l=b, u=None, q=None, - verbose=False, adaptive_rho=True) - for ii in tqdm(range(data.shape[0])): + verbose=False) + return_values = np.zeros( + (len(batch_indices),) + data.shape[1:3] + (A.shape[1],), + dtype=np.float64) + for i, ii in enumerate(batch_indices): for jj in range(data.shape[1]): for kk in range(data.shape[2]): if mask[ii, jj, kk]: c = np.dot(-R.T, data[ii, jj, kk]) m.update(q=c) results = m.solve() - coeff[ii, jj, kk] = results.x - coeff = coeff.reshape(og_data_shape[:-1] + (n,)) - return MSDeconvFit(self, coeff, None) + return_values[i, jj, kk] = results.x + return return_values + + # Launch tasks in chunks + all_indices = list(range(data.shape[0])) + indices_chunked = list(chunk_indices(all_indices, n_cpus * 2)) + futures = [ + process_batch_remote.remote(batch, data_id, mask_id, + Q_id, A_id, + b_id, R_id) + for batch in indices_chunked + ] + + # Collect and assign results + for batch, future in zip( + indices_chunked, tqdm(futures)): + results = ray.get(future) + for i, ii in enumerate(batch): + coeff[ii] = results[i] else: - x0 = np.linalg.pinv(A) @ np.ones(A.shape[0]) - x0 = find_analytic_center(A, b, x0) - - Rt = -R.T - R_pinv = np.linalg.pinv(R).astype(np.float64) - data = np.ascontiguousarray(data, dtype=np.float64) - - Rt = np.ascontiguousarray(Rt, dtype=np.float64) - R_pinv = np.ascontiguousarray(R_pinv, dtype=np.float64) - Q = np.ascontiguousarray(Q, dtype=np.float64) - A = np.ascontiguousarray(A, dtype=np.float64) - At = np.ascontiguousarray(A.T, dtype=np.float64) - b = np.ascontiguousarray(b, dtype=np.float64) - x0 = np.ascontiguousarray(x0, dtype=np.float64) - - warnings.simplefilter('ignore', category=NumbaPerformanceWarning) - - if n_cpus > 1: - ray.init(ignore_reinit_error=True) - - data_id = ray.put(data) - mask_id = ray.put(mask) - Rt_id = ray.put(Rt) - R_pinv_id = ray.put(R_pinv) - Q_id = ray.put(Q) - A_id = ray.put(A) - At_id = ray.put(At) - A_outer_id = ray.put(A_outer) - b_id = ray.put(b) - x0_id = ray.put(x0) - - @ray.remote( - num_cpus=n_cpus, - runtime_env={"env_vars": { - "NUMBA_THREADING_LAYER": f"{numba_threading_layer}"}}) - def process_batch_remote(batch_indices, data, mask, Rt, R_pinv, - Q, A, At, A_outer, b, x0, max_iter, tol): - return [_process_slice( - data[ii], mask[ii], Rt, R_pinv, Q, A, At, - A_outer, b, x0, max_iter, tol - ) for ii in batch_indices] - - # Launch tasks in chunks - all_indices = list(range(data.shape[0])) - futures = [ - process_batch_remote.remote(batch, data_id, mask_id, - Rt_id, R_pinv_id, - Q_id, A_id, At_id, - A_outer_id, b_id, x0_id, - max_iter, tol) - for batch in chunk_indices(all_indices, n_cpus * 2) - ] - - # Collect and assign results - for batch, future in zip( - chunk_indices(all_indices, n_cpus * 2), tqdm(futures)): - results = ray.get(future) - for i, ii in enumerate(batch): - coeff[ii] = results[i] - else: - for ii in tqdm(range(data.shape[0])): - coeff[ii] = _process_slice( - data[ii], mask[ii], - Rt, - R_pinv, - Q, - A, - At, - A_outer, - b, - x0, - max_iter, tol) - - coeff = coeff.reshape(og_data_shape[:-1] + (n,)) - return MSDeconvFit(self, coeff, None) + m = osqp.OSQP() + m.setup( + P=csr_matrix(Q), A=csr_matrix(A), l=b, + u=None, q=None, + verbose=False, adaptive_rho=True) + for ii in tqdm(range(data.shape[0])): + for jj in range(data.shape[1]): + for kk in range(data.shape[2]): + if mask[ii, jj, kk]: + c = np.dot(-R.T, data[ii, jj, kk]) + m.update(q=c) + results = m.solve() + coeff[ii, jj, kk] = results.x + coeff = coeff.reshape(og_data_shape[:-1] + (n,)) + return MSDeconvFit(self, coeff, None) MultiShellDeconvModel.fit = _fit diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 14a787d51..dd135d860 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -43,7 +43,6 @@ from AFQ.models.fwdti import _fit as fwdti_fit_model from AFQ.models.QBallTP import ( extract_odf, anisotropic_index, anisotropic_power) -from AFQ.models.dam import fit_dam, csf_dam, t1_dam from AFQ.models.wmgm_interface import fit_wm_gm_interface, pve_from_subcortex from AFQ.models.msmt import MultiShellDeconvModel from AFQ.models.asym_filtering import ( @@ -162,69 +161,6 @@ def b0_mask(b0, brain_mask): return masked_data, meta -@immlib.calc("dam_params") -@as_file(suffix='_model-dam_param-slopeintercept_dwimap.nii.gz', - subfolder="models") -@as_img -def dam_fit(data, gtab, masked_b0, - dam_low_signal_thresh=50): - """ - direction-averaged signal map (DAM) [1] slope and intercept - - Parameters - ---------- - dam_low_signal_thresh : float, optional - The threshold below which a voxel is considered to have low signal. - Default: 50 - - References - ---------- - .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., - & Garyfallidis, E. (2020). Segmentation of the brain using - direction-averaged signal of DWI images. - Magnetic Resonance Imaging, 69, 1-7. Elsevier. - https://doi.org/10.1016/j.mri.2020.02.010 - """ - b0_img = nib.load(masked_b0) - params_map = fit_dam( - data, gtab, b0_img, - dam_low_signal_thresh=dam_low_signal_thresh) - - return params_map, dict(low_signal_thresh=dam_low_signal_thresh) - - -@immlib.calc("dam_csf") -@as_file(suffix='_model-dam_param-csf_probseg.nii.gz', - subfolder="models") -@as_img -def dam_csf(dam_params): - """ - CSF probability map from DAM intercept - """ - dam_intercept_data = nib.load(dam_params).get_fdata()[..., 1] - csf, threshold = csf_dam(dam_intercept_data) - - return csf, dict( - DAMParamsFile=dam_params, - threshold=threshold) - - -@immlib.calc("dam_pseudot1") -@as_file(suffix='_model-dam_param-pseudot1_dwimap.nii.gz', - subfolder="models") -@as_img -def dam_pseudot1(dam_params, dam_csf): - """ - Pseudo T1 map from DAM fit - """ - dam_slope_data = nib.load(dam_params).get_fdata()[..., 0] - dam_csf_data = nib.load(dam_csf).get_fdata() - - pseudo_t1 = t1_dam(dam_slope_data, dam_csf_data) - - return pseudo_t1, dict(source=dam_params) - - @immlib.calc("dki_csf") @as_file(suffix='_model-dki_param-csf_probseg.nii.gz', subfolder="models") @@ -1713,7 +1649,6 @@ def get_data_plan(kwargs): t1_subcortex, configure_ncpus_nthreads, t1w_pve, wm_gm_interface, - dam_fit, dam_csf, dam_pseudot1, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, csd_anisotropic_index, csd_aodf, msmt_params, msmt_apm, msmt_aodf, From 74c6f62dfe1b30915ad7af7c1f0f25b61050a316 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 10:11:23 -0700 Subject: [PATCH 096/116] clarifications --- AFQ/definitions/image.py | 10 +++++----- AFQ/nn/brainchop.py | 7 ++++++- AFQ/tasks/tractography.py | 5 ++--- examples/tutorial_examples/viz_008_endpoints.py | 4 ++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index d3fc99cb6..c2fb0c5c7 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -13,7 +13,7 @@ __all__ = [ "ImageFile", "FullImage", "RoiImage", "LabelledImageFile", "ThresholdedImageFile", "ScalarImage", "ThresholdedScalarImage", - "TemplateImage", "ThreeTImage"] + "TemplateImage", "ThreeTissueImage"] logger = logging.getLogger('AFQ') @@ -657,16 +657,16 @@ def get_image_getter(self, task_name): for probseg in self.probsegs] -class ThreeTImage(ImageDefinition): +class ThreeTissueImage(ImageDefinition): """ - Define an image for use in PFT tractography. Only use + Define a three tissue image for use in PFT tractography. Only use if tracker set to 'pft' in tractography. Use to generate WM/GM/CSF probsegs from T1w. Examples -------- api.GroupAFQ(tracking_params={ - "stop_image": ThreeTImage(), + "stop_image": ThreeTissueImage(), "stop_threshold": "ACT", "tracker": "pft"}) """ @@ -680,7 +680,7 @@ def get_name(self): def get_image_getter(self, task_name): if task_name == "data": raise ValueError(( - "ThreeTImage cannot be used in this context, as they" + "ThreeTissueImage cannot be used in this context, as they" "require later derivatives to be calculated")) def csf_getter(data_imap): diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index a6b2b5f3f..6cce37bee 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -66,8 +66,13 @@ def run_brainchop(t1_img, model): subprocess.run(cmd, input=full_input, check=True) output_img = nib.load(tmp_out_file) + # This line below forces the data into memory to avoid issues with + # temporary files being deleted too early. + # Otherwise, nibabel lazy-loads the data and the file gets deleted + # before the data is accessed, because the file is in a temporary + # Directory. output_img = nib.Nifti1Image( output_img.get_fdata().copy(), - output_img.affine.copy()) # force into memory + output_img.affine.copy()) return output_img diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index e5548661b..4277cc9b2 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -6,14 +6,13 @@ import dipy.data as dpd import immlib -import multiprocessing from AFQ.tasks.decorators import as_file from AFQ.tasks.utils import with_name from AFQ.definitions.utils import Definition import AFQ.tractography.tractography as aft from AFQ.tasks.utils import get_default_args -from AFQ.definitions.image import ScalarImage, ThreeTImage +from AFQ.definitions.image import ScalarImage, ThreeTissueImage from AFQ.tractography.utils import gen_seeds, get_percentile_threshold from trx.trx_file_memmap import TrxFile @@ -458,7 +457,7 @@ def get_tractography_plan(kwargs): if kwargs["tracking_params"]["stop_mask"] is None: kwargs["tracking_params"]["stop_threshold"] = "ACT" - kwargs["tracking_params"]["stop_mask"] = ThreeTImage() + kwargs["tracking_params"]["stop_mask"] = ThreeTissueImage() logger.info(( "No stop mask given, using ACT " "and 3T prob maps esimated from T1w")) diff --git a/examples/tutorial_examples/viz_008_endpoints.py b/examples/tutorial_examples/viz_008_endpoints.py index 4ae7a3357..34d8c1dcd 100644 --- a/examples/tutorial_examples/viz_008_endpoints.py +++ b/examples/tutorial_examples/viz_008_endpoints.py @@ -27,7 +27,7 @@ from dipy.align import resample ############################################################ -# Use an example subject from the Human Brain Network (HBN). +# Use an example subject from the Healthy Brain Network (HBN). subject_id = "NDARKP893TWU" # Example subject ID ses_id = "HBNsiteRU" # Example session ID @@ -76,7 +76,7 @@ "sphere": get_sphere(name="repulsion724"), "seed_mask": afm.ScalarImage("wm_gm_interface"), "seed_threshold": 0.5, - "stop_mask": afm.ThreeTImage(), + "stop_mask": afm.ThreeTissueImage(), "stop_threshold": "ACT", "n_seeds": 2000000, "random_seeds": True}, From 125dd2879e5d0f5e60dc4c3902524ac8a1558cdf Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 10:13:44 -0700 Subject: [PATCH 097/116] remove unused functions --- AFQ/models/dki.py | 109 ---------------------------------------------- 1 file changed, 109 deletions(-) diff --git a/AFQ/models/dki.py b/AFQ/models/dki.py index c03978236..7049962f5 100644 --- a/AFQ/models/dki.py +++ b/AFQ/models/dki.py @@ -358,112 +358,3 @@ def predict(params_file, gtab, S0_file=None, out_dir=None): nib.save(nib.Nifti1Image(pred, img.affine), fname) return fname - - -def fit_dki_csf(dki_md_data): - """ - CSF probability map from DKI MD inspired by [1] - - References - ---------- - .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., - & Garyfallidis, E. (2020). Segmentation of the brain using - direction-averaged signal of DWI images. - Magnetic Resonance Imaging, 69, 1-7. Elsevier. - https://doi.org/10.1016/j.mri.2020.02.010 - """ - - beta_values = dki_md_data.flatten() - beta_values = beta_values[beta_values != 0] - - # Make a smoothed histogram - hist, bin_edges = np.histogram(beta_values, bins=200, density=True) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - smoothed_hist = gaussian_filter1d(hist, sigma=2) - - # Find the main peak - peaks, _ = find_peaks(smoothed_hist, height=0.1 * np.max(smoothed_hist)) - if len(peaks) == 0: - raise ValueError(( - "DKI MD: No peaks found in the histogram, " - "required for DKI CSF estimate")) - - main_peak_idx = peaks[np.argmax(smoothed_hist[peaks])] - main_peak_val = bin_centers[main_peak_idx] - - # Estimate uncertainty in peak center location - # The FWHM of a Gaussian is 2.355*sigma, so sigma ≈ FWHM/2.355 - width_results = peak_widths( - smoothed_hist, [main_peak_idx], rel_height=0.5) - peak_width_md = abs( - bin_centers[1] - bin_centers[0]) * width_results[0][0] - peak_sigma = peak_width_md / 2.355 - - # Find the threshold symmetric to 0 with respect to the peak center - main_peak_val = 2 * main_peak_val - peak_sigma = 2 * peak_sigma - - dki_md_data[dki_md_data != 0] = \ - 0.5 * (1 + erf((dki_md_data[dki_md_data != 0] - main_peak_val) - / (peak_sigma * np.sqrt(2)))) - - return dki_md_data, main_peak_val, peak_sigma - - -def fit_dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul): - """ - WM probability map from DKI FA - - Parameters - ---------- - dki_fa_data : ndarray - DKI FA data from which to calculate the WM probability map. - dki_wm_ll : float - Lower limit of FA in white matter to calculate probability mask. - dki_gm_ul : float - Upper limit of FA in gray matter to calculate probability mask. - """ - - uncertain_slice = np.logical_and( - dki_fa_data > dki_wm_ll, - dki_fa_data <= dki_gm_ul) - wm_data = dki_fa_data.copy() - wm_data[dki_fa_data >= dki_gm_ul] = 1.0 - wm_data[uncertain_slice] = (dki_fa_data[uncertain_slice] - dki_wm_ll) /\ - (dki_gm_ul - dki_wm_ll) - wm_data[dki_fa_data < dki_wm_ll] = 0.0 - - return wm_data - - -def fit_dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul): - """ - GM probability map from DKI FA - - Parameters - ---------- - dki_fa_data : ndarray - DKI FA data from which to calculate the GM probability map. - dki_csf_data : ndarray - DKI CSF data to remove CSF contribution from GM probability map. - dki_wm_ll : float - Lower limit of FA in white matter to calculate probability mask. - dki_gm_ul : float - Upper limit of FA in gray matter to calculate probability mask. - """ - - uncertain_slice = np.logical_and( - dki_fa_data > dki_wm_ll, - dki_fa_data <= dki_gm_ul) - gm_data = dki_fa_data.copy() - gm_data[dki_fa_data >= dki_gm_ul] = 0.0 - gm_data[uncertain_slice] = (dki_gm_ul - dki_fa_data[uncertain_slice]) /\ - (dki_gm_ul - dki_wm_ll) - gm_data[np.logical_and( - dki_fa_data < dki_wm_ll, - dki_fa_data != 0)] = 1.0 - - gm_data = gm_data - dki_csf_data # Remove CSF contribution - gm_data[gm_data < 0.0] = 0.0 - - return gm_data From 4ddac160fbca706968681868e103cbffb7e8b9e3 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 10:58:03 -0700 Subject: [PATCH 098/116] add tests --- AFQ/recognition/tests/test_utils.py | 87 +++++++++++++++++++++++++++++ AFQ/tests/test_nn.py | 20 +++++++ 2 files changed, 107 insertions(+) create mode 100644 AFQ/tests/test_nn.py diff --git a/AFQ/recognition/tests/test_utils.py b/AFQ/recognition/tests/test_utils.py index 0a58d8254..9189b2ff3 100644 --- a/AFQ/recognition/tests/test_utils.py +++ b/AFQ/recognition/tests/test_utils.py @@ -7,6 +7,7 @@ import AFQ.recognition.curvature as abv import AFQ.recognition.utils as abu import AFQ.recognition.cleaning as abc +import AFQ.recognition.other_bundles as abo from dipy.io.stateful_tractogram import StatefulTractogram, Space @@ -23,6 +24,16 @@ streamlines = tg.streamlines +def _make_straight_streamlines(n_sl=5, length=10, axis=0, offset=0): + """Utility: make straight streamlines along one axis.""" + sls = [] + for i in range(n_sl): + sl = np.zeros((length, 3)) + sl[:, axis] = np.linspace(0, length - 1, length) + offset + sls.append(sl) + return sls + + def test_segment_sl_curve(): sl_disp_0 = abv.sl_curve(streamlines[4], 4) npt.assert_array_almost_equal( @@ -79,3 +90,79 @@ def test_segment_orientation(): primary_axis="I/S", affine=np.eye(4), tol=33) npt.assert_array_equal(cleaned_idx_tol, cleaned_idx) + + +def test_clean_isolation_forest_basic(): + cleaned_idx = abc.clean_by_isolation_forest(streamlines, + n_points=20, + min_sl=10) + # Should return either a boolean mask or integer indices + npt.assert_(isinstance(cleaned_idx, (np.ndarray,))) + npt.assert_(cleaned_idx.shape[0] <= len(streamlines)) + + +def test_clean_isolation_forest_outlier_thresh(): + cleaned_loose = abc.clean_by_isolation_forest(streamlines, + n_points=20, + percent_outlier_thresh=50, + min_sl=10) + cleaned_strict = abc.clean_by_isolation_forest(streamlines, + n_points=20, + percent_outlier_thresh=5, + min_sl=10) + npt.assert_(np.sum(cleaned_loose) >= np.sum(cleaned_strict)) + + +def test_clean_by_overlap_keep_remove(): + img = nib.Nifti1Image(np.zeros((20, 20, 20)), np.eye(4)) + + this_bundle = _make_straight_streamlines(n_sl=3, length=10, axis=0) + other_bundle = _make_straight_streamlines(n_sl=3, length=10, axis=0) + + cleaned_remove = abo.clean_by_overlap(this_bundle, other_bundle, + overlap=5, img=img, remove=True) + npt.assert_equal(cleaned_remove, np.zeros(3, dtype=bool)) + + cleaned_keep = abo.clean_by_overlap(this_bundle, other_bundle, + overlap=5, img=img, remove=False) + npt.assert_equal(cleaned_keep, np.ones(3, dtype=bool)) + + +def test_clean_by_overlap_partial_overlap(): + img = nib.Nifti1Image(np.zeros((20, 20, 20)), np.eye(4)) + + this_bundle = _make_straight_streamlines(n_sl=2, length=10, axis=0) + other_bundle = _make_straight_streamlines(n_sl=2, length=10, axis=1) + + # These bundles are orthogonal, so minimal overlap + cleaned = abo.clean_by_overlap(this_bundle, other_bundle, + overlap=2, img=img, remove=False) + npt.assert_equal(cleaned, np.zeros(2, dtype=bool)) + + +def test_clean_relative_to_other_core_entire_vs_closest(): + # Two bundles along x axis, separated along z + this_bundle = np.array(_make_straight_streamlines(n_sl=2, + length=5, + axis=0)) + this_bundle[0, :, 2] += 5 + this_bundle[1, :, 2] -= 5 + other_bundle = np.array(_make_straight_streamlines(n_sl=2, + length=5, + axis=0)) + affine = np.eye(4) + cleaned_entire = abo.clean_relative_to_other_core('inferior', + this_bundle, + other_bundle, + affine, + entire=True) + npt.assert_equal(cleaned_entire, [True, False]) + + # With entire=False, same result in this synthetic case + cleaned_closest = abo.clean_relative_to_other_core('inferior', + this_bundle, + other_bundle, + affine, + entire=False) + npt.assert_equal(cleaned_closest, [True, False]) + diff --git a/AFQ/tests/test_nn.py b/AFQ/tests/test_nn.py new file mode 100644 index 000000000..98b095b4b --- /dev/null +++ b/AFQ/tests/test_nn.py @@ -0,0 +1,20 @@ +import nibabel as nib +import os.path as op +import tempfile +import numpy.testing as npt + +from AFQ.nn.brainchop import run_brainchop +import AFQ.data.fetch as afd + +def test_run_brainchop(): + tmpdir = tempfile.mkdtemp() + afd.organize_stanford_data(path=tmpdir) + + t1_path = op.join( + tmpdir, + ( + "stanford_hardi/derivatives/freesurfer/" + "sub-01/ses-01/anat/sub-01_ses-01_T1w.nii.gz")) + chopped_brain = run_brainchop(nib.load(t1_path), "mindgrab") + + npt.assert_(chopped_brain.get_fdata().sum() > 200000) From 10d430fa8012c7ffb8ac02d04b6ab5222a449b47 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 11:07:03 -0700 Subject: [PATCH 099/116] make core only configurable --- AFQ/recognition/cleaning.py | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index c62361559..37a1043a9 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -68,15 +68,17 @@ def clean_by_orientation(streamlines, primary_axis, affine, tol=None): def clean_by_orientation_mahalanobis(streamlines, n_points=100, - core_only=True, min_sl=20, + core_only=0.6, min_sl=20, distance_threshold=3, clean_rounds=5): fgarray = np.array(abu.resample_tg(streamlines, n_points)) - if core_only: - fgarray = fgarray[:, - int(n_points * 0.2):int(n_points * 0.8), - :] # Crop to middle 60% + if core_only != 0: + crop_edge = (1.0 - core_only) / 2 + fgarray = fgarray[ + :, + int(n_points * crop_edge):int(n_points * (1 - crop_edge)), + :] # Crop to middle 60% fgarray_dists = fgarray[:, 1:, :] - fgarray[:, :-1, :] idx = np.arange(len(fgarray)) @@ -119,7 +121,7 @@ def clean_by_orientation_mahalanobis(streamlines, n_points=100, def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, length_threshold=4, min_sl=20, stat=np.mean, - core_only=True, return_idx=False): + core_only=0.6, return_idx=False): """ Clean a segmented fiber group based on the Mahalnobis distance of each streamline @@ -147,13 +149,13 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, stat : callable or str, optional. The statistic of each node relative to which the Mahalanobis is calculated. Default: `np.mean` (but can also use median, etc.) - core_only : bool, optional - If True, only the core of the bundle is used for cleaning. - The core is defined as the middle 60% of each streamline. - This means streamlines are allowed to deviate in the starting - and ending 20% of the bundle. This is useful for allowing more - diverse endpoints. - Default: True. + core_only : float, optional + If non-zero, only the core of the bundle is used for cleaning. + The core is commonly defined as the middle 60% of each streamline, + thus our default is 0.6. This means streamlines are allowed to + deviate in the starting and ending 20% of the bundle. This is useful + for allowing more diverse endpoints. + Default: 0.6 return_idx : bool Whether to return indices in the original streamlines. Default: False. @@ -184,10 +186,12 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, # Resample once up-front: fgarray = np.asarray(abu.resample_tg(streamlines, n_points)) - if core_only: - fgarray = fgarray[:, - int(n_points * 0.2):int(n_points * 0.8), - :] # Crop to middle 60% + if core_only != 0: + crop_edge = (1.0 - core_only) / 2 + fgarray = fgarray[ + :, + int(n_points * crop_edge):int(n_points * (1 - crop_edge)), + :] # Crop to middle 60% # Keep this around, so you can use it for indexing at the very end: idx = np.arange(len(fgarray)) From f21935ff29ff5f1158cdfe3bd343c3b52ec9b5fc Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 11:12:21 -0700 Subject: [PATCH 100/116] remove more unused code; kwargs for entire/remove in relative bundle cleaning --- AFQ/recognition/other_bundles.py | 10 ++-- AFQ/tasks/data.py | 84 -------------------------------- 2 files changed, 6 insertions(+), 88 deletions(-) diff --git a/AFQ/recognition/other_bundles.py b/AFQ/recognition/other_bundles.py index d6f395bf9..f85bfc17a 100644 --- a/AFQ/recognition/other_bundles.py +++ b/AFQ/recognition/other_bundles.py @@ -10,7 +10,7 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, - overlap, img, remove): + overlap, img, remove=False): """ Cleans a set of streamlines by only keeping (or removing) those with significant overlap with another set of streamlines. @@ -28,10 +28,11 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, img : nibabel.Nifti1Image or ndarray A reference 3D image that defines the spatial dimensions for the density map. - remove : bool + remove : bool, optional If True, streamlines that overlap in less than `overlap` nodes are removed. If False, streamlines that overlap in more than `overlap` nodes are removed. + Default: False. Returns ------- @@ -67,7 +68,7 @@ def clean_by_overlap(this_bundle_sls, other_bundle_sls, def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine, - entire): + entire=False): """ Removes streamlines from a set that lie on the opposite side of a specified core axis compared to another set of streamlines. @@ -84,10 +85,11 @@ def clean_relative_to_other_core(core, this_fgarray, other_fgarray, affine, An array of reference streamlines to define the core. affine : ndarray The affine transformation matrix. - entire : bool + entire : bool, optional If True, the entire streamline must lie on the correct side of the core to be retained. If False, only the closest point on the streamline to the core is considered. + Default: False. Returns ------- diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index dd135d860..5cb8da4f6 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -38,7 +38,6 @@ from AFQ.models.csd import _fit as csd_fit_model from AFQ.models.csd import CsdNanResponseError from AFQ.models.dki import _fit as dki_fit_model -from AFQ.models.dki import fit_dki_csf, fit_dki_gm, fit_dki_wm from AFQ.models.dti import _fit as dti_fit_model from AFQ.models.fwdti import _fit as fwdti_fit_model from AFQ.models.QBallTP import ( @@ -161,88 +160,6 @@ def b0_mask(b0, brain_mask): return masked_data, meta -@immlib.calc("dki_csf") -@as_file(suffix='_model-dki_param-csf_probseg.nii.gz', - subfolder="models") -@as_img -def dki_csf(dki_md): - """ - CSF probability map from DKI MD inspired by [1] - - References - ---------- - .. [1] Cheng, H., Newman, S., Afzali, M., Fadnavis, S., - & Garyfallidis, E. (2020). Segmentation of the brain using - direction-averaged signal of DWI images. - Magnetic Resonance Imaging, 69, 1-7. Elsevier. - https://doi.org/10.1016/j.mri.2020.02.010 - """ - dki_md_data = nib.load(dki_md).get_fdata() - - dki_md_data, main_peak_val, peak_sigma = fit_dki_csf(dki_md_data) - - return dki_md_data, dict( - DKI_MD_source=dki_md, - main_peak_val=main_peak_val, - peak_sigma=peak_sigma) - - -@immlib.calc("dki_wm") -@as_file(suffix='_model-dki_param-wm_probseg.nii.gz', - subfolder="models") -@as_img -def dki_wm(dki_fa, dki_wm_ll=0.1, dki_gm_ul=0.3): - """ - WM probability map from DKI FA - - Parameters - ---------- - dki_wm_ll : float, optional - Lower limit of FA in white matter to calculate probability mask. - Default: 0.1 - dki_gm_ul : float, optional - Upper limit of FA in gray matter to calculate probability mask. - Default: 0.3 - """ - dki_fa_data = nib.load(dki_fa).get_fdata() - - wm_data = fit_dki_wm(dki_fa_data, dki_wm_ll, dki_gm_ul) - - return wm_data, dict( - DKI_FA_source=dki_fa, - dki_wm_ll=dki_wm_ll, - dki_gm_ul=dki_gm_ul) - - -@immlib.calc("dki_gm") -@as_file(suffix='_model-dki_param-gm_probseg.nii.gz', - subfolder="models") -@as_img -def dki_gm(dki_fa, dki_csf, dki_wm_ll=0.1, dki_gm_ul=0.3): - """ - GM probability map from DKI FA - - Parameters - ---------- - dki_wm_ll : float, optional - Lower limit of FA in white matter to calculate probability mask. - Default: 0.1 - dki_gm_ul : float, optional - Upper limit of FA in gray matter to calculate probability mask. - Default: 0.3 - """ - dki_fa_data = nib.load(dki_fa).get_fdata() - dki_csf_data = nib.load(dki_csf).get_fdata() - - gm_data = fit_dki_gm(dki_fa_data, dki_csf_data, dki_wm_ll, dki_gm_ul) - - return gm_data, dict( - DKI_FA_source=dki_fa, - DKI_CSF_source=dki_csf, - dki_wm_ll=dki_wm_ll, - dki_gm_ul=dki_gm_ul) - - @immlib.calc("t1w_pve") @as_file(suffix='_desc-pve_probseg.nii.gz') def t1w_pve(t1_subcortex): @@ -1662,7 +1579,6 @@ def get_data_plan(kwargs): dti_ga, dti_rd, dti_ad, dki_ad, dki_rk, dki_ak, dti_params, dki_params, fwdti_params, dki_cl, dki_cp, dki_cs, - dki_csf, dki_wm, dki_gm, rumba_fit, rumba_params, rumba_model, rumba_f_csf, rumba_f_gm, rumba_f_wm, csd_params, get_bundle_dict]) From 6258a1607a9def55d2b4f7fe9ce6c147a47bffaa Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 12:26:21 -0700 Subject: [PATCH 101/116] add determinism to IF test --- AFQ/recognition/cleaning.py | 7 +++++-- AFQ/recognition/criteria.py | 4 ++-- AFQ/recognition/tests/test_utils.py | 6 ++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/AFQ/recognition/cleaning.py b/AFQ/recognition/cleaning.py index 37a1043a9..5f183eb7f 100644 --- a/AFQ/recognition/cleaning.py +++ b/AFQ/recognition/cleaning.py @@ -260,7 +260,7 @@ def clean_bundle(tg, n_points=100, clean_rounds=5, distance_threshold=3, def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, - min_sl=20, n_jobs=None): + min_sl=20, n_jobs=None, random_state=None): """ Use Isolation Forest (IF) to clean streamlines. Nodes are passed to IF, and outlier nodes are identified. @@ -284,6 +284,9 @@ def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, n_jobs : int, optional Number of parallel jobs to use for LOF. Default: None (single-threaded). + random_state : int, optional + Random state for IsolationForest. + Default: None Returns ------- @@ -312,7 +315,7 @@ def clean_by_isolation_forest(tg, n_points=100, percent_outlier_thresh=15, axis=1) idx = np.arange(len(fgarray)) - lof = IsolationForest(n_jobs=n_jobs) + lof = IsolationForest(n_jobs=n_jobs, random_state=random_state) outliers = lof.fit_predict(X_) outliers = outliers.reshape(fgarray.shape[:2]) outliers = np.sum(outliers == -1, axis=1) diff --git a/AFQ/recognition/criteria.py b/AFQ/recognition/criteria.py index 45be0f9de..3fbe5fa28 100644 --- a/AFQ/recognition/criteria.py +++ b/AFQ/recognition/criteria.py @@ -344,13 +344,13 @@ def orient_mahal(b_sls, bundle_def, **kwargs): b_sls.select(accept_idx, "orient_mahal") -def isolation_forest(b_sls, bundle_def, n_cpus, **kwargs): +def isolation_forest(b_sls, bundle_def, n_cpus, rng, **kwargs): b_sls.initiate_selection("isolation_forest") accept_idx = abc.clean_by_isolation_forest( b_sls.get_selected_sls(), percent_outlier_thresh=bundle_def["isolation_forest"].get( "percent_outlier_thresh", 25), - n_jobs=n_cpus) + n_jobs=n_cpus, random_state=rng) b_sls.select(accept_idx, "isolation_forest") diff --git a/AFQ/recognition/tests/test_utils.py b/AFQ/recognition/tests/test_utils.py index 9189b2ff3..a0b1f10cf 100644 --- a/AFQ/recognition/tests/test_utils.py +++ b/AFQ/recognition/tests/test_utils.py @@ -105,11 +105,13 @@ def test_clean_isolation_forest_outlier_thresh(): cleaned_loose = abc.clean_by_isolation_forest(streamlines, n_points=20, percent_outlier_thresh=50, - min_sl=10) + min_sl=10, + random_state=42) cleaned_strict = abc.clean_by_isolation_forest(streamlines, n_points=20, percent_outlier_thresh=5, - min_sl=10) + min_sl=10, + random_state=42) npt.assert_(np.sum(cleaned_loose) >= np.sum(cleaned_strict)) From 96d5f6c5ab08b35ac49168674f4cc9cf4fddca2c Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 12:32:14 -0700 Subject: [PATCH 102/116] more reasonable tracking chunk siz --- AFQ/tasks/tractography.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 4277cc9b2..c3eeb9dc8 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -280,10 +280,12 @@ def delete_lazyt(self, id): sft = trx_concatenate(sfts) else: lazyt = aft.track(fodf, **this_tracking_params) + # Chunk size is number of streamlines tracked before saving to disk. sft = TrxFile.from_lazy_tractogram( lazyt, seed, - dtype_dict=dtype_dict) + dtype_dict=dtype_dict, + chunk_size=1e5) n_streamlines = len(sft) else: From 8cc3c7cf9a50650b50d6b700d1a9bf9360bee43c Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 12:51:01 -0700 Subject: [PATCH 103/116] cleaner error messages --- AFQ/tasks/decorators.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AFQ/tasks/decorators.py b/AFQ/tasks/decorators.py index 678fe52f5..54b74fca8 100644 --- a/AFQ/tasks/decorators.py +++ b/AFQ/tasks/decorators.py @@ -80,8 +80,12 @@ def wrapper_as_file(*args, **kwargs): if not op.exists(this_file): logger.info(f"Calculating {suffix}") - gen, meta = func( - *args, **kwargs) + try: + gen, meta = func( + *args, **kwargs) + except Exception: + print(f"Error in task: {func.__qualname__}") + raise logger.info(f"{suffix} completed. Saving to {this_file}") if isinstance(gen, nib.Nifti1Image): From 052276856415a698907c48958a6246001f98b9da Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 14:27:38 -0700 Subject: [PATCH 104/116] use t1 for visualizations --- AFQ/tasks/data.py | 32 ++++++++++++++++++++++---------- AFQ/tasks/viz.py | 5 ++--- AFQ/viz/plotly_backend.py | 4 ++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 5cb8da4f6..508ae3752 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -1427,9 +1427,26 @@ def t1_brain_mask(t1_file): model="brainchop") +@immlib.calc("t1_masked") +@as_file(suffix='_desc-masked_T1w.nii.gz') +def t1_masked(t1_file, t1_brain_mask): + """ + full path to a nifti file containing the T1w masked + """ + t1_img = nib.load(t1_file) + t1_data = t1_img.get_fdata() + t1_mask = nib.load(t1_brain_mask) + t1_data[t1_mask.get_fdata() == 0] = 0 + t1_img_masked = nib.Nifti1Image( + t1_data, t1_img.affine) + return t1_img_masked, dict( + T1w=t1_file, + BrainMask=t1_brain_mask) + + @immlib.calc("t1_subcortex") @as_file(suffix='_desc-subcortex_probseg.nii.gz') -def t1_subcortex(t1_file, t1_brain_mask): +def t1_subcortex(t1_masked): """ full path to a nifti file containing segmentation of subcortical structures from T1w image using Brainchop @@ -1441,18 +1458,13 @@ def t1_subcortex(t1_file, t1_brain_mask): Software, 8(83), 5098. https://doi.org/10.21105/joss.05098 """ - t1_img = nib.load(t1_file) - t1_data = t1_img.get_fdata() - t1_mask = nib.load(t1_brain_mask) - t1_data[t1_mask.get_fdata() == 0] = 0 - t1_img_masked = nib.Nifti1Image( - t1_data, t1_img.affine) + t1_img_masked = nib.load(t1_masked) subcortical_img = run_brainchop( t1_img_masked, "subcortical") meta = dict( - T1w=t1_file, + T1w=t1_masked, model="subcortical", labels=[ "Unknown", "Cerebral-White-Matter", "Cerebral-Cortex", @@ -1562,8 +1574,8 @@ def get_data_plan(kwargs): "strings/scalar definitions") data_tasks = with_name([ - get_data_gtab, b0, b0_mask, brain_mask, t1_brain_mask, - t1_subcortex, + get_data_gtab, b0, b0_mask, brain_mask, + t1_brain_mask, t1_subcortex, t1_masked, configure_ncpus_nthreads, t1w_pve, wm_gm_interface, dti_fit, dki_fit, fwdti_fit, anisotropic_power_map, diff --git a/AFQ/tasks/viz.py b/AFQ/tasks/viz.py index 72925bb47..1a369dea0 100644 --- a/AFQ/tasks/viz.py +++ b/AFQ/tasks/viz.py @@ -75,7 +75,7 @@ def viz_bundles(base_fname, mapping = mapping_imap["mapping"] scalar_dict = segmentation_imap["scalar_dict"] profiles_file = segmentation_imap["profiles"] - volume = data_imap["masked_b0"] + volume = data_imap["t1_masked"] shade_by_volume = data_imap[best_scalar] volume = _viz_prepare_vol(volume, False, mapping, scalar_dict) shade_by_volume = _viz_prepare_vol( @@ -168,9 +168,8 @@ def viz_indivBundle(base_fname, """ mapping = mapping_imap["mapping"] bundle_dict = data_imap["bundle_dict"] - reg_template = data_imap["reg_template"] scalar_dict = segmentation_imap["scalar_dict"] - volume = data_imap["masked_b0"] + volume = data_imap["t1_masked"] shade_by_volume = data_imap[best_scalar] profiles = pd.read_csv(segmentation_imap["profiles"]) diff --git a/AFQ/viz/plotly_backend.py b/AFQ/viz/plotly_backend.py index 493daeb8e..9a6c73c13 100644 --- a/AFQ/viz/plotly_backend.py +++ b/AFQ/viz/plotly_backend.py @@ -598,8 +598,8 @@ def _draw_slice(figure, axis, volume, opacity=0.3, pos=0.5, colorscale="greys", invert_colorscale=False): height = int(volume.shape[axis] * pos) - v_min = np.percentile(volume, 10) - sf = np.percentile(volume, 90) - v_min + v_min = np.percentile(volume, 20) + sf = np.percentile(volume, 80) - v_min if axis == Axes.X: X, Y, Z = np.mgrid[height:height + 1, From c9bb42e157d4266f50f1472c0924297608c1bc69 Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 25 Sep 2025 16:20:59 -0700 Subject: [PATCH 105/116] remove some pins that were holding us back --- docs/source/reference/kwargs.rst | 28 +++++++--------------- docs/source/reference/methods.rst | 40 +++++++++++++------------------ setup.cfg | 5 +--- 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/docs/source/reference/kwargs.rst b/docs/source/reference/kwargs.rst index 2c15a4fb6..b32df3bd6 100644 --- a/docs/source/reference/kwargs.rst +++ b/docs/source/reference/kwargs.rst @@ -18,13 +18,10 @@ Here are the arguments you can pass to kwargs, to customize the tractometry pipe DATA ========================================================== min_bval: float - Minimum b value you want to use from the dataset (other than b0), inclusive. If None, there is no minimum limit. Default: None + Minimum b value you want to use from the dataset (other than b0), inclusive. If None, there is no minimum limit. Default: -np.inf max_bval: float - Maximum b value you want to use from the dataset (other than b0), inclusive. If None, there is no maximum limit. Default: None - -filter_b: bool - Whether to filter the DWI data based on min or max bvals. Default: True + Maximum b value you want to use from the dataset (other than b0), inclusive. If None, there is no maximum limit. Default: np.inf b0_threshold: int The value of b under which it is considered to be b0. Default: 50. @@ -35,15 +32,6 @@ ray_n_cpus: int numba_n_threads: int The number of threads to use for Numba. If None, uses the number of available CPUs minus one. MSMT and ASYM fits use Numba. Default: None -dam_low_signal_thresh: float - The threshold below which a voxel is considered to have low signal. Default: 50 - -dki_wm_ll: float - Lower limit of FA in white matter to calculate probability mask. Default: 0.1 - -dki_gm_ul: float - Upper limit of FA in gray matter to calculate probability mask. Default: 0.3 - robust_tensor_fitting: bool Whether to use robust_tensor_fitting when doing dti. Only applies to dti. Default: False @@ -57,7 +45,7 @@ csd_response: tuple or None The response function to be used by CSD, as a tuple with two elements. The first is the eigen-values as an (3,) ndarray and the second is the signal value for the response function without diffusion-weighting (i.e. S0). If not provided, auto_response will be used to calculate these values. Default: None csd_sh_order_max: int or None - default: infer the number of parameters from the number of data volumes, but no larger than 8. Default: None + If None, infer the number of parameters from the number of data volumes, but no larger than 8. Default: None csd_lambda_: float weight given to the constrained-positivity regularization part of the deconvolution equation. Default: 1 @@ -83,10 +71,10 @@ rumba_csf_response: float rumba_n_iter: int Number of iterations for fODF estimation. Must be a positive int. Default: 600 -opdt_sh_order: int +opdt_sh_order_max: int Spherical harmonics order for OPDT model. Must be even. Default: 8 -csa_sh_order: int +csa_sh_order_max: int Spherical harmonics order for CSA model. Must be even. Default: 8 sphere: Sphere class instance @@ -119,7 +107,7 @@ reg_subject_spec: str SEGMENTATION ========================================================== segmentation_params: dict - The parameters for segmentation. Default: use the default behavior of the seg.Segmentation object. + The parameters for segmentation. Defaults to using the default behavior of the seg.Segmentation object. endpoint_threshold: float The threshold for the endpoint maps. If None, no endpoint maps are exported as distance to endpoints maps, which the user can then threshold as needed. Default: 3 @@ -138,7 +126,7 @@ scalars: list of strings and/or scalar definitions TRACTOGRAPHY ========================================================== tracking_params: dict - The parameters for tracking. Default: use the default behavior of the aft.track function. Seed mask and seed threshold, if not specified, are replaced with scalar masks from scalar[0] thresholded to 0.2. The ``seed_mask`` and ``stop_mask`` items of this dict may be ``AFQ.definitions.image.ImageFile`` instances. If ``tracker`` is set to "pft" then ``stop_mask`` should be an instance of ``AFQ.definitions.image.PFTImage``. + The parameters for tracking. Defaults to using the default behavior of the aft.track function. Seed mask and seed threshold, if not specified, are replaced with scalar masks from scalar[0] thresholded to 0.2. The ``seed_mask`` and ``stop_mask`` items of this dict may be ``AFQ.definitions.image.ImageFile`` instances. If ``tracker`` is set to "pft" then ``stop_mask`` should be an instance of ``AFQ.definitions.image.PFTImage``. import_tract: dict or str or None BIDS filters for inputing a user made tractography file, or a path to the tractography file. If None, DIPY is used to generate the tractography. Default: None @@ -172,7 +160,7 @@ n_points_indiv: int or None n_points to resample streamlines to before plotting. If None, no resampling is done. Default: 40 virtual_frame_buffer: bool - Whether to use a virtual fram buffer. This is neccessary if generating GIFs in a headless environment. Default: False + Whether to use a virtual frame buffer. This is neccessary if generating GIFs in a headless environment. Default: False viz_backend_spec: str Which visualization backend to use. See Visualization Backends page in documentation for details https://tractometry.org/pyAFQ/reference/viz_backend.html One of {"fury", "plotly", "plotly_no_gif"}. Default: "plotly_no_gif" diff --git a/docs/source/reference/methods.rst b/docs/source/reference/methods.rst index cf2c6ae69..441cf93ca 100644 --- a/docs/source/reference/methods.rst +++ b/docs/source/reference/methods.rst @@ -35,6 +35,18 @@ base_fname: Base file name for outputs +pve_wm: + White matter partial volume estimate map + + +pve_gm: + Gray matter partial volume estimate map + + +pve_csf: + Cerebrospinal fluid partial volume estimate map + + data: DWI data as an ndarray for selected b values @@ -67,30 +79,6 @@ masked_b0: full path to a nifti file containing the mean b0 after applying the brain mask -dam_params: - direction-averaged signal map (DAM) [1] slope and intercept - - -dam_csf: - CSF probability map from DAM intercept - - -dam_pseudot1: - Pseudo T1 map from DAM fit - - -dki_csf: - CSF probability map from DKI MD inspired by [1] - - -dki_wm: - WM probability map from DKI FA - - -dki_gm: - GM probability map from DKI FA - - t1w_pve: WM, GM, CSF segmentations from subcortex segmentation from brainchop on T1w image @@ -459,6 +447,10 @@ t1_brain_mask: full path to a nifti file containing brain mask from T1w image, +t1_masked: + full path to a nifti file containing the T1w masked + + t1_subcortex: full path to a nifti file containing segmentation of subcortical structures from T1w image using Brainchop diff --git a/setup.cfg b/setup.cfg index 8559192f4..1f99a21b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,12 +58,9 @@ packages = find: [options.extras_require] dev = - docutils==0.15.2 - astroid<=2.15.8 sphinx memory-profiler - pytest==7.2.0 - pytest-cov==2.10.0 + pytest flake8 sphinx_gallery sphinx_rtd_theme From b955c894c5a226dbafd2ed4ce378f9c74d87f680 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 26 Sep 2025 09:54:05 -0700 Subject: [PATCH 106/116] try upgrading ipython --- setup.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 1f99a21b8..35e11e7a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,7 +67,7 @@ dev = numpydoc==1.2 sphinx-autoapi rapidfuzz - xvfbwrapper==0.2.9 + xvfbwrapper>=0.2.9 moto>=3.0.0,<5.0.0 pydata-sphinx-theme sphinx-design @@ -76,8 +76,8 @@ dev = wget fury = fury==0.12.0 - xvfbwrapper==0.2.9 - ipython>=7.13.0,<=7.20.0 + xvfbwrapper>=0.2.9 + ipython fsl = fslpy afqbrowser = @@ -85,7 +85,7 @@ afqbrowser = plot = pingouin>=0.3 seaborn>=0.11.0 - ipython>=7.13.0,<=7.20.0 + ipython aws = s3bids>=0.1.7 s3fs From eacf590333742e0726dce3ae0815b1b0b56dff08 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 26 Sep 2025 11:44:25 -0700 Subject: [PATCH 107/116] add back in pytest cov --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 35e11e7a7..2d8896613 100644 --- a/setup.cfg +++ b/setup.cfg @@ -61,6 +61,7 @@ dev = sphinx memory-profiler pytest + pytest-cov flake8 sphinx_gallery sphinx_rtd_theme From e187e79f873d4dd9e16e03e38204d84dee66be30 Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 26 Sep 2025 16:27:32 -0700 Subject: [PATCH 108/116] BF --- AFQ/tasks/segmentation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index 29bf91cb9..e3b829697 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -279,7 +279,6 @@ def export_density_maps(bundles, data_imap): source=bundles, bundles=list(seg_sft.bundle_names)) -@as_file('_desc-profiles_tractography.csv') @immlib.calc("endpoint_maps") @as_file('_desc-endpoints_tractography.nii.gz') def export_endpoint_maps(bundles, data_imap, endpoint_threshold=3): From 37f482d084598edbc5379d473937ff4b137e0e37 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 29 Sep 2025 09:22:13 -0700 Subject: [PATCH 109/116] fix n_cpu code for mssmt --- AFQ/tasks/data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/AFQ/tasks/data.py b/AFQ/tasks/data.py index 508ae3752..220463239 100644 --- a/AFQ/tasks/data.py +++ b/AFQ/tasks/data.py @@ -111,12 +111,13 @@ def configure_ncpus_nthreads(ray_n_cpus=None, numba_n_threads=None): ray_n_cpus : int, optional The number of CPUs to use for parallel processing with Ray. If None, uses the number of available CPUs minus one. - Tractography and Recognition use Ray. + Tractography, Recognition, and MSMT use Ray. Default: None numba_n_threads : int, optional The number of threads to use for Numba. - If None, uses the number of available CPUs minus one. - MSMT and ASYM fits use Numba. + If None, uses the number of available CPUs minus one, + but with a maximum of 16. + ASYM fit uses Numba. Default: None """ if ray_n_cpus is None: @@ -372,7 +373,7 @@ def msdki_msk(msdki_tf): @as_img def msmt_params(brain_mask, gtab, data, dwi_affine, t1w_pve, - n_threads, + n_cpus, msmt_sh_order=8, msmt_fa_thr=0.7): """ @@ -433,7 +434,7 @@ def msmt_params(brain_mask, gtab, data, mcsd_model = MultiShellDeconvModel(gtab, response_mcsd) logger.info("Fitting Multi-Shell CSD model...") mcsd_fit = mcsd_model.fit( - data, mask, n_threads=n_threads) + data, mask, n_cpus=n_cpus) meta = dict( SphericalHarmonicDegree=msmt_sh_order, From 8e94f73e5ed3c471f01ef6093028abfe548437ca Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 2 Oct 2025 13:25:17 -0700 Subject: [PATCH 110/116] display full brainchop errors --- AFQ/nn/brainchop.py | 53 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/AFQ/nn/brainchop.py b/AFQ/nn/brainchop.py index 6cce37bee..da25292b5 100644 --- a/AFQ/nn/brainchop.py +++ b/AFQ/nn/brainchop.py @@ -19,6 +19,37 @@ logger = logging.getLogger('AFQ') +def _brainchop_reslice(tmp_t1_file, tmp_out_file, output_dtype, full_input): + cmd = [ + "niimath", "-", "-reslice_nn", tmp_t1_file, + "-gz", "1", tmp_out_file, "-odt", output_dtype] + + subprocess.run(cmd, input=full_input, check=True) + + +def _run_brainchop_command(func, args): + """ + Run a Brainchop command line interface + with the provided arguments, but with error handling. + """ + try: + return func(*args) + except subprocess.CalledProcessError as e: + logger.error("Command failed: %s", e.cmd) + logger.error("Return code: %s", e.returncode) + if e.stdout: + if isinstance(e.stdout, bytes): + logger.error("STDOUT:\n%s", e.stdout.decode()) + else: + logger.error("STDOUT:\n%s", e.stdout) + if e.stderr: + if isinstance(e.stderr, bytes): + logger.error("STDERR:\n%s", e.stderr.decode()) + else: + logger.error("STDERR:\n%s", e.stderr) + raise + + def run_brainchop(t1_img, model): """ Run the Brainchop command line interface with the provided arguments. @@ -34,14 +65,14 @@ def run_brainchop(t1_img, model): t1_data = np.clip(t1_data, 0, t1_data.max()) nib.save(nib.Nifti1Image(t1_data, t1_img.affine), tmp_t1_file) - volume, header = conform(tmp_t1_file) + volume, header = _run_brainchop_command(conform, [tmp_t1_file]) image = Tensor(volume.transpose((2, 1, 0)).astype(np.float32)).rearrange( "... -> 1 1 ..." ) try: - output_channels = model(image) + output_channels = _run_brainchop_command(model, [image]) except Exception as e: if "clang" in str(e).lower(): with Context(PYTHON=1): @@ -56,14 +87,20 @@ def run_brainchop(t1_img, model): .astype(np.uint8) ) - labels, new_header = bwlabel(header, output) + labels, new_header = _run_brainchop_command( + bwlabel, + [header, output]) full_input = new_header + labels.tobytes() - cmd = [ - "niimath", "-", "-reslice_nn", tmp_t1_file, - "-gz", "1", tmp_out_file, "-odt", output_dtype] - - subprocess.run(cmd, input=full_input, check=True) + _run_brainchop_command( + _brainchop_reslice, + [ + tmp_t1_file, + tmp_out_file, + output_dtype, + full_input + ] + ) output_img = nib.load(tmp_out_file) # This line below forces the data into memory to avoid issues with From cbb05bdccf62546db927e94fcd28fbc1f7e1907a Mon Sep 17 00:00:00 2001 From: 36000 Date: Thu, 2 Oct 2025 16:13:24 -0700 Subject: [PATCH 111/116] try t1w mapping --- AFQ/tasks/mapping.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/AFQ/tasks/mapping.py b/AFQ/tasks/mapping.py index 6d42cd590..823e8c5eb 100644 --- a/AFQ/tasks/mapping.py +++ b/AFQ/tasks/mapping.py @@ -15,6 +15,7 @@ from dipy.io.streamline import load_tractogram from dipy.io.stateful_tractogram import Space +from dipy.align import resample logger = logging.getLogger('AFQ') @@ -189,7 +190,7 @@ def sls_mapping(base_fname, dwi_data_file, reg_subject, data_imap, @immlib.calc("reg_subject") def get_reg_subject(data_imap, - reg_subject_spec="power_map"): + reg_subject_spec="t1w"): """ Nifti1Image which represents this subject when registering the subject to the template @@ -199,11 +200,11 @@ def get_reg_subject(data_imap, reg_subject_spec : str, instance of `AFQ.definitions.ImageDefinition`, optional # noqa The source image data to be registered. Can either be a Nifti1Image, an ImageFile, or str. - if "b0", "dti_fa_subject", "subject_sls", or "power_map," + if "b0", "dti_fa_subject", "subject_sls", "t1w", or "power_map," image data will be loaded automatically. If "subject_sls" is used, slr registration will be used and reg_template should be "hcp_atlas". - Default: "power_map" + Default: "t1w" """ if not isinstance(reg_subject_spec, str)\ and not isinstance(reg_subject_spec, nib.Nifti1Image): @@ -216,6 +217,7 @@ def get_reg_subject(data_imap, "power_map": "csd_pmap", "dti_fa_subject": "dti_fa", "subject_sls": "b0", + "t1w": "t1_masked" } bm = nib.load(data_imap["brain_mask"]) @@ -223,6 +225,11 @@ def get_reg_subject(data_imap, reg_subject_spec = data_imap[filename_dict[reg_subject_spec]] if isinstance(reg_subject_spec, str): img = nib.load(reg_subject_spec) + + if not np.allclose(img.affine, bm.affine) or not np.allclose( + img.get_fdata().shape, bm.get_fdata().shape): + img = resample(img, bm) + bm = bm.get_fdata().astype(bool) masked_data = img.get_fdata() masked_data[~bm] = 0 From 176fefb81d799f2d62271032b32372e181934d7a Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 13 Oct 2025 15:00:59 -0700 Subject: [PATCH 112/116] BF for GPU Streamlines and better error for Custom ROI from DWI --- AFQ/definitions/image.py | 12 ++++++++++++ AFQ/tractography/gputractography.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/AFQ/definitions/image.py b/AFQ/definitions/image.py index c2fb0c5c7..12b275e59 100644 --- a/AFQ/definitions/image.py +++ b/AFQ/definitions/image.py @@ -341,6 +341,18 @@ def _image_getter_helper(mapping, if self.only_wmgmi: wmgmi = nib.load( data_imap["wm_gm_interface"]).get_fdata() + if not np.allclose(wmgmi.shape, image_data.shape): + logger.error("WM/GM Interface shape: %s", wmgmi.shape) + logger.error("ROI image shape: %s", image_data.shape) + raise ValueError(( + "wm_gm_interface and ROI image do not have the " + "same shape, cannot apply wm_gm_interface." + "If ROI image shape is different from DWI shape, " + "consider if you need to map your ROIs to DWI space. " + "If only resampling is required, " + "set resample_subject_to " + "to True in your BundleDict instantiation.")) + image_data = np.logical_and( image_data, wmgmi) if np.sum(image_data) == 0: diff --git a/AFQ/tractography/gputractography.py b/AFQ/tractography/gputractography.py index 166ac2c04..273150028 100644 --- a/AFQ/tractography/gputractography.py +++ b/AFQ/tractography/gputractography.py @@ -87,6 +87,9 @@ def gpu_track(data, gtab, seed_path, stop_path, stop_threshold = 0.3 stop_img = stop_path[0] # Grab WM + if isinstance(stop_img, str): + stop_img = nib.load(stop_img) + stop_img = resample( stop_img.get_fdata(), seed_img.get_fdata(), From f6fbd4715b689b3f3af316d8dcbb754f25557d32 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 13 Oct 2025 15:54:50 -0700 Subject: [PATCH 113/116] correctly handle plot exception in export_all considering immlib --- AFQ/api/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AFQ/api/utils.py b/AFQ/api/utils.py index 7025133fb..54e0a9b5c 100644 --- a/AFQ/api/utils.py +++ b/AFQ/api/utils.py @@ -159,12 +159,12 @@ def export_all_helper(api_afq_object, xforms, indiv, viz): if viz: try: + import pingouin + import seaborn + import IPython + except (ImportError, ModuleNotFoundError): + api_afq_object.logger.warning(viz_import_msg_error("plot")) + else: api_afq_object.export("tract_profile_plots") - except ImportError as e: - plot_err_message = viz_import_msg_error("plot") - if str(e) != plot_err_message: - raise - else: - api_afq_object.logger.warning(plot_err_message) api_afq_object.export("all_bundles_figure") api_afq_object.export("indiv_bundles_figures") From 53687988686fb2dccb5e73eeea5e42b1f1b5f559 Mon Sep 17 00:00:00 2001 From: 36000 Date: Tue, 14 Oct 2025 12:02:49 -0700 Subject: [PATCH 114/116] correctly adjust for t1 space in new visualizations --- AFQ/api/group.py | 5 ++++- AFQ/api/participant.py | 4 +++- AFQ/tasks/viz.py | 14 ++++++++++---- AFQ/viz/fury_backend.py | 17 ++++++++++++++--- AFQ/viz/plotly_backend.py | 15 ++++++++++++--- AFQ/viz/utils.py | 32 ++++++++++++++++++++++++++------ 6 files changed, 69 insertions(+), 18 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 031a4fa26..de653604b 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -737,6 +737,7 @@ def group_montage(self, bundle_name, size, view, direc, slice_pos=None): best_scalar = self.export("best_scalar", collapse=False)[ self.valid_sub_list[0]][self.valid_ses_list[0]] + t1_dict = self.export("t1_masked", collapse=False) viz_backend_dict = self.export("viz_backend", collapse=False) b0_backend_dict = self.export("b0", collapse=False) dwi_affine_dict = self.export("dwi_affine", collapse=False) @@ -750,6 +751,7 @@ def group_montage(self, bundle_name, size, view, direc, slice_pos=None): this_ses = self.valid_ses_list[ii] viz_backend = viz_backend_dict[this_sub][this_ses] b0 = b0_backend_dict[this_sub][this_ses] + t1 = nib.load(t1_dict[this_sub][this_ses]) dwi_affine = dwi_affine_dict[this_sub][this_ses] bundles = bundles_dict[this_sub][this_ses] best_scalar = best_scalar_dict[this_sub][this_ses] @@ -774,7 +776,7 @@ def group_montage(self, bundle_name, size, view, direc, slice_pos=None): slice_kwargs["z_pos"] = slice_pos figure = viz_backend.visualize_volume( - best_scalar, + t1, opacity=1.0, flip_axes=flip_axes, interact=False, @@ -785,6 +787,7 @@ def group_montage(self, bundle_name, size, view, direc, slice_pos=None): figure = viz_backend.visualize_bundles( bundles, + affine=t1.affine, shade_by_volume=best_scalar, flip_axes=flip_axes, bundle=bundle_name, diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index a3024162c..1fd8f3e6d 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -266,6 +266,7 @@ def participant_montage(self, images_per_row=2): self.logger.info("Generating Montage...") viz_backend = self.export("viz_backend") best_scalar = self.export(self.export("best_scalar")) + t1 = nib.load(self.export("t1_masked")) size = (images_per_row, math.ceil(len(bundle_dict) / images_per_row)) for ii, bundle_name in enumerate(tqdm(bundle_dict)): flip_axes = [False, False, False] @@ -273,12 +274,13 @@ def participant_montage(self, images_per_row=2): flip_axes[i] = (self.export("dwi_affine")[i, i] < 0) figure = viz_backend.visualize_volume( - best_scalar, + t1, flip_axes=flip_axes, interact=False, inline=False) figure = viz_backend.visualize_bundles( self.export("bundles"), + affine=t1.affine, shade_by_volume=best_scalar, color_by_direction=True, flip_axes=flip_axes, diff --git a/AFQ/tasks/viz.py b/AFQ/tasks/viz.py index 1a369dea0..445638837 100644 --- a/AFQ/tasks/viz.py +++ b/AFQ/tasks/viz.py @@ -23,11 +23,11 @@ def _viz_prepare_vol(vol, xform, mapping, scalar_dict): if vol in scalar_dict.keys(): vol = scalar_dict[vol] - if isinstance(vol, str): - vol = nib.load(vol) - vol = vol.get_fdata() + if isinstance(vol, str): - vol = nib.load(vol).get_fdata() + vol = nib.load(vol) + + vol = vol.get_fdata() if xform: vol = mapping.transform_inverse(vol) vol[np.isnan(vol)] = 0 @@ -76,6 +76,7 @@ def viz_bundles(base_fname, scalar_dict = segmentation_imap["scalar_dict"] profiles_file = segmentation_imap["profiles"] volume = data_imap["t1_masked"] + t1_affine = nib.load(data_imap["t1_masked"]).affine shade_by_volume = data_imap[best_scalar] volume = _viz_prepare_vol(volume, False, mapping, scalar_dict) shade_by_volume = _viz_prepare_vol( @@ -102,6 +103,7 @@ def viz_bundles(base_fname, figure = viz_backend.visualize_bundles( segmentation_imap["bundles"], + affine=t1_affine, shade_by_volume=shade_by_volume, sbv_lims=sbv_lims_bundles, include_profiles=(pd.read_csv(profiles_file), best_scalar), @@ -170,6 +172,7 @@ def viz_indivBundle(base_fname, bundle_dict = data_imap["bundle_dict"] scalar_dict = segmentation_imap["scalar_dict"] volume = data_imap["t1_masked"] + t1_affine = nib.load(data_imap["t1_masked"]).affine shade_by_volume = data_imap[best_scalar] profiles = pd.read_csv(segmentation_imap["profiles"]) @@ -213,6 +216,7 @@ def viz_indivBundle(base_fname, if len(bundles.get_bundle(bundle_name)) > 0: figure = viz_backend.visualize_bundles( bundles, + affine=t1_affine, shade_by_volume=shade_by_volume, sbv_lims=sbv_lims_indiv, bundle=bundle_name, @@ -292,6 +296,7 @@ def viz_indivBundle(base_fname, inline=False) core_fig = viz_backend.visualize_bundles( segmentation_imap["bundles"], + affine=t1_affine, shade_by_volume=shade_by_volume, sbv_lims=sbv_lims_indiv, bundle=bundle_name, @@ -306,6 +311,7 @@ def viz_indivBundle(base_fname, segmentation_imap["bundles"], bundle_name, best_scalar, + affine=t1_affine, flip_axes=flip_axes, figure=core_fig, include_profile=True) diff --git a/AFQ/viz/fury_backend.py b/AFQ/viz/fury_backend.py index 42d9d7e64..8f2ad3984 100644 --- a/AFQ/viz/fury_backend.py +++ b/AFQ/viz/fury_backend.py @@ -36,7 +36,9 @@ def _inline_interact(scene, inline, interact): return scene -def visualize_bundles(seg_sft, n_points=None, +def visualize_bundles(seg_sft, + affine=None, + n_points=None, bundle=None, colors=None, color_by_direction=False, opacity=1.0, @@ -54,6 +56,10 @@ def visualize_bundles(seg_sft, n_points=None, A SegmentedSFT containing streamline information or a path to a segmented trk file. + affine : ndarray (4, 4), optional + Affine of the image to register streamlines to. + Default: None + n_points : int or None n_points to resample streamlines to before plotting. If None, no resampling is done. @@ -102,7 +108,7 @@ def visualize_bundles(seg_sft, n_points=None, figure.SetBackground(background[0], background[1], background[2]) for (sls, color, name, dimensions) in vut.tract_generator( - seg_sft, bundle, colors, n_points): + seg_sft, bundle, colors, n_points, affine): sls = list(sls) if name == "all_bundles": color = line_colors(sls) @@ -495,6 +501,7 @@ def _draw_core(sls, n_points, figure, bundle_name, indiv_profile, def single_bundle_viz(indiv_profile, seg_sft, bundle, scalar_name, + affine=None, flip_axes=[False, False, False], labelled_nodes=[0, -1], figure=None, @@ -518,6 +525,10 @@ def single_bundle_viz(indiv_profile, seg_sft, scalar_name : str The name of the scalar being used. + affine : ndarray (4, 4), optional + Affine of the image to register streamlines to. + Default: None + flip_axes : ndarray Which axes to flip, to orient the image as RAS, which is how we visualize. @@ -545,7 +556,7 @@ def single_bundle_viz(indiv_profile, seg_sft, n_points = len(indiv_profile) sls, _, bundle_name, dimensions = next(vut.tract_generator( - seg_sft, bundle, None, n_points)) + seg_sft, bundle, None, n_points, affine)) _draw_core( sls, n_points, figure, bundle_name, indiv_profile, diff --git a/AFQ/viz/plotly_backend.py b/AFQ/viz/plotly_backend.py index 9a6c73c13..ff81450fe 100644 --- a/AFQ/viz/plotly_backend.py +++ b/AFQ/viz/plotly_backend.py @@ -302,7 +302,7 @@ def _plot_profiles(profiles, bundle_name, color, fig, scalar): tickfont=dict(color='white')))) -def visualize_bundles(seg_sft, n_points=None, +def visualize_bundles(seg_sft, affine=None, n_points=None, bundle=None, colors=None, shade_by_volume=None, color_by_streamline=None, n_sls_viz=3600, sbv_lims=[None, None], include_profiles=(None, None), @@ -318,6 +318,10 @@ def visualize_bundles(seg_sft, n_points=None, A SegmentedSFT containing streamline information or a path to a segmented trk file. + affine : ndarray (4, 4), optional + Affine of the image to register streamlines to. + Default: None + n_points : int or None n_points to resample streamlines to before plotting. If None, no resampling is done. @@ -413,7 +417,7 @@ def visualize_bundles(seg_sft, n_points=None, set_layout(figure) for (sls, color, name, dimensions) in vut.tract_generator( - seg_sft, bundle, colors, n_points, + seg_sft, bundle, colors, n_points, affine, n_sls_viz=n_sls_viz): if isinstance(color_by_streamline, dict): if name in color_by_streamline: @@ -808,6 +812,7 @@ def _draw_core(sls, n_points, figure, bundle_name, indiv_profile, def single_bundle_viz(indiv_profile, seg_sft, bundle, scalar_name, + affine=None, flip_axes=[False, False, False], labelled_nodes=[0, -1], figure=None, @@ -831,6 +836,10 @@ def single_bundle_viz(indiv_profile, seg_sft, scalar_name : str The name of the scalar being used. + affine : ndarray (4, 4), optional + Affine of the image to register streamlines to. + Default: None + flip_axes : ndarray Which axes to flip, to orient the image as RAS, which is how we visualize. @@ -866,7 +875,7 @@ def single_bundle_viz(indiv_profile, seg_sft, n_points = len(indiv_profile) sls, _, bundle_name, dimensions = next(vut.tract_generator( - seg_sft, bundle, None, n_points)) + seg_sft, bundle, None, n_points, affine)) line_color = _draw_core( sls, n_points, figure, bundle_name, indiv_profile, diff --git a/AFQ/viz/utils.py b/AFQ/viz/utils.py index ad1d379ae..a9e9552f4 100644 --- a/AFQ/viz/utils.py +++ b/AFQ/viz/utils.py @@ -8,10 +8,11 @@ import matplotlib.pyplot as plt import matplotlib.patches as mpatches -import matplotlib.transforms as mtransforms import nibabel as nib import dipy.tracking.streamlinespeed as dps +from dipy.tracking.streamline import transform_streamlines +from dipy.io.stateful_tractogram import StatefulTractogram, Space from dipy.align import resample import AFQ.utils.volume as auv @@ -381,8 +382,17 @@ def viz_import_msg_error(module): return msg +def _sls_to_t1(sls, ref_sft, t1_affine): + sft = StatefulTractogram.from_sft(sls, ref_sft) + sft.to_rasmm() + sls = transform_streamlines( + sft.streamlines, + np.linalg.inv(t1_affine)) + return sls + + def tract_generator(trk_file, bundle, colors, n_points, - n_sls_viz=65536, n_sls_min=256): + t1_affine, n_sls_viz=65536, n_sls_min=256): """ Generates bundles of streamlines from the tractogram. Only generates from relevant bundle if bundle is set. @@ -407,11 +417,15 @@ def tract_generator(trk_file, bundle, colors, n_points, n_points to resample streamlines to before plotting. If None, no resampling is done. + t1_affine : ndarray (4, 4) + Affine of the T1-weighted image to register streamlines to. + n_sls_viz : int Number of streamlines to randomly select if plotting all bundles. Selections will be proportional to the original number of streamlines per bundle. Default: 3600 + n_sls_min : int Minimun number of streamlines to display per bundle. Default: 75 @@ -429,7 +443,10 @@ def tract_generator(trk_file, bundle, colors, n_points, if colors is None: colors = gen_color_dict(seg_sft.bundle_names) - seg_sft.sft.to_vox() + if t1_affine is None: + t1_affine = np.eye(4) + + seg_sft.sft.to_rasmm() streamlines = seg_sft.sft.streamlines viz_logger.info("Generating colorful lines from tractography...") @@ -445,7 +462,8 @@ def tract_generator(trk_file, bundle, colors, n_points, streamlines = streamlines[idx] if n_points is not None: streamlines = dps.set_number_of_points(streamlines, n_points) - yield streamlines, colors[0], "all_bundles", seg_sft.sft.dimensions + yield _sls_to_t1(streamlines, seg_sft.sft, t1_affine), \ + colors[0], "all_bundles", seg_sft.sft.dimensions else: if bundle is None: # No selection: visualize all of them: @@ -465,7 +483,8 @@ def tract_generator(trk_file, bundle, colors, n_points, color = colors[bundle_name] else: color = colors[0] - yield these_sls, color, bundle_name, seg_sft.sft.dimensions + yield _sls_to_t1(these_sls, seg_sft.sft, t1_affine), \ + color, bundle_name, seg_sft.sft.dimensions else: these_sls = seg_sft.get_bundle(bundle).streamlines if len(these_sls) > n_sls_viz: @@ -478,7 +497,8 @@ def tract_generator(trk_file, bundle, colors, n_points, color = colors[bundle] else: color = colors[0] - yield these_sls, color, bundle, seg_sft.sft.dimensions + yield _sls_to_t1(these_sls, seg_sft.sft, t1_affine), \ + color, bundle, seg_sft.sft.dimensions def bbox(img): From b3bac0a1208e1ddc24de41586172b164a43bc0ac Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 17 Oct 2025 12:08:06 -0700 Subject: [PATCH 115/116] one more fix for the viz --- AFQ/tasks/viz.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/AFQ/tasks/viz.py b/AFQ/tasks/viz.py index 445638837..233f1bf1e 100644 --- a/AFQ/tasks/viz.py +++ b/AFQ/tasks/viz.py @@ -10,7 +10,6 @@ from dipy.align import resample from AFQ.tasks.utils import get_fname, with_name, str_to_desc -import AFQ.utils.volume as auv from AFQ.viz.utils import Viz import AFQ.utils.streamlines as aus from AFQ.utils.path import write_json, drop_extension @@ -20,13 +19,15 @@ logger = logging.getLogger('AFQ') -def _viz_prepare_vol(vol, xform, mapping, scalar_dict): +def _viz_prepare_vol(vol, xform, mapping, scalar_dict, ref): if vol in scalar_dict.keys(): vol = scalar_dict[vol] if isinstance(vol, str): vol = nib.load(vol) + vol = resample(vol, ref) + vol = vol.get_fdata() if xform: vol = mapping.transform_inverse(vol) @@ -75,12 +76,12 @@ def viz_bundles(base_fname, mapping = mapping_imap["mapping"] scalar_dict = segmentation_imap["scalar_dict"] profiles_file = segmentation_imap["profiles"] - volume = data_imap["t1_masked"] + volume = nib.load(data_imap["t1_masked"]) t1_affine = nib.load(data_imap["t1_masked"]).affine shade_by_volume = data_imap[best_scalar] - volume = _viz_prepare_vol(volume, False, mapping, scalar_dict) shade_by_volume = _viz_prepare_vol( - shade_by_volume, False, mapping, scalar_dict) + shade_by_volume, False, mapping, scalar_dict, volume) + volume = _viz_prepare_vol(volume, False, mapping, scalar_dict, volume) flip_axes = [False, False, False] for i in range(3): @@ -171,16 +172,16 @@ def viz_indivBundle(base_fname, mapping = mapping_imap["mapping"] bundle_dict = data_imap["bundle_dict"] scalar_dict = segmentation_imap["scalar_dict"] - volume = data_imap["t1_masked"] + volume = nib.load(data_imap["t1_masked"]) t1_affine = nib.load(data_imap["t1_masked"]).affine shade_by_volume = data_imap[best_scalar] profiles = pd.read_csv(segmentation_imap["profiles"]) start_time = time() - volume = _viz_prepare_vol( - volume, False, mapping, scalar_dict) shade_by_volume = _viz_prepare_vol( - shade_by_volume, False, mapping, scalar_dict) + shade_by_volume, False, mapping, scalar_dict, volume) + volume = _viz_prepare_vol( + volume, False, mapping, scalar_dict, volume) flip_axes = [False, False, False] for i in range(3): From b2cebb2e1731e303db612e5065a9f344090e615a Mon Sep 17 00:00:00 2001 From: 36000 Date: Fri, 17 Oct 2025 13:20:39 -0700 Subject: [PATCH 116/116] fix ROI viz as well --- AFQ/tasks/viz.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/AFQ/tasks/viz.py b/AFQ/tasks/viz.py index 233f1bf1e..1e4acc305 100644 --- a/AFQ/tasks/viz.py +++ b/AFQ/tasks/viz.py @@ -172,16 +172,16 @@ def viz_indivBundle(base_fname, mapping = mapping_imap["mapping"] bundle_dict = data_imap["bundle_dict"] scalar_dict = segmentation_imap["scalar_dict"] - volume = nib.load(data_imap["t1_masked"]) + volume_img = nib.load(data_imap["t1_masked"]) t1_affine = nib.load(data_imap["t1_masked"]).affine shade_by_volume = data_imap[best_scalar] profiles = pd.read_csv(segmentation_imap["profiles"]) start_time = time() - shade_by_volume = _viz_prepare_vol( - shade_by_volume, False, mapping, scalar_dict, volume) volume = _viz_prepare_vol( - volume, False, mapping, scalar_dict, volume) + volume_img, False, mapping, scalar_dict, volume_img) + shade_by_volume = _viz_prepare_vol( + shade_by_volume, False, mapping, scalar_dict, volume_img) flip_axes = [False, False, False] for i in range(3): @@ -235,8 +235,10 @@ def viz_indivBundle(base_fname, name = roi_fname.split("desc-")[1].split("_")[0] if "probseg" in roi_fname: name = f"{name}Probseg" + roi_img = nib.load(roi_fname) + roi_img = resample(roi_img, volume_img) figure = viz_backend.visualize_roi( - roi_fname, + roi_img, name=name, flip_axes=flip_axes, inline=False,

MwQQXZ&NWnKRvJ{QCIK)jj25 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 Hod^F5f{^l1 literal 0 HcmV?d00001 diff --git a/docs/source/_static/endpoint_maps_threshold_4.png b/docs/source/_static/endpoint_maps_threshold_4.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d3cbe031156bfa03b0c89f1f1c7bb724b42dba GIT binary patch literal 115835 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/source/reference/kwargs.rst b/docs/source/reference/kwargs.rst index 978658348..23c4bc445 100644 --- a/docs/source/reference/kwargs.rst +++ b/docs/source/reference/kwargs.rst @@ -29,9 +29,36 @@ filter_b: bool b0_threshold: int The value of b under which it is considered to be b0. Default: 50. +dam_low_signal_thresh: float + The threshold below which a voxel is considered to have low signal. Default: 50 + +dki_wm_ll: float + Lower limit of FA in white matter to calculate probability mask. Default: 0.1 + +dki_gm_ul: float + Upper limit of FA in gray matter to calculate probability mask. Default: 0.3 + +pve_nclass: int + The number of tissue classes to segment Default: 3 + robust_tensor_fitting: bool Whether to use robust_tensor_fitting when doing dti. Only applies to dti. Default: False +msmt_sh_order: int + Spherical harmonic order to use for the MSMT CSD fit. Default: 8 + +msmt_fa_thr: float + The threshold on the FA used to calculate the multi shell auto response. Can be useful to reduce for baby subjects. Default: 0.7 + +ray_n_cpus: int + The number of CPUs to use for the MSMT CSD fit. Default: None + +numba_n_threads: int + The number of threads to use for the MSMT CSD fit. Default: None, which will use the default number of threads for the system. + +numba_threading_layer: str + The threading layer to use for Numba. Default: "workqueue". + csd_response: tuple or None The response function to be used by CSD, as a tuple with two elements. The first is the eigen-values as an (3,) ndarray and the second is the signal value for the response function without diffusion-weighting (i.e. S0). If not provided, auto_response will be used to calculate these values. Default: None @@ -103,6 +130,9 @@ SEGMENTATION segmentation_params: dict The parameters for segmentation. Default: use the default behavior of the seg.Segmentation object. +endpoint_threshold: float + The threshold for the endpoint maps. If None, no endpoint maps are exported as distance to endpoints maps, which the user can then threshold as needed. Default: 3 + profile_weights: str How to weight each streamline (1D) or each node (2D) when calculating the tract-profiles. If callable, this is a function that calculates weights. If None, no weighting will be applied. If "gauss", gaussian weights will be used. If "median", the median of values at each node will be used instead of a mean or weighted mean. Default: "gauss" diff --git a/docs/source/reference/methods.rst b/docs/source/reference/methods.rst index f0775a0df..1a72c1a4f 100644 --- a/docs/source/reference/methods.rst +++ b/docs/source/reference/methods.rst @@ -59,6 +59,38 @@ masked_b0: full path to a nifti file containing the mean b0 after applying the brain mask +dam_params: + direction-averaged signal map (DAM) [1] slope and intercept + + +dam_csf: + CSF probability map from DAM intercept + + +dam_pseudot1: + Pseudo T1 map from DAM fit + + +dki_csf: + CSF probability map from DKI MD inspired by [1] + + +dki_wm: + WM probability map from DKI FA + + +dki_gm: + GM probability map from DKI FA + + +t1w_pve: + Tissue classification using the Markov Random Fields modeling approach on the T1w image [1, 2] + + +wm_gm_interface: + + + dti_tf: DTI TensorFit object @@ -99,10 +131,38 @@ msdki_msk: full path to a nifti file containing the MSDKI mean signal kurtosis +msmtcsd_params: + full path to a nifti file containing parameters for the MSMT CSD fit + + +msmt_apm: + full path to a nifti file containing the anisotropic power map + + +msmt_aodf_params: + full path to a nifti file containing MSMT CSD ODFs filtered by unified filtering [1] + + +msmt_aodf_asi: + full path to a nifti file containing the MSMT CSD Asymmetric Index (ASI) [1] + + +msmt_aodf_opm: + full path to a nifti file containing the MSMT CSD odd-power map [1] + + +msmt_aodf_nufid: + full path to a nifti file containing the MSMT CSD Number of fiber directions (nufid) map [1] + + csd_params: full path to a nifti file containing parameters for the CSD fit +csd_aodf_params: + full path to a nifti file containing SSST CSD ODFs filtered by unified filtering [1] + + csd_pmap: full path to a nifti file containing the anisotropic power map @@ -355,6 +415,18 @@ dki_kfa: full path to a nifti file containing the DKI kurtosis FA file +dki_cl: + full path to a nifti file containing the DKI linearity file + + +dki_cp: + full path to a nifti file containing the DKI planarity file + + +dki_cs: + full path to a nifti file containing the DKI sphericity file + + dki_ga: full path to a nifti file containing the DKI geodesic anisotropy @@ -431,6 +503,10 @@ density_maps: full path to 4d nifti file containing streamline counts per voxel per bundle, where the 4th dimension encodes the bundle +endpoint_maps: + full path to a NIfTI file containing endpoint maps for each bundle + + profiles: full path to a CSV file containing tract profiles diff --git a/examples/tutorial_examples/viz_008_endpoints.py b/examples/tutorial_examples/viz_008_endpoints.py new file mode 100644 index 000000000..a009e47a7 --- /dev/null +++ b/examples/tutorial_examples/viz_008_endpoints.py @@ -0,0 +1,189 @@ +""" +=================== +PyAFQ Endpoint Maps +=================== +Here we extract endpoint maps for pyAFQ run under different configurations +for an HBN subject. +""" + +#################################################### +# Import libraries, load the defautl tract templates +import matplotlib +matplotlib.use('Agg') # Use Agg backend for headless plotting +import matplotlib.pyplot as plt + +import nibabel as nib +import numpy as np + +from AFQ.api.group import GroupAFQ +import AFQ.definitions.image as afm +from dipy.data import get_sphere + +import os.path as op +import os +import AFQ.data.fetch as afd + +from AFQ.viz.utils import COLOR_DICT +from dipy.align import resample + +############################################################ +# Use an example subject from the Human Brain Network (HBN). + +subject_id = "NDARKP893TWU" # Example subject ID +ses_id = "HBNsiteRU" # Example session ID +_, study_dir = afd.fetch_hbn_preproc([subject_id]) + +brain_mask_definition = afm.ImageFile( + suffix="mask", + filters={"scope": "qsiprep", "desc": "brain"}) + +endpoint_maps = { # Endpoint maps by threshold in mm + "2": {}, + "3": {}, + "4": {} +} + +bundle_names = [ # pyAFQ defaults + "Left Anterior Thalamic", "Right Anterior Thalamic", + "Left Cingulum Cingulate", "Right Cingulum Cingulate", + "Left Corticospinal", "Right Corticospinal", + "Left Inferior Fronto-occipital", "Right Inferior Fronto-occipital", + "Left Inferior Longitudinal", "Right Inferior Longitudinal", + "Left Superior Longitudinal", "Right Superior Longitudinal", + "Left Arcuate", "Right Arcuate", + "Left Uncinate", "Right Uncinate", + "Left Posterior Arcuate", "Right Posterior Arcuate", + "Left Vertical Occipital", "Right Vertical Occipital", + "Callosum Anterior Frontal", "Callosum Motor", + "Callosum Occipital", "Callosum Orbital", + "Callosum Posterior Parietal", "Callosum Superior Frontal", + "Callosum Superior Parietal", "Callosum Temporal" +] + +########################################################################### +# Compare endpoint maps for single-shell and multi-shell CSD +# For both local (probabilistic) and PFT (particle filtering) tractography. + +for odf_model in ["csd", "msmtcsd"]: + for tracker in ["local", "pft"]: + output_dir = op.join( + study_dir, "derivatives", + f"afq_{odf_model}_{tracker}") + + myafq = GroupAFQ( + op.join(afd.afq_home, "HBN"), + participant_labels=[subject_id], + preproc_pipeline="qsiprep", + tracking_params={ + "tracker": tracker, + "odf_model": odf_model, + "sphere": get_sphere(name="repulsion724"), + "seed_mask": afm.ScalarImage("wm_gm_interface"), + "seed_threshold": 0.5, + "stop_mask": afm.ThreeTImage(), + "stop_threshold": "ACT", + "n_seeds": 2000000, + "random_seeds": True}, + output_dir=output_dir, + endpoint_threshold=None, + brain_mask_definition=brain_mask_definition) + + endpoints_maps = myafq.export("endpoint_maps") + + # Copy outputs of first runs for later use + # Up to but not including tractography + if odf_model == "csd" and tracker == "local": + other_output_paths = [ + op.join(study_dir, ( + "derivatives/afq_csd_pft/" + f"sub-{subject_id}/ses-{ses_id}/dwi")), + op.join(study_dir, ( + "derivatives/afq_msmtcsd_local/" + f"sub-{subject_id}/ses-{ses_id}/dwi")), + op.join(study_dir, ( + "derivatives/afq_msmtcsd_pft/" + f"sub-{subject_id}/ses-{ses_id}/dwi")), + ] + for other_output_path in other_output_paths: + os.makedirs(other_output_path, exist_ok=True) + myafq.cmd_outputs( + "cp", suffix=other_output_path, up_to="track") + + endpoint_data = nib.load( + endpoints_maps[subject_id]).get_fdata() + endpoint_maps["2"][f"{odf_model}_{tracker}"] = \ + np.logical_and(endpoint_data < 2.0, endpoint_data != 0.0) + endpoint_maps["3"][f"{odf_model}_{tracker}"] = \ + np.logical_and(endpoint_data < 3.0, endpoint_data != 0.0) + endpoint_maps["4"][f"{odf_model}_{tracker}"] = \ + np.logical_and(endpoint_data < 4.0, endpoint_data != 0.0) + +t1 = nib.load(myafq.export("t1_file")[subject_id]) +b0 = nib.load(myafq.export("b0")[subject_id]) + +t1_dwi_space = resample( + t1.get_fdata(), + b0.get_fdata(), + moving_affine=t1.affine, + static_affine=b0.affine).get_fdata() + +# Find the best z-slice for visualization +sum_of_all_maps = np.zeros(endpoint_maps["2"]["csd_local"].shape[:3]) +for threshold, maps in endpoint_maps.items(): + for map_name, _map in maps.items(): + sum_of_all_maps += np.sum(_map, axis=-1) +best_z = np.argmax(np.sum(sum_of_all_maps, axis=(0, 1))) + +t1_slice = t1_dwi_space[..., best_z] +t1_slice = (t1_slice - t1_slice.min()) / (t1_slice.max() - t1_slice.min()) + +for threshold, maps in endpoint_maps.items(): + fig, axes = plt.subplots(2, 2, figsize=(10, 10)) + fig.suptitle(f"Endpoint Maps for Threshold < {threshold} mm") + for ii, (map_name, _map) in enumerate(maps.items()): + image = np.zeros(_map.shape[:2] + (3,)) + image_counts = np.zeros(_map.shape[:2]) + for z in range(_map.shape[3]): + # Assign each z-slice a unique color from our colormap + rgb = COLOR_DICT[bundle_names[z]] + image += _map[..., best_z, z][..., np.newaxis] * rgb + image_counts += _map[..., best_z, z] + + # Interesting Metrics + endpoint_voxel_count = np.sum(image_counts) + median_vc = np.median(np.sum(_map, axis=(0, 1, 2))) + + # Normalize the image by the number of bundles + image_counts[image_counts == 0] = 1 + image /= image_counts[..., np.newaxis] + + # Blend the T1 slice with the endpoint map + image = (np.stack([t1_slice] * 3, axis=-1) + image) / 2.0 + axes[ii // 2, ii % 2].imshow(np.rot90(image), interpolation='none') + axes[ii // 2, ii % 2].axis("off") + axes[ii // 2, ii % 2].set_title(map_name.replace("_", " ").upper()) + + axes[ii // 2, ii % 2].text( + 0.5, 0.1, + f'Total Endpoint Voxel Count {endpoint_voxel_count}', + color='white', + ha='center', va='center', + transform=axes[ii // 2, ii % 2].transAxes) + + axes[ii // 2, ii % 2].text( + 0.5, 0.05, + f'Median across bundles {median_vc}', + color='white', + ha='center', va='center', + transform=axes[ii // 2, ii % 2].transAxes) + + plt.tight_layout() + plt.savefig(f"endpoint_maps_threshold_{threshold}.png") + plt.close(fig) + +########################################################################### +# This Example would take too long to run in the documentation. +# So, we provide the results as static images. +# .. image:: ../../_static/endpoint_maps_threshold_2.png +# .. image:: ../../_static/endpoint_maps_threshold_3.png +# .. image:: ../../_static/endpoint_maps_threshold_4.png From e66188974d673e9f58468950c9be905044563966 Mon Sep 17 00:00:00 2001 From: 36000 Date: Mon, 28 Jul 2025 16:27:24 -0700 Subject: [PATCH 046/116] update threshold files --- .../_static/endpoint_maps_threshold_2.png | Bin 109391 -> 104146 bytes .../_static/endpoint_maps_threshold_3.png | Bin 113169 -> 109139 bytes .../_static/endpoint_maps_threshold_4.png | Bin 115835 -> 109540 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/_static/endpoint_maps_threshold_2.png b/docs/source/_static/endpoint_maps_threshold_2.png index 6e59b37cb3763a1dc7e7e6c47ce1a2a8d2886a48..54188070a8ffdb5f4e4f532f342b5a79afa11602 100644 GIT binary patch literal 104146 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%0A>Ly>{RrP!qZkqzB9qO|1~_E>Q}CI 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 n`fi~p?0ZQ{{Ujq~%V8I%&5l2=_5J!!BAfsO literal 109391 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�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 literal 113169 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