Skip to content

Commit 735dfcc

Browse files
authored
Merge pull request #588 from ANTsX/reset_index_after_filter
ENH: Reset index after ITK filters, keep ITK object and python interface consistent
2 parents b19e616 + 09ef1b6 commit 735dfcc

File tree

6 files changed

+82
-30
lines changed

6 files changed

+82
-30
lines changed

ants/lib/LOCAL_antsImage.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,36 @@ py::tuple getSpacing( py::capsule & myPointer )
345345

346346
extern std::string ptrstr(py::capsule c);
347347

348+
/*
349+
This function resets the region of an image to index from zero if needed. This
350+
keeps the voxel indices in the numpy matrix consistent with the ITK image, and
351+
also keeps the origin of physical space of the consistent with how it will be
352+
saved as NIFTI.
353+
*/
354+
template <typename ImageType>
355+
static void FixNonZeroIndex( typename ImageType::Pointer img )
356+
{
357+
assert(img);
358+
359+
typename ImageType::RegionType r = img->GetLargestPossibleRegion();
360+
typename ImageType::IndexType idx = r.GetIndex();
361+
362+
for (unsigned int i = 0; i < ImageType::ImageDimension; ++i)
363+
{
364+
// if any index is non-zero, reset the origin and region
365+
if ( idx[i] != 0 )
366+
{
367+
typename ImageType::PointType o;
368+
img->TransformIndexToPhysicalPoint( idx, o );
369+
img->SetOrigin( o );
370+
371+
idx.Fill( 0 );
372+
r.SetIndex( idx );
373+
img->SetRegions( r );
374+
375+
return;
376+
}
377+
}
378+
}
348379

349380
#endif

ants/lib/LOCAL_cropImage.cxx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,7 @@ typename ImageType::Pointer cropImageHelper( typename ImageType::Pointer image,
4545
cropper->SetDirectionCollapseToSubmatrix();
4646
cropper->UpdateLargestPossibleRegion();
4747
cropper->GetOutput()->SetSpacing( image->GetSpacing() );
48-
typename ImageType::RegionType region =
49-
cropper->GetOutput()->GetLargestPossibleRegion();
50-
typename ImageType::IndexType ind = region.GetIndex();
51-
typename ImageType::PointType neworig;
52-
image->TransformIndexToPhysicalPoint( ind, neworig );
53-
ind.Fill(0);
54-
region.SetIndex( ind );
55-
cropper->GetOutput()->SetRegions( region );
56-
cropper->GetOutput()->SetOrigin( neworig );
48+
FixNonZeroIndex<ImageType>( cropper->GetOutput() );
5749
return cropper->GetOutput();
5850
}
5951
return nullptr;
@@ -97,15 +89,7 @@ typename ImageType::Pointer cropIndHelper( typename ImageType::Pointer image,
9789
cropper->SetDirectionCollapseToSubmatrix();
9890
cropper->Update();
9991
cropper->GetOutput()->SetSpacing( image->GetSpacing() );
100-
typename ImageType::RegionType region =
101-
cropper->GetOutput()->GetLargestPossibleRegion();
102-
typename ImageType::IndexType ind = region.GetIndex();
103-
typename ImageType::PointType neworig;
104-
image->TransformIndexToPhysicalPoint( ind, neworig );
105-
ind.Fill(0);
106-
region.SetIndex( ind );
107-
cropper->GetOutput()->SetRegions( region );
108-
cropper->GetOutput()->SetOrigin( neworig );
92+
FixNonZeroIndex<ImageType>( cropper->GetOutput() );
10993
return cropper->GetOutput();
11094
}
11195
return nullptr;

ants/lib/LOCAL_padImage.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
namespace py = pybind11;
1515

