# Resampling to volume to smaller size and smaller voxel spacing

**URL:** https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984
**Category:** Beginner Questions
**Created:** [May 13, 2022, 7:04am UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984 "2022-05-13T07:04:15Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![shadab](https://discourse.itk.org/letter_avatar_proxy/v4/letter/s/e68b1a/32.png) [@shadab](https://discourse.itk.org/u/shadab)
#### Post date: [May 13, 2022, 7:04am UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/1 "2022-05-13T07:04:15Z")

</div>

Hi ITK community,

I have recently started learning SimpleITK and have been stuck on a specific type of Resampling that I have been trying to implement. I have PET/CT Nifti images. My typical PET and CT volumes have the following characteristics:

PET Size: (168, 168, 324)  
PET Spacing: (4.07283, 4.07283, 3.0)

CT Size: (512, 512, 486)  
CT Spacing: (0.9765625, 0.9765625, 2.0)

I want to downsample my CT images to match their corresponding PET Size, but I also want all my voxel spacing for both PET and CT to be isotropic and equal to (1.0, 1.0, 1.0). So far, I have tried codes by people who have already asked similar questions, but most of them ended up cropping my final image. Is it possible to perform this resampling?

I am attaching the latest version of my code where I am trying to resample a PET image to a volume where I keep the size the same as the original PET image, but change the voxel spacing to (1.0, 1.0, 1.0). This code results in getting the final resampled image cropped. Please let me know what I am doing wrong.

Thanks in advance for your help. 🙂

```auto
def reSample_fixedSize_fixedSpacing (img_sitk,interpolation, new_spacing):
   
    dimension = img_sitk.GetDimension()
    reference_size = img_sitk.GetSize()#[new_size,new_size,new_size]
    reference_physical_size = np.zeros(img_sitk.GetDimension())
    reference_physical_size[:] = [(sz-1)*spc if sz*spc>mx else mx for sz,spc,mx in zip(img_sitk.GetSize(), img_sitk.GetSpacing(), reference_physical_size)]
    reference_direction = img_sitk.GetDirection()
    reference_origin = img_sitk.GetOrigin()
    
    reference_image = sitk.Image(reference_size, img_sitk.GetPixelIDValue())
    reference_image.SetOrigin(reference_origin)
    reference_image.SetSpacing(new_spacing)
    reference_image.SetDirection(reference_direction)

    reference_center = np.array(reference_image.TransformContinuousIndexToPhysicalPoint(np.array(reference_image.GetSize())/2.0))
    
    transform = sitk.AffineTransform(dimension)
    transform.SetMatrix(img_sitk.GetDirection())
    transform.SetTranslation(np.array(img_sitk.GetOrigin()) - reference_origin)
  
    centering_transform = sitk.TranslationTransform(dimension)
    img_center = np.array(img_sitk.TransformContinuousIndexToPhysicalPoint(np.array(img_sitk.GetSize())/2.0))
    centering_transform.SetOffset(np.array(transform.GetInverse().TransformPoint(img_center) - reference_center))
    centered_transform = sitk.CompositeTransform([centering_transform,transform])
    new_img = sitk.Resample(img_sitk, reference_image, centered_transform, interpolation, 0.0)
    return new_img

ptpath = '14695318_20170802_pt.nii.gz'
ptimg = sitk.ReadImage(ptpath, imageIO="NiftiImageIO")

resamp_ptimg = reSample_fixedSize_fixedSpacing(ptimg, sitk.sitkBSpline, [1.0, 1.0, 1.0])

info = os.path.basename(ptpath).split('_')
filename = info[0] + '_' + info[1]
sitk.WriteImage(resamp_ptimg,filename+'_pt_resamp.nii')

```

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 13, 2022, 1:51pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/2 "2022-05-13T13:51:27Z")

</div>

Hello @shadab,

Welcome to SimpleITK!

