Transform(3pm) User Contributed Perl Documentation Transform(3pm)

# NAME¶

PDL::Transform - Coordinate transforms, image warping, and N-D functions

# SYNOPSIS¶

use PDL::Transform;

``` my \$t = new PDL::Transform::<type>(<opt>)
\$out = \$t->apply(\$in)  # Apply transform to some N-vectors (Transform method)
\$out = \$in->apply(\$t)  # Apply transform to some N-vectors (PDL method)
\$im1 = \$t->map(\$im);   # Transform image coordinates (Transform method)
\$im1 = \$im->map(\$t);   # Transform image coordinates (PDL method)
\$t2 = \$t->compose(\$t1);  # compose two transforms
\$t2 = \$t x \$t1;          # compose two transforms (by analogy to matrix mult.)
\$t3 = \$t2->inverse();    # invert a transform
\$t3 = !\$t2;              # invert a transform (by analogy to logical "not")
```

# DESCRIPTION¶

PDL::Transform is a convenient way to represent coordinate transformations and resample images. It embodies functions mapping R^N -> R^M, both with and without inverses. Provision exists for parametrizing functions, and for composing them. You can use this part of the Transform object to keep track of arbitrary functions mapping R^N -> R^M with or without inverses.

The simplest way to use a Transform object is to transform vector data between coordinate systems. The "apply" method accepts a PDL whose 0th dimension is coordinate index (all other dimensions are broadcasted over) and transforms the vectors into the new coordinate system.

Transform also includes image resampling, via the "map" method. You define a coordinate transform using a Transform object, then use it to remap an image PDL. The output is a remapped, resampled image.

You can define and compose several transformations, then apply them all at once to an image. The image is interpolated only once, when all the composed transformations are applied.

In keeping with standard practice, but somewhat counterintuitively, the "map" engine uses the inverse transform to map coordinates FROM the destination dataspace (or image plane) TO the source dataspace; hence PDL::Transform keeps track of both the forward and inverse transform.

For terseness and convenience, most of the constructors are exported into the current package with the name "t_<transform>", so the following (for example) are synonyms:

```  \$t = new PDL::Transform::Radial();  # Long way
\$t = t_radial();                    # Short way
```

Several math operators are overloaded, so that you can compose and invert functions with expression syntax instead of method syntax (see below).

# EXAMPLE¶

Coordinate transformations and mappings are a little counterintuitive at first. Here are some examples of transforms in action:

```   use PDL::Transform;
\$x = rfits('m51.fits');   # Substitute path if necessary!
\$ts = t_linear(Scale=>3); # Scaling transform
\$w = pgwin(xs);
\$w->imag(\$x);
## Grow m51 by a factor of 3; origin is at lower left.
\$y = \$ts->map(\$x,{pix=>1});    # pix option uses direct pixel coord system
\$w->imag(\$y);
## Shrink m51 by a factor of 3; origin still at lower left.
\$c = \$ts->unmap(\$x, {pix=>1});
\$w->imag(\$c);
## Grow m51 by a factor of 3; origin is at scientific origin.
\$d = \$ts->map(\$x,\$x->hdr);    # FITS hdr template prevents autoscaling
\$w->imag(\$d);
## Shrink m51 by a factor of 3; origin is still at sci. origin.
\$e = \$ts->unmap(\$x,\$x->hdr);
\$w->imag(\$e);
## A no-op: shrink m51 by a factor of 3, then autoscale back to size
\$f = \$ts->map(\$x);            # No template causes autoscaling of output
```

'!'
The bang is a unary inversion operator. It binds exactly as tightly as the normal bang operator.
'x'
By analogy to matrix multiplication, 'x' is the compose operator, so these two expressions are equivalent:

```  \$f->inverse()->compose(\$g)->compose(\$f) # long way
!\$f x \$g x \$f                           # short way
```

Both of those expressions are equivalent to the mathematical expression f^-1 o g o f, or f^-1(g(f(x))).

'**'
By analogy to numeric powers, you can apply an operator a positive integer number of times with the ** operator:

```  \$f->compose(\$f)->compose(\$f)  # long way
\$f**3                         # short way
```

# INTERNALS¶

Transforms are perl hashes. Here's a list of the meaning of each key:

Ref to a subroutine that evaluates the transformed coordinates. It's called with the input coordinate, and the "params" hash. This springboarding is done via explicit ref rather than by subclassing, for convenience both in coding new transforms (just add the appropriate sub to the module) and in adding custom transforms at run-time. Note that, if possible, new "func"s should support inplace operation to save memory when the data are flagged inplace. But "func" should always return its result even when flagged to compute in-place.

"func" should treat the 0th dimension of its input as a dimensional index (running 0..N-1 for R^N operation) and broadcast over all other input dimensions.

Ref to an inverse method that reverses the transformation. It must accept the same "params" hash that the forward method accepts. This key can be left undefined in cases where there is no inverse.
Number of useful dimensions for indexing on the input and output sides (ie the order of the 0th dimension of the coordinates to be fed in or that come out). If this is set to 0, then as many are allocated as needed.
A shorthand name for the transformation (convenient for debugging). You should plan on using UNIVERAL::isa to identify classes of transformation, e.g. all linear transformations should be subclasses of PDL::Transform::Linear. That makes it easier to add smarts to, e.g., the compose() method.
An array containing the name of the quantity that is expected from the input ndarray for the transform, for each dimension. This field is advisory, and can be left blank if there's no obvious quantity associated with the transform. This is analogous to the CTYPEn field used in FITS headers.
Same as itype, but reporting what quantity is delivered for each dimension.
The units expected on input, if a specific unit (e.g. degrees) is expected. This field is advisory, and can be left blank if there's no obvious unit associated with the transform.
Same as iunit, but reporting what quantity is delivered for each dimension.
Hash ref containing relevant parameters or anything else the func needs to work right.
Bit indicating whether the transform has been inverted. That is useful for some stringifications (see the PDL::Transform::Linear stringifier), and may be useful for other things.

Transforms should be inplace-aware where possible, to prevent excessive memory usage.

If you define a new type of transform, consider generating a new stringify method for it. Just define the sub "stringify" in the subclass package. It should call SUPER::stringify to generate the first line (though the PDL::Transform::Composition bends this rule by tweaking the top-level line), then output (indented) additional lines as necessary to fully describe the transformation.

Transforms have a mechanism for labeling the units and type of each coordinate, but it is just advisory. A routine to identify and, if necessary, modify units by scaling would be a good idea. Currently, it just assumes that the coordinates are correct for (e.g.) FITS scientific-to-pixel transformations.

Composition works OK but should probably be done in a more sophisticated way so that, for example, linear transformations are combined at the matrix level instead of just strung together pixel-to-pixel.

 2022-10-26 perl v5.36.0