.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Transform 3pm" .TH Transform 3pm "2023-06-17" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" PDL::Transform \- Coordinate transforms, image warping, and N\-D functions .SH "SYNOPSIS" .IX Header "SYNOPSIS" use PDL::Transform; .PP .Vb 1 \& my $t = PDL::Transform::\->new() \& \& $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") .Ve .SH "DESCRIPTION" .IX Header "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. .PP The simplest way to use a Transform object is to transform vector data between coordinate systems. The \*(L"apply\*(R" method accepts a \s-1PDL\s0 whose 0th dimension is coordinate index (all other dimensions are broadcasted over) and transforms the vectors into the new coordinate system. .PP Transform also includes image resampling, via the \*(L"map\*(R" method. You define a coordinate transform using a Transform object, then use it to remap an image \s-1PDL.\s0 The output is a remapped, resampled image. .PP 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. .PP In keeping with standard practice, but somewhat counterintuitively, the \*(L"map\*(R" engine uses the inverse transform to map coordinates \&\s-1FROM\s0 the destination dataspace (or image plane) \s-1TO\s0 the source dataspace; hence PDL::Transform keeps track of both the forward and inverse transform. .PP For terseness and convenience, most of the constructors are exported into the current package with the name \f(CW\*(C`t_\*(C'\fR, so the following (for example) are synonyms: .PP .Vb 1 \& $t = PDL::Transform::Radial\->new; # Long way \& \& $t = t_radial(); # Short way .Ve .PP Several math operators are overloaded, so that you can compose and invert functions with expression syntax instead of method syntax (see below). .SH "EXAMPLE" .IX Header "EXAMPLE" Coordinate transformations and mappings are a little counterintuitive at first. Here are some examples of transforms in action: .PP .Vb 3 \& use PDL::Transform; \& $x = rfits(\*(Aqm51.fits\*(Aq); # 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 .Ve .SH "OPERATOR OVERLOADS" .IX Header "OPERATOR OVERLOADS" .IP "'!'" 3 The bang is a unary inversion operator. It binds exactly as tightly as the normal bang operator. .IP "'x'" 3 .IX Item "'x'" By analogy to matrix multiplication, 'x' is the compose operator, so these two expressions are equivalent: .Sp .Vb 2 \& $f\->inverse()\->compose($g)\->compose($f) # long way \& !$f x $g x $f # short way .Ve .Sp Both of those expressions are equivalent to the mathematical expression f^\-1 o g o f, or f^\-1(g(f(x))). .IP "'**'" 3 By analogy to numeric powers, you can apply an operator a positive integer number of times with the ** operator: .Sp .Vb 2 \& $f\->compose($f)\->compose($f) # long way \& $f**3 # short way .Ve .SH "INTERNALS" .IX Header "INTERNALS" Transforms are perl hashes. Here's a list of the meaning of each key: .IP "func" 3 .IX Item "func" Ref to a subroutine that evaluates the transformed coordinates. It's called with the input coordinate, and the \*(L"params\*(R" 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 \f(CW\*(C`func\*(C'\fRs should support inplace operation to save memory when the data are flagged inplace. But \f(CW\*(C`func\*(C'\fR should always return its result even when flagged to compute in-place. .Sp \&\f(CW\*(C`func\*(C'\fR 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. .IP "inv" 3 .IX Item "inv" Ref to an inverse method that reverses the transformation. It must accept the same \*(L"params\*(R" hash that the forward method accepts. This key can be left undefined in cases where there is no inverse. .IP "idim, odim" 3 .IX Item "idim, odim" 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. .IP "name" 3 .IX Item "name" 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 \fBcompose()\fR method. .IP "itype" 3 .IX Item "itype" 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 \s-1FITS\s0 headers. .IP "oname" 3 .IX Item "oname" Same as itype, but reporting what quantity is delivered for each dimension. .IP "iunit" 3 .IX Item "iunit" 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. .IP "ounit" 3 .IX Item "ounit" Same as iunit, but reporting what quantity is delivered for each dimension. .IP "params" 3 .IX Item "params" Hash ref containing relevant parameters or anything else the func needs to work right. .IP "is_inverse" 3 .IX Item "is_inverse" 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. .PP Transforms should be inplace-aware where possible, to prevent excessive memory usage. .PP If you define a new type of transform, consider generating a new stringify method for it. Just define the sub \*(L"stringify\*(R" 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. .SH "NOTES" .IX Header "NOTES" 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.) \s-1FITS\s0 scientific-to-pixel transformations. .PP Composition works \s-1OK\s0 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. .SH "MODULE INTERFACE" .IX Header "MODULE INTERFACE" There are both operators and constructors. The constructors are all exported, all begin with \*(L"t_\*(R", and all return objects that are subclasses of PDL::Transform. .PP The \*(L"apply\*(R", \*(L"invert\*(R", \*(L"map\*(R", and \*(L"unmap\*(R" methods are also exported to the \f(CW\*(C`PDL\*(C'\fR package: they are both Transform methods and \s-1PDL\s0 methods. .SH "FUNCTIONS" .IX Header "FUNCTIONS" .SS "apply" .IX Subsection "apply" .Vb 1 \& Signature: (data(); PDL::Transform t) .Ve .PP .Vb 2 \& $out = $data\->apply($t); \& $out = $t\->apply($data); .Ve .PP Apply a transformation to some input coordinates. .PP In the example, \f(CW$t\fR is a PDL::Transform and \f(CW$data\fR is a \s-1PDL\s0 to be interpreted as a collection of N\-vectors (with index in the 0th dimension). The output is a similar but transformed \s-1PDL.\s0 .PP For convenience, this is both a \s-1PDL\s0 method and a Transform method. .SS "invert" .IX Subsection "invert" .Vb 1 \& Signature: (data(); PDL::Transform t) .Ve .PP .Vb 2 \& $out = $t\->invert($data); \& $out = $data\->invert($t); .Ve .PP Apply an inverse transformation to some input coordinates. .PP In the example, \f(CW$t\fR is a PDL::Transform and \f(CW$data\fR is an ndarray to be interpreted as a collection of N\-vectors (with index in the 0th dimension). The output is a similar ndarray. .PP For convenience this is both a \s-1PDL\s0 method and a PDL::Transform method. .SS "map" .IX Subsection "map" .Vb 2 \& Signature: (k0(); pdl *in; pdl *out; pdl *map; SV *boundary; SV *method; \& long big; double blur; double sv_min; char flux; SV *bv) .Ve .SS "match" .IX Subsection "match" .Vb 3 \& $y = $x\->match($c); # Match $c\*(Aqs header and size \& $y = $x\->match([100,200]); # Rescale to 100x200 pixels \& $y = $x\->match([100,200],{rect=>1}); # Rescale and remove rotation/skew. .Ve .PP Resample a scientific image to the same coordinate system as another. .PP The example above is syntactic sugar for .PP .Vb 1 \& $y = $x\->map(t_identity, $c, ...); .Ve .PP it resamples the input \s-1PDL\s0 with the identity transformation in scientific coordinates, and matches the pixel coordinate system to \&\f(CW$c\fR's \s-1FITS\s0 header. .PP There is one difference between match and map: match makes the \&\f(CW\*(C`rectify\*(C'\fR option to \f(CW\*(C`map\*(C'\fR default to 0, not 1. This only affects matching where autoscaling is required (i.e. the array ref example above). By default, that example simply scales \f(CW$x\fR to the new size and maintains any rotation or skew in its scientific-to-pixel coordinate transform. .SS "map" .IX Subsection "map" .Vb 2 \& $y = $x\->map($xform,[