|
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