What you are missing is the understanding that in ITK/SimpleITK an image is a physical/spatial object with a metric size, see [Fundamental Concepts](https://simpleitk.readthedocs.io/en/master/fundamentalConcepts.html). In ITK/SimpleITK nomenclature the term `Size` means number of pixels/voxels and the term `Spacing` refers to the distance between consecutive indexes and is a metric unit (usually mm). Defining a resampling grid is done in physical space.

“I want to downsample my CT images to match their corresponding PET Size, but I also want all my voxel spacing for both PET and CT to be isotropic and equal to (1.0, 1.0, 1.0).”

If you want the PET image to be isotropic `(1.0,1.0,1.0)`, while retaining the original number of voxels, you are defining a grid that is smaller than the original PET image because the new spacing is smaller than the original one and the `Size` is the same as the original:

```auto
pet_original_size = (168, 168, 324)
pet_original_spacing = (4.07283, 4.07283, 3.0)
pet_original_physical_size = [(sz-1)*spc for sz,spc in zip(pet_original_size,pet_original_spacing)]

pet_new_spacing = [1.0,1.0,1.0]
pet_physical_size_using_new_spacing_original_size = [(sz-1)*spc for sz,spc in zip(pet_original_size, pet_new_spacing)]

print(pet_original_physical_size)
print(pet_physical_size_using_new_spacing_original_size)

```

If you want both images to have the same spacing and size you need to:

1. Resample PET using given `new_spacing` and compute the required `new_size` to maintain the original physical size.
2. Resample CT using `new_spacing` and the PET’s `new_size`.

---

<div class="post-metadata">

### Author: ![shadab](https://discourse.itk.org/letter_avatar_proxy/v4/letter/s/e68b1a/32.png) [@shadab](https://discourse.itk.org/u/shadab)
#### Post date: [May 13, 2022, 7:19pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/3 "2022-05-13T19:19:04Z")

</div>

Hi @zivy

Thank you so much for your detailed explanation. I understand the difference between `Size` and “Physical size” of the image in the world of SimpleITK. But, my aim is to do exactly what you mentioned in your reply. I want to change the physical size of the image, but I want to keep my `Spacing` and `Size` to whatever I specify them to be. So, that would mean downsampling CT to corresponding PET sizes while retaining the number of voxels for PET after resampling and setting the spacing for both PET and CT to `(1.0, 1.0, 1.0)` after resampling.

I was just wondering if this is possible, as no one really does this (as far as I have noticed)!

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 13, 2022, 7:56pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/4 "2022-05-13T19:56:14Z")

</div>

Hello @shadab ,

Sorry, but not really sure what it is you are attempting to do. Based on your requirements you are intentionally cropping the PET image, so not sure why you are surprised that the image is cropped. Be aware that this is not a symmetric cropping around the center of the image.

Possibly if you provide the expected input and output we could better help. Is the following what you are thinking?

Input:

```auto
PET Size: (168, 168, 324)
PET Spacing: (4.07283, 4.07283, 3.0)
PET Origin: ?

CT Size: (512, 512, 486)
CT Spacing: (0.9765625, 0.9765625, 2.0)
CT Origin: ?

```

Output:

```auto
PET Size: (168, 168, 324)
PET Spacing: (1.0, 1.0, 1.0)
PET Origin: ?

CT Size: (168, 168, 324)
CT Spacing: (1.0, 1.0, 1.0)
CT Origin: ?

```

---

<div class="post-metadata">

### Author: ![shadab](https://discourse.itk.org/letter_avatar_proxy/v4/letter/s/e68b1a/32.png) [@shadab](https://discourse.itk.org/u/shadab)
#### Post date: [May 13, 2022, 8:07pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/5 "2022-05-13T20:07:42Z")

</div>

HI @zivy

I want the whole 3D image to be contained (maybe “resized” is the correct terminology here?) in a smaller physical volume after resampling while having the final `Spacing` and final `Size` to whatever I specify.

As for the inputs and outputs, you are correct.

Input:

```auto
PET Size: (168, 168, 324)
PET Spacing: (4.07283, 4.07283, 3.0)
PET Origin: (-342.45751953125, -491.60284423828125, -1466.5)
PET Direction: (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)

CT Size: (512, 512, 486)
CT Spacing: (0.9765625, 0.9765625, 2.0)
CT Origin: (-249.51171875, -399.51171875, -1467.5)
CT Direction: (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)

```

Output:

```auto
PET Size: (168, 168, 324)
PET Spacing: (1.0, 1.0, 1.0)
PET Origin: ?
PET Direction: ?

CT Size: (168, 168, 324)
CT Spacing: (1.0, 1.0, 1.0)
CT Origin: ?
CT Direction: ?

```

For the Output Origin and Direction for PET and CT, as long as the final resampled CT image is well-aligned/registered to the final resampled PET image, it will be great.

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 13, 2022, 8:41pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/6 "2022-05-13T20:41:26Z")

