File Coverage

blib/lib/PDL/Transform.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             #
2             # GENERATED WITH PDL::PP from lib/PDL/Transform.pd! Don't modify!
3             #
4             package PDL::Transform;
5              
6             our @EXPORT_OK = qw(apply invert map map unmap t_inverse t_compose t_wrap t_identity t_lookup t_linear t_scale t_offset t_rot t_fits t_code t_cylindrical t_radial t_quadratic t_cubic t_quadratic t_spherical t_projective );
7             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
8              
9 1     1   884 use PDL::Core;
  1         1  
  1         28  
10 1     1   5 use PDL::Exporter;
  1         2  
  1         4  
11 1     1   4 use DynaLoader;
  1         1  
  1         941  
12              
13              
14            
15             our @ISA = ( 'PDL::Exporter','DynaLoader' );
16             push @PDL::Core::PP, __PACKAGE__;
17             bootstrap PDL::Transform ;
18              
19              
20              
21              
22              
23              
24              
25              
26             #line 2 "lib/PDL/Transform.pd"
27              
28             =head1 NAME
29              
30             PDL::Transform - Coordinate transforms, image warping, and N-D functions
31              
32             =head1 SYNOPSIS
33              
34             use PDL::Transform;
35              
36             my $t = PDL::Transform::->new()
37              
38             $out = $t->apply($in) # Apply transform to some N-vectors (Transform method)
39             $out = $in->apply($t) # Apply transform to some N-vectors (PDL method)
40              
41             $im1 = $t->map($im); # Transform image coordinates (Transform method)
42             $im1 = $im->map($t); # Transform image coordinates (PDL method)
43              
44             $t2 = $t->compose($t1); # compose two transforms
45             $t2 = $t x $t1; # compose two transforms (by analogy to matrix mult.)
46              
47             $t3 = $t2->inverse(); # invert a transform
48             $t3 = !$t2; # invert a transform (by analogy to logical "not")
49              
50             =head1 DESCRIPTION
51              
52             PDL::Transform is a convenient way to represent coordinate
53             transformations and resample images. It embodies functions mapping
54             R^N -> R^M, both with and without inverses. Provision exists for
55             parametrizing functions, and for composing them. You can use this
56             part of the Transform object to keep track of arbitrary functions
57             mapping R^N -> R^M with or without inverses.
58              
59             The simplest way to use a Transform object is to transform vector
60             data between coordinate systems. The L method
61             accepts a PDL whose 0th dimension is coordinate index (all other
62             dimensions are broadcasted over) and transforms the vectors into the new
63             coordinate system.
64              
65             Transform also includes image resampling, via the L method.
66             You define a coordinate transform using a Transform object, then use
67             it to remap an image PDL. The output is a remapped, resampled image.
68              
69             You can define and compose several transformations, then apply them
70             all at once to an image. The image is interpolated only once, when
71             all the composed transformations are applied.
72              
73             In keeping with standard practice, but somewhat counterintuitively,
74             the L engine uses the inverse transform to map coordinates
75             FROM the destination dataspace (or image plane) TO the source dataspace;
76             hence PDL::Transform keeps track of both the forward and inverse transform.
77              
78             For terseness and convenience, most of the constructors are exported
79             into the current package with the name C<< t_ >>, so the following
80             (for example) are synonyms:
81              
82             $t = PDL::Transform::Radial->new; # Long way
83              
84             $t = t_radial(); # Short way
85              
86             Several math operators are overloaded, so that you can compose and
87             invert functions with expression syntax instead of method syntax (see below).
88              
89             =head1 EXAMPLE
90              
91             Coordinate transformations and mappings are a little counterintuitive
92             at first. Here are some examples of transforms in action:
93              
94             use PDL::Transform;
95             use PDL::Graphics::Simple;
96             $x = rfits('m51.fits'); # Substitute path if necessary!
97             $ts = t_linear(Scale=>3); # Scaling transform
98              
99             $w = pgswin(xs);
100             $w->plot(with=>'image', $x);
101              
102             ## Grow m51 by a factor of 3; origin is at lower left.
103             $y = $ts->map($x,{pix=>1}); # pix option uses direct pixel coord system
104             $w->plot(with=>'image', $y);
105              
106             ## Shrink m51 by a factor of 3; origin still at lower left.
107             $c = $ts->unmap($x, {pix=>1});
108             $w->plot(with=>'image', $c);
109              
110             ## Grow m51 by a factor of 3; origin is at scientific origin.
111             $d = $ts->map($x,$x->hdr); # FITS hdr template prevents autoscaling
112             $w->plot(with=>'image', $d);
113              
114             ## Shrink m51 by a factor of 3; origin is still at sci. origin.
115             $e = $ts->unmap($x,$x->hdr);
116             $w->plot(with=>'image', $e);
117              
118             ## A no-op: shrink m51 by a factor of 3, then autoscale back to size
119             $f = $ts->map($x); # No template causes autoscaling of output
120              
121             =head1 OPERATOR OVERLOADS
122              
123             =over 3
124              
125             =item '!'
126              
127             The bang is a unary inversion operator. It binds exactly as
128             tightly as the normal bang operator.
129              
130             =item 'x'
131              
132             By analogy to matrix multiplication, 'x' is the compose operator, so these
133             two expressions are equivalent:
134              
135             $f->inverse()->compose($g)->compose($f) # long way
136             !$f x $g x $f # short way
137              
138             Both of those expressions are equivalent to the mathematical expression
139             f^-1 o g o f, or f^-1(g(f(x))).
140              
141             =item '**'
142              
143             By analogy to numeric powers, you can apply an operator a positive
144             integer number of times with the ** operator:
145              
146             $f->compose($f)->compose($f) # long way
147             $f**3 # short way
148              
149             =back
150              
151             =head1 INTERNALS
152              
153             Transforms are perl hashes. Here's a list of the meaning of each key:
154              
155             =over 3
156              
157             =item func
158              
159             Ref to a subroutine that evaluates the transformed coordinates. It's
160             called with the input coordinate, and the "params" hash. This
161             springboarding is done via explicit ref rather than by subclassing,
162             for convenience both in coding new transforms (just add the
163             appropriate sub to the module) and in adding custom transforms at
164             run-time. Note that, if possible, new Cs should support
165             L operation to save memory when the data are flagged
166             inplace. But C should always return its result even when
167             flagged to compute in-place.
168              
169             C should treat the 0th dimension of its input as a dimensional
170             index (running 0..N-1 for R^N operation) and broadcast over all other input
171             dimensions.
172              
173             =item inv
174              
175             Ref to an inverse method that reverses the transformation. It must
176             accept the same "params" hash that the forward method accepts. This
177             key can be left undefined in cases where there is no inverse.
178              
179             =item idim, odim
180              
181             Number of useful dimensions for indexing on the input and output sides
182             (ie the order of the 0th dimension of the coordinates to be fed in or
183             that come out). If this is set to 0, then as many are allocated as needed.
184              
185             =item name
186              
187             A shorthand name for the transformation (convenient for debugging).
188             You should plan on using UNIVERAL::isa to identify classes of
189             transformation, e.g. all linear transformations should be subclasses
190             of PDL::Transform::Linear. That makes it easier to add smarts to,
191             e.g., the compose() method.
192              
193             =item itype
194              
195             An array containing the name of the quantity that is expected from the
196             input ndarray for the transform, for each dimension. This field is advisory,
197             and can be left blank if there's no obvious quantity associated with
198             the transform. This is analogous to the CTYPEn field used in FITS headers.
199              
200             =item oname
201              
202             Same as itype, but reporting what quantity is delivered for each
203             dimension.
204              
205             =item iunit
206              
207             The units expected on input, if a specific unit (e.g. degrees) is expected.
208             This field is advisory, and can be left blank if there's no obvious
209             unit associated with the transform.
210              
211             =item ounit
212              
213             Same as iunit, but reporting what quantity is delivered for each dimension.
214              
215             =item params
216              
217             Hash ref containing relevant parameters or anything else the func needs to
218             work right.
219              
220             =item is_inverse
221              
222             Bit indicating whether the transform has been inverted. That is useful
223             for some stringifications (see the PDL::Transform::Linear
224             stringifier), and may be useful for other things.
225              
226             =back
227              
228             Transforms should be inplace-aware where possible, to prevent excessive
229             memory usage.
230              
231             If you define a new type of transform, consider generating a new stringify
232             method for it. Just define the sub "stringify" in the subclass package.
233             It should call SUPER::stringify to generate the first line (though
234             the PDL::Transform::Composition bends this rule by tweaking the
235             top-level line), then output (indented) additional lines as necessary to
236             fully describe the transformation.
237              
238             =head1 NOTES
239              
240             Transforms have a mechanism for labeling the units and type of each
241             coordinate, but it is just advisory. A routine to identify and, if
242             necessary, modify units by scaling would be a good idea. Currently,
243             it just assumes that the coordinates are correct for (e.g.) FITS
244             scientific-to-pixel transformations.
245              
246             Composition works OK but should probably be done in a more
247             sophisticated way so that, for example, linear transformations are
248             combined at the matrix level instead of just strung together
249             pixel-to-pixel.
250              
251             =head1 MODULE INTERFACE
252              
253             There are both operators and constructors. The constructors are all
254             exported, all begin with "t_", and all return objects that are subclasses
255             of PDL::Transform.
256              
257             The L, L, L,
258             and L methods are also exported to the C package: they
259             are both Transform methods and PDL methods.
260              
261             =cut
262              
263             use strict;
264             use warnings;
265             #line 266 "lib/PDL/Transform.pm"
266              
267              
268             =head1 FUNCTIONS
269              
270             =cut
271              
272              
273              
274              
275              
276             #line 315 "lib/PDL/Transform.pd"
277              
278             =head2 apply
279              
280             =for sig
281              
282             Signature: (data(); PDL::Transform t)
283              
284             =for usage
285              
286             $out = $data->apply($t);
287             $out = $t->apply($data);
288              
289             =for ref
290              
291             Apply a transformation to some input coordinates.
292              
293             In the example, C<$t> is a PDL::Transform and C<$data> is a PDL to
294             be interpreted as a collection of N-vectors (with index in the 0th
295             dimension). The output is a similar but transformed PDL.
296              
297             For convenience, this is both a PDL method and a Transform method.
298              
299             =cut
300              
301             use Carp;
302              
303             *PDL::apply = \&apply;
304             sub apply {
305             my ($me, $from) = UNIVERSAL::isa($_[0],'PDL') ? @_[1,0] : @_;
306             croak "apply requires both a PDL and a PDL::Transform.\n"
307             unless UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($from,'PDL');
308             croak "Applying a PDL::Transform with no func! Oops.\n"
309             unless(defined($me->{func}) and ref($me->{func}) eq 'CODE');
310             my $result = $me->{func}->($from,$me->{params});
311             $result->is_inplace(0); # clear inplace flag, just in case.
312             if (defined $from->gethdr && $from->hdr->{NAXIS}) {
313             my $nd = $me->{odim} || $me->{idim} || 2;
314             for my $d (1..$nd) {
315             $result->hdr->{"CTYPE$d"} = ( (!defined($me->{otype}) ? "" :
316             ref($me->{otype}) ne 'ARRAY' ? $me->{otype} :
317             $me->{otype}[$d-1])
318             || $from->hdr->{"CTYPE$d"}
319             || "");
320             $result->hdr->{"CUNIT$d"} = ( (!defined($me->{ounit}) ? "" :
321             ref($me->{ounit}) ne 'ARRAY' ? $me->{ounit} :
322             $me->{ounit}[$d-1])
323             || $from->hdr->{"CUNIT$d"}
324             || $from->hdr->{"CTYPE$d"}
325             || "");
326             }
327             }
328             return $result;
329             }
330              
331             #line 373 "lib/PDL/Transform.pd"
332              
333             =head2 invert
334              
335             =for sig
336              
337             Signature: (data(); PDL::Transform t)
338              
339             =for usage
340              
341             $out = $t->invert($data);
342             $out = $data->invert($t);
343              
344             =for ref
345              
346             Apply an inverse transformation to some input coordinates.
347              
348             In the example, C<$t> is a PDL::Transform and C<$data> is an ndarray to
349             be interpreted as a collection of N-vectors (with index in the 0th
350             dimension). The output is a similar ndarray.
351              
352             For convenience this is both a PDL method and a PDL::Transform method.
353              
354             =cut
355              
356             *PDL::invert = \&invert;
357             sub invert {
358             my ($me, $from) = UNIVERSAL::isa($_[0],'PDL') ? @_[1,0] : @_;
359             croak "invert requires a PDL and a PDL::Transform (did you want 'inverse' instead?)\n"
360             unless UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($from,'PDL');
361             croak "Inverting a PDL::Transform with no inverse! Oops.\n"
362             unless(defined($me->{inv}) and ref($me->{inv}) eq 'CODE');
363             my $result = $me->{inv}->($from, $me->{params});
364             $result->is_inplace(0); # make sure inplace flag is clear.
365             return $result;
366             }
367             #line 368 "lib/PDL/Transform.pm"
368              
369              
370             =head2 map
371              
372             =for sig
373              
374             Signature: (k0(); pdl *in; pdl *out; pdl *map; SV *boundary; SV *method;
375             long big; double blur; double sv_min; char flux; SV *bv)
376             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
377             float double ldouble)
378              
379             =head2 match
380              
381             =for usage
382              
383             $y = $x->match($c); # Match $c's header and size
384             $y = $x->match([100,200]); # Rescale to 100x200 pixels
385             $y = $x->match([100,200],{rect=>1}); # Rescale and remove rotation/skew.
386              
387             =for ref
388              
389             Resample a scientific image to the same coordinate system as another.
390              
391             The example above is syntactic sugar for
392              
393             $y = $x->map(t_identity, $c, ...);
394              
395             it resamples the input PDL with the identity transformation in
396             scientific coordinates, and matches the pixel coordinate system to
397             $c's FITS header.
398              
399             There is one difference between match and map: match makes the
400             C option to C default to 0, not 1. This only affects
401             matching where autoscaling is required (i.e. the array ref example
402             above). By default, that example simply scales $x to the new size and
403             maintains any rotation or skew in its scientific-to-pixel coordinate
404             transform.
405              
406             =head2 map
407              
408             =for usage
409              
410             $y = $x->map($xform,[