Replies: 10 comments 14 replies
-
Hi, @kleingeo. I am modifying the In [1]: import torchio as tio
In [2]: colin = tio.datasets.Colin27()
In [3]: transform = tio.RandomAffine()
In [4]: transformed = transform(colin)
In [5]: sitk_transform = transformed.history[0].get_affine_transform(transformed.t1)
In [6]: sitk_transform
Out[6]: <SimpleITK.SimpleITK.CompositeTransform; proxy of <Swig Object of type 'itk::simple::CompositeTransform *' at 0x7fe2313bed20> > This should be available in |
Beta Was this translation helpful? Give feedback.
-
Hi
not sure to understand here: |
Beta Was this translation helpful? Give feedback.
-
Ok, I missed the landmarks objectiv, ... although what is it exactly; coordinate in voxel space or in mm space ? But I still do not understand why you need an inversion here, what you want is to apply to your landmak the same affine as the but I just see fernando has the same question ... |
Beta Was this translation helpful? Give feedback.
-
to get any landmark from mm to voxel space, the only thing you need, is the affine from the voxel space of the transformed image. (and it's inverse indeed) The alternative way to do, would be to add a torchio type 'landmarks' and let the torchio transforms take care of it ... kind of related to the surface issue ... (#537) |
Beta Was this translation helpful? Give feedback.
-
Right now, with SimpleITK, the way it is working for me is as follows. Right now I take the inverse of the transform. They way I understand it (and correct me if I am wrong) is: T x index = coord -> where T is the transform, index is in voxel space and coord is in physical space compos = sitk.CompositeTransform([trans, rot, scale, translate_output_center])
resample_filter = sitk.ResampleImageFilter()
resample_filter.SetSize(size)
resample_filter.SetInterpolator(sitk.sitkLinear)
resample_filter.SetOutputSpacing(output_spacing)
resample_filter.SetOutputOrigin(image.GetOrigin())
resample_filter.SetOutputDirection(image.GetDirection())
resample_filter.SetTransform(composite_transform)
output_image = resample_filter.Execute(image)
# landmark is a list of coodinates in physical space (ie, [x, y, z] in mm)
new_landmark = output_image.TransformPhysicalPointToContinuousIndex(composite_transform.GetInverse().TransformPoint(landmark)) |
Beta Was this translation helpful? Give feedback.
-
yes the index to coord is correct, In [5]: sitk_transform = transformed.history[0].get_affine_transform(transformed.t1)
In [6]: new_landmark_mm = sitk_transform(landmark) does it do the same thing ? or may be you need to transform back to voxel with something like In [7]: new_landmark_vox = np.linalg.inv(transformed.t1.affine) * new_landmark |
Beta Was this translation helpful? Give feedback.
-
Hi To get a reproducible example we can test, we need to use the same subject and the same landmark import torchio as tio
import numpy as np
import torch
torch.manual_seed(1) #so that we get the same random transform
subject = tio.datasets.Colin27()
# Random affine transform (without translations)
aff_trans = tio.RandomAffine(
scales=0.15,
degrees=0.25 * 180 / np.pi,
translation=0,
center='origin',
default_pad_value='minimum')
subject_trans = aff_trans(subject)
sitk_transform = subject_trans.history[0].get_affine_transform(subject_trans['t1'])
center_landmark_vox = [90,108,90,1]
center_landmark_mm = np.matmul(subject['t1'].affine, center_landmark_vox)[:3]
transformed_landmark_mm = sitk_transform.TransformPoint(center_landmark_mm)
In [72]: transformed_landmark_mm
Out[72]: (2.1402958874783193, -13.87748607416791, 20.090379359295813)
transformed_landmark_vox = np.matmul( np.linalg.inv(subject['t1'].affine), list(transformed_landmark_mm) +[1] ) This is look almost fine. I visually test different point, and I do see in the transformed imaged (at the predicted position by transformed_landmark_vox (or mm). |
Beta Was this translation helpful? Give feedback.
-
yes apply_affine may be easier to use, but I thing it is important to understand what need to be done @kleingeo when you write :
this is kind of strange, and the multiplication with np.array([-1, -1, 1]) should not be needeed ! Not sure why you need it, but it may be related to negativ number in the diagonal of your image's affine, ... to investigate and make things easier, you can reoder the data in order to have only positive number on the affine |
Beta Was this translation helpful? Give feedback.
-
I also posted in the SimpleITK forum as to you need the Basically, the transform points from the output to the input (not sure why...) which is why you need the inverse of the transform to go from input to out. https://discourse.itk.org/t/transform-point-from-affine-transform/4429 |
Beta Was this translation helpful? Give feedback.
-
Ok, I'll have to check again if the GetInverse make more sense For the affine, yes there is a signs difference, As explained #661 (reply in thread). This is expected, as the internal representation is LPS for itk and RAS for nibabel (and torchio) In torchio, you can see that the function I think that is fine, (and that answer to my question #661 (reply in thread)): torchio internal data representation is RAS, (and the affine is set accordingly) So to resume _ Now about applying the sitk transform you get from history, it should be fine as it apply in the mm space |
Beta Was this translation helpful? Give feedback.
-
I am wondering about transforming coordinates. If I have an image and associate coordinate (x, y, z) and I apply a transform to image (say resampling and some affine augmentation), is there a way to transform the coordinate into the new space?
Beta Was this translation helpful? Give feedback.
All reactions