Skip to content

Commit 0ceaef4

Browse files
authored
Merge pull request #324 from effigies/rf/revert_to_old_grid
RF: Generate the b-spline design matrix directly for efficiency
2 parents 72b9b42 + 948adc6 commit 0ceaef4

File tree

3 files changed

+37
-14
lines changed

3 files changed

+37
-14
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,6 @@ venv.bak/
110110

111111
# Mac OSX
112112
.DS_Store
113+
114+
.*.swp
115+
line-contributors.txt

sdcflows/tests/test_transform.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,23 @@ def test_conversions(tmpdir, testdata_dir, pe_dir):
161161
fmap_nii.get_fdata(dtype="float32"),
162162
new_nii.get_fdata(dtype="float32"),
163163
)
164+
165+
166+
def test_grid_bspline_weights():
167+
target_shape = (10, 10, 10)
168+
target_aff = [[1, 0, 0, -5], [0, 1, 0, -5], [0, 0, 1, -5], [0, 0, 0, 1]]
169+
ctrl_shape = (4, 4, 4)
170+
ctrl_aff = [[3, 0, 0, -6], [0, 3, 0, -6], [0, 0, 3, -6], [0, 0, 0, 1]]
171+
172+
weights = tf.grid_bspline_weights(
173+
nb.Nifti1Image(np.zeros(target_shape), target_aff),
174+
nb.Nifti1Image(np.zeros(ctrl_shape), ctrl_aff),
175+
).tocsr()
176+
assert weights.shape == (64, 1000)
177+
# Empirically determined numbers intended to indicate that something
178+
# significant has changed. If it turns out we've been doing this wrong,
179+
# these numbers will probably change.
180+
assert np.isclose(weights[0, 0], 0.18919244)
181+
assert np.isclose(weights[-1, -1], 0.18919244)
182+
assert np.isclose(weights.sum(axis=1).max(), 26.833675)
183+
assert np.isclose(weights.sum(axis=1).min(), 1.5879614)

sdcflows/transform.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import numpy as np
2828
from warnings import warn
2929
from scipy import ndimage as ndi
30-
from scipy.interpolate import BSpline
31-
from scipy.sparse import vstack as sparse_vstack, kron
30+
from scipy.signal import cubic
31+
from scipy.sparse import vstack as sparse_vstack, kron, lil_array
3232

3333
import nibabel as nb
3434
import nitransforms as nt
@@ -405,18 +405,18 @@ def grid_bspline_weights(target_nii, ctrl_nii, dtype="float32"):
405405
coords[axis] = np.arange(sample_shape[axis], dtype=dtype)
406406

407407
# Calculate the index component of samples w.r.t. B-Spline knots along current axis
408-
x = nb.affines.apply_affine(target_to_grid, coords.T)[:, axis]
409-
pad_left = max(int(-np.rint(x.min())), 0)
410-
pad_right = max(int(np.rint(x.max()) - knots_shape[axis]), 0)
411-
412-
# BSpline.design_matrix requires all x be within -4 and 4 padding
413-
# This padding results from the B-Spline degree (3) plus one
414-
t = np.arange(-4 - pad_left, knots_shape[axis] + 4 + pad_right, dtype=dtype)
415-
416-
# Calculate K x N collocation matrix (discarding extra padding)
417-
colloc_ax = BSpline.design_matrix(x, t, 3)[:, (2 + pad_left):-(2 + pad_right)]
418-
# Design matrix returns K x N and we want N x K
419-
wd.append(colloc_ax.T.tocsr())
408+
locs = nb.affines.apply_affine(target_to_grid, coords.T)[:, axis]
409+
knots = np.arange(knots_shape[axis], dtype=dtype)
410+
411+
distance = np.abs(locs[np.newaxis, ...] - knots[..., np.newaxis])
412+
within_support = distance < 2.0
413+
d_vals, d_idxs = np.unique(distance[within_support], return_inverse=True)
414+
bs_w = cubic(d_vals)
415+
416+
colloc_ax = lil_array((knots_shape[axis], sample_shape[axis]), dtype=dtype)
417+
colloc_ax[within_support] = bs_w[d_idxs]
418+
419+
wd.append(colloc_ax)
420420

421421
# Calculate the tensor product of the three design matrices
422422
return kron(kron(wd[0], wd[1]), wd[2]).astype(dtype)

0 commit comments

Comments
 (0)