1616
template < typename ImageType >
17-
py::capsule padImage( py::capsule & antsImage,
17+
py::capsule padImage( py::capsule & antsImage,
1818
std::vector<int> lowerPadDims,
1919
std::vector<int> upperPadDims,
2020
float padValue )
@@ -47,7 +47,7 @@ py::capsule padImage( py::capsule & antsImage,
4747
padFilter->SetPadUpperBound( upperExtendRegion );
4848
padFilter->SetConstant( padValue );
4949
padFilter->Update();
50-
50+
FixNonZeroIndex<ImageType>( padFilter->GetOutput() );
5151
return wrap< ImageType >( padFilter->GetOutput() );
5252
}
5353

ants/utils/pad_image.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ def pad_image(image, shape=None, pad_width=None, value=0.0, return_padvals=False
1919
shape : tuple
2020
- if shape is given, the image will be padded in each dimension
2121
until it has this shape
22-
- if shape is not given, the image will be padded along each
23-
dimension to match the largest existing dimension so that it
24-
has isotropic dimension
22+
- if shape and pad_width are both None, the image will be padded along
23+
each dimension to match the largest existing dimension so that it has
24+
isotropic dimensions.
2525
2626
pad_width : list of integers or list-of-list of integers
2727
How much to pad in each direction. If a single list is
28-
supplied (e.g., [4,4,4]), then the image will be padded by half
28+
supplied (e.g., [4,4,4]), then the image will be padded by half
2929
that amount on both sides. If a list-of-list is supplied
3030
(e.g., [(0,4),(0,4),(0,4)]), then the image will be
3131
padded unevenly on the different sides
@@ -72,7 +72,7 @@ def pad_image(image, shape=None, pad_width=None, value=0.0, return_padvals=False
7272
libfn = utils.get_lib_fn('padImageF%i' % ndim)
7373
itkimage = libfn(image.pointer, lower_pad_vals, upper_pad_vals, value)
7474

75-
new_image = iio.ANTsImage(pixeltype='float', dimension=ndim,
75+
new_image = iio.ANTsImage(pixeltype='float', dimension=ndim,
7676
components=image.components, pointer=itkimage).clone(inpixeltype)
7777
if return_padvals:
7878
return new_image, lower_pad_vals, upper_pad_vals

tests/test_core_ants_image.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def test__add__(self):
235235
def test__radd__(self):
236236
if os.name == "nt":
237237
return
238-
238+
239239
#self.setUp()
240240
for img in self.imgs:
241241
# op on constant
@@ -253,7 +253,7 @@ def test__radd__(self):
253253
img2 = img.clone()
254254
img2.set_spacing([2.31]*img.dimension)
255255
img3 = img + img2
256-
256+
257257
def test__sub__(self):
258258
#self.setUp()
259259
for img in self.imgs:
@@ -276,7 +276,7 @@ def test__sub__(self):
276276
def test__rsub__(self):
277277
if os.name == "nt":
278278
return
279-
279+
280280
#self.setUp()
281281
for img in self.imgs:
282282
# op on constant
@@ -294,7 +294,7 @@ def test__rsub__(self):
294294
img2 = img.clone()
295295
img2.set_spacing([2.31]*img.dimension)
296296
img3 = img - img2
297-
297+
298298
def test__mul__(self):
299299
#self.setUp()
300300
for img in self.imgs:
@@ -335,7 +335,7 @@ def test__rmul__(self):
335335
img2 = img.clone()
336336
img2.set_spacing([2.31]*img.dimension)
337337
img3 = img * img2
338-
338+
339339
def test__div__(self):
340340
#self.setUp()
341341
for img in self.imgs:

tests/test_utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,43 @@ def test_decrop_image_example(self):
237237
# full image not float
238238
cropped = ants.crop_image(fi, mask.clone("unsigned int"), 1)
239239

240+
class TestModule_pad_image(unittest.TestCase):
241+
def setUp(self):
242+
self.img2d = ants.image_read(ants.get_ants_data("r16"))
243+
self.img3d = ants.image_read(ants.get_ants_data("mni")).resample_image((2, 2, 2))
244+
245+
def tearDown(self):
246+
pass
247+
248+
def test_pad_image_example(self):
249+
img = self.img2d.clone()
250+
img.set_origin((0, 0))
251+
img.set_spacing((2, 2))
252+
# isotropic pad via pad_width
253+
padded = ants.pad_image(img, pad_width=(10, 10))
254+
self.assertEqual(padded.shape, (img.shape[0] + 10, img.shape[1] + 10))
255+
for img_orig_elem, pad_orig_elem in zip(img.origin, padded.origin):
256+
self.assertAlmostEqual(pad_orig_elem, img_orig_elem - 10, places=3)
257+
258+
img = self.img2d.clone()
259+
img.set_origin((0, 0))
260+
img.set_spacing((2, 2))
261+
img = ants.resample_image(img, (128,128), 1, 1)
262+
# isotropic pad via shape
263+
padded = ants.pad_image(img, shape=(160,160))
264+
self.assertSequenceEqual(padded.shape, (160, 160))
265+
266+
img = self.img3d.clone()
267+
img = ants.resample_image(img, (128,160,96), 1, 1)
268+
padded = ants.pad_image(img)
269+
self.assertSequenceEqual(padded.shape, (160, 160, 160))
270+
271+
# pad only on superior side
272+
img = self.img3d.clone()
273+
padded = ants.pad_image(img, pad_width=[(0,4),(0,8),(0,12)])
274+
self.assertSequenceEqual(padded.shape, (img.shape[0] + 4, img.shape[1] + 8, img.shape[2] + 12))
275+
self.assertSequenceEqual(img.origin, padded.origin)
276+
240277

241278
class TestModule_denoise_image(unittest.TestCase):
242279
def setUp(self):

0 commit comments

Comments
 (0)