-
-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add spline_filter and spline_filter1d #215
Conversation
exclude use of boundary modes that require additional padding
The required depth is related to the magnitude of the spline prefilter poles
input_output_shape_per_dim requires sizes larger than the depth used by spline_filter
cc @m-albert regarding previous discussion related to this |
This is a simple performance benchmark on a somewhat large 3D volume (512 x 512 x 512) from time import time import dask.array as da
import numpy as np
import scipy.ndimage as ndi
from dask_image.ndinterp import spline_filter
# define test image
a = 512
ndim = 3
shape = (a,) * ndim
dtype = np.float64
im = np.random.standard_normal(shape).astype(dtype, copy=False)
# transform into dask array
chunksize = (128,) * ndim
dim = da.from_array(im, chunks=chunksize)
kwargs = dict(order=3, mode='constant', output=dtype)
tstart = time()
y_cpu = ndi.spline_filter(im, **kwargs)
dur = time() - tstart
print(f"Duration (scipy.ndimage) = {dur}")
tstart = time()
y = spline_filter(dim, **kwargs)
y = y.compute()
dur = time() - tstart
print(f"Duration (dask-image) = {dur}") On my system with a 10-core Intel Skylake X CPU the result is: for
or for
|
I see there are quite a few test failures. I think a number of these tests cases will have to be skipped when SciPy < 1.6. I can fix that locally and then update here, later. Currently all cases are passing in my local environment with SciPy 1.6.3 installed. |
Hi @grlee77, this is great! The speedup on the CPU is considerable and of course this allows applying Really nice that you added prefilter functionality to
Locally all tests passing for me, too.
Looking at the CI tests it seems there could be an error unrelated to the Call of x = dask.array<array, shape=(48, 48, 48), dtype=float64, chunksize=(33, 33, 33), chunktype=numpy.ndarray>
depth = {0: 0, 1: 0, 2: 22}
boundary = {0: 'none', 1: 'none', 2: 'none'} resulting in:
Probably the affected chunk is the last one along the array, which is smaller than the previous ones. Could this potentially be avoided by previous padding? Strange though that this doesn't occur locally... |
I am guessing this is related to the Dask version. I have the recent 2021.4.1 release locally, but the CI job I looked at is using 2.8.1 (released Nov 2019). |
local testing indicates that the test failures don't occur for dask>=2020.12.0. Specifically, it is this PR that fixed it dask/dask#6708 |
The one case that is expected to still fail on current dask is if the depth is larger than any of the overall array dimensions. I could add a test for that case if desired (e.g. we cannot spline_filter a small image of shape (16, 16) with order=5). edit: actually we should be able to support that case. We probably just need to make sure to set depth=0 on any axis where there is only a single chunk. |
older dask didn't automatically rechunk during map_overlap fix veriable names: _supported_periodic_modes -> _supported_prefilter_modes _unsupported_periodic_modes -> _unsupported_prefilter_modes
Of course, that makes sense! Thanks for checking this. |
The coveralls check shouldn't block this PR. I've just had a look at it & things there are mostly fine. |
|
||
# magnitude of the maximum filter pole for each order | ||
# (obtained from scipy/ndimage/src/ni_splines.c) | ||
_maximum_pole = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the comment explaining the source of these magic numbers 😄
tests/test_dask_image/test_ndinterp/test_affine_transformation.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. I've made a few suggestions, and will leave this open for a bit for other reviewers to make comments too.
This PR adds the spline prefiltering functions that are correspond to:
scipy.ndimage.spline_filter
scipy.ndimage.spline_filter1d
The periodic modes
"wrap"
and"grid-wrap"
are not currently supported, but all other modes are.The one empirical nature of this implementation is the equation I came up with for estimating the overlap (depth) needed based on the interpolation order. In practice these are infinite impulse response (IIR) filters, but in practice the error terms decay pretty quickly with distance from an edge and the rate of decay is related to the magnitude of the filter poles. The current settings are enough for tests to pass at the tolerance of 1e-6 used in the tests. With the current default, the
depth
currently ends up being 11, 14, 19 and 22 for splineorder
of 2, 3, 4 and 5, respectively. I exposed adepth
keyword-only argument in case users want to override this setting, but am not certain if that is desirable.This PR also enables use of
prefilter=True
in the existingaffine_transform
function whenmode='constant'
. This is needed to get good results with order > 1. Althoughreflect
andmirror
are also supported by thespline_filter
functions added here, they are not currently supported inaffine_transform
itself. I have not yet tried to supportmode='nearest'
ormode='grid-constant'
for use withinaffine_transform
because those two cases do not have an analytic solution implemented and involve the need for temporary internal padding in SciPy's own implementation, making it more difficult to adapt here.