</div>

Hello @shadab,

I suspect the code below is what you want. It means that you are willing to accept that the objects will change their physical size and that the change is non-isotropic.

```auto
import SimpleITK as sitk

def scale_resample(image, interpolator, new_size, new_spacing):
    original_physical_size = [(sz-1)*spc for sz,spc in zip(image.GetSize(), image.GetSpacing())]
    new_physical_size = [(sz-1)*spc for sz,spc in zip(new_size, new_spacing)]

    tx = sitk.ScaleTransform(image.GetDimension(), [ops/nps for nps, ops in zip(new_physical_size, original_physical_size)])
    return sitk.Resample(image,
                         size = new_size,
                         transform = tx,
                         interpolator = interpolator,
                         outputSpacing = new_spacing)

file_name = # set the file name
new_size = (168, 168, 324)
new_spacing = (1.0, 1.0, 1.0)
image = sitk.ReadImage(file_name)

sitk.Show(scale_resample(image, sitk.sitkLinear, new_size, new_spacing))

```

---

<div class="post-metadata">

### Author: ![shadab](https://discourse.itk.org/letter_avatar_proxy/v4/letter/s/e68b1a/32.png) [@shadab](https://discourse.itk.org/u/shadab)
#### Post date: [May 13, 2022, 9:09pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/7 "2022-05-13T21:09:12Z")

</div>

> [@zivy](#):
>
> ```auto
> sitk.Show(scale_resample(image, sitk.sitkLinear, new_size, new_spacing))
> 
> ```

Hi @zivy

Thanks again for your reply. I used the code you gave above and now it returns an empty 3D array (all elements are zeros).

The `sitk.Show()` command didn’t really work for me, and I got the following error:

```auto
RuntimeError: Exception thrown in SimpleITK Show: D:\a\1\sitk\Code\IO\src\sitkImageViewer.cxx:620:
sitk::ERROR: No ImageJ/Fiji application found.

```

I just ended up getting the array from the generated image and used this:

```auto
def scale_resample(image, interpolator, new_size, new_spacing):
    original_physical_size = [(sz-1)*spc for sz,spc in zip(image.GetSize(), image.GetSpacing())]
    new_physical_size = [(sz-1)*spc for sz,spc in zip(new_size, new_spacing)]

    tx = sitk.ScaleTransform(image.GetDimension(), [ops/nps for nps, ops in zip(new_physical_size, original_physical_size)])
    return sitk.Resample(image,
                         size = new_size,
                         transform = tx,
                         interpolator = interpolator,
                         outputSpacing = new_spacing)

#%%
ptpath = '14695318_20170802_pt.nii.gz'
ptimg = sitk.ReadImage(ptpath)
new_size = ptimg.GetSize()
new_spacing = [1.0, 1.0, 1.0]

#%%
resampled_ptimg = scale_resample(ptimg, sitk.sitkLinear, new_size, new_spacing)
resampled_ptarray = sitk.GetArrayFromImage(resampled_ptimg)

```

After that, I tried to print any element that is non-zero but didn’t get any output:

```auto
flatarray = resampled_ptarray.flatten()
for i in flatarray:
    if i != 0:
        print(i)

```

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 16, 2022, 2:32pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/8 "2022-05-16T14:32:51Z")

</div>

Hello @shadab,

Results are surprising, can you share a sample image where the code doesn’t work?

With respect to the `Show` function [see the FAQ](https://simpleitk.readthedocs.io/en/master/faq.html#why-isn-t-fiji-or-imagej-found-by-the-show-function-runtimeerror-exception-thrown).

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 17, 2022, 2:51pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/10 "2022-05-17T14:51:53Z")

</div>

Hello @shadab,

Thanks for sharing the data. The problem with the code above was that I assumed the image origin is at `[0,0,0]` and direction cosine matrix was the identity. The code below works for the general setting. Hopefully this addresses your question.

