Skip to content

Commit e19b022

Browse files
committed
tighten scope of conform function
1 parent 4e62b7c commit e19b022

File tree

2 files changed

+37
-51
lines changed

2 files changed

+37
-51
lines changed

nibabel/processing.py

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .funcs import as_closest_canonical
2626
from .spaces import vox2out_vox
2727
from .nifti1 import Nifti1Header, Nifti1Image
28+
from .orientations import axcodes2ornt
2829
from .imageclasses import spatial_axes_first
2930

3031
SIGMA2FWHM = np.sqrt(8 * np.log(2))
@@ -313,36 +314,12 @@ def smooth_image(img,
313314
return out_class(sm_data, img.affine, img.header)
314315

315316

316-
def _transform_range(x, new_min, new_max):
317-
""" Transform data to a new range, while maintaining ratios.
318-
319-
Parameters
320-
----------
321-
x : array-like
322-
The data to transform.
323-
new_min, new_max : scalar
324-
The minimum and maximum of the output array.
325-
326-
Returns
327-
-------
328-
transformed : array-like
329-
A copy of the transformed data.
330-
331-
Examples
332-
--------
333-
>>> _transform_range([2, 4, 6], -1, 1)
334-
array([-1., 0., 1.])
335-
"""
336-
x = np.asarray(x)
337-
x_min, x_max = x.min(), x.max()
338-
return (x - x_min) * (new_max - new_min) / (x_max - x_min) + new_min
339-
340-
341317
def conform(from_img,
342318
out_shape=(256, 256, 256),
343319
voxel_size=(1.0, 1.0, 1.0),
344320
order=3,
345321
cval=0.0,
322+
orientation='RAS',
346323
out_class=Nifti1Image):
347324
""" Resample image to ``out_shape`` with voxels of size ``voxel_size``.
348325
@@ -351,10 +328,12 @@ def conform(from_img,
351328
function:
352329
- Resamples data to ``output_shape``
353330
- Resamples voxel sizes to ``voxel_size``
354-
- Transforms data to range [0, 255] (while maintaining ratios)
355-
- Casts to unsigned eight-bit integer
356331
- Reorients to RAS (``mri_convert --conform`` reorients to LIA)
357332
333+
Unlike ``mri_convert --conform``, this command does not:
334+
- Transform data to range [0, 255]
335+
- Cast to unsigned eight-bit integer
336+
358337
Parameters
359338
----------
360339
from_img : object
@@ -373,6 +352,8 @@ def conform(from_img,
373352
Value used for points outside the boundaries of the input if
374353
``mode='constant'``. Default is 0.0 (see
375354
``scipy.ndimage.affine_transform``)
355+
orientation : str, optional
356+
Orientation of output image. Default is "RAS".
376357
out_class : None or SpatialImage class, optional
377358
Class of output image. If None, use ``from_img.__class__``.
378359
@@ -383,24 +364,29 @@ def conform(from_img,
383364
resampling `from_img` into axes aligned to the output space of
384365
``from_img.affine``
385366
"""
386-
if from_img.ndim != 3:
367+
# Only support 3D images. This can be made more general in the future, once tests
368+
# are written.
369+
required_ndim = 3
370+
if from_img.ndim != required_ndim:
387371
raise ValueError("Only 3D images are supported.")
372+
elif len(out_shape) != required_ndim:
373+
raise ValueError("`out_shape` must have {} values".format(required_ndim))
374+
elif len(voxel_size) != required_ndim:
375+
raise ValueError("`voxel_size` must have {} values".format(required_ndim))
376+
388377
# Create fake image of the image we want to resample to.
389378
hdr = Nifti1Header()
390379
hdr.set_data_shape(out_shape)
391380
hdr.set_zooms(voxel_size)
392381
dst_aff = hdr.get_best_affine()
393382
to_img = Nifti1Image(np.empty(out_shape), affine=dst_aff, header=hdr)
383+
394384
# Resample input image.
395385
out_img = resample_from_to(
396386
from_img=from_img, to_vox_map=to_img, order=order, mode="constant",
397387
cval=cval, out_class=out_class)
398-
# Cast to uint8.
399-
data = out_img.get_fdata()
400-
data = _transform_range(data, new_min=0.0, new_max=255.0)
401-
data = data.round(out=data).astype(np.uint8)
402-
out_img._dataobj = data
403-
out_img.set_data_dtype(data.dtype)
404-
# Reorient to RAS.
405-
out_img = as_closest_canonical(out_img)
406-
return out_img
388+
389+
labels =list(zip('LPI', 'RAS'))
390+
# list(zip('RPI', 'LAS')
391+
# Reorient to desired orientation.
392+
return out_img.as_reoriented(axcodes2ornt(orientation, labels=labels))

nibabel/tests/test_processing.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import nibabel as nib
2121
from nibabel.processing import (sigma2fwhm, fwhm2sigma, adapt_affine,
2222
resample_from_to, resample_to_output, smooth_image,
23-
_transform_range, conform)
23+
conform)
2424
from nibabel.nifti1 import Nifti1Image
2525
from nibabel.nifti2 import Nifti2Image
2626
from nibabel.orientations import aff2axcodes, flip_axis, inv_ornt_aff
@@ -429,30 +429,30 @@ def test_against_spm_resample():
429429
assert_spm_resampling_close(moved_anat, moved2output, spm2output);
430430

431431

432-
def test__transform_range():
433-
assert_array_equal(_transform_range([2, 4, 6], -1, 1), [-1, 0, 1])
434-
assert_array_equal(_transform_range([-1, 0, 1], 2, 6), [2, 4, 6])
435-
assert_array_equal(_transform_range(np.arange(11), 0, 5),
436-
np.arange(0, 5.5, 0.5))
437-
assert_array_equal(_transform_range(np.arange(-100, 101), 0, 200),
438-
np.arange(201))
439-
440-
441432
@needs_scipy
442433
def test_conform():
443434
anat = nib.load(pjoin(DATA_DIR, 'anatomical.nii'))
444435

436+
# Test with default arguments.
445437
c = conform(anat)
446438
assert c.shape == (256, 256, 256)
447439
assert c.header.get_zooms() == (1, 1, 1)
448-
assert c.dataobj.dtype == np.dtype(np.uint8)
440+
assert c.dataobj.dtype.type == anat.dataobj.dtype.type
449441
assert aff2axcodes(c.affine) == ('R', 'A', 'S')
442+
assert isinstance(c, Nifti1Image)
450443

451-
c = conform(anat, out_shape=(100, 100, 200), voxel_size=(2, 2, 1.5))
444+
# Test with non-default arguments.
445+
c = conform(anat, out_shape=(100, 100, 200), voxel_size=(2, 2, 1.5),
446+
orientation="LPI", out_class=Nifti2Image)
452447
assert c.shape == (100, 100, 200)
453448
assert c.header.get_zooms() == (2, 2, 1.5)
454-
assert c.dataobj.dtype == np.dtype(np.uint8)
455-
assert aff2axcodes(c.affine) == ('R', 'A', 'S')
449+
assert c.dataobj.dtype.type == anat.dataobj.dtype.type
450+
assert aff2axcodes(c.affine) == ('L', 'P', 'I')
451+
assert isinstance(c, Nifti2Image)
452+
453+
# Error on non-3D arguments.
454+
assert_raises(ValueError, conform, anat, out_shape=(100, 100))
455+
assert_raises(ValueError, conform, anat, voxel_size=(2, 2))
456456

457457
# Error on non-3D images.
458458
func = nib.load(pjoin(DATA_DIR, 'functional.nii'))

0 commit comments

Comments
 (0)