|
8 | 8 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
|
9 | 9 | """Resampling utilities."""
|
10 | 10 |
|
| 11 | +from os import cpu_count |
| 12 | +from concurrent.futures import ProcessPoolExecutor, as_completed |
11 | 13 | from pathlib import Path
|
12 | 14 | import numpy as np
|
13 | 15 | from nibabel.loadsave import load as _nbload
|
|
25 | 27 | """Minimum number of volumes to automatically serialize 4D transforms."""
|
26 | 28 |
|
27 | 29 |
|
| 30 | +def _apply_volume( |
| 31 | + index, |
| 32 | + data, |
| 33 | + targets, |
| 34 | + order=3, |
| 35 | + mode="constant", |
| 36 | + cval=0.0, |
| 37 | + prefilter=True, |
| 38 | +): |
| 39 | + return index, ndi.map_coordinates( |
| 40 | + data, |
| 41 | + targets, |
| 42 | + order=order, |
| 43 | + mode=mode, |
| 44 | + cval=cval, |
| 45 | + prefilter=prefilter, |
| 46 | + ) |
| 47 | + |
| 48 | + |
28 | 49 | def apply(
|
29 | 50 | transform,
|
30 | 51 | spatialimage,
|
@@ -135,34 +156,47 @@ def apply(
|
135 | 156 | else None
|
136 | 157 | )
|
137 | 158 |
|
138 |
| - # Order F ensures individual volumes are contiguous in memory |
139 |
| - # Also matches NIfTI, making final save more efficient |
140 |
| - resampled = np.zeros( |
141 |
| - (len(ref_ndcoords), len(transform)), dtype=input_dtype, order="F" |
142 |
| - ) |
| 159 | + if njobs is None: |
| 160 | + njobs = cpu_count() |
143 | 161 |
|
144 |
| - for t in range(n_resamplings): |
145 |
| - xfm_t = transform if n_resamplings == 1 else transform[t] |
| 162 | + with ProcessPoolExecutor(max_workers=min(njobs, n_resamplings)) as executor: |
| 163 | + results = [] |
| 164 | + for t in range(n_resamplings): |
| 165 | + xfm_t = transform if n_resamplings == 1 else transform[t] |
146 | 166 |
|
147 |
| - if targets is None: |
148 |
| - targets = ImageGrid(spatialimage).index( # data should be an image |
149 |
| - _as_homogeneous(xfm_t.map(ref_ndcoords), dim=_ref.ndim) |
150 |
| - ) |
| 167 | + if targets is None: |
| 168 | + targets = ImageGrid(spatialimage).index( # data should be an image |
| 169 | + _as_homogeneous(xfm_t.map(ref_ndcoords), dim=_ref.ndim) |
| 170 | + ) |
151 | 171 |
|
152 |
| - # Interpolate |
153 |
| - resampled[..., t] = ndi.map_coordinates( |
154 |
| - ( |
| 172 | + data_t = ( |
155 | 173 | data
|
156 | 174 | if data is not None
|
157 | 175 | else spatialimage.dataobj[..., t].astype(input_dtype, copy=False)
|
158 |
| - ), |
159 |
| - targets, |
160 |
| - order=order, |
161 |
| - mode=mode, |
162 |
| - cval=cval, |
163 |
| - prefilter=prefilter, |
| 176 | + ) |
| 177 | + |
| 178 | + results.append( |
| 179 | + executor.submit( |
| 180 | + _apply_volume, |
| 181 | + t, |
| 182 | + data_t, |
| 183 | + targets, |
| 184 | + order=order, |
| 185 | + mode=mode, |
| 186 | + cval=cval, |
| 187 | + prefilter=prefilter, |
| 188 | + ) |
| 189 | + ) |
| 190 | + |
| 191 | + # Order F ensures individual volumes are contiguous in memory |
| 192 | + # Also matches NIfTI, making final save more efficient |
| 193 | + resampled = np.zeros( |
| 194 | + (len(ref_ndcoords), len(transform)), dtype=input_dtype, order="F" |
164 | 195 | )
|
165 | 196 |
|
| 197 | + for future in as_completed(results): |
| 198 | + t, resampled_t = future.result() |
| 199 | + resampled[..., t] = resampled_t |
166 | 200 | else:
|
167 | 201 | data = np.asanyarray(spatialimage.dataobj, dtype=input_dtype)
|
168 | 202 |
|
|
0 commit comments