```auto
def scale_resample(image, interpolator, new_size, new_spacing):
    original_physical_size = [(sz-1)*spc for sz,spc in zip(image.GetSize(), image.GetSpacing())]
    new_physical_size = [(sz-1)*spc for sz,spc in zip(new_size, new_spacing)]

    scale_tx = sitk.ScaleTransform(image.GetDimension(), [ops/nps for nps, ops in zip(new_physical_size, original_physical_size)])
    rigid_tx = sitk.Euler3DTransform()
    rigid_tx.SetMatrix(image.GetDirection())
    rigid_tx.SetTranslation(image.GetOrigin())
    #accomodate for non identity direction cosine matrix, ensure that scaling is
    #along the image axes and not the canonical ones.
    tx = sitk.CompositeTransform([rigid_tx, scale_tx, rigid_tx.GetInverse()])

    #output image has the same origin and direction as the input, but is a
    #scaled version of the content
    return sitk.Resample(image,
                         size = new_size,
                         transform = tx,
                         interpolator = interpolator,
                         outputOrigin = image.GetOrigin(),
                         outputDirection = image.GetDirection(),
                         outputSpacing = new_spacing)

```

---

<div class="post-metadata">

### Author: ![shadab](https://discourse.itk.org/letter_avatar_proxy/v4/letter/s/e68b1a/32.png) [@shadab](https://discourse.itk.org/u/shadab)
#### Post date: [May 19, 2022, 1:44am UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/11 "2022-05-19T01:44:40Z")

</div>

Hi @zivy

I realized something about my image only now, and now my goals have changed. Now I want to keep my PET images as it is, and only resample CT images to PET `size` and `spacing`. But the `origin` of PET and their corresponding CT images are not the same, and the Direction cosines may also need not be the same. The physical size of the CT image is also slightly smaller than the corresponding PET image. But in the current image pairs, the PET and CT images are well aligned/registered. I am using the following code to resample CT to PET `size` and `spacing` now:

```auto
import SimpleITK as sitk

def resample_ct2pt(ctimg, ptimg, interpolator):#, interpolator, new_size, new_spacing):
    resampler = sitk.ResampleImageFilter()
    resampler.SetOutputOrigin(ctimg.GetOrigin())
    resampler.SetOutputDirection(ctimg.GetDirection())
    resampler.SetSize(ptimg.GetSize())
    resampler.SetOutputSpacing(ptimg.GetSpacing())
    resampler.SetInterpolator(interpolator)
    resampled_ctimg = resampler.Execute(ctimg)
    
    return resampled_ctimg 

ctpath = # ct image here
ptpath = # pet image here
info = os.path.basename(ptpath).split('_')
filename = info[0] + '_' + info[1]

ctimg = sitk.ReadImage(ctpath, imageIO="NiftiImageIO")
ptimg = sitk.ReadImage(ptpath, imageIO="NiftiImageIO")

resampled_ctimg = resample_ct2pt(ctimg, ptimg, sitk.sitkLinear)
sitk.WriteImage(resampled_ctimg,filename+'_ct_new.nii')

```

I have the following two screenshots: The top image is the PET/CT overlapped in the Lifex software before resampling, and the bottom image is after resampling. I have a feeling that they might not be as well aligned as before (because of the new grey region that appears on the right side of the image in the bottom images). How can I ensure they are aligned in the same way after resampling as they were before?

Thank you for your help so far.

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [May 19, 2022, 1:55pm UTC](https://discourse.itk.org/t/resampling-to-volume-to-smaller-size-and-smaller-voxel-spacing/4984/12 "2022-05-19T13:55:21Z")

</div>

Hello @shadab,

1. The grey region is due to the `defaultPixelValue`, which has a default value of 0. When working with CT a more appropriate default value is -1000 (HU value of air).
2. A quick way to check spatial correspondence between images is to use [ITK-SNAP](http://www.itksnap.org/) with its linked cursor. Open two instances of the software with the two images, place the cursor in one of them on an easily identifiable point. If the images are aligned, the cursor in the second instance will move to the corresponding point. This is similiar to the linked cursor approach used in [this notebook](https://github.com/InsightSoftwareConsortium/SimpleITK-Notebooks/blob/6879e6ad035d92b98b8b93aa407262b6675e574c/Python/63_Registration_Initialization.ipynb).
3. When resampling a high resolution CT onto a low resolution grid (PET is usually much lower resolution) you will likely need to blur the CT before resampling, for details see [this notebook](https://github.com/InsightSoftwareConsortium/SimpleITK-Notebooks/blob/master/Python/70_Data_Augmentation.ipynb) section titled **Before we start, a word of caution**.

Finally, no need for a `resample_ct2pt` function as this is a one liner:

```auto
resampled_ctimg = sitk.Resample(sitk.SmoothingRecursiveGaussian(ctimg, sigma), ptimg, defaultPixelValue=-1000)

```
