Scaling (multiplying by a scalar value) an affine transformation

I’m interested in scaling (multiplying by a scalar) an affine transformation for reasons. My reading thus-far indicates that the mathematical constructions underlying the homogeneous matrix representation for an affine in ITK don’t allow for direct simplistic multiplication.

So I’m wondering
Is there some sort of mathematical process to scale an affine transform?

If this isn’t possible, my next idea would be to decompose the transform and to scale the components separately and reconstruct a new affine afterwards, however, it does not appear that ITK has a generalized function for decomposing affines, is that correct?

Hi Gabriel, in itkAffineTransformation.h, there is a Scale function that might help you. There is a non isotropic scaling version just below.

Is there some sort of mathematical process to scale an affine transform?

Scaling is itself an affine transformation, the matrix has the scaling factor(s) on its diagonal, and the translation is a zero vector. So you can even concatenate your original transform and the scaling transform independently.

Have a look here to see affine transformation in homogenous coordinates.

Hope it helps.

Hi Gabriel, in itkAffineTransformation.h, there is a Scale function that might help you. There is a non isotropic scaling version just below.

I thought I was explicit enough, but I guess not. I’ll try again.

I’m not talking about image scaling. I’m talking about composing the entire transformation with a scalar fraction, i.e. If I multiply but a 0.5 scaling factor, a 3x scale becomes 1.5x, a 30 deg rotation about X becomes at 15 degree rotation etc. (this example may be wrong in details depending on how exactly such a process works, I’m okay with that)

I think the operation you refer to (scaling is a bad choice to describe it) is not possible by any simple mean. What do you want to scale? The angle in a rotation (over which center point?), a translation (in which direction?), a shear (from where?), or any of the multiple other types of transformation possible that the matrix represents. All these operation can’t be “scaled” all together because they don’t share much in common.

I guess decomposing your already known affine transformation (from rotation, translation, shear, whatever) and apply whatever factor you want to all these operations independently in the way you like is the way to go. But I would recommend you give that operation another name, it’s not scaling.

I guess not either, but good luck!

I suspected as much. Am I correct that ITK does not have a canned implementation to do such a decomposition? I can’t seem to find one.

It seems that you want to interpolate between identity transform and some given transform (0% = identity transform, 100% = the target transform, 50% = “halfway between”). This kind of continuous transform interpolation is a well-studied topic.

A commonly used general-purpose method is interpolating the rotation component using SLERP and the translation component using weighted average.

2 Likes

Yes absolutely @lassoan that’s an excellent alternative description of what I’m hoping to achieve!

Do you have any links/references to offer I can read more about these topics?

These VTK classes should help you getting started:

1 Like

Hello @gdevenyi,

If you are interested in the theory of rotation interpolation, a very short description is here, section 4 in the handout, and the original SLERP paper.

1 Like

Thanks to @lassoan, this works as expected:

#!/usr/bin/env python

# https://vtk.org/doc/nightly/html/classvtkTransformInterpolator.html
# https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1Transform.html
# https://vtk.org/doc/nightly/html/classvtkMatrix4x4.html
# https://www.paraview.org/Bug/view.php?id=10102#c37133


import vtk
import SimpleITK as sitk

# Input transform
input_itk_transform = sitk.ReadTransform("trans0_GenericAffine.mat")
output_itk_transform = sitk.AffineTransform(3)
# Dump the matrix 4x3 matrix
input_itk_transform_paramters = input_itk_transform.GetParameters()

# Create VTK input/output transform, and an identity for the interpolation
input_vtk_transform = vtk.vtkTransform()
identity_vtk_transform = vtk.vtkTransform()
output_vtk_transform = vtk.vtkTransform()

# Setup the VTK transform by reconstructing from the ITK matrix parameters
input_vtk_transform.SetMatrix(
    input_itk_transform_paramters[0:3]
    + input_itk_transform_paramters[9:10]
    + input_itk_transform_paramters[3:6]
    + input_itk_transform_paramters[10:11]
    + input_itk_transform_paramters[6:9]
    + input_itk_transform_paramters[11:12]
    + (0, 0, 0, 1)
)

# Create an interpolator
vtk_transform_interpolator = vtk.vtkTransformInterpolator()

# Build an interpolation stack, identity transform and the input transform
vtk_transform_interpolator.AddTransform(0, identity_vtk_transform)
vtk_transform_interpolator.AddTransform(1, input_vtk_transform)

#Generate some steps for testing
for scale in [0.1,0.25,0.5,0.75,1]:
    # Generate a transform a fractional step between identity and the input transform
    vtk_transform_interpolator.InterpolateTransform(scale, output_vtk_transform)


    output_itk_transform.SetParameters(
        (
            output_vtk_transform.GetMatrix().GetElement(0, 0),
            output_vtk_transform.GetMatrix().GetElement(0, 1),
            output_vtk_transform.GetMatrix().GetElement(0, 2),
            output_vtk_transform.GetMatrix().GetElement(1, 0),
            output_vtk_transform.GetMatrix().GetElement(1, 1),
            output_vtk_transform.GetMatrix().GetElement(1, 2),
            output_vtk_transform.GetMatrix().GetElement(2, 0),
            output_vtk_transform.GetMatrix().GetElement(2, 1),
            output_vtk_transform.GetMatrix().GetElement(2, 2),
            output_vtk_transform.GetMatrix().GetElement(0, 3),
            output_vtk_transform.GetMatrix().GetElement(1, 3),
            output_vtk_transform.GetMatrix().GetElement(2, 3)
        )
    )


    sitk.WriteTransform(output_itk_transform, f"output{str(scale)}.mat")
3